summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Ameer J2023-11-26 21:08:53 -0500
committerGravatar GitHub2023-11-26 21:08:53 -0500
commit1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a (patch)
treec219aacab776c0a1e3956614b60a01fa2f6164cb /src
parentshader_recompiler: Align SSBO offsets in GlobalMemory functions (diff)
parentMerge pull request #11535 from GPUCode/upload_cmdbuf (diff)
downloadyuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.tar.gz
yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.tar.xz
yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.zip
Merge branch 'master' into ssbo-align
Diffstat (limited to '')
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/android/app/build.gradle.kts5
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt61
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt92
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt97
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt40
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt24
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt12
-rw-r--r--src/android/app/src/main/jni/android_config.cpp70
-rw-r--r--src/android/app/src/main/jni/android_config.h41
-rw-r--r--src/android/app/src/main/jni/android_settings.cpp (renamed from src/android/app/src/main/jni/uisettings.cpp)2
-rw-r--r--src/android/app/src/main/jni/android_settings.h (renamed from src/android/app/src/main/jni/uisettings.h)2
-rw-r--r--src/android/app/src/main/jni/config.cpp330
-rw-r--r--src/android/app/src/main/jni/config.h47
-rw-r--r--src/android/app/src/main/jni/default_ini.h511
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.cpp8
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.h4
-rw-r--r--src/android/app/src/main/jni/native.cpp48
-rw-r--r--src/android/app/src/main/jni/native.h8
-rw-r--r--src/android/app/src/main/jni/native_config.cpp23
-rw-r--r--src/android/app/src/main/jni/native_log.cpp31
-rw-r--r--src/android/app/src/main/res/drawable/ic_audio.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_code.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_graphics.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_system_settings.xml9
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_about.xml233
-rw-r--r--src/android/app/src/main/res/layout/card_home_option.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_about.xml10
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml11
-rw-r--r--src/android/app/src/main/res/layout/fragment_home_settings.xml9
-rw-r--r--src/android/app/src/main/res/layout/fragment_search.xml1
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting.xml72
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml8
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml3
-rw-r--r--src/android/app/src/main/res/resources.properties1
-rw-r--r--src/android/app/src/main/res/values-ar/strings.xml385
-rw-r--r--src/android/app/src/main/res/values-ckb/strings.xml336
-rw-r--r--src/android/app/src/main/res/values-de/strings.xml119
-rw-r--r--src/android/app/src/main/res/values-es/strings.xml180
-rw-r--r--src/android/app/src/main/res/values-fr/strings.xml180
-rw-r--r--src/android/app/src/main/res/values-he/strings.xml367
-rw-r--r--src/android/app/src/main/res/values-hu/strings.xml402
-rw-r--r--src/android/app/src/main/res/values-it/strings.xml192
-rw-r--r--src/android/app/src/main/res/values-ja/strings.xml218
-rw-r--r--src/android/app/src/main/res/values-ko/strings.xml273
-rw-r--r--src/android/app/src/main/res/values-nb/strings.xml113
-rw-r--r--src/android/app/src/main/res/values-pl/strings.xml81
-rw-r--r--src/android/app/src/main/res/values-pt-rBR/strings.xml210
-rw-r--r--src/android/app/src/main/res/values-pt-rPT/strings.xml192
-rw-r--r--src/android/app/src/main/res/values-ru/strings.xml159
-rw-r--r--src/android/app/src/main/res/values-uk/strings.xml90
-rw-r--r--src/android/app/src/main/res/values-vi/strings.xml340
-rw-r--r--src/android/app/src/main/res/values-zh-rCN/strings.xml138
-rw-r--r--src/android/app/src/main/res/values-zh-rTW/strings.xml137
-rw-r--r--src/android/app/src/main/res/values/arrays.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml9
-rw-r--r--src/android/app/src/main/res/xml/locales_config.xml17
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.cpp214
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.cpp4
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp222
-rw-r--r--src/audio_core/opus/decoder.cpp358
-rw-r--r--src/audio_core/opus/decoder.h106
-rw-r--r--src/audio_core/opus/decoder_manager.cpp204
-rw-r--r--src/audio_core/opus/decoder_manager.h76
-rw-r--r--src/audio_core/opus/hardware_opus.cpp482
-rw-r--r--src/audio_core/opus/hardware_opus.h90
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp2
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp2
-rw-r--r--src/audio_core/sink/sink_stream.cpp12
-rw-r--r--src/audio_core/sink/sink_stream.h6
-rw-r--r--src/common/arm64/native_clock.cpp21
-rw-r--r--src/common/page_table.cpp30
-rw-r--r--src/common/page_table.h17
-rw-r--r--src/common/settings.cpp6
-rw-r--r--src/common/settings.h46
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_input.cpp9
-rw-r--r--src/common/settings_input.h7
-rw-r--r--src/core/CMakeLists.txt26
-rw-r--r--src/core/arm/arm_interface.cpp16
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/debugger/gdbstub.cpp233
-rw-r--r--src/core/file_sys/romfs.cpp44
-rw-r--r--src/core/file_sys/romfs.h9
-rw-r--r--src/core/hid/emulated_console.h8
-rw-r--r--src/core/hid/emulated_controller.cpp144
-rw-r--r--src/core/hid/emulated_controller.h4
-rw-r--r--src/core/hid/hid_core.cpp5
-rw-r--r--src/core/hid/hid_types.h110
-rw-r--r--src/core/hid/input_interpreter.cpp11
-rw-r--r--src/core/hid/input_interpreter.h4
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp13
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.h7
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp39
-rw-r--r--src/core/hle/kernel/k_capabilities.h17
-rw-r--r--src/core/hle/kernel/k_device_address_space.cpp4
-rw-r--r--src/core/hle/kernel/k_device_address_space.h10
-rw-r--r--src/core/hle/kernel/k_memory_layout.h8
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp12
-rw-r--r--src/core/hle/kernel/k_page_table.cpp3519
-rw-r--r--src/core/hle/kernel/k_page_table.h542
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp5716
-rw-r--r--src/core/hle/kernel/k_page_table_base.h759
-rw-r--r--src/core/hle/kernel/k_process.cpp18
-rw-r--r--src/core/hle/kernel/k_process.h14
-rw-r--r--src/core/hle/kernel/k_process_page_table.h480
-rw-r--r--src/core/hle/kernel/k_server_session.cpp2
-rw-r--r--src/core/hle/kernel/k_system_resource.cpp2
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp4
-rw-r--r--src/core/hle/kernel/process_capability.cpp389
-rw-r--r--src/core/hle/kernel/process_capability.h266
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp9
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp3
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp8
-rw-r--r--src/core/hle/result.h31
-rw-r--r--src/core/hle/service/acc/acc.cpp56
-rw-r--r--src/core/hle/service/am/am.cpp91
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp3
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp3
-rw-r--r--src/core/hle/service/am/applets/applets.h24
-rw-r--r--src/core/hle/service/btm/btm.cpp56
-rw-r--r--src/core/hle/service/friend/friend.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h43
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h6
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp54
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h6
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h6
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp588
-rw-r--r--src/core/hle/service/hid/controllers/npad.h205
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp88
-rw-r--r--src/core/hle/service/hid/controllers/palma.h8
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.cpp (renamed from src/core/hle/service/hid/controllers/console_sixaxis.cpp)38
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.h (renamed from src/core/hle/service/hid/controllers/console_sixaxis.h)31
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp413
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.h111
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h6
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h6
-rw-r--r--src/core/hle/service/hid/hid.cpp2861
-rw-r--r--src/core/hle/service/hid/hid.h212
-rw-r--r--src/core/hle/service/hid/hid_debug_server.cpp159
-rw-r--r--src/core/hle/service/hid/hid_debug_server.h26
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.cpp99
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.h54
-rw-r--r--src/core/hle/service/hid/hid_server.cpp2371
-rw-r--r--src/core/hle/service/hid/hid_server.h149
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp539
-rw-r--r--src/core/hle/service/hid/hid_system_server.h63
-rw-r--r--src/core/hle/service/hid/hid_util.h146
-rw-r--r--src/core/hle/service/hid/irs.cpp7
-rw-r--r--src/core/hle/service/hid/irs.h5
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp16
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h9
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp2
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp123
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h34
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp241
-rw-r--r--src/core/hle/service/hid/resource_manager.h111
-rw-r--r--src/core/hle/service/hid/ring_lifo.h6
-rw-r--r--src/core/hle/service/ldn/ldn.cpp10
-rw-r--r--src/core/hle/service/ldr/ldr.cpp45
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/ioctl_serialization.h159
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp82
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp115
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h29
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp117
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h35
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp87
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp47
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h12
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp27
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.h9
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.cpp12
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp19
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_slot.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_transform_flags.h2
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.cpp20
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.h2
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp29
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp22
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h2
-rw-r--r--src/core/hle/service/nvnflinger/status.h2
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp34
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.h25
-rw-r--r--src/core/hle/service/set/set_sys.cpp96
-rw-r--r--src/core/hle/service/set/set_sys.h36
-rw-r--r--src/core/hle/service/sockets/bsd.cpp77
-rw-r--r--src/core/hle/service/sockets/bsd.h2
-rw-r--r--src/core/hle/service/time/clock_types.h5
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp2
-rw-r--r--src/core/memory.cpp31
-rw-r--r--src/core/memory/cheat_engine.cpp12
-rw-r--r--src/frontend_common/CMakeLists.txt10
-rw-r--r--src/frontend_common/config.cpp1008
-rw-r--r--src/frontend_common/config.h211
-rw-r--r--src/input_common/drivers/gc_adapter.cpp8
-rw-r--r--src/input_common/drivers/joycon.cpp8
-rw-r--r--src/input_common/drivers/sdl_driver.cpp20
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/udp_client.cpp8
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp6
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp6
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp56
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h2
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h2
-rw-r--r--src/shader_recompiler/frontend/ir/modifiers.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp29
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp1
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp10
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp79
-rw-r--r--src/shader_recompiler/profile.h1
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h102
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h4
-rw-r--r--src/video_core/buffer_cache/usage_tracker.h79
-rw-r--r--src/video_core/engines/fermi_2d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/fence_manager.h5
-rw-r--r--src/video_core/host1x/codecs/codec.cpp329
-rw-r--r--src/video_core/host1x/codecs/codec.h39
-rw-r--r--src/video_core/host1x/codecs/h264.cpp4
-rw-r--r--src/video_core/host1x/codecs/h264.h1
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp419
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.h213
-rw-r--r--src/video_core/host1x/nvdec.cpp2
-rw-r--r--src/video_core/host1x/nvdec.h2
-rw-r--r--src/video_core/host1x/vic.cpp62
-rw-r--r--src/video_core/host1x/vic.h4
-rw-r--r--src/video_core/query_cache/query_cache.h2
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h17
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp135
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp36
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h21
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp29
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h21
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h1
-rw-r--r--src/video_core/texture_cache/slot_vector.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp9
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h11
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h4
-rw-r--r--src/yuzu/CMakeLists.txt57
-rw-r--r--src/yuzu/applets/qt_controller.cpp1
-rw-r--r--src/yuzu/configuration/config.cpp1306
-rw-r--r--src/yuzu/configuration/config.h179
-rw-r--r--src/yuzu/configuration/configure_audio.cpp10
-rw-r--r--src/yuzu/configuration/configure_camera.cpp2
-rw-r--r--src/yuzu/configuration/configure_camera.h2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui86
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp36
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input.h2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp8
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h8
-rw-r--r--src/yuzu/configuration/configure_input_per_game.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_per_game.h5
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp47
-rw-r--r--src/yuzu/configuration/configure_input_player.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.ui389
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp45
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp7
-rw-r--r--src/yuzu/configuration/configure_per_game.h7
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp1
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp6
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp4
-rw-r--r--src/yuzu/configuration/configure_ringcon.h2
-rw-r--r--src/yuzu/configuration/configure_system.cpp1
-rw-r--r--src/yuzu/configuration/configure_tas.h2
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.cpp2
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_ui.cpp7
-rw-r--r--src/yuzu/configuration/input_profiles.cpp10
-rw-r--r--src/yuzu/configuration/input_profiles.h4
-rw-r--r--src/yuzu/configuration/qt_config.cpp549
-rw-r--r--src/yuzu/configuration/qt_config.h55
-rw-r--r--src/yuzu/configuration/shared_translation.cpp518
-rw-r--r--src/yuzu/configuration/shared_translation.h43
-rw-r--r--src/yuzu/configuration/shared_widget.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.cpp6
-rw-r--r--src/yuzu/game_list.cpp13
-rw-r--r--src/yuzu/game_list_p.h8
-rw-r--r--src/yuzu/game_list_worker.cpp18
-rw-r--r--src/yuzu/hotkeys.cpp28
-rw-r--r--src/yuzu/hotkeys.h16
-rw-r--r--src/yuzu/main.cpp659
-rw-r--r--src/yuzu/main.h31
-rw-r--r--src/yuzu/main.ui22
-rw-r--r--src/yuzu/uisettings.cpp65
-rw-r--r--src/yuzu/uisettings.h93
-rw-r--r--src/yuzu/util/util.cpp15
-rw-r--r--src/yuzu/util/util.h3
-rw-r--r--src/yuzu/vk_device_info.cpp1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt9
-rw-r--r--src/yuzu_cmd/config.cpp279
-rw-r--r--src/yuzu_cmd/config.h38
-rw-r--r--src/yuzu_cmd/default_ini.h553
-rw-r--r--src/yuzu_cmd/sdl_config.cpp257
-rw-r--r--src/yuzu_cmd/sdl_config.h49
-rw-r--r--src/yuzu_cmd/yuzu.cpp5
370 files changed, 23771 insertions, 16371 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d7f68618c..e04d2418b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -21,7 +21,7 @@ if (MSVC)
21 # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. 21 # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
22 add_definitions(-DWIN32_LEAN_AND_MEAN) 22 add_definitions(-DWIN32_LEAN_AND_MEAN)
23 23
24 # Ensure that projects build with Unicode support. 24 # Ensure that projects are built with Unicode support.
25 add_definitions(-DUNICODE -D_UNICODE) 25 add_definitions(-DUNICODE -D_UNICODE)
26 26
27 # /W4 - Level 4 warnings 27 # /W4 - Level 4 warnings
@@ -54,11 +54,11 @@ if (MSVC)
54 /GT 54 /GT
55 55
56 # Modules 56 # Modules
57 /experimental:module- # Disable module support explicitly due to conflicts with precompiled headers 57 /experimental:module- # Explicitly disable module support due to conflicts with precompiled headers.
58 58
59 # External headers diagnostics 59 # External headers diagnostics
60 /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers 60 /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
61 /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers 61 /external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them.
62 62
63 # Warnings 63 # Warnings
64 /W4 64 /W4
@@ -187,6 +187,7 @@ add_subdirectory(audio_core)
187add_subdirectory(video_core) 187add_subdirectory(video_core)
188add_subdirectory(network) 188add_subdirectory(network)
189add_subdirectory(input_common) 189add_subdirectory(input_common)
190add_subdirectory(frontend_common)
190add_subdirectory(shader_recompiler) 191add_subdirectory(shader_recompiler)
191 192
192if (YUZU_ROOM) 193if (YUZU_ROOM)
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index ac43d84b7..5721327e7 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -47,6 +47,10 @@ android {
47 jniLibs.useLegacyPackaging = true 47 jniLibs.useLegacyPackaging = true
48 } 48 }
49 49
50 androidResources {
51 generateLocaleConfig = true
52 }
53
50 defaultConfig { 54 defaultConfig {
51 // TODO If this is ever modified, change application_id in strings.xml 55 // TODO If this is ever modified, change application_id in strings.xml
52 applicationId = "org.yuzu.yuzu_emu" 56 applicationId = "org.yuzu.yuzu_emu"
@@ -215,7 +219,6 @@ dependencies {
215 implementation("io.coil-kt:coil:2.2.2") 219 implementation("io.coil-kt:coil:2.2.2")
216 implementation("androidx.core:core-splashscreen:1.0.1") 220 implementation("androidx.core:core-splashscreen:1.0.1")
217 implementation("androidx.window:window:1.2.0-beta03") 221 implementation("androidx.window:window:1.2.0-beta03")
218 implementation("org.ini4j:ini4j:0.5.4")
219 implementation("androidx.constraintlayout:constraintlayout:2.1.4") 222 implementation("androidx.constraintlayout:constraintlayout:2.1.4")
220 implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") 223 implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
221 implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") 224 implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index a67351727..f10131b24 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -26,7 +26,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
26 android:supportsRtl="true" 26 android:supportsRtl="true"
27 android:isGame="true" 27 android:isGame="true"
28 android:appCategory="game" 28 android:appCategory="game"
29 android:localeConfig="@xml/locales_config"
30 android:banner="@drawable/tv_banner" 29 android:banner="@drawable/tv_banner"
31 android:fullBackupContent="@xml/data_extraction_rules" 30 android:fullBackupContent="@xml/data_extraction_rules"
32 android:dataExtractionRules="@xml/data_extraction_rules_api_31" 31 android:dataExtractionRules="@xml/data_extraction_rules_api_31"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 07f1b4842..f2ba2504c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -230,8 +230,6 @@ object NativeLibrary {
230 */ 230 */
231 external fun onTouchReleased(finger_id: Int) 231 external fun onTouchReleased(finger_id: Int)
232 232
233 external fun reloadSettings()
234
235 external fun initGameIni(gameID: String?) 233 external fun initGameIni(gameID: String?)
236 234
237 external fun setAppDirectory(directory: String) 235 external fun setAppDirectory(directory: String)
@@ -252,7 +250,7 @@ object NativeLibrary {
252 250
253 external fun reloadKeys(): Boolean 251 external fun reloadKeys(): Boolean
254 252
255 external fun initializeSystem() 253 external fun initializeSystem(reload: Boolean)
256 254
257 external fun defaultCPUCore(): Int 255 external fun defaultCPUCore(): Int
258 256
@@ -462,12 +460,12 @@ object NativeLibrary {
462 } 460 }
463 461
464 fun setEmulationActivity(emulationActivity: EmulationActivity?) { 462 fun setEmulationActivity(emulationActivity: EmulationActivity?) {
465 Log.verbose("[NativeLibrary] Registering EmulationActivity.") 463 Log.debug("[NativeLibrary] Registering EmulationActivity.")
466 sEmulationActivity = WeakReference(emulationActivity) 464 sEmulationActivity = WeakReference(emulationActivity)
467 } 465 }
468 466
469 fun clearEmulationActivity() { 467 fun clearEmulationActivity() {
470 Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") 468 Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
471 sEmulationActivity.clear() 469 sEmulationActivity.clear()
472 } 470 }
473 471
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 8c053670c..d114bd53d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -11,6 +11,7 @@ import java.io.File
11import org.yuzu.yuzu_emu.utils.DirectoryInitialization 11import org.yuzu.yuzu_emu.utils.DirectoryInitialization
12import org.yuzu.yuzu_emu.utils.DocumentsTree 12import org.yuzu.yuzu_emu.utils.DocumentsTree
13import org.yuzu.yuzu_emu.utils.GpuDriverHelper 13import org.yuzu.yuzu_emu.utils.GpuDriverHelper
14import org.yuzu.yuzu_emu.utils.Log
14 15
15fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir 16fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
16 17
@@ -49,6 +50,7 @@ class YuzuApplication : Application() {
49 DirectoryInitialization.start() 50 DirectoryInitialization.start()
50 GpuDriverHelper.initializeDriverParameters() 51 GpuDriverHelper.initializeDriverParameters()
51 NativeLibrary.logDeviceInfo() 52 NativeLibrary.logDeviceInfo()
53 Log.logDeviceInfo()
52 54
53 createNotificationChannels() 55 createNotificationChannels()
54 } 56 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index f37875ffe..f41d7bdbf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -47,6 +47,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
47import org.yuzu.yuzu_emu.model.Game 47import org.yuzu.yuzu_emu.model.Game
48import org.yuzu.yuzu_emu.utils.ForegroundService 48import org.yuzu.yuzu_emu.utils.ForegroundService
49import org.yuzu.yuzu_emu.utils.InputHandler 49import org.yuzu.yuzu_emu.utils.InputHandler
50import org.yuzu.yuzu_emu.utils.Log
50import org.yuzu.yuzu_emu.utils.MemoryUtil 51import org.yuzu.yuzu_emu.utils.MemoryUtil
51import org.yuzu.yuzu_emu.utils.NfcReader 52import org.yuzu.yuzu_emu.utils.NfcReader
52import org.yuzu.yuzu_emu.utils.ThemeHelper 53import org.yuzu.yuzu_emu.utils.ThemeHelper
@@ -80,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
80 } 81 }
81 82
82 override fun onCreate(savedInstanceState: Bundle?) { 83 override fun onCreate(savedInstanceState: Bundle?) {
84 Log.gameLaunched = true
83 ThemeHelper.setTheme(this) 85 ThemeHelper.setTheme(this)
84 86
85 super.onCreate(savedInstanceState) 87 super.onCreate(savedInstanceState)
@@ -105,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
105 107
106 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 108 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
107 if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { 109 if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
108 if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { 110 if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
109 Toast.makeText( 111 Toast.makeText(
110 this, 112 this,
111 getString( 113 getString(
@@ -371,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
371 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() 373 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
372 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() 374 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
373 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 375 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
376 val isEmulationActive = emulationViewModel.emulationStarted.value &&
377 !emulationViewModel.isEmulationStopping.value
374 pictureInPictureParamsBuilder.setAutoEnterEnabled( 378 pictureInPictureParamsBuilder.setAutoEnterEnabled(
375 BooleanSetting.PICTURE_IN_PICTURE.boolean 379 BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
376 ) 380 )
377 } 381 }
378 setPictureInPictureParams(pictureInPictureParamsBuilder.build()) 382 setPictureInPictureParams(pictureInPictureParamsBuilder.build())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 0c82cdba8..2ef638559 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
22import androidx.core.graphics.drawable.toDrawable 22import androidx.core.graphics.drawable.toDrawable
23import androidx.documentfile.provider.DocumentFile 23import androidx.documentfile.provider.DocumentFile
24import androidx.lifecycle.ViewModelProvider 24import androidx.lifecycle.ViewModelProvider
25import androidx.lifecycle.lifecycleScope
25import androidx.navigation.findNavController 26import androidx.navigation.findNavController
26import androidx.preference.PreferenceManager 27import androidx.preference.PreferenceManager
27import androidx.recyclerview.widget.AsyncDifferConfig 28import androidx.recyclerview.widget.AsyncDifferConfig
28import androidx.recyclerview.widget.DiffUtil 29import androidx.recyclerview.widget.DiffUtil
29import androidx.recyclerview.widget.ListAdapter 30import androidx.recyclerview.widget.ListAdapter
30import androidx.recyclerview.widget.RecyclerView 31import androidx.recyclerview.widget.RecyclerView
32import kotlinx.coroutines.Dispatchers
33import kotlinx.coroutines.launch
34import kotlinx.coroutines.withContext
31import org.yuzu.yuzu_emu.HomeNavigationDirections 35import org.yuzu.yuzu_emu.HomeNavigationDirections
32import org.yuzu.yuzu_emu.R 36import org.yuzu.yuzu_emu.R
33import org.yuzu.yuzu_emu.YuzuApplication 37import org.yuzu.yuzu_emu.YuzuApplication
@@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
92 data = Uri.parse(holder.game.path) 96 data = Uri.parse(holder.game.path)
93 } 97 }
94 98
95 val layerDrawable = ResourcesCompat.getDrawable( 99 activity.lifecycleScope.launch {
96 YuzuApplication.appContext.resources, 100 withContext(Dispatchers.IO) {
97 R.drawable.shortcut, 101 val layerDrawable = ResourcesCompat.getDrawable(
98 null 102 YuzuApplication.appContext.resources,
99 ) as LayerDrawable 103 R.drawable.shortcut,
100 layerDrawable.setDrawableByLayerId( 104 null
101 R.id.shortcut_foreground, 105 ) as LayerDrawable
102 GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) 106 layerDrawable.setDrawableByLayerId(
103 ) 107 R.id.shortcut_foreground,
104 val inset = YuzuApplication.appContext.resources 108 GameIconUtils.getGameIcon(activity, holder.game)
105 .getDimensionPixelSize(R.dimen.icon_inset) 109 .toDrawable(YuzuApplication.appContext.resources)
106 layerDrawable.setLayerInset(1, inset, inset, inset, inset)
107 val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
108 .setShortLabel(holder.game.title)
109 .setIcon(
110 IconCompat.createWithAdaptiveBitmap(
111 layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
112 ) 110 )
113 ) 111 val inset = YuzuApplication.appContext.resources
114 .setIntent(openIntent) 112 .getDimensionPixelSize(R.dimen.icon_inset)
115 .build() 113 layerDrawable.setLayerInset(1, inset, inset, inset, inset)
116 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) 114 val shortcut =
115 ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
116 .setShortLabel(holder.game.title)
117 .setIcon(
118 IconCompat.createWithAdaptiveBitmap(
119 layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
120 )
121 )
122 .setIntent(openIntent)
123 .build()
124 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
125 }
126 }
117 127
118 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) 128 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
119 view.findNavController().navigate(action) 129 view.findNavController().navigate(action)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 08e2a973d..d005c656e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -7,7 +7,7 @@ import android.text.TextUtils
7import android.widget.Toast 7import android.widget.Toast
8import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 10import org.yuzu.yuzu_emu.utils.NativeConfig
11 11
12object Settings { 12object Settings {
13 private val context get() = YuzuApplication.appContext 13 private val context get() = YuzuApplication.appContext
@@ -19,7 +19,7 @@ object Settings {
19 context.getString(R.string.ini_saved), 19 context.getString(R.string.ini_saved),
20 Toast.LENGTH_SHORT 20 Toast.LENGTH_SHORT
21 ).show() 21 ).show()
22 SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) 22 NativeConfig.saveSettings()
23 } else { 23 } else {
24 // TODO: Save custom game settings 24 // TODO: Save custom game settings
25 Toast.makeText( 25 Toast.makeText(
@@ -82,7 +82,6 @@ object Settings {
82 82
83 enum class MenuTag(val titleId: Int) { 83 enum class MenuTag(val titleId: Int) {
84 SECTION_ROOT(R.string.advanced_settings), 84 SECTION_ROOT(R.string.advanced_settings),
85 SECTION_GENERAL(R.string.preferences_general),
86 SECTION_SYSTEM(R.string.preferences_system), 85 SECTION_SYSTEM(R.string.preferences_system),
87 SECTION_RENDERER(R.string.preferences_graphics), 86 SECTION_RENDERER(R.string.preferences_graphics),
88 SECTION_AUDIO(R.string.preferences_audio), 87 SECTION_AUDIO(R.string.preferences_audio),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
index 522cc49df..425160024 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
@@ -3,10 +3,13 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.DrawableRes
7
6class RunnableSetting( 8class RunnableSetting(
7 titleId: Int, 9 titleId: Int,
8 descriptionId: Int, 10 descriptionId: Int,
9 val isRuntimeRunnable: Boolean, 11 val isRuntimeRunnable: Boolean,
12 @DrawableRes val iconId: Int = 0,
10 val runnable: () -> Unit 13 val runnable: () -> Unit
11) : SettingsItem(emptySetting, titleId, descriptionId) { 14) : SettingsItem(emptySetting, titleId, descriptionId) {
12 override val type = TYPE_RUNNABLE 15 override val type = TYPE_RUNNABLE
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index b3b3fc209..6aba69dbe 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -73,7 +73,7 @@ abstract class SettingsItem(
73 R.string.frame_limit_slider, 73 R.string.frame_limit_slider,
74 R.string.frame_limit_slider_description, 74 R.string.frame_limit_slider_description,
75 1, 75 1,
76 200, 76 400,
77 "%" 77 "%"
78 ) 78 )
79 ) 79 )
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index b343e527e..94953b18a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -3,11 +3,14 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.DrawableRes
7import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.Settings 8import org.yuzu.yuzu_emu.features.settings.model.Settings
7 9
8class SubmenuSetting( 10class SubmenuSetting(
9 titleId: Int, 11 @StringRes titleId: Int,
10 descriptionId: Int, 12 @StringRes descriptionId: Int,
13 @DrawableRes val iconId: Int,
11 val menuKey: Settings.MenuTag 14 val menuKey: Settings.MenuTag
12) : SettingsItem(emptySetting, titleId, descriptionId) { 15) : SettingsItem(emptySetting, titleId, descriptionId) {
13 override val type = TYPE_SUBMENU 16 override val type = TYPE_SUBMENU
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index c73edd50e..48bdbdd75 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -21,7 +21,6 @@ import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.flow.collectLatest 22import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch 23import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.NativeLibrary
25import java.io.IOException 24import java.io.IOException
26import org.yuzu.yuzu_emu.R 25import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 26import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
165 settingsViewModel.shouldSave = false 164 settingsViewModel.shouldSave = false
166 165
167 // Delete settings file because the user may have changed values that do not exist in the UI 166 // Delete settings file because the user may have changed values that do not exist in the UI
167 NativeConfig.unloadConfig()
168 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 168 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
169 if (!settingsFile.delete()) { 169 if (!settingsFile.delete()) {
170 throw IOException("Failed to delete $settingsFile") 170 throw IOException("Failed to delete $settingsFile")
171 } 171 }
172 NativeLibrary.reloadSettings() 172 NativeConfig.initializeConfig()
173 173
174 Toast.makeText( 174 Toast.makeText(
175 applicationContext, 175 applicationContext,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 70d8ec14b..769baf744 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
20import androidx.navigation.findNavController 20import androidx.navigation.findNavController
21import androidx.navigation.fragment.navArgs 21import androidx.navigation.fragment.navArgs
22import androidx.recyclerview.widget.LinearLayoutManager 22import androidx.recyclerview.widget.LinearLayoutManager
23import com.google.android.material.divider.MaterialDividerItemDecoration
24import com.google.android.material.transition.MaterialSharedAxis 23import com.google.android.material.transition.MaterialSharedAxis
25import kotlinx.coroutines.flow.collectLatest 24import kotlinx.coroutines.flow.collectLatest
26import kotlinx.coroutines.launch 25import kotlinx.coroutines.launch
@@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
68 ) 67 )
69 68
70 binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) 69 binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
71 val dividerDecoration = MaterialDividerItemDecoration(
72 requireContext(),
73 LinearLayoutManager.VERTICAL
74 )
75 dividerDecoration.isLastItemDecorated = false
76 binding.listSettings.apply { 70 binding.listSettings.apply {
77 adapter = settingsAdapter 71 adapter = settingsAdapter
78 layoutManager = LinearLayoutManager(requireContext()) 72 layoutManager = LinearLayoutManager(requireContext())
79 addItemDecoration(dividerDecoration)
80 } 73 }
81 74
82 binding.toolbarSettings.setNavigationOnClickListener { 75 binding.toolbarSettings.setNavigationOnClickListener {
@@ -94,17 +87,6 @@ class SettingsFragment : Fragment() {
94 } 87 }
95 } 88 }
96 } 89 }
97 launch {
98 settingsViewModel.isUsingSearch.collectLatest {
99 if (it) {
100 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
101 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
102 } else {
103 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
104 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
105 }
106 }
107 }
108 } 90 }
109 91
110 if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { 92 if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
@@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
112 binding.toolbarSettings.setOnMenuItemClickListener { 94 binding.toolbarSettings.setOnMenuItemClickListener {
113 when (it.itemId) { 95 when (it.itemId) {
114 R.id.action_search -> { 96 R.id.action_search -> {
115 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
116 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
117 view.findNavController() 97 view.findNavController()
118 .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) 98 .navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
119 true 99 true
@@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
129 setInsets() 109 setInsets()
130 } 110 }
131 111
132 override fun onResume() {
133 super.onResume()
134 settingsViewModel.setIsUsingSearch(false)
135 }
136
137 private fun setInsets() { 112 private fun setInsets() {
138 ViewCompat.setOnApplyWindowInsetsListener( 113 ViewCompat.setOnApplyWindowInsetsListener(
139 binding.root 114 binding.root
@@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
144 val leftInsets = barInsets.left + cutoutInsets.left 119 val leftInsets = barInsets.left + cutoutInsets.left
145 val rightInsets = barInsets.right + cutoutInsets.right 120 val rightInsets = barInsets.right + cutoutInsets.right
146 121
147 val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
148 val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams 122 val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
149 mlpSettingsList.leftMargin = sideMargin + leftInsets 123 mlpSettingsList.leftMargin = leftInsets
150 mlpSettingsList.rightMargin = sideMargin + rightInsets 124 mlpSettingsList.rightMargin = rightInsets
151 binding.listSettings.layoutParams = mlpSettingsList 125 binding.listSettings.layoutParams = mlpSettingsList
152 binding.listSettings.updatePadding( 126 binding.listSettings.updatePadding(
153 bottom = barInsets.bottom 127 bottom = barInsets.bottom
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 766414a6c..8b71e32f3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context
7import android.content.SharedPreferences 6import android.content.SharedPreferences
8import android.os.Build 7import android.os.Build
9import android.widget.Toast 8import android.widget.Toast
@@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
32 private val preferences: SharedPreferences 31 private val preferences: SharedPreferences
33 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 32 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
34 33
35 private val context: Context get() = YuzuApplication.appContext
36
37 // Extension for populating settings list based on paired settings 34 // Extension for populating settings list based on paired settings
38 fun ArrayList<SettingsItem>.add(key: String) { 35 fun ArrayList<SettingsItem>.add(key: String) {
39 val item = SettingsItem.settingsItems[key]!! 36 val item = SettingsItem.settingsItems[key]!!
@@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
53 val sl = ArrayList<SettingsItem>() 50 val sl = ArrayList<SettingsItem>()
54 when (menuTag) { 51 when (menuTag) {
55 Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) 52 Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
56 Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
57 Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) 53 Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
58 Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) 54 Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
59 Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) 55 Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
@@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
75 71
76 private fun addConfigSettings(sl: ArrayList<SettingsItem>) { 72 private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
77 sl.apply { 73 sl.apply {
78 add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
79 add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
80 add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
81 add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
82 add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
83 add( 74 add(
84 RunnableSetting(R.string.reset_to_default, 0, false) { 75 SubmenuSetting(
85 settingsViewModel.setShouldShowResetSettingsDialog(true) 76 R.string.preferences_system,
86 } 77 R.string.preferences_system_description,
78 R.drawable.ic_system_settings,
79 Settings.MenuTag.SECTION_SYSTEM
80 )
81 )
82 add(
83 SubmenuSetting(
84 R.string.preferences_graphics,
85 R.string.preferences_graphics_description,
86 R.drawable.ic_graphics,
87 Settings.MenuTag.SECTION_RENDERER
88 )
89 )
90 add(
91 SubmenuSetting(
92 R.string.preferences_audio,
93 R.string.preferences_audio_description,
94 R.drawable.ic_audio,
95 Settings.MenuTag.SECTION_AUDIO
96 )
97 )
98 add(
99 SubmenuSetting(
100 R.string.preferences_debug,
101 R.string.preferences_debug_description,
102 R.drawable.ic_code,
103 Settings.MenuTag.SECTION_DEBUG
104 )
105 )
106 add(
107 RunnableSetting(
108 R.string.reset_to_default,
109 R.string.reset_to_default_description,
110 false,
111 R.drawable.ic_restore
112 ) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
87 ) 113 )
88 } 114 }
89 } 115 }
90 116
91 private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { 117 private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
92 sl.apply { 118 sl.apply {
93 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) 119 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
94 add(ShortSetting.RENDERER_SPEED_LIMIT.key) 120 add(ShortSetting.RENDERER_SPEED_LIMIT.key)
95 add(IntSetting.CPU_ACCURACY.key)
96 add(BooleanSetting.PICTURE_IN_PICTURE.key)
97 }
98 }
99
100 private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
101 sl.apply {
102 add(BooleanSetting.USE_DOCKED_MODE.key) 121 add(BooleanSetting.USE_DOCKED_MODE.key)
103 add(IntSetting.REGION_INDEX.key) 122 add(IntSetting.REGION_INDEX.key)
104 add(IntSetting.LANGUAGE_INDEX.key) 123 add(IntSetting.LANGUAGE_INDEX.key)
@@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
116 add(IntSetting.RENDERER_ANTI_ALIASING.key) 135 add(IntSetting.RENDERER_ANTI_ALIASING.key)
117 add(IntSetting.RENDERER_SCREEN_LAYOUT.key) 136 add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
118 add(IntSetting.RENDERER_ASPECT_RATIO.key) 137 add(IntSetting.RENDERER_ASPECT_RATIO.key)
138 add(BooleanSetting.PICTURE_IN_PICTURE.key)
119 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) 139 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
120 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) 140 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
121 add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) 141 add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
@@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
249 add(BooleanSetting.RENDERER_DEBUG.key) 269 add(BooleanSetting.RENDERER_DEBUG.key)
250 270
251 add(HeaderSetting(R.string.cpu)) 271 add(HeaderSetting(R.string.cpu))
272 add(IntSetting.CPU_ACCURACY.key)
252 add(BooleanSetting.CPU_DEBUG_MODE.key) 273 add(BooleanSetting.CPU_DEBUG_MODE.key)
253 add(SettingsItem.FASTMEM_COMBINED) 274 add(SettingsItem.FASTMEM_COMBINED)
254 } 275 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 83a2e94f1..036195624 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder 4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.core.content.res.ResourcesCompat
7import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 9import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
9import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
@@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
16 17
17 override fun bind(item: SettingsItem) { 18 override fun bind(item: SettingsItem) {
18 setting = item as RunnableSetting 19 setting = item as RunnableSetting
20 if (item.iconId != 0) {
21 binding.icon.visibility = View.VISIBLE
22 binding.icon.setImageDrawable(
23 ResourcesCompat.getDrawable(
24 binding.icon.resources,
25 item.iconId,
26 binding.icon.context.theme
27 )
28 )
29 } else {
30 binding.icon.visibility = View.GONE
31 }
32
19 binding.textSettingName.setText(item.nameId) 33 binding.textSettingName.setText(item.nameId)
20 if (item.descriptionId != 0) { 34 if (item.descriptionId != 0) {
21 binding.textSettingDescription.setText(item.descriptionId) 35 binding.textSettingDescription.setText(item.descriptionId)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index 1cf581a9d..8100c65dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder 4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.core.content.res.ResourcesCompat
7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
@@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
15 16
16 override fun bind(item: SettingsItem) { 17 override fun bind(item: SettingsItem) {
17 this.item = item as SubmenuSetting 18 this.item = item as SubmenuSetting
19 if (item.iconId != 0) {
20 binding.icon.visibility = View.VISIBLE
21 binding.icon.setImageDrawable(
22 ResourcesCompat.getDrawable(
23 binding.icon.resources,
24 item.iconId,
25 binding.icon.context.theme
26 )
27 )
28 } else {
29 binding.icon.visibility = View.GONE
30 }
31
18 binding.textSettingName.setText(item.nameId) 32 binding.textSettingName.setText(item.nameId)
19 if (item.descriptionId != 0) { 33 if (item.descriptionId != 0) {
20 binding.textSettingDescription.setText(item.descriptionId) 34 binding.textSettingDescription.setText(item.descriptionId)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 2b04d666a..3ae5b4653 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,15 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import android.widget.Toast
7import java.io.* 6import java.io.*
8import org.ini4j.Wini
9import org.yuzu.yuzu_emu.R
10import org.yuzu.yuzu_emu.YuzuApplication
11import org.yuzu.yuzu_emu.features.settings.model.*
12import org.yuzu.yuzu_emu.utils.DirectoryInitialization 7import org.yuzu.yuzu_emu.utils.DirectoryInitialization
13import org.yuzu.yuzu_emu.utils.Log
14import org.yuzu.yuzu_emu.utils.NativeConfig
15 8
16/** 9/**
17 * Contains static methods for interacting with .ini files in which settings are stored. 10 * Contains static methods for interacting with .ini files in which settings are stored.
@@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
19object SettingsFile { 12object SettingsFile {
20 const val FILE_NAME_CONFIG = "config" 13 const val FILE_NAME_CONFIG = "config"
21 14
22 /**
23 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
24 * telling why it failed.
25 *
26 * @param fileName The target filename without a path or extension.
27 */
28 fun saveFile(fileName: String) {
29 val ini = getSettingsFile(fileName)
30 try {
31 val wini = Wini(ini)
32 for (specificCategory in Settings.Category.values()) {
33 val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
34 for (setting in Settings.settingsList) {
35 if (setting.key!!.isEmpty()) continue
36
37 val settingCategoryHeader =
38 NativeConfig.getConfigHeader(setting.category.ordinal)
39 val iniSetting: String? = wini.get(categoryHeader, setting.key)
40 if (iniSetting != null || settingCategoryHeader == categoryHeader) {
41 wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
42 }
43 }
44 }
45 wini.store()
46 } catch (e: IOException) {
47 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
48 val context = YuzuApplication.appContext
49 Toast.makeText(
50 context,
51 context.getString(R.string.error_saving, fileName, e.message),
52 Toast.LENGTH_SHORT
53 ).show()
54 }
55 }
56
57 fun getSettingsFile(fileName: String): File = 15 fun getSettingsFile(fileName: String): File =
58 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") 16 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
59} 17}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index 2ff827c6b..a1620fbb7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
114 val leftInsets = barInsets.left + cutoutInsets.left 114 val leftInsets = barInsets.left + cutoutInsets.left
115 val rightInsets = barInsets.right + cutoutInsets.right 115 val rightInsets = barInsets.right + cutoutInsets.right
116 116
117 val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams 117 val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
118 mlpAppBar.leftMargin = leftInsets 118 mlpToolbar.leftMargin = leftInsets
119 mlpAppBar.rightMargin = rightInsets 119 mlpToolbar.rightMargin = rightInsets
120 binding.appbarAbout.layoutParams = mlpAppBar 120 binding.toolbarAbout.layoutParams = mlpToolbar
121 121
122 val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams 122 val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
123 mlpScrollAbout.leftMargin = leftInsets 123 mlpScrollAbout.leftMargin = leftInsets
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 07bd78bf7..c32fa0d7e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -10,7 +10,6 @@ import android.content.DialogInterface
10import android.content.SharedPreferences 10import android.content.SharedPreferences
11import android.content.pm.ActivityInfo 11import android.content.pm.ActivityInfo
12import android.content.res.Configuration 12import android.content.res.Configuration
13import android.graphics.Color
14import android.net.Uri 13import android.net.Uri
15import android.os.Bundle 14import android.os.Bundle
16import android.os.Handler 15import android.os.Handler
@@ -155,7 +154,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
155 } 154 }
156 155
157 binding.surfaceEmulation.holder.addCallback(this) 156 binding.surfaceEmulation.holder.addCallback(this)
158 binding.showFpsText.setTextColor(Color.YELLOW)
159 binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } 157 binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
160 158
161 binding.drawerLayout.addDrawerListener(object : DrawerListener { 159 binding.drawerLayout.addDrawerListener(object : DrawerListener {
@@ -312,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
312 ViewUtils.showView(binding.surfaceInputOverlay) 310 ViewUtils.showView(binding.surfaceInputOverlay)
313 ViewUtils.hideView(binding.loadingIndicator) 311 ViewUtils.hideView(binding.loadingIndicator)
314 312
313 emulationState.updateSurface()
314
315 // Setup overlay 315 // Setup overlay
316 updateShowFpsOverlay() 316 updateShowFpsOverlay()
317 } 317 }
@@ -412,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
412 val FRAMETIME = 2 412 val FRAMETIME = 2
413 val SPEED = 3 413 val SPEED = 3
414 perfStatsUpdater = { 414 perfStatsUpdater = {
415 if (emulationViewModel.emulationStarted.value == true) { 415 if (emulationViewModel.emulationStarted.value) {
416 val perfStats = NativeLibrary.getPerfStats() 416 val perfStats = NativeLibrary.getPerfStats()
417 if (perfStats[FPS] > 0 && _binding != null) { 417 if (_binding != null) {
418 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) 418 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
419 } 419 }
420 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) 420 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
421 } 421 }
422 } 422 }
423 perfStatsUpdateHandler.post(perfStatsUpdater!!) 423 perfStatsUpdateHandler.post(perfStatsUpdater!!)
@@ -462,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
462 if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { 462 if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
463 // Restrict emulation and overlays to the top of the screen 463 // Restrict emulation and overlays to the top of the screen
464 binding.emulationContainer.layoutParams.height = it.bounds.top 464 binding.emulationContainer.layoutParams.height = it.bounds.top
465 binding.overlayContainer.layoutParams.height = it.bounds.top
466 // Restrict input and menu drawer to the bottom of the screen 465 // Restrict input and menu drawer to the bottom of the screen
467 binding.inputContainer.layoutParams.height = it.bounds.bottom 466 binding.inputContainer.layoutParams.height = it.bounds.bottom
468 binding.inGameMenu.layoutParams.height = it.bounds.bottom 467 binding.inGameMenu.layoutParams.height = it.bounds.bottom
@@ -476,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
476 if (!isFolding) { 475 if (!isFolding) {
477 binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 476 binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
478 binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 477 binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
479 binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
480 binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 478 binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
481 isInFoldableLayout = false 479 isInFoldableLayout = false
482 updateOrientation() 480 updateOrientation()
@@ -484,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
484 } 482 }
485 binding.emulationContainer.requestLayout() 483 binding.emulationContainer.requestLayout()
486 binding.inputContainer.requestLayout() 484 binding.inputContainer.requestLayout()
487 binding.overlayContainer.requestLayout()
488 binding.inGameMenu.requestLayout() 485 binding.inGameMenu.requestLayout()
489 } 486 }
490 487
@@ -710,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
710 } 707 }
711 708
712 v.setPadding(left, cutInsets.top, right, 0) 709 v.setPadding(left, cutInsets.top, right, 0)
713
714 // Ensure FPS text doesn't get cut off by rounded display corners
715 val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge)
716 if (cutInsets.left == 0) {
717 binding.showFpsText.setPadding(
718 sidePadding,
719 cutInsets.top,
720 cutInsets.right,
721 cutInsets.bottom
722 )
723 } else {
724 binding.showFpsText.setPadding(
725 cutInsets.left,
726 cutInsets.top,
727 cutInsets.right,
728 cutInsets.bottom
729 )
730 }
731 windowInsets 710 windowInsets
732 } 711 }
733 } 712 }
@@ -805,6 +784,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
805 } 784 }
806 785
807 @Synchronized 786 @Synchronized
787 fun updateSurface() {
788 if (surface != null) {
789 NativeLibrary.surfaceChanged(surface)
790 }
791 }
792
793 @Synchronized
808 fun clearSurface() { 794 fun clearSurface() {
809 if (surface == null) { 795 if (surface == null) {
810 Log.warning("[EmulationFragment] clearSurface called, but surface already null.") 796 Log.warning("[EmulationFragment] clearSurface called, but surface already null.")
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 6e19fc6c0..4720daec4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -42,6 +42,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
42import org.yuzu.yuzu_emu.ui.main.MainActivity 42import org.yuzu.yuzu_emu.ui.main.MainActivity
43import org.yuzu.yuzu_emu.utils.FileUtil 43import org.yuzu.yuzu_emu.utils.FileUtil
44import org.yuzu.yuzu_emu.utils.GpuDriverHelper 44import org.yuzu.yuzu_emu.utils.GpuDriverHelper
45import org.yuzu.yuzu_emu.utils.Log
45 46
46class HomeSettingsFragment : Fragment() { 47class HomeSettingsFragment : Fragment() {
47 private var _binding: FragmentHomeSettingsBinding? = null 48 private var _binding: FragmentHomeSettingsBinding? = null
@@ -86,28 +87,6 @@ class HomeSettingsFragment : Fragment() {
86 ) 87 )
87 add( 88 add(
88 HomeSetting( 89 HomeSetting(
89 R.string.open_user_folder,
90 R.string.open_user_folder_description,
91 R.drawable.ic_folder_open,
92 { openFileManager() }
93 )
94 )
95 add(
96 HomeSetting(
97 R.string.preferences_theme,
98 R.string.theme_and_color_description,
99 R.drawable.ic_palette,
100 {
101 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
102 null,
103 Settings.MenuTag.SECTION_THEME
104 )
105 binding.root.findNavController().navigate(action)
106 }
107 )
108 )
109 add(
110 HomeSetting(
111 R.string.gpu_driver_manager, 90 R.string.gpu_driver_manager,
112 R.string.install_gpu_driver_description, 91 R.string.install_gpu_driver_description,
113 R.drawable.ic_build, 92 R.drawable.ic_build,
@@ -123,17 +102,6 @@ class HomeSettingsFragment : Fragment() {
123 ) 102 )
124 add( 103 add(
125 HomeSetting( 104 HomeSetting(
126 R.string.manage_yuzu_data,
127 R.string.manage_yuzu_data_description,
128 R.drawable.ic_install,
129 {
130 binding.root.findNavController()
131 .navigate(R.id.action_homeSettingsFragment_to_installableFragment)
132 }
133 )
134 )
135 add(
136 HomeSetting(
137 R.string.applets, 105 R.string.applets,
138 R.string.applets_description, 106 R.string.applets_description,
139 R.drawable.ic_applet, 107 R.drawable.ic_applet,
@@ -148,6 +116,17 @@ class HomeSettingsFragment : Fragment() {
148 ) 116 )
149 add( 117 add(
150 HomeSetting( 118 HomeSetting(
119 R.string.manage_yuzu_data,
120 R.string.manage_yuzu_data_description,
121 R.drawable.ic_install,
122 {
123 binding.root.findNavController()
124 .navigate(R.id.action_homeSettingsFragment_to_installableFragment)
125 }
126 )
127 )
128 add(
129 HomeSetting(
151 R.string.select_games_folder, 130 R.string.select_games_folder,
152 R.string.select_games_folder_description, 131 R.string.select_games_folder_description,
153 R.drawable.ic_add, 132 R.drawable.ic_add,
@@ -172,6 +151,28 @@ class HomeSettingsFragment : Fragment() {
172 ) 151 )
173 add( 152 add(
174 HomeSetting( 153 HomeSetting(
154 R.string.open_user_folder,
155 R.string.open_user_folder_description,
156 R.drawable.ic_folder_open,
157 { openFileManager() }
158 )
159 )
160 add(
161 HomeSetting(
162 R.string.preferences_theme,
163 R.string.theme_and_color_description,
164 R.drawable.ic_palette,
165 {
166 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
167 null,
168 Settings.MenuTag.SECTION_THEME
169 )
170 binding.root.findNavController().navigate(action)
171 }
172 )
173 )
174 add(
175 HomeSetting(
175 R.string.about, 176 R.string.about,
176 R.string.about_description, 177 R.string.about_description,
177 R.drawable.ic_info_outline, 178 R.drawable.ic_info_outline,
@@ -312,19 +313,32 @@ class HomeSettingsFragment : Fragment() {
312 } 313 }
313 } 314 }
314 315
316 // Share the current log if we just returned from a game but share the old log
317 // if we just started the app and the old log exists.
315 private fun shareLog() { 318 private fun shareLog() {
316 val file = DocumentFile.fromSingleUri( 319 val currentLog = DocumentFile.fromSingleUri(
317 mainActivity, 320 mainActivity,
318 DocumentsContract.buildDocumentUri( 321 DocumentsContract.buildDocumentUri(
319 DocumentProvider.AUTHORITY, 322 DocumentProvider.AUTHORITY,
320 "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" 323 "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt"
321 ) 324 )
322 )!! 325 )!!
323 if (file.exists()) { 326 val oldLog = DocumentFile.fromSingleUri(
324 val intent = Intent(Intent.ACTION_SEND) 327 mainActivity,
325 .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) 328 DocumentsContract.buildDocumentUri(
326 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 329 DocumentProvider.AUTHORITY,
327 .putExtra(Intent.EXTRA_STREAM, file.uri) 330 "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt"
331 )
332 )!!
333
334 val intent = Intent(Intent.ACTION_SEND)
335 .setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN)
336 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
337 if (!Log.gameLaunched && oldLog.exists()) {
338 intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri)
339 startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
340 } else if (currentLog.exists()) {
341 intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri)
328 startActivity(Intent.createChooser(intent, getText(R.string.share_log))) 342 startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
329 } else { 343 } else {
330 Toast.makeText( 344 Toast.makeText(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index ec116ab62..6940fc757 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
21import org.yuzu.yuzu_emu.model.HomeViewModel 21import org.yuzu.yuzu_emu.model.HomeViewModel
22import org.yuzu.yuzu_emu.model.Installable 22import org.yuzu.yuzu_emu.model.Installable
23import org.yuzu.yuzu_emu.ui.main.MainActivity 23import org.yuzu.yuzu_emu.ui.main.MainActivity
24import java.time.LocalDateTime
25import java.time.format.DateTimeFormatter
24 26
25class InstallableFragment : Fragment() { 27class InstallableFragment : Fragment() {
26 private var _binding: FragmentInstallablesBinding? = null 28 private var _binding: FragmentInstallablesBinding? = null
@@ -78,7 +80,15 @@ class InstallableFragment : Fragment() {
78 R.string.manage_save_data, 80 R.string.manage_save_data,
79 R.string.import_export_saves_description, 81 R.string.import_export_saves_description,
80 install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, 82 install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
81 export = { mainActivity.exportSave() } 83 export = {
84 mainActivity.exportSaves.launch(
85 "yuzu saves - ${
86 LocalDateTime.now().format(
87 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
88 )
89 }.zip"
90 )
91 }
82 ) 92 )
83 } else { 93 } else {
84 Installable( 94 Installable(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
index 9d0594c6e..f95d545bf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
@@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
40 40
41 override fun onCreate(savedInstanceState: Bundle?) { 41 override fun onCreate(savedInstanceState: Bundle?) {
42 super.onCreate(savedInstanceState) 42 super.onCreate(savedInstanceState)
43 enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) 43 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
44 returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) 44 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
45 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
46 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
45 } 47 }
46 48
47 override fun onCreateView( 49 override fun onCreateView(
@@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
55 57
56 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 58 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
57 super.onViewCreated(view, savedInstanceState) 59 super.onViewCreated(view, savedInstanceState)
58 settingsViewModel.setIsUsingSearch(true)
59 60
60 if (savedInstanceState != null) { 61 if (savedInstanceState != null) {
61 binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) 62 binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
index de84b2adb..2fa3ab31b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
@@ -18,8 +18,8 @@ class Game(
18 val version: String = "", 18 val version: String = "",
19 val isHomebrew: Boolean = false 19 val isHomebrew: Boolean = false
20) : Parcelable { 20) : Parcelable {
21 val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" 21 val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
22 val keyLastPlayedTime get() = "${programId}_LastPlayed" 22 val keyLastPlayedTime get() = "${path}_LastPlayed"
23 23
24 override fun equals(other: Any?): Boolean { 24 override fun equals(other: Any?): Boolean {
25 if (other !is Game) { 25 if (other !is Game) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
index 53fa7a8de..6f947674e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
@@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
29 val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList 29 val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
30 private val _shouldReloadSettingsList = MutableStateFlow(false) 30 private val _shouldReloadSettingsList = MutableStateFlow(false)
31 31
32 val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
33 private val _isUsingSearch = MutableStateFlow(false)
34
35 val sliderProgress: StateFlow<Int> get() = _sliderProgress 32 val sliderProgress: StateFlow<Int> get() = _sliderProgress
36 private val _sliderProgress = MutableStateFlow(-1) 33 private val _sliderProgress = MutableStateFlow(-1)
37 34
@@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
57 _shouldReloadSettingsList.value = value 54 _shouldReloadSettingsList.value = value
58 } 55 }
59 56
60 fun setIsUsingSearch(value: Boolean) {
61 _isUsingSearch.value = value
62 }
63
64 fun setSliderTextValue(value: Float, units: String) { 57 fun setSliderTextValue(value: Float, units: String) {
65 _sliderProgress.value = value.toInt() 58 _sliderProgress.value = value.toInt()
66 _sliderTextValue.value = String.format( 59 _sliderTextValue.value = String.format(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index ba1177426..bd2f4cd25 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main
6import android.content.Intent 6import android.content.Intent
7import android.net.Uri 7import android.net.Uri
8import android.os.Bundle 8import android.os.Bundle
9import android.provider.DocumentsContract
10import android.view.View 9import android.view.View
11import android.view.ViewGroup.MarginLayoutParams 10import android.view.ViewGroup.MarginLayoutParams
12import android.view.WindowManager 11import android.view.WindowManager
@@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
20import androidx.core.view.ViewCompat 19import androidx.core.view.ViewCompat
21import androidx.core.view.WindowCompat 20import androidx.core.view.WindowCompat
22import androidx.core.view.WindowInsetsCompat 21import androidx.core.view.WindowInsetsCompat
23import androidx.documentfile.provider.DocumentFile
24import androidx.lifecycle.Lifecycle 22import androidx.lifecycle.Lifecycle
25import androidx.lifecycle.lifecycleScope 23import androidx.lifecycle.lifecycleScope
26import androidx.lifecycle.repeatOnLifecycle 24import androidx.lifecycle.repeatOnLifecycle
@@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
41import org.yuzu.yuzu_emu.R 39import org.yuzu.yuzu_emu.R
42import org.yuzu.yuzu_emu.activities.EmulationActivity 40import org.yuzu.yuzu_emu.activities.EmulationActivity
43import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 41import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
44import org.yuzu.yuzu_emu.features.DocumentProvider
45import org.yuzu.yuzu_emu.features.settings.model.Settings 42import org.yuzu.yuzu_emu.features.settings.model.Settings
46import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 43import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 44import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
53import org.yuzu.yuzu_emu.utils.* 50import org.yuzu.yuzu_emu.utils.*
54import java.io.BufferedInputStream 51import java.io.BufferedInputStream
55import java.io.BufferedOutputStream 52import java.io.BufferedOutputStream
56import java.io.FileOutputStream
57import java.time.LocalDateTime
58import java.time.format.DateTimeFormatter
59import java.util.zip.ZipEntry 53import java.util.zip.ZipEntry
60import java.util.zip.ZipInputStream 54import java.util.zip.ZipInputStream
61 55
@@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
73 67
74 // Get first subfolder in saves folder (should be the user folder) 68 // Get first subfolder in saves folder (should be the user folder)
75 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" 69 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
76 private var lastZipCreated: File? = null
77 70
78 override fun onCreate(savedInstanceState: Bundle?) { 71 override fun onCreate(savedInstanceState: Bundle?) {
79 val splashScreen = installSplashScreen() 72 val splashScreen = installSplashScreen()
@@ -403,7 +396,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
403 } else { 396 } else {
404 firmwarePath.deleteRecursively() 397 firmwarePath.deleteRecursively()
405 cacheFirmwareDir.copyRecursively(firmwarePath, true) 398 cacheFirmwareDir.copyRecursively(firmwarePath, true)
406 NativeLibrary.initializeSystem() 399 NativeLibrary.initializeSystem(true)
407 getString(R.string.save_file_imported_success) 400 getString(R.string.save_file_imported_success)
408 } 401 }
409 } catch (e: Exception) { 402 } catch (e: Exception) {
@@ -632,6 +625,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
632 } 625 }
633 626
634 // Clear existing user data 627 // Clear existing user data
628 NativeConfig.unloadConfig()
635 File(DirectoryInitialization.userDirectory!!).deleteRecursively() 629 File(DirectoryInitialization.userDirectory!!).deleteRecursively()
636 630
637 // Copy archive to internal storage 631 // Copy archive to internal storage
@@ -649,7 +643,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
649 } 643 }
650 644
651 // Reinitialize relevant data 645 // Reinitialize relevant data
652 NativeLibrary.initializeSystem() 646 NativeLibrary.initializeSystem(true)
647 NativeConfig.initializeConfig()
653 gamesViewModel.reloadGames(false) 648 gamesViewModel.reloadGames(false)
654 649
655 return@newInstance getString(R.string.user_data_import_success) 650 return@newInstance getString(R.string.user_data_import_success)
@@ -657,74 +652,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
657 } 652 }
658 653
659 /** 654 /**
660 * Zips the save files located in the given folder path and creates a new zip file with the current date and time.
661 * @return true if the zip file is successfully created, false otherwise.
662 */
663 private fun zipSave(): Boolean {
664 try {
665 val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
666 tempFolder.mkdirs()
667 val saveFolder = File(savesFolderRoot)
668 val outputZipFile = File(
669 tempFolder,
670 "yuzu saves - ${
671 LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
672 }.zip"
673 )
674 outputZipFile.createNewFile()
675 val result = FileUtil.zipFromInternalStorage(
676 saveFolder,
677 savesFolderRoot,
678 BufferedOutputStream(FileOutputStream(outputZipFile))
679 )
680 if (result == TaskState.Failed) {
681 return false
682 }
683 lastZipCreated = outputZipFile
684 } catch (e: Exception) {
685 return false
686 }
687 return true
688 }
689
690 /**
691 * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. 655 * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
692 */ 656 */
693 fun exportSave() { 657 val exportSaves = registerForActivityResult(
694 CoroutineScope(Dispatchers.IO).launch { 658 ActivityResultContracts.CreateDocument("application/zip")
695 val wasZipCreated = zipSave() 659 ) { result ->
696 val lastZipFile = lastZipCreated 660 if (result == null) {
697 if (!wasZipCreated || lastZipFile == null) { 661 return@registerForActivityResult
698 withContext(Dispatchers.Main) { 662 }
699 Toast.makeText(
700 this@MainActivity,
701 getString(R.string.export_save_failed),
702 Toast.LENGTH_LONG
703 ).show()
704 }
705 return@launch
706 }
707 663
708 withContext(Dispatchers.Main) { 664 IndeterminateProgressDialogFragment.newInstance(
709 val file = DocumentFile.fromSingleUri( 665 this,
710 this@MainActivity, 666 R.string.save_files_exporting,
711 DocumentsContract.buildDocumentUri( 667 false
712 DocumentProvider.AUTHORITY, 668 ) {
713 "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" 669 val zipResult = FileUtil.zipFromInternalStorage(
714 ) 670 File(savesFolderRoot),
715 )!! 671 savesFolderRoot,
716 val intent = Intent(Intent.ACTION_SEND) 672 BufferedOutputStream(contentResolver.openOutputStream(result))
717 .setDataAndType(file.uri, "application/zip") 673 )
718 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 674 return@newInstance when (zipResult) {
719 .putExtra(Intent.EXTRA_STREAM, file.uri) 675 TaskState.Completed -> getString(R.string.export_success)
720 startForResultExportSave.launch( 676 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
721 Intent.createChooser(
722 intent,
723 getString(R.string.share_save_file)
724 )
725 )
726 } 677 }
727 } 678 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
728 } 679 }
729 680
730 private val startForResultExportSave = 681 private val startForResultExportSave =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 79a07f7ef..21270fc84 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -15,7 +15,8 @@ object DirectoryInitialization {
15 fun start() { 15 fun start() {
16 if (!areDirectoriesReady) { 16 if (!areDirectoriesReady) {
17 initializeInternalStorage() 17 initializeInternalStorage()
18 NativeLibrary.initializeSystem() 18 NativeLibrary.initializeSystem(false)
19 NativeConfig.initializeConfig()
19 areDirectoriesReady = true 20 areDirectoriesReady = true
20 } 21 }
21 } 22 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
index 654d62f52..2e9b0beb8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
8import android.widget.ImageView 8import android.widget.ImageView
9import androidx.core.graphics.drawable.toBitmap 9import androidx.core.graphics.drawable.toBitmap
10import androidx.core.graphics.drawable.toDrawable 10import androidx.core.graphics.drawable.toDrawable
11import androidx.lifecycle.LifecycleOwner
11import coil.ImageLoader 12import coil.ImageLoader
12import coil.decode.DataSource 13import coil.decode.DataSource
13import coil.executeBlocking
14import coil.fetch.DrawableResult 14import coil.fetch.DrawableResult
15import coil.fetch.FetchResult 15import coil.fetch.FetchResult
16import coil.fetch.Fetcher 16import coil.fetch.Fetcher
@@ -76,12 +76,13 @@ object GameIconUtils {
76 imageLoader.enqueue(request) 76 imageLoader.enqueue(request)
77 } 77 }
78 78
79 fun getGameIcon(game: Game): Bitmap { 79 suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
80 val request = ImageRequest.Builder(YuzuApplication.appContext) 80 val request = ImageRequest.Builder(YuzuApplication.appContext)
81 .data(game) 81 .data(game)
82 .lifecycle(lifecycleOwner)
82 .error(R.drawable.default_icon) 83 .error(R.drawable.default_icon)
83 .build() 84 .build()
84 return imageLoader.executeBlocking(request) 85 return imageLoader.execute(request)
85 .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) 86 .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
86 } 87 }
87} 88}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index fc6a8b5cb..e63382e1d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -27,6 +27,8 @@ object InputHandler {
27 0x054C -> getInputDS5ButtonKey(event.keyCode) 27 0x054C -> getInputDS5ButtonKey(event.keyCode)
28 0x057E -> getInputJoyconButtonKey(event.keyCode) 28 0x057E -> getInputJoyconButtonKey(event.keyCode)
29 0x1532 -> getInputRazerButtonKey(event.keyCode) 29 0x1532 -> getInputRazerButtonKey(event.keyCode)
30 0x3537 -> getInputRedmagicButtonKey(event.keyCode)
31 0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
30 else -> getInputGenericButtonKey(event.keyCode) 32 else -> getInputGenericButtonKey(event.keyCode)
31 } 33 }
32 34
@@ -68,7 +70,7 @@ object InputHandler {
68 private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { 70 private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int {
69 var deviceIndex = index 71 var deviceIndex = index
70 if (deviceId != -1) { 72 if (deviceId != -1) {
71 deviceIndex = controllerIds[deviceId]!! 73 deviceIndex = controllerIds[deviceId] ?: 0
72 } 74 }
73 75
74 // TODO: Joycons are handled as different controllers. Find a way to merge them. 76 // TODO: Joycons are handled as different controllers. Find a way to merge them.
@@ -227,6 +229,42 @@ object InputHandler {
227 } 229 }
228 } 230 }
229 231
232 private fun getInputRedmagicButtonKey(key: Int): Int {
233 return when (key) {
234 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
235 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
236 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
237 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
238 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
239 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
240 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
241 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
242 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
243 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
244 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
245 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
246 else -> -1
247 }
248 }
249
250 private fun getInputBackboneLabsButtonKey(key: Int): Int {
251 return when (key) {
252 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
253 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
254 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
255 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
256 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
257 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
258 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
259 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
260 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
261 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
262 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
263 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
264 else -> -1
265 }
266 }
267
230 private fun getInputGenericButtonKey(key: Int): Int { 268 private fun getInputGenericButtonKey(key: Int): Int {
231 return when (key) { 269 return when (key) {
232 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A 270 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt
index a193e82a4..aebe84b0f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt
@@ -3,38 +3,29 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.util.Log 6import android.os.Build
7import org.yuzu.yuzu_emu.BuildConfig 7
8
9/**
10 * Contains methods that call through to [android.util.Log], but
11 * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log
12 * levels in release builds.
13 */
14object Log { 8object Log {
15 private const val TAG = "Yuzu Frontend" 9 // Tracks whether we should share the old log or the current log
10 var gameLaunched = false
16 11
17 fun verbose(message: String) { 12 external fun debug(message: String)
18 if (BuildConfig.DEBUG) {
19 Log.v(TAG, message)
20 }
21 }
22 13
23 fun debug(message: String) { 14 external fun warning(message: String)
24 if (BuildConfig.DEBUG) {
25 Log.d(TAG, message)
26 }
27 }
28 15
29 fun info(message: String) { 16 external fun info(message: String)
30 Log.i(TAG, message)
31 }
32 17
33 fun warning(message: String) { 18 external fun error(message: String)
34 Log.w(TAG, message)
35 }
36 19
37 fun error(message: String) { 20 external fun critical(message: String)
38 Log.e(TAG, message) 21
22 fun logDeviceInfo() {
23 info("Device Manufacturer - ${Build.MANUFACTURER}")
24 info("Device Model - ${Build.MODEL}")
25 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
26 info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
27 info("SoC Model - ${Build.SOC_MODEL}")
28 }
29 info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
39 } 30 }
40} 31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
index aa4a5539a..9076a86c4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
@@ -27,7 +27,7 @@ object MemoryUtil {
27 const val Pb = Tb * 1024 27 const val Pb = Tb * 1024
28 const val Eb = Pb * 1024 28 const val Eb = Pb * 1024
29 29
30 private fun bytesToSizeUnit(size: Float): String = 30 private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
31 when { 31 when {
32 size < Kb -> { 32 size < Kb -> {
33 context.getString( 33 context.getString(
@@ -39,63 +39,59 @@ object MemoryUtil {
39 size < Mb -> { 39 size < Mb -> {
40 context.getString( 40 context.getString(
41 R.string.memory_formatted, 41 R.string.memory_formatted,
42 (size / Kb).hundredths, 42 if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
43 context.getString(R.string.memory_kilobyte) 43 context.getString(R.string.memory_kilobyte)
44 ) 44 )
45 } 45 }
46 size < Gb -> { 46 size < Gb -> {
47 context.getString( 47 context.getString(
48 R.string.memory_formatted, 48 R.string.memory_formatted,
49 (size / Mb).hundredths, 49 if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
50 context.getString(R.string.memory_megabyte) 50 context.getString(R.string.memory_megabyte)
51 ) 51 )
52 } 52 }
53 size < Tb -> { 53 size < Tb -> {
54 context.getString( 54 context.getString(
55 R.string.memory_formatted, 55 R.string.memory_formatted,
56 (size / Gb).hundredths, 56 if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
57 context.getString(R.string.memory_gigabyte) 57 context.getString(R.string.memory_gigabyte)
58 ) 58 )
59 } 59 }
60 size < Pb -> { 60 size < Pb -> {
61 context.getString( 61 context.getString(
62 R.string.memory_formatted, 62 R.string.memory_formatted,
63 (size / Tb).hundredths, 63 if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
64 context.getString(R.string.memory_terabyte) 64 context.getString(R.string.memory_terabyte)
65 ) 65 )
66 } 66 }
67 size < Eb -> { 67 size < Eb -> {
68 context.getString( 68 context.getString(
69 R.string.memory_formatted, 69 R.string.memory_formatted,
70 (size / Pb).hundredths, 70 if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
71 context.getString(R.string.memory_petabyte) 71 context.getString(R.string.memory_petabyte)
72 ) 72 )
73 } 73 }
74 else -> { 74 else -> {
75 context.getString( 75 context.getString(
76 R.string.memory_formatted, 76 R.string.memory_formatted,
77 (size / Eb).hundredths, 77 if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
78 context.getString(R.string.memory_exabyte) 78 context.getString(R.string.memory_exabyte)
79 ) 79 )
80 } 80 }
81 } 81 }
82 82
83 // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for 83 val totalMemory: Float
84 // the potential error created by memInfo.totalMem
85 private val totalMemory: Float
86 get() { 84 get() {
87 val memInfo = ActivityManager.MemoryInfo() 85 val memInfo = ActivityManager.MemoryInfo()
88 with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { 86 with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
89 getMemoryInfo(memInfo) 87 getMemoryInfo(memInfo)
90 } 88 }
91 89
92 return ceil( 90 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
93 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 91 memInfo.advertisedMem.toFloat()
94 memInfo.advertisedMem.toFloat() 92 } else {
95 } else { 93 memInfo.totalMem.toFloat()
96 memInfo.totalMem.toFloat() 94 }
97 }
98 )
99 } 95 }
100 96
101 fun isLessThan(minimum: Int, size: Float): Boolean = 97 fun isLessThan(minimum: Int, size: Float): Boolean =
@@ -109,5 +105,7 @@ object MemoryUtil {
109 else -> totalMemory < Kb && totalMemory < minimum 105 else -> totalMemory < Kb && totalMemory < minimum
110 } 106 }
111 107
112 fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) 108 // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
109 // the potential error created by memInfo.totalMem
110 fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
113} 111}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
index 9425f8b99..87e579fa7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -4,6 +4,30 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6object NativeConfig { 6object NativeConfig {
7 /**
8 * Creates a Config object and opens the emulation config.
9 */
10 @Synchronized
11 external fun initializeConfig()
12
13 /**
14 * Destroys the stored config object. This automatically saves the existing config.
15 */
16 @Synchronized
17 external fun unloadConfig()
18
19 /**
20 * Reads values saved to the config file and saves them.
21 */
22 @Synchronized
23 external fun reloadSettings()
24
25 /**
26 * Saves settings values in memory to disk.
27 */
28 @Synchronized
29 external fun saveSettings()
30
7 external fun getBoolean(key: String, getDefault: Boolean): Boolean 31 external fun getBoolean(key: String, getDefault: Boolean): Boolean
8 external fun setBoolean(key: String, value: Boolean) 32 external fun setBoolean(key: String, value: Boolean)
9 33
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 1c36661f5..2acc93da8 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
6 android_common/android_common.h 6 android_common/android_common.h
7 applets/software_keyboard.cpp 7 applets/software_keyboard.cpp
8 applets/software_keyboard.h 8 applets/software_keyboard.h
9 config.cpp
10 config.h
11 default_ini.h
12 emu_window/emu_window.cpp 9 emu_window/emu_window.cpp
13 emu_window/emu_window.h 10 emu_window/emu_window.h
14 id_cache.cpp 11 id_cache.cpp
@@ -16,14 +13,17 @@ add_library(yuzu-android SHARED
16 native.cpp 13 native.cpp
17 native.h 14 native.h
18 native_config.cpp 15 native_config.cpp
19 uisettings.cpp 16 android_settings.cpp
20 game_metadata.cpp 17 game_metadata.cpp
18 native_log.cpp
19 android_config.cpp
20 android_config.h
21) 21)
22 22
23set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 23set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
24 24
25target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) 25target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common)
26target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) 26target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
27if (ARCHITECTURE_arm64) 27if (ARCHITECTURE_arm64)
28 target_link_libraries(yuzu-android PRIVATE adrenotools) 28 target_link_libraries(yuzu-android PRIVATE adrenotools)
29endif() 29endif()
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
new file mode 100644
index 000000000..3041c25c9
--- /dev/null
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -0,0 +1,70 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "android_config.h"
5#include "android_settings.h"
6#include "common/settings_setting.h"
7
8AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
9 : Config(config_type) {
10 Initialize(config_name);
11 if (config_type != ConfigType::InputProfile) {
12 ReadAndroidValues();
13 SaveAndroidValues();
14 }
15}
16
17AndroidConfig::~AndroidConfig() {
18 if (global) {
19 AndroidConfig::SaveAllValues();
20 }
21}
22
23void AndroidConfig::ReloadAllValues() {
24 Reload();
25 ReadAndroidValues();
26 SaveAndroidValues();
27}
28
29void AndroidConfig::SaveAllValues() {
30 Save();
31 SaveAndroidValues();
32}
33
34void AndroidConfig::ReadAndroidValues() {
35 if (global) {
36 ReadAndroidUIValues();
37 }
38}
39
40void AndroidConfig::ReadAndroidUIValues() {
41 BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
42
43 ReadCategory(Settings::Category::Android);
44
45 EndGroup();
46}
47
48void AndroidConfig::SaveAndroidValues() {
49 if (global) {
50 SaveAndroidUIValues();
51 }
52
53 WriteToIni();
54}
55
56void AndroidConfig::SaveAndroidUIValues() {
57 BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
58
59 WriteCategory(Settings::Category::Android);
60
61 EndGroup();
62}
63
64std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
65 auto& map = Settings::values.linkage.by_category;
66 if (map.contains(category)) {
67 return Settings::values.linkage.by_category[category];
68 }
69 return AndroidSettings::values.linkage.by_category[category];
70}
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
new file mode 100644
index 000000000..e679392fd
--- /dev/null
+++ b/src/android/app/src/main/jni/android_config.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "frontend_common/config.h"
7
8class AndroidConfig final : public Config {
9public:
10 explicit AndroidConfig(const std::string& config_name = "config",
11 ConfigType config_type = ConfigType::GlobalConfig);
12 ~AndroidConfig() override;
13
14 void ReloadAllValues() override;
15 void SaveAllValues() override;
16
17protected:
18 void ReadAndroidValues();
19 void ReadAndroidUIValues();
20 void ReadHidbusValues() override {}
21 void ReadDebugControlValues() override {}
22 void ReadPathValues() override {}
23 void ReadShortcutValues() override {}
24 void ReadUIValues() override {}
25 void ReadUIGamelistValues() override {}
26 void ReadUILayoutValues() override {}
27 void ReadMultiplayerValues() override {}
28
29 void SaveAndroidValues();
30 void SaveAndroidUIValues();
31 void SaveHidbusValues() override {}
32 void SaveDebugControlValues() override {}
33 void SavePathValues() override {}
34 void SaveShortcutValues() override {}
35 void SaveUIValues() override {}
36 void SaveUIGamelistValues() override {}
37 void SaveUILayoutValues() override {}
38 void SaveMultiplayerValues() override {}
39
40 std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
41};
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/android_settings.cpp
index f2f0bad50..16023a6b0 100644
--- a/src/android/app/src/main/jni/uisettings.cpp
+++ b/src/android/app/src/main/jni/android_settings.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "uisettings.h" 4#include "android_settings.h"
5 5
6namespace AndroidSettings { 6namespace AndroidSettings {
7 7
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/android_settings.h
index 494654af7..37bc33918 100644
--- a/src/android/app/src/main/jni/uisettings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -13,7 +13,7 @@ struct Values {
13 Settings::Linkage linkage; 13 Settings::Linkage linkage;
14 14
15 // Android 15 // Android
16 Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture", 16 Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture",
17 Settings::Category::Android}; 17 Settings::Category::Android};
18 Settings::Setting<s32> screen_layout{linkage, 18 Settings::Setting<s32> screen_layout{linkage,
19 5, 19 5,
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
deleted file mode 100644
index 81120ab0f..000000000
--- a/src/android/app/src/main/jni/config.cpp
+++ /dev/null
@@ -1,330 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <memory>
5#include <optional>
6#include <sstream>
7
8#include <INIReader.h>
9#include "common/fs/file.h"
10#include "common/fs/fs.h"
11#include "common/fs/path_util.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/settings_enums.h"
15#include "core/hle/service/acc/profile_manager.h"
16#include "input_common/main.h"
17#include "jni/config.h"
18#include "jni/default_ini.h"
19#include "uisettings.h"
20
21namespace FS = Common::FS;
22
23Config::Config(const std::string& config_name, ConfigType config_type)
24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
25 Initialize(config_name);
26}
27
28Config::~Config() = default;
29
30bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 void(FS::CreateParentDir(config_loc));
32 config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
33 const auto config_loc_str = FS::PathToUTF8String(config_loc);
34 if (config->ParseError() < 0) {
35 if (retry) {
36 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
37 config_loc_str);
38
39 void(FS::CreateParentDir(config_loc));
40 void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
41
42 config = std::make_unique<INIReader>(config_loc_str);
43
44 return LoadINI(default_contents, false);
45 }
46 LOG_ERROR(Config, "Failed.");
47 return false;
48 }
49 LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
50 return true;
51}
52
53template <>
54void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
55 std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
56 if (setting_value.empty()) {
57 setting_value = setting.GetDefault();
58 }
59 setting = std::move(setting_value);
60}
61
62template <>
63void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
64 setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
65}
66
67template <typename Type, bool ranged>
68void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
69 setting = static_cast<Type>(
70 config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
71}
72
73void Config::ReadValues() {
74 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
75 ReadSetting("ControlsGeneral", Settings::values.touch_device);
76 ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
77 ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
78 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
79 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
80 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
81 Settings::values.touchscreen.enabled =
82 config->GetBoolean("ControlsGeneral", "touch_enabled", true);
83 Settings::values.touchscreen.rotation_angle =
84 config->GetInteger("ControlsGeneral", "touch_angle", 0);
85 Settings::values.touchscreen.diameter_x =
86 config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
87 Settings::values.touchscreen.diameter_y =
88 config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
89
90 int num_touch_from_button_maps =
91 config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
92 if (num_touch_from_button_maps > 0) {
93 for (int i = 0; i < num_touch_from_button_maps; ++i) {
94 Settings::TouchFromButtonMap map;
95 map.name = config->Get("ControlsGeneral",
96 std::string("touch_from_button_maps_") + std::to_string(i) +
97 std::string("_name"),
98 "default");
99 const int num_touch_maps = config->GetInteger(
100 "ControlsGeneral",
101 std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
102 0);
103 map.buttons.reserve(num_touch_maps);
104
105 for (int j = 0; j < num_touch_maps; ++j) {
106 std::string touch_mapping =
107 config->Get("ControlsGeneral",
108 std::string("touch_from_button_maps_") + std::to_string(i) +
109 std::string("_bind_") + std::to_string(j),
110 "");
111 map.buttons.emplace_back(std::move(touch_mapping));
112 }
113
114 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
115 }
116 } else {
117 Settings::values.touch_from_button_maps.emplace_back(
118 Settings::TouchFromButtonMap{"default", {}});
119 num_touch_from_button_maps = 1;
120 }
121 Settings::values.touch_from_button_map_index = std::clamp(
122 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
123
124 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
125
126 // Data Storage
127 ReadSetting("Data Storage", Settings::values.use_virtual_sd);
128 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
129 config->Get("Data Storage", "nand_directory",
130 FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
131 FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
132 config->Get("Data Storage", "sdmc_directory",
133 FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
134 FS::SetYuzuPath(FS::YuzuPath::LoadDir,
135 config->Get("Data Storage", "load_directory",
136 FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
137 FS::SetYuzuPath(FS::YuzuPath::DumpDir,
138 config->Get("Data Storage", "dump_directory",
139 FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
140 ReadSetting("Data Storage", Settings::values.gamecard_inserted);
141 ReadSetting("Data Storage", Settings::values.gamecard_current_game);
142 ReadSetting("Data Storage", Settings::values.gamecard_path);
143
144 // System
145 ReadSetting("System", Settings::values.current_user);
146 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
147 Service::Account::MAX_USERS - 1);
148
149 // Disable docked mode by default on Android
150 Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
151 ? Settings::ConsoleMode::Docked
152 : Settings::ConsoleMode::Handheld);
153
154 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
155 if (rng_seed_enabled) {
156 Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
157 } else {
158 Settings::values.rng_seed.SetValue(0);
159 }
160 Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
161
162 const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
163 if (custom_rtc_enabled) {
164 Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
165 } else {
166 Settings::values.custom_rtc = 0;
167 }
168 Settings::values.custom_rtc_enabled = custom_rtc_enabled;
169
170 ReadSetting("System", Settings::values.language_index);
171 ReadSetting("System", Settings::values.region_index);
172 ReadSetting("System", Settings::values.time_zone_index);
173 ReadSetting("System", Settings::values.sound_index);
174
175 // Core
176 ReadSetting("Core", Settings::values.use_multi_core);
177 ReadSetting("Core", Settings::values.memory_layout_mode);
178
179 // Cpu
180 ReadSetting("Cpu", Settings::values.cpu_accuracy);
181 ReadSetting("Cpu", Settings::values.cpu_debug_mode);
182 ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
183 ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
184 ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
185 ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
186 ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
187 ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
188 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
189 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
190 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
191 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
192 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
193 ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
194 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
195 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
196 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
197 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
198 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
199 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
200
201 // Renderer
202 ReadSetting("Renderer", Settings::values.renderer_backend);
203 ReadSetting("Renderer", Settings::values.renderer_debug);
204 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
205 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
206 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
207 ReadSetting("Renderer", Settings::values.vulkan_device);
208
209 ReadSetting("Renderer", Settings::values.resolution_setup);
210 ReadSetting("Renderer", Settings::values.scaling_filter);
211 ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
212 ReadSetting("Renderer", Settings::values.anti_aliasing);
213 ReadSetting("Renderer", Settings::values.fullscreen_mode);
214 ReadSetting("Renderer", Settings::values.aspect_ratio);
215 ReadSetting("Renderer", Settings::values.max_anisotropy);
216 ReadSetting("Renderer", Settings::values.use_speed_limit);
217 ReadSetting("Renderer", Settings::values.speed_limit);
218 ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
219 ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
220 ReadSetting("Renderer", Settings::values.vsync_mode);
221 ReadSetting("Renderer", Settings::values.shader_backend);
222 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
223 ReadSetting("Renderer", Settings::values.nvdec_emulation);
224 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
225 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
226
227 ReadSetting("Renderer", Settings::values.bg_red);
228 ReadSetting("Renderer", Settings::values.bg_green);
229 ReadSetting("Renderer", Settings::values.bg_blue);
230
231 // Use GPU accuracy normal by default on Android
232 Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
233 "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
234
235 // Use GPU default anisotropic filtering on Android
236 Settings::values.max_anisotropy =
237 static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
238
239 // Disable ASTC compute by default on Android
240 Settings::values.accelerate_astc.SetValue(
241 config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
242 : Settings::AstcDecodeMode::Cpu);
243
244 // Enable asynchronous presentation by default on Android
245 Settings::values.async_presentation =
246 config->GetBoolean("Renderer", "async_presentation", true);
247
248 // Disable force_max_clock by default on Android
249 Settings::values.renderer_force_max_clock =
250 config->GetBoolean("Renderer", "force_max_clock", false);
251
252 // Disable use_reactive_flushing by default on Android
253 Settings::values.use_reactive_flushing =
254 config->GetBoolean("Renderer", "use_reactive_flushing", false);
255
256 // Audio
257 ReadSetting("Audio", Settings::values.sink_id);
258 ReadSetting("Audio", Settings::values.audio_output_device_id);
259 ReadSetting("Audio", Settings::values.volume);
260
261 // Miscellaneous
262 // log_filter has a different default here than from common
263 Settings::values.log_filter = "*:Info";
264 ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
265
266 // Debugging
267 Settings::values.record_frame_times =
268 config->GetBoolean("Debugging", "record_frame_times", false);
269 ReadSetting("Debugging", Settings::values.dump_exefs);
270 ReadSetting("Debugging", Settings::values.dump_nso);
271 ReadSetting("Debugging", Settings::values.enable_fs_access_log);
272 ReadSetting("Debugging", Settings::values.reporting_services);
273 ReadSetting("Debugging", Settings::values.quest_flag);
274 ReadSetting("Debugging", Settings::values.use_debug_asserts);
275 ReadSetting("Debugging", Settings::values.use_auto_stub);
276 ReadSetting("Debugging", Settings::values.disable_macro_jit);
277 ReadSetting("Debugging", Settings::values.disable_macro_hle);
278 ReadSetting("Debugging", Settings::values.use_gdbstub);
279 ReadSetting("Debugging", Settings::values.gdbstub_port);
280
281 const auto title_list = config->Get("AddOns", "title_ids", "");
282 std::stringstream ss(title_list);
283 std::string line;
284 while (std::getline(ss, line, '|')) {
285 const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
286 const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
287
288 std::stringstream inner_ss(disabled_list);
289 std::string inner_line;
290 std::vector<std::string> out;
291 while (std::getline(inner_ss, inner_line, '|')) {
292 out.push_back(inner_line);
293 }
294
295 Settings::values.disabled_addons.insert_or_assign(title_id, out);
296 }
297
298 // Web Service
299 ReadSetting("WebService", Settings::values.enable_telemetry);
300 ReadSetting("WebService", Settings::values.web_api_url);
301 ReadSetting("WebService", Settings::values.yuzu_username);
302 ReadSetting("WebService", Settings::values.yuzu_token);
303
304 // Network
305 ReadSetting("Network", Settings::values.network_interface);
306
307 // Android
308 ReadSetting("Android", AndroidSettings::values.picture_in_picture);
309 ReadSetting("Android", AndroidSettings::values.screen_layout);
310}
311
312void Config::Initialize(const std::string& config_name) {
313 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
314 const auto config_file = fmt::format("{}.ini", config_name);
315
316 switch (type) {
317 case ConfigType::GlobalConfig:
318 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
319 break;
320 case ConfigType::PerGameConfig:
321 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
322 break;
323 case ConfigType::InputProfile:
324 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
325 LoadINI(DefaultINI::android_config_file);
326 return;
327 }
328 LoadINI(DefaultINI::android_config_file);
329 ReadValues();
330}
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
deleted file mode 100644
index e1e8f47ed..000000000
--- a/src/android/app/src/main/jni/config.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <filesystem>
7#include <memory>
8#include <optional>
9#include <string>
10
11#include "common/settings.h"
12
13class INIReader;
14
15class Config {
16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
17
18public:
19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_name = "config",
26 ConfigType config_type = ConfigType::GlobalConfig);
27 ~Config();
28
29 void Initialize(const std::string& config_name);
30
31private:
32 /**
33 * Applies a value read from the config to a Setting.
34 *
35 * @param group The name of the INI group
36 * @param setting The yuzu setting to modify
37 */
38 template <typename Type, bool ranged>
39 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
40
41 void ReadValues();
42
43 const ConfigType type;
44 std::unique_ptr<INIReader> config;
45 std::string config_loc;
46 const bool global;
47};
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
deleted file mode 100644
index d81422a74..000000000
--- a/src/android/app/src/main/jni/default_ini.h
+++ /dev/null
@@ -1,511 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace DefaultINI {
7
8const char* android_config_file = R"(
9
10[ControlsP0]
11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
13# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15
16# Indicates if this player should be connected at boot
17connected=
18
19# for button input, the following devices are available:
20# - "keyboard" (default) for keyboard input. Required parameters:
21# - "code": the code of the key to bind
22# - "sdl" for joystick input using SDL. Required parameters:
23# - "guid": SDL identification GUID of the joystick
24# - "port": the index of the joystick to bind
25# - "button"(optional): the index of the button to bind
26# - "hat"(optional): the index of the hat to bind as direction buttons
27# - "axis"(optional): the index of the axis to bind
28# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
29# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
30# triggered if the axis value crosses
31# - "direction"(only used for axis): "+" means the button is triggered when the axis value
32# is greater than the threshold; "-" means the button is triggered when the axis value
33# is smaller than the threshold
34button_a=
35button_b=
36button_x=
37button_y=
38button_lstick=
39button_rstick=
40button_l=
41button_r=
42button_zl=
43button_zr=
44button_plus=
45button_minus=
46button_dleft=
47button_dup=
48button_dright=
49button_ddown=
50button_lstick_left=
51button_lstick_up=
52button_lstick_right=
53button_lstick_down=
54button_sl=
55button_sr=
56button_home=
57button_screenshot=
58
59# for analog input, the following devices are available:
60# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
61# - "up", "down", "left", "right": sub-devices for each direction.
62# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
63# - "modifier": sub-devices as a modifier.
64# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
65# Must be in range of 0.0-1.0. Defaults to 0.5
66# - "sdl" for joystick input using SDL. Required parameters:
67# - "guid": SDL identification GUID of the joystick
68# - "port": the index of the joystick to bind
69# - "axis_x": the index of the axis to bind as x-axis (default to 0)
70# - "axis_y": the index of the axis to bind as y-axis (default to 1)
71lstick=
72rstick=
73
74# for motion input, the following devices are available:
75# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
76# - "code": the code of the key to bind
77# - "sdl" for motion input using SDL. Required parameters:
78# - "guid": SDL identification GUID of the joystick
79# - "port": the index of the joystick to bind
80# - "motion": the index of the motion sensor to bind
81# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
82# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
83# - "port": the port of the cemu hook server
84# - "pad": the index of the joystick
85# - "motion": the index of the motion sensor of the joystick to bind
86motionleft=
87motionright=
88
89[ControlsGeneral]
90# To use the debug_pad, prepend `debug_pad_` before each button setting above.
91# i.e. debug_pad_button_a=
92
93# Enable debug pad inputs to the guest
94# 0 (default): Disabled, 1: Enabled
95debug_pad_enabled =
96
97# Whether to enable or disable vibration
98# 0: Disabled, 1 (default): Enabled
99vibration_enabled=
100
101# Whether to enable or disable accurate vibrations
102# 0 (default): Disabled, 1: Enabled
103enable_accurate_vibrations=
104
105# Enables controller motion inputs
106# 0: Disabled, 1 (default): Enabled
107motion_enabled =
108
109# Defines the udp device's touch screen coordinate system for cemuhookudp devices
110# - "min_x", "min_y", "max_x", "max_y"
111touch_device=
112
113# for mapping buttons to touch inputs.
114#touch_from_button_map=1
115#touch_from_button_maps_0_name=default
116#touch_from_button_maps_0_count=2
117#touch_from_button_maps_0_bind_0=foo
118#touch_from_button_maps_0_bind_1=bar
119# etc.
120
121# List of Cemuhook UDP servers, delimited by ','.
122# Default: 127.0.0.1:26760
123# Example: 127.0.0.1:26760,123.4.5.67:26761
124udp_input_servers =
125
126# Enable controlling an axis via a mouse input.
127# 0 (default): Off, 1: On
128mouse_panning =
129
130# Set mouse sensitivity.
131# Default: 1.0
132mouse_panning_sensitivity =
133
134# Emulate an analog control stick from keyboard inputs.
135# 0 (default): Disabled, 1: Enabled
136emulate_analog_keyboard =
137
138# Enable mouse inputs to the guest
139# 0 (default): Disabled, 1: Enabled
140mouse_enabled =
141
142# Enable keyboard inputs to the guest
143# 0 (default): Disabled, 1: Enabled
144keyboard_enabled =
145
146[Core]
147# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled
149use_multi_core =
150
151# Enable unsafe extended guest system memory layout (8GB DRAM)
152# 0 (default): Disabled, 1: Enabled
153use_unsafe_extended_memory_layout =
154
155[Cpu]
156# Adjusts various optimizations.
157# Auto-select mode enables choice unsafe optimizations.
158# Accurate enables only safe optimizations.
159# Unsafe allows any unsafe optimizations.
160# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
161cpu_accuracy =
162
163# Allow disabling safe optimizations.
164# 0 (default): Disabled, 1: Enabled
165cpu_debug_mode =
166
167# Enable inline page tables optimization (faster guest memory access)
168# 0: Disabled, 1 (default): Enabled
169cpuopt_page_tables =
170
171# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
172# 0: Disabled, 1 (default): Enabled
173cpuopt_block_linking =
174
175# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
176# 0: Disabled, 1 (default): Enabled
177cpuopt_return_stack_buffer =
178
179# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
180# 0: Disabled, 1 (default): Enabled
181cpuopt_fast_dispatcher =
182
183# Enable context elimination CPU Optimization (reduce host memory use for guest context)
184# 0: Disabled, 1 (default): Enabled
185cpuopt_context_elimination =
186
187# Enable constant propagation CPU optimization (basic IR optimization)
188# 0: Disabled, 1 (default): Enabled
189cpuopt_const_prop =
190
191# Enable miscellaneous CPU optimizations (basic IR optimization)
192# 0: Disabled, 1 (default): Enabled
193cpuopt_misc_ir =
194
195# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
196# 0: Disabled, 1 (default): Enabled
197cpuopt_reduce_misalign_checks =
198
199# Enable Host MMU Emulation (faster guest memory access)
200# 0: Disabled, 1 (default): Enabled
201cpuopt_fastmem =
202
203# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
204# 0: Disabled, 1 (default): Enabled
205cpuopt_fastmem_exclusives =
206
207# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
208# 0: Disabled, 1 (default): Enabled
209cpuopt_recompile_exclusives =
210
211# Enable optimization to ignore invalid memory accesses (faster guest memory access)
212# 0: Disabled, 1 (default): Enabled
213cpuopt_ignore_memory_aborts =
214
215# Enable unfuse FMA (improve performance on CPUs without FMA)
216# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
217# 0: Disabled, 1 (default): Enabled
218cpuopt_unsafe_unfuse_fma =
219
220# Enable faster FRSQRTE and FRECPE
221# Only enabled if cpu_accuracy is set to Unsafe.
222# 0: Disabled, 1 (default): Enabled
223cpuopt_unsafe_reduce_fp_error =
224
225# Enable faster ASIMD instructions (32 bits only)
226# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
227# 0: Disabled, 1 (default): Enabled
228cpuopt_unsafe_ignore_standard_fpcr =
229
230# Enable inaccurate NaN handling
231# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
232# 0: Disabled, 1 (default): Enabled
233cpuopt_unsafe_inaccurate_nan =
234
235# Disable address space checks (64 bits only)
236# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
237# 0: Disabled, 1 (default): Enabled
238cpuopt_unsafe_fastmem_check =
239
240# Enable faster exclusive instructions
241# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
242# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor =
244
245[Renderer]
246# Which backend API to use.
247# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
248backend =
249
250# Whether to enable asynchronous presentation (Vulkan only)
251# 0: Off, 1 (default): On
252async_presentation =
253
254# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
255# 0 (default): Disabled, 1: Enabled
256force_max_clock =
257
258# Enable graphics API debugging mode.
259# 0 (default): Disabled, 1: Enabled
260debug =
261
262# Enable shader feedback.
263# 0 (default): Disabled, 1: Enabled
264renderer_shader_feedback =
265
266# Enable Nsight Aftermath crash dumps
267# 0 (default): Disabled, 1: Enabled
268nsight_aftermath =
269
270# Disable shader loop safety checks, executing the shader without loop logic changes
271# 0 (default): Disabled, 1: Enabled
272disable_shader_loop_safety_checks =
273
274# Which Vulkan physical device to use (defaults to 0)
275vulkan_device =
276
277# 0: 0.5x (360p/540p) [EXPERIMENTAL]
278# 1: 0.75x (540p/810p) [EXPERIMENTAL]
279# 2 (default): 1x (720p/1080p)
280# 3: 2x (1440p/2160p)
281# 4: 3x (2160p/3240p)
282# 5: 4x (2880p/4320p)
283# 6: 5x (3600p/5400p)
284# 7: 6x (4320p/6480p)
285resolution_setup =
286
287# Pixel filter to use when up- or down-sampling rendered frames.
288# 0: Nearest Neighbor
289# 1 (default): Bilinear
290# 2: Bicubic
291# 3: Gaussian
292# 4: ScaleForce
293# 5: AMD FidelityFXâ„¢ï¸ Super Resolution [Vulkan Only]
294scaling_filter =
295
296# Anti-Aliasing (AA)
297# 0 (default): None, 1: FXAA
298anti_aliasing =
299
300# Whether to use fullscreen or borderless window mode
301# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
302fullscreen_mode =
303
304# Aspect ratio
305# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
306aspect_ratio =
307
308# Anisotropic filtering
309# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
310max_anisotropy =
311
312# Whether to enable VSync or not.
313# OpenGL: Values other than 0 enable VSync
314# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
315# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
316# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
317# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
318# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
319# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
320use_vsync =
321
322# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
323# not available and GLASM is selected, GLSL will be used.
324# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
325shader_backend =
326
327# Whether to allow asynchronous shader building.
328# 0 (default): Off, 1: On
329use_asynchronous_shaders =
330
331# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
332# 0 (default): Off, 1: On
333use_reactive_flushing =
334
335# NVDEC emulation.
336# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
337nvdec_emulation =
338
339# Accelerate ASTC texture decoding.
340# 0 (default): Off, 1: On
341accelerate_astc =
342
343# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
344# 0: Off, 1: On (default)
345use_speed_limit =
346
347# Limits the speed of the game to run no faster than this value as a percentage of target speed
348# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
349speed_limit =
350
351# Whether to use disk based shader cache
352# 0: Off, 1 (default): On
353use_disk_shader_cache =
354
355# Which gpu accuracy level to use
356# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
357gpu_accuracy =
358
359# Whether to use asynchronous GPU emulation
360# 0 : Off (slow), 1 (default): On (fast)
361use_asynchronous_gpu_emulation =
362
363# Inform the guest that GPU operations completed more quickly than they did.
364# 0: Off, 1 (default): On
365use_fast_gpu_time =
366
367# Force unmodified buffers to be flushed, which can cost performance.
368# 0: Off (default), 1: On
369use_pessimistic_flushes =
370
371# Whether to use garbage collection or not for GPU caches.
372# 0 (default): Off, 1: On
373use_caches_gc =
374
375# The clear color for the renderer. What shows up on the sides of the bottom screen.
376# Must be in range of 0-255. Defaults to 0 for all.
377bg_red =
378bg_blue =
379bg_green =
380
381[Audio]
382# Which audio output engine to use.
383# auto (default): Auto-select
384# cubeb: Cubeb audio engine (if available)
385# sdl2: SDL2 audio engine (if available)
386# null: No audio output
387output_engine =
388
389# Which audio device to use.
390# auto (default): Auto-select
391output_device =
392
393# Output volume.
394# 100 (default): 100%, 0; mute
395volume =
396
397[Data Storage]
398# Whether to create a virtual SD card.
399# 1: Yes, 0 (default): No
400use_virtual_sd =
401
402# Whether or not to enable gamecard emulation
403# 1: Yes, 0 (default): No
404gamecard_inserted =
405
406# Whether or not the gamecard should be emulated as the current game
407# If 'gamecard_inserted' is 0 this setting is irrelevant
408# 1: Yes, 0 (default): No
409gamecard_current_game =
410
411# Path to an XCI file to use as the gamecard
412# If 'gamecard_inserted' is 0 this setting is irrelevant
413# If 'gamecard_current_game' is 1 this setting is irrelevant
414gamecard_path =
415
416[System]
417# Whether the system is docked
418# 1 (default): Yes, 0: No
419use_docked_mode =
420
421# Sets the seed for the RNG generator built into the switch
422# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
423rng_seed_enabled =
424rng_seed =
425
426# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
427# This will auto-increment, with the time set being the time the game is started
428# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
429custom_rtc_enabled =
430custom_rtc =
431
432# Sets the systems language index
433# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
434# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
435# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
436language_index =
437
438# The system region that yuzu will use during emulation
439# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
440region_index =
441
442# The system time zone that yuzu will use during emulation
443# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
444time_zone_index =
445
446# Sets the sound output mode.
447# 0: Mono, 1 (default): Stereo, 2: Surround
448sound_index =
449
450[Miscellaneous]
451# A filter which removes logs below a certain logging level.
452# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
453log_filter = *:Trace
454
455# Use developer keys
456# 0 (default): Disabled, 1: Enabled
457use_dev_keys =
458
459[Debugging]
460# Record frame time data, can be found in the log directory. Boolean value
461record_frame_times =
462# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
463dump_exefs=false
464# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
465dump_nso=false
466# Determines whether or not yuzu will save the filesystem access log.
467enable_fs_access_log=false
468# Enables verbose reporting services
469reporting_services =
470# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
471# false: Retail/Normal Mode (default), true: Kiosk Mode
472quest_flag =
473# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
474# false: Disabled (default), true: Enabled
475use_debug_asserts =
476# Determines whether unimplemented HLE service calls should be automatically stubbed.
477# false: Disabled (default), true: Enabled
478use_auto_stub =
479# Enables/Disables the macro JIT compiler
480disable_macro_jit=false
481# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
482# false: Disabled (default), true: Enabled
483use_gdbstub=false
484# The port to use for the GDB server, if it is enabled.
485gdbstub_port=6543
486
487[WebService]
488# Whether or not to enable telemetry
489# 0: No, 1 (default): Yes
490enable_telemetry =
491# URL for Web API
492web_api_url = https://api.yuzu-emu.org
493# Username and token for yuzu Web Service
494# See https://profile.yuzu-emu.org/ for more info
495yuzu_username =
496yuzu_token =
497
498[Network]
499# Name of the network interface device to use with yuzu LAN play.
500# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
501# e.g. On Windows: 'Ethernet', 'Wi-Fi'
502network_interface =
503
504[AddOns]
505# Used to disable add-ons
506# List of title IDs of games that will have add-ons disabled (separated by '|'):
507title_ids =
508# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
509# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
510)";
511} // namespace DefaultINI
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp
index a7e414b81..c4f631924 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.cpp
+++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp
@@ -9,6 +9,7 @@
9#include "input_common/drivers/virtual_gamepad.h" 9#include "input_common/drivers/virtual_gamepad.h"
10#include "input_common/main.h" 10#include "input_common/main.h"
11#include "jni/emu_window/emu_window.h" 11#include "jni/emu_window/emu_window.h"
12#include "jni/native.h"
12 13
13void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { 14void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
14 m_window_width = ANativeWindow_getWidth(surface); 15 m_window_width = ANativeWindow_getWidth(surface);
@@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() {
57 m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); 58 m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
58} 59}
59 60
61void EmuWindow_Android::OnFrameDisplayed() {
62 if (!m_first_frame) {
63 EmulationSession::GetInstance().OnEmulationStarted();
64 m_first_frame = true;
65 }
66}
67
60EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, 68EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
61 ANativeWindow* surface, 69 ANativeWindow* surface,
62 std::shared_ptr<Common::DynamicLibrary> driver_library) 70 std::shared_ptr<Common::DynamicLibrary> driver_library)
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h
index b38087f73..a34a0e479 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.h
+++ b/src/android/app/src/main/jni/emu_window/emu_window.h
@@ -45,7 +45,7 @@ public:
45 float gyro_z, float accel_x, float accel_y, float accel_z); 45 float gyro_z, float accel_x, float accel_y, float accel_z);
46 void OnReadNfcTag(std::span<u8> data); 46 void OnReadNfcTag(std::span<u8> data);
47 void OnRemoveNfcTag(); 47 void OnRemoveNfcTag();
48 void OnFrameDisplayed() override {} 48 void OnFrameDisplayed() override;
49 49
50 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { 50 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
51 return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; 51 return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
@@ -61,4 +61,6 @@ private:
61 float m_window_height{}; 61 float m_window_height{};
62 62
63 std::shared_ptr<Common::DynamicLibrary> m_driver_library; 63 std::shared_ptr<Common::DynamicLibrary> m_driver_library;
64
65 bool m_first_frame = false;
64}; 66};
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 0e458df38..617288ae4 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -52,8 +52,8 @@
52#include "core/hle/service/am/applets/applets.h" 52#include "core/hle/service/am/applets/applets.h"
53#include "core/hle/service/filesystem/filesystem.h" 53#include "core/hle/service/filesystem/filesystem.h"
54#include "core/loader/loader.h" 54#include "core/loader/loader.h"
55#include "frontend_common/config.h"
55#include "jni/android_common/android_common.h" 56#include "jni/android_common/android_common.h"
56#include "jni/config.h"
57#include "jni/id_cache.h" 57#include "jni/id_cache.h"
58#include "jni/native.h" 58#include "jni/native.h"
59#include "video_core/renderer_base.h" 59#include "video_core/renderer_base.h"
@@ -199,8 +199,8 @@ bool EmulationSession::IsPaused() const {
199 return m_is_running && m_is_paused; 199 return m_is_running && m_is_paused;
200} 200}
201 201
202const Core::PerfStatsResults& EmulationSession::PerfStats() const { 202const Core::PerfStatsResults& EmulationSession::PerfStats() {
203 std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); 203 m_perf_stats = m_system.GetAndResetPerfStats();
204 return m_perf_stats; 204 return m_perf_stats;
205} 205}
206 206
@@ -247,7 +247,14 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
247 } 247 }
248} 248}
249 249
250void EmulationSession::InitializeSystem() { 250void EmulationSession::InitializeSystem(bool reload) {
251 if (!reload) {
252 // Initialize logging system
253 Common::Log::Initialize();
254 Common::Log::SetColorConsoleBackendEnabled(true);
255 Common::Log::Start();
256 }
257
251 // Initialize filesystem. 258 // Initialize filesystem.
252 m_system.SetFilesystem(m_vfs); 259 m_system.SetFilesystem(m_vfs);
253 m_system.GetUserChannel().clear(); 260 m_system.GetUserChannel().clear();
@@ -365,8 +372,6 @@ void EmulationSession::RunEmulation() {
365 m_system.InitializeDebugger(); 372 m_system.InitializeDebugger();
366 } 373 }
367 374
368 OnEmulationStarted();
369
370 while (true) { 375 while (true) {
371 { 376 {
372 [[maybe_unused]] std::unique_lock lock(m_mutex); 377 [[maybe_unused]] std::unique_lock lock(m_mutex);
@@ -376,11 +381,6 @@ void EmulationSession::RunEmulation() {
376 break; 381 break;
377 } 382 }
378 } 383 }
379 {
380 // Refresh performance stats.
381 std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
382 m_perf_stats = m_system.GetAndResetPerfStats();
383 }
384 } 384 }
385} 385}
386 386
@@ -462,10 +462,6 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
462} 462}
463 463
464static Core::SystemResultStatus RunEmulation(const std::string& filepath) { 464static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
465 Common::Log::Initialize();
466 Common::Log::SetColorConsoleBackendEnabled(true);
467 Common::Log::Start();
468
469 MicroProfileOnThreadCreate("EmuThread"); 465 MicroProfileOnThreadCreate("EmuThread");
470 SCOPE_EXIT({ MicroProfileShutdown(); }); 466 SCOPE_EXIT({ MicroProfileShutdown(); });
471 467
@@ -666,12 +662,13 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
666 } 662 }
667} 663}
668 664
669void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) { 665void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
670 // Create the default config.ini. 666 jboolean reload) {
671 Config{};
672 // Initialize the emulated system. 667 // Initialize the emulated system.
673 EmulationSession::GetInstance().System().Initialize(); 668 if (!reload) {
674 EmulationSession::GetInstance().InitializeSystem(); 669 EmulationSession::GetInstance().System().Initialize();
670 }
671 EmulationSession::GetInstance().InitializeSystem(reload);
675} 672}
676 673
677jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { 674jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
@@ -681,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
681void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( 678void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
682 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} 679 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
683 680
684void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
685 Config{};
686}
687
688void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
689 jstring j_game_id) {
690 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
691
692 env->ReleaseStringUTFChars(j_game_id, game_id.data());
693}
694
695jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { 681jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
696 jdoubleArray j_stats = env->NewDoubleArray(4); 682 jdoubleArray j_stats = env->NewDoubleArray(4);
697 683
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index 0aa2b085b..78ef96802 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -41,9 +41,9 @@ public:
41 void RunEmulation(); 41 void RunEmulation();
42 void ShutdownEmulation(); 42 void ShutdownEmulation();
43 43
44 const Core::PerfStatsResults& PerfStats() const; 44 const Core::PerfStatsResults& PerfStats();
45 void ConfigureFilesystemProvider(const std::string& filepath); 45 void ConfigureFilesystemProvider(const std::string& filepath);
46 void InitializeSystem(); 46 void InitializeSystem(bool reload);
47 Core::SystemResultStatus InitializeEmulation(const std::string& filepath); 47 Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
48 48
49 bool IsHandheldOnly(); 49 bool IsHandheldOnly();
@@ -52,9 +52,10 @@ public:
52 void OnGamepadDisconnectEvent([[maybe_unused]] int index); 52 void OnGamepadDisconnectEvent([[maybe_unused]] int index);
53 SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); 53 SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
54 54
55 static void OnEmulationStarted();
56
55private: 57private:
56 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); 58 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
57 static void OnEmulationStarted();
58 static void OnEmulationStopped(Core::SystemResultStatus result); 59 static void OnEmulationStopped(Core::SystemResultStatus result);
59 60
60private: 61private:
@@ -80,6 +81,5 @@ private:
80 81
81 // Synchronization 82 // Synchronization
82 std::condition_variable_any m_cv; 83 std::condition_variable_any m_cv;
83 mutable std::mutex m_perf_stats_mutex;
84 mutable std::mutex m_mutex; 84 mutable std::mutex m_mutex;
85}; 85};
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 8a704960c..8e81816e5 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -5,11 +5,14 @@
5 5
6#include <jni.h> 6#include <jni.h>
7 7
8#include "android_config.h"
9#include "android_settings.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
9#include "common/settings.h" 11#include "common/settings.h"
12#include "frontend_common/config.h"
10#include "jni/android_common/android_common.h" 13#include "jni/android_common/android_common.h"
11#include "jni/config.h" 14
12#include "uisettings.h" 15std::unique_ptr<AndroidConfig> config;
13 16
14template <typename T> 17template <typename T>
15Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { 18Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
28 31
29extern "C" { 32extern "C" {
30 33
34void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
35 config = std::make_unique<AndroidConfig>();
36}
37
38void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
39 config.reset();
40}
41
42void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
43 config->AndroidConfig::ReloadAllValues();
44}
45
46void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
47 config->AndroidConfig::SaveAllValues();
48}
49
31jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, 50jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
32 jstring jkey, jboolean getDefault) { 51 jstring jkey, jboolean getDefault) {
33 auto setting = getSetting<bool>(env, jkey); 52 auto setting = getSetting<bool>(env, jkey);
diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp
new file mode 100644
index 000000000..33d691dc8
--- /dev/null
+++ b/src/android/app/src/main/jni/native_log.cpp
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <common/logging/log.h>
5#include <jni.h>
6
7#include "android_common/android_common.h"
8
9extern "C" {
10
11void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
12 LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage));
13}
14
15void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
16 LOG_WARNING(Frontend, "{}", GetJString(env, jmessage));
17}
18
19void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
20 LOG_INFO(Frontend, "{}", GetJString(env, jmessage));
21}
22
23void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
24 LOG_ERROR(Frontend, "{}", GetJString(env, jmessage));
25}
26
27void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
28 LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
29}
30
31} // extern "C"
diff --git a/src/android/app/src/main/res/drawable/ic_audio.xml b/src/android/app/src/main/res/drawable/ic_audio.xml
new file mode 100644
index 000000000..e306c3b0c
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_audio.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_code.xml b/src/android/app/src/main/res/drawable/ic_code.xml
new file mode 100644
index 000000000..26f83b39b
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_code.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_graphics.xml b/src/android/app/src/main/res/drawable/ic_graphics.xml
new file mode 100644
index 000000000..2fdb5a4d6
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_graphics.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_system_settings.xml b/src/android/app/src/main/res/drawable/ic_system_settings.xml
new file mode 100644
index 000000000..7701a2bab
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_system_settings.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
new file mode 100644
index 000000000..a26ffbc73
--- /dev/null
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
@@ -0,0 +1,233 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:id="@+id/coordinator_about"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:background="?attr/colorSurface">
9
10 <com.google.android.material.appbar.AppBarLayout
11 android:id="@+id/appbar_about"
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:fitsSystemWindows="true">
15
16 <com.google.android.material.appbar.MaterialToolbar
17 android:id="@+id/toolbar_about"
18 android:layout_width="match_parent"
19 android:layout_height="?attr/actionBarSize"
20 app:navigationIcon="@drawable/ic_back"
21 app:title="@string/about" />
22
23 </com.google.android.material.appbar.AppBarLayout>
24
25 <androidx.core.widget.NestedScrollView
26 android:id="@+id/scroll_about"
27 android:layout_width="match_parent"
28 android:layout_height="match_parent"
29 android:fadeScrollbars="false"
30 android:scrollbars="vertical"
31 app:layout_behavior="@string/appbar_scrolling_view_behavior">
32
33 <LinearLayout
34 android:id="@+id/content_about"
35 android:layout_width="match_parent"
36 android:layout_height="match_parent"
37 android:orientation="horizontal">
38
39 <ImageView
40 android:id="@+id/image_logo"
41 android:layout_width="200dp"
42 android:layout_height="200dp"
43 android:layout_gravity="center_horizontal"
44 android:padding="20dp"
45 android:src="@drawable/ic_yuzu_title" />
46
47 <LinearLayout
48 android:layout_width="wrap_content"
49 android:layout_height="wrap_content"
50 android:orientation="vertical">
51
52 <LinearLayout
53 android:layout_width="match_parent"
54 android:layout_height="wrap_content"
55 android:orientation="vertical"
56 android:paddingHorizontal="16dp"
57 android:paddingVertical="16dp">
58
59 <com.google.android.material.textview.MaterialTextView
60 style="@style/TextAppearance.Material3.TitleMedium"
61 android:layout_width="match_parent"
62 android:layout_height="wrap_content"
63 android:layout_marginHorizontal="24dp"
64 android:text="@string/about"
65 android:textAlignment="viewStart" />
66
67 <com.google.android.material.textview.MaterialTextView
68 style="@style/TextAppearance.Material3.BodyMedium"
69 android:layout_width="match_parent"
70 android:layout_height="wrap_content"
71 android:layout_marginHorizontal="24dp"
72 android:layout_marginTop="6dp"
73 android:text="@string/about_app_description"
74 android:textAlignment="viewStart" />
75
76 </LinearLayout>
77
78 <com.google.android.material.divider.MaterialDivider
79 android:layout_width="match_parent"
80 android:layout_height="wrap_content"
81 android:layout_marginHorizontal="20dp" />
82
83 <LinearLayout
84 android:id="@+id/button_contributors"
85 android:layout_width="match_parent"
86 android:layout_height="wrap_content"
87 android:background="?attr/selectableItemBackground"
88 android:orientation="vertical"
89 android:paddingHorizontal="16dp"
90 android:paddingVertical="16dp">
91
92 <com.google.android.material.textview.MaterialTextView
93 style="@style/TextAppearance.Material3.TitleMedium"
94 android:layout_width="match_parent"
95 android:layout_height="wrap_content"
96 android:layout_marginHorizontal="24dp"
97 android:text="@string/contributors"
98 android:textAlignment="viewStart" />
99
100 <com.google.android.material.textview.MaterialTextView
101 style="@style/TextAppearance.Material3.BodyMedium"
102 android:layout_width="match_parent"
103 android:layout_height="wrap_content"
104 android:layout_marginHorizontal="24dp"
105 android:layout_marginTop="6dp"
106 android:text="@string/contributors_description"
107 android:textAlignment="viewStart" />
108
109 </LinearLayout>
110
111 <com.google.android.material.divider.MaterialDivider
112 android:layout_width="match_parent"
113 android:layout_height="wrap_content"
114 android:layout_marginHorizontal="20dp" />
115
116 <LinearLayout
117 android:id="@+id/button_licenses"
118 android:layout_width="match_parent"
119 android:layout_height="wrap_content"
120 android:background="?attr/selectableItemBackground"
121 android:orientation="vertical"
122 android:paddingHorizontal="16dp"
123 android:paddingVertical="16dp">
124
125 <com.google.android.material.textview.MaterialTextView
126 style="@style/TextAppearance.Material3.TitleMedium"
127 android:layout_width="match_parent"
128 android:layout_height="wrap_content"
129 android:layout_marginHorizontal="24dp"
130 android:text="@string/licenses"
131 android:textAlignment="viewStart" />
132
133 <com.google.android.material.textview.MaterialTextView
134 style="@style/TextAppearance.Material3.BodyMedium"
135 android:layout_width="match_parent"
136 android:layout_height="wrap_content"
137 android:layout_marginHorizontal="24dp"
138 android:layout_marginTop="6dp"
139 android:text="@string/licenses_description"
140 android:textAlignment="viewStart" />
141
142 </LinearLayout>
143
144 <com.google.android.material.divider.MaterialDivider
145 android:layout_width="match_parent"
146 android:layout_height="wrap_content"
147 android:layout_marginHorizontal="20dp" />
148
149 <LinearLayout
150 android:id="@+id/button_build_hash"
151 android:layout_width="match_parent"
152 android:layout_height="wrap_content"
153 android:background="?attr/selectableItemBackground"
154 android:orientation="vertical"
155 android:paddingHorizontal="16dp"
156 android:paddingVertical="16dp">
157
158 <com.google.android.material.textview.MaterialTextView
159 style="@style/TextAppearance.Material3.TitleMedium"
160 android:layout_width="match_parent"
161 android:layout_height="wrap_content"
162 android:layout_marginHorizontal="24dp"
163 android:text="@string/build"
164 android:textAlignment="viewStart" />
165
166 <com.google.android.material.textview.MaterialTextView
167 android:id="@+id/text_build_hash"
168 style="@style/TextAppearance.Material3.BodyMedium"
169 android:layout_width="match_parent"
170 android:layout_height="wrap_content"
171 android:layout_marginHorizontal="24dp"
172 android:layout_marginTop="6dp"
173 android:textAlignment="viewStart"
174 tools:text="abc123" />
175
176 </LinearLayout>
177
178 <com.google.android.material.divider.MaterialDivider
179 android:layout_width="match_parent"
180 android:layout_height="wrap_content"
181 android:layout_marginHorizontal="20dp" />
182
183 <LinearLayout
184 android:layout_width="match_parent"
185 android:layout_height="wrap_content"
186 android:layout_marginHorizontal="40dp"
187 android:layout_marginTop="12dp"
188 android:layout_marginBottom="16dp"
189 android:gravity="center_horizontal"
190 android:orientation="horizontal">
191
192 <Button
193 android:id="@+id/button_discord"
194 style="?attr/materialIconButtonStyle"
195 android:layout_width="0dp"
196 android:layout_height="wrap_content"
197 android:layout_weight="1"
198 app:icon="@drawable/ic_discord"
199 app:iconGravity="textEnd"
200 app:iconSize="24dp"
201 app:iconTint="?attr/colorOnSurface" />
202
203 <Button
204 android:id="@+id/button_website"
205 style="?attr/materialIconButtonStyle"
206 android:layout_width="0dp"
207 android:layout_height="wrap_content"
208 android:layout_weight="1"
209 app:icon="@drawable/ic_website"
210 app:iconGravity="textEnd"
211 app:iconSize="24dp"
212 app:iconTint="?attr/colorOnSurface" />
213
214 <Button
215 android:id="@+id/button_github"
216 style="?attr/materialIconButtonStyle"
217 android:layout_width="0dp"
218 android:layout_height="wrap_content"
219 android:layout_weight="1"
220 app:icon="@drawable/ic_github"
221 app:iconGravity="textEnd"
222 app:iconSize="24dp"
223 app:iconTint="?attr/colorOnSurface" />
224
225 </LinearLayout>
226
227 </LinearLayout>
228
229 </LinearLayout>
230
231 </androidx.core.widget.NestedScrollView>
232
233</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml
index 6e8a232f9..cb667c928 100644
--- a/src/android/app/src/main/res/layout/card_home_option.xml
+++ b/src/android/app/src/main/res/layout/card_home_option.xml
@@ -6,8 +6,8 @@
6 android:id="@+id/option_card" 6 android:id="@+id/option_card"
7 android:layout_width="match_parent" 7 android:layout_width="match_parent"
8 android:layout_height="wrap_content" 8 android:layout_height="wrap_content"
9 android:layout_marginVertical="12dp" 9 android:layout_marginBottom="24dp"
10 android:layout_marginHorizontal="16dp" 10 android:layout_marginHorizontal="12dp"
11 android:background="?attr/selectableItemBackground" 11 android:background="?attr/selectableItemBackground"
12 android:backgroundTint="?attr/colorSurfaceVariant" 12 android:backgroundTint="?attr/colorSurfaceVariant"
13 android:clickable="true" 13 android:clickable="true"
diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml
index 3e1d98451..a24f5230e 100644
--- a/src/android/app/src/main/res/layout/fragment_about.xml
+++ b/src/android/app/src/main/res/layout/fragment_about.xml
@@ -38,17 +38,17 @@
38 38
39 <ImageView 39 <ImageView
40 android:id="@+id/image_logo" 40 android:id="@+id/image_logo"
41 android:layout_width="250dp" 41 android:layout_width="150dp"
42 android:layout_height="250dp" 42 android:layout_height="150dp"
43 android:layout_marginTop="20dp" 43 android:layout_marginTop="24dp"
44 android:layout_marginBottom="28dp"
44 android:layout_gravity="center_horizontal" 45 android:layout_gravity="center_horizontal"
45 android:src="@drawable/ic_yuzu_title" /> 46 android:src="@drawable/ic_yuzu_title" />
46 47
47 <com.google.android.material.divider.MaterialDivider 48 <com.google.android.material.divider.MaterialDivider
48 android:layout_width="match_parent" 49 android:layout_width="match_parent"
49 android:layout_height="wrap_content" 50 android:layout_height="wrap_content"
50 android:layout_marginHorizontal="20dp" 51 android:layout_marginHorizontal="20dp" />
51 android:layout_marginTop="28dp" />
52 52
53 <LinearLayout 53 <LinearLayout
54 android:layout_width="match_parent" 54 android:layout_width="match_parent"
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index 750ce094a..5252adf54 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -134,18 +134,21 @@
134 <FrameLayout 134 <FrameLayout
135 android:id="@+id/overlay_container" 135 android:id="@+id/overlay_container"
136 android:layout_width="match_parent" 136 android:layout_width="match_parent"
137 android:layout_height="match_parent"> 137 android:layout_height="match_parent"
138 android:fitsSystemWindows="true">
138 139
139 <TextView 140 <com.google.android.material.textview.MaterialTextView
140 android:id="@+id/show_fps_text" 141 android:id="@+id/show_fps_text"
142 style="@style/TextAppearance.Material3.BodySmall"
141 android:layout_width="wrap_content" 143 android:layout_width="wrap_content"
142 android:layout_height="wrap_content" 144 android:layout_height="wrap_content"
143 android:layout_gravity="left" 145 android:layout_gravity="left"
144 android:clickable="false" 146 android:clickable="false"
145 android:focusable="false" 147 android:focusable="false"
146 android:shadowColor="@android:color/black" 148 android:paddingHorizontal="20dp"
147 android:textColor="@android:color/white" 149 android:textColor="@android:color/white"
148 android:textSize="12sp" 150 android:shadowColor="@android:color/black"
151 android:shadowRadius="3"
149 tools:ignore="RtlHardcoded" /> 152 tools:ignore="RtlHardcoded" />
150 153
151 </FrameLayout> 154 </FrameLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml
index 1cb421dcb..d84093ba3 100644
--- a/src/android/app/src/main/res/layout/fragment_home_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml
@@ -14,13 +14,14 @@
14 android:layout_width="match_parent" 14 android:layout_width="match_parent"
15 android:layout_height="match_parent" 15 android:layout_height="match_parent"
16 android:orientation="vertical" 16 android:orientation="vertical"
17 android:background="?attr/colorSurface"> 17 android:background="?attr/colorSurface"
18 android:paddingHorizontal="8dp">
18 19
19 <ImageView 20 <ImageView
20 android:id="@+id/logo_image" 21 android:id="@+id/logo_image"
21 android:layout_width="128dp" 22 android:layout_width="96dp"
22 android:layout_height="128dp" 23 android:layout_height="96dp"
23 android:layout_margin="64dp" 24 android:layout_marginVertical="32dp"
24 android:layout_gravity="center_horizontal" 25 android:layout_gravity="center_horizontal"
25 android:src="@drawable/ic_yuzu_full" /> 26 android:src="@drawable/ic_yuzu_full" />
26 27
diff --git a/src/android/app/src/main/res/layout/fragment_search.xml b/src/android/app/src/main/res/layout/fragment_search.xml
index b8d54d947..efdfd7047 100644
--- a/src/android/app/src/main/res/layout/fragment_search.xml
+++ b/src/android/app/src/main/res/layout/fragment_search.xml
@@ -127,6 +127,7 @@
127 android:layout_height="wrap_content" 127 android:layout_height="wrap_content"
128 android:clipToPadding="false" 128 android:clipToPadding="false"
129 android:paddingVertical="4dp" 129 android:paddingVertical="4dp"
130 app:checkedChip="@id/chip_recently_played"
130 app:chipSpacingHorizontal="12dp" 131 app:chipSpacingHorizontal="12dp"
131 app:singleLine="true" 132 app:singleLine="true"
132 app:singleSelection="true"> 133 app:singleSelection="true">
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml
index f1037a740..544280e75 100644
--- a/src/android/app/src/main/res/layout/list_item_setting.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting.xml
@@ -10,41 +10,59 @@
10 android:focusable="true" 10 android:focusable="true"
11 android:gravity="center_vertical" 11 android:gravity="center_vertical"
12 android:minHeight="72dp" 12 android:minHeight="72dp"
13 android:padding="@dimen/spacing_large"> 13 android:padding="16dp">
14 14
15 <LinearLayout 15 <LinearLayout
16 android:layout_width="match_parent" 16 android:layout_width="match_parent"
17 android:layout_height="wrap_content" 17 android:layout_height="wrap_content"
18 android:orientation="vertical"> 18 android:orientation="horizontal">
19 19
20 <com.google.android.material.textview.MaterialTextView 20 <ImageView
21 android:id="@+id/text_setting_name" 21 android:id="@+id/icon"
22 style="@style/TextAppearance.Material3.HeadlineMedium" 22 android:layout_width="24dp"
23 android:layout_width="match_parent" 23 android:layout_height="24dp"
24 android:layout_height="wrap_content" 24 android:layout_marginStart="8dp"
25 android:textAlignment="viewStart" 25 android:layout_marginEnd="24dp"
26 android:textSize="16sp" 26 android:layout_gravity="center_vertical"
27 app:lineHeight="22dp" 27 android:visibility="gone"
28 tools:text="Setting Name" /> 28 app:tint="?attr/colorOnSurface" />
29
30 <com.google.android.material.textview.MaterialTextView
31 android:id="@+id/text_setting_description"
32 style="@style/TextAppearance.Material3.BodySmall"
33 android:layout_width="match_parent"
34 android:layout_height="wrap_content"
35 android:layout_marginTop="@dimen/spacing_small"
36 android:textAlignment="viewStart"
37 tools:text="@string/app_disclaimer" />
38 29
39 <com.google.android.material.textview.MaterialTextView 30 <LinearLayout
40 android:id="@+id/text_setting_value"
41 style="@style/TextAppearance.Material3.LabelMedium"
42 android:layout_width="match_parent" 31 android:layout_width="match_parent"
43 android:layout_height="wrap_content" 32 android:layout_height="wrap_content"
44 android:layout_marginTop="@dimen/spacing_small" 33 android:orientation="vertical">
45 android:textAlignment="viewStart" 34
46 android:textStyle="bold" 35 <com.google.android.material.textview.MaterialTextView
47 tools:text="1x" /> 36 android:id="@+id/text_setting_name"
37 style="@style/TextAppearance.Material3.HeadlineMedium"
38 android:layout_width="match_parent"
39 android:layout_height="wrap_content"
40 android:textAlignment="viewStart"
41 android:textSize="17sp"
42 app:lineHeight="22dp"
43 tools:text="Setting Name" />
44
45 <com.google.android.material.textview.MaterialTextView
46 android:id="@+id/text_setting_description"
47 style="@style/TextAppearance.Material3.BodySmall"
48 android:layout_width="match_parent"
49 android:layout_height="wrap_content"
50 android:layout_marginTop="@dimen/spacing_small"
51 android:textAlignment="viewStart"
52 tools:text="@string/app_disclaimer" />
53
54 <com.google.android.material.textview.MaterialTextView
55 android:id="@+id/text_setting_value"
56 style="@style/TextAppearance.Material3.LabelMedium"
57 android:layout_width="match_parent"
58 android:layout_height="wrap_content"
59 android:layout_marginTop="@dimen/spacing_small"
60 android:textAlignment="viewStart"
61 android:textStyle="bold"
62 android:textSize="13sp"
63 tools:text="1x" />
64
65 </LinearLayout>
48 66
49 </LinearLayout> 67 </LinearLayout>
50 68
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
index a5767adee..a8f5aff78 100644
--- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
@@ -8,9 +8,7 @@
8 android:clickable="true" 8 android:clickable="true"
9 android:focusable="true" 9 android:focusable="true"
10 android:minHeight="72dp" 10 android:minHeight="72dp"
11 android:paddingVertical="@dimen/spacing_large" 11 android:padding="16dp">
12 android:paddingStart="@dimen/spacing_large"
13 android:paddingEnd="24dp">
14 12
15 <com.google.android.material.materialswitch.MaterialSwitch 13 <com.google.android.material.materialswitch.MaterialSwitch
16 android:id="@+id/switch_widget" 14 android:id="@+id/switch_widget"
@@ -24,7 +22,7 @@
24 android:layout_height="wrap_content" 22 android:layout_height="wrap_content"
25 android:layout_alignParentTop="true" 23 android:layout_alignParentTop="true"
26 android:layout_centerVertical="true" 24 android:layout_centerVertical="true"
27 android:layout_marginEnd="@dimen/spacing_large" 25 android:layout_marginEnd="24dp"
28 android:layout_toStartOf="@+id/switch_widget" 26 android:layout_toStartOf="@+id/switch_widget"
29 android:gravity="center_vertical" 27 android:gravity="center_vertical"
30 android:orientation="vertical"> 28 android:orientation="vertical">
@@ -35,7 +33,7 @@
35 android:layout_width="wrap_content" 33 android:layout_width="wrap_content"
36 android:layout_height="wrap_content" 34 android:layout_height="wrap_content"
37 android:textAlignment="viewStart" 35 android:textAlignment="viewStart"
38 android:textSize="16sp" 36 android:textSize="17sp"
39 app:lineHeight="28dp" 37 app:lineHeight="28dp"
40 tools:text="@string/frame_limit_enable" /> 38 tools:text="@string/frame_limit_enable" />
41 39
diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml
index cf85bc0da..21276b19e 100644
--- a/src/android/app/src/main/res/layout/list_item_settings_header.xml
+++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml
@@ -7,7 +7,8 @@
7 android:layout_height="wrap_content" 7 android:layout_height="wrap_content"
8 android:layout_gravity="start|center_vertical" 8 android:layout_gravity="start|center_vertical"
9 android:paddingHorizontal="@dimen/spacing_large" 9 android:paddingHorizontal="@dimen/spacing_large"
10 android:paddingVertical="16dp" 10 android:paddingTop="16dp"
11 android:paddingBottom="8dp"
11 android:textAlignment="viewStart" 12 android:textAlignment="viewStart"
12 android:textColor="?attr/colorPrimary" 13 android:textColor="?attr/colorPrimary"
13 android:textStyle="bold" 14 android:textStyle="bold"
diff --git a/src/android/app/src/main/res/resources.properties b/src/android/app/src/main/res/resources.properties
new file mode 100644
index 000000000..467b3efec
--- /dev/null
+++ b/src/android/app/src/main/res/resources.properties
@@ -0,0 +1 @@
unqualifiedResLocale=en-US
diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml
new file mode 100644
index 000000000..07dffffe8
--- /dev/null
+++ b/src/android/app/src/main/res/values-ar/strings.xml
@@ -0,0 +1,385 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3
4 <string name="emulation_notification_channel_name">المحاكي نشط</string>
5 <string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون المحاكي نشطاً</string>
6 <string name="emulation_notification_running">يوزو يعمل</string>
7 <string name="notice_notification_channel_name">الإشعارات والأخطاء</string>
8 <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string>
9 <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string>
10
11 <!-- Setup strings -->
12 <string name="welcome">مرحبًا</string>
13 <string name="welcome_description">والانتقال إلى المحاكاة <b>يوزو</b> تعر٠على كيÙية إعداد.</string>
14 <string name="get_started">لنبدأ</string>
15 <string name="keys">Ø§Ù„Ù…ÙØ§ØªÙŠØ­</string>
16 <string name="keys_description">اختر مل٠&lt;b>prod.keys&lt;/b> من الزر ادناه</string>
17 <string name="select_keys">إختيار Ø§Ù„Ù…ÙØ§ØªÙŠØ­</string>
18 <string name="games">الألعاب</string>
19 <string name="games_description">اختر مجلد &lt;b>العابك&lt;/b> من الزر ادناه.</string>
20 <string name="done">إنهاء</string>
21 <string name="done_description">كل شيء جاهز./n استمتع بألعابك!</string>
22 <string name="text_continue">استمر</string>
23 <string name="next">التالي</string>
24 <string name="back">عودة</string>
25 <string name="add_games">Ø¥Ø¶Ø§ÙØ© ألعاب</string>
26 <string name="add_games_description">إختار مجلد ألعابك</string>
27 <string name="step_complete">مكتمل</string>
28
29 <!-- Home strings -->
30 <string name="home_games">الألعاب</string>
31 <string name="home_search">البحث</string>
32 <string name="home_settings">الإعدادات</string>
33 <string name="empty_gamelist">لم يتم العثور على Ù…Ù„ÙØ§Øª او لم يتم تحديد مسار العاب.</string>
34 <string name="search_and_filter_games">بحث وتصÙية الألعاب</string>
35 <string name="select_games_folder">تحديد مجلد الألعاب</string>
36 <string name="select_games_folder_description">يسمح لـ يوزو بملء قائمة الألعاب</string>
37 <string name="add_games_warning">تخط٠اختيار مجلد الالعاب؟</string>
38 <string name="add_games_warning_description">لن يتم عرض الألعاب ÙÙŠ قائمة الألعاب إذا لم يتم تحديد مجلد</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">البحث عن ألعاب</string>
41 <string name="search_settings">إعدادات البحث</string>
42 <string name="games_dir_selected">تم تحديد مجلد الألعاب</string>
43 <string name="install_prod_keys">تثبيت prod.keys</string>
44 <string name="install_prod_keys_description">مطلوب Ù„ÙÙƒ تشÙير ألعاب البيع بالتجزئة</string>
45 <string name="install_prod_keys_warning">تخطي Ø¥Ø¶Ø§ÙØ© Ø§Ù„Ù…ÙØ§ØªÙŠØ­ØŸ</string>
46 <string name="install_prod_keys_warning_description">مطلوب Ù…ÙØ§ØªÙŠØ­ صالحة لمحاكاة ألعاب البيع بالتجزئة. ستعمل تطبيقات البيرة المنزلية Ùقط إذا تابعت</string>
47 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
48 <string name="notifications">التنبيهات</string>
49 <string name="notifications_description">امنح إذن الإشعار باستخدام الزر أدناه</string>
50 <string name="give_permission">منح الإذن</string>
51 <string name="notification_warning">تخطي منح إذن الإشعارات؟</string>
52 <string name="notification_warning_description">لن يتمكن يوزو من إشعارك بالمعلومات المهمة</string>
53 <string name="permission_denied">تم Ø±ÙØ¶ الإذن</string>
54 <string name="permission_denied_description">لقد Ø±ÙØ¶Øª هذا الإذن عدة مرات ويتعين عليك الآن منحه يدويًا ÙÙŠ إعدادات النظام</string>
55 <string name="about">حول</string>
56 <string name="about_description">بناء الإصدار، والاعتمادات، وأكثر من ذلك</string>
57 <string name="warning_help">مساعدة</string>
58 <string name="warning_skip">تخطي</string>
59 <string name="warning_cancel">إلغاء</string>
60 <string name="install_amiibo_keys">تثبيت Ù…ÙØ§ØªÙŠØ­ أميبو</string>
61 <string name="install_amiibo_keys_description">مطلوب لاستخدام أميبو ÙÙŠ اللعبة</string>
62 <string name="invalid_keys_file">تم تحديد Ù…Ù„Ù Ù…ÙØ§ØªÙŠØ­ غير صالح</string>
63 <string name="install_keys_success">تم تثبيت Ø§Ù„Ù…ÙØ§ØªÙŠØ­ بنجاح</string>
64 <string name="reading_keys_failure">خطأ ÙÙŠ قراءة Ù…ÙØ§ØªÙŠØ­ التشÙير</string>
65 <string name="invalid_keys_error">Ù…ÙØ§ØªÙŠØ­ التشÙير غير صالحة</string>
66 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
67 <string name="install_keys_failure_description">المل٠المحدد غير صحيح أو تالÙ. يرجى إعادة Ø§Ù„Ù…ÙØ§ØªÙŠØ­ الخاصة بك</string>
68 <string name="install_gpu_driver">GPU تثبيت برنامج تشغيل</string>
69 <string name="install_gpu_driver_description">قم بتثبيت برامج تشغيل بديلة للحصول على أداء أو دقة Ø£ÙØ¶Ù„</string>
70 <string name="advanced_settings">إعدادات متقدمة</string>
71 <string name="advanced_settings_game">إعدادات متقدمة: %1$s</string>
72 <string name="settings_description">تكوين إعدادات المحاكي</string>
73 <string name="search_recently_played">لعبت مؤخرا</string>
74 <string name="search_recently_added">أضي٠مؤخرا</string>
75 <string name="search_retail">بيع بالتجزئة</string>
76 <string name="search_homebrew">البيرة المنزلية</string>
77 <string name="open_user_folder">ÙØªØ­ مجلد يوزو</string>
78 <string name="open_user_folder_description">إدارة Ù…Ù„ÙØ§Øª يوزو الداخلية</string>
79 <string name="theme_and_color_description">تعديل مظهر التطبيق</string>
80 <string name="no_file_manager">لم يتم العثور على مدير Ø§Ù„Ù…Ù„ÙØ§Øª</string>
81 <string name="notification_no_directory_link">لا يمكن ÙØªØ­ مجلد يوزو</string>
82 <string name="notification_no_directory_link_description">الرجاء تحديد موقع مجلد المستخدم باستخدام اللوحة الجانبية لمدير Ø§Ù„Ù…Ù„ÙØ§Øª يدويًا</string>
83 <string name="manage_save_data">إدارة Ø­ÙØ¸ البيانات</string>
84 <string name="manage_save_data_description">Ø­ÙØ¸ البيانات التي تم العثور عليها. يرجى اختيار أحد الخيارات التالية</string>
85 <string name="import_export_saves_description">استيراد أو تصدير Ù…Ù„ÙØ§Øª Ø§Ù„Ø­ÙØ¸</string>
86 <string name="save_file_imported_success">تم الاستيراد بنجاح</string>
87 <string name="save_file_invalid_zip_structure">بنية مجلد Ø§Ù„Ø­ÙØ¸ غير صالحة</string>
88 <string name="save_file_invalid_zip_structure_description">يجب أن يكون اسم المجلد Ø§Ù„ÙØ±Ø¹ÙŠ Ø§Ù„Ø£ÙˆÙ„ هو معر٠عنوان اللعبة.</string>
89 <string name="import_saves">استيراد</string>
90 <string name="export_saves">تصدير</string>
91 <string name="install_firmware">تثبيت البرامج الثابتة</string>
92 <string name="firmware_installing">تثبيت البرامج الثابتة</string>
93 <string name="firmware_installed_success">تم تثبيت البرامج الثابتة بنجاح</string>
94 <string name="firmware_installed_failure">ÙØ´Ù„ تثبيت البرامج الثابتة</string>
95 <string name="share_log">مشاركة سجلات التصحيح</string>
96 <string name="share_log_description">مشاركة مل٠سجل يوزو لتصحيح المشكلات</string>
97 <string name="share_log_missing">لم يتم العثور على مل٠السجل</string>
98 <string name="install_game_content">تثبيت محتوى اللعبة</string>
99 <string name="install_game_content_description">DLC قم بتثبيت تحديثات اللعبة أو</string>
100 <string name="installing_game_content">جار٠تثبيت المحتوى</string>
101 <string name="install_game_content_failure_base">لا ÙŠÙØ³Ù…Ø­ بتثبيت الألعاب الأساسية لتجنب التعارضات المحتملة.</string>
102 <string name="install_game_content_success_install">%1$d تم التثبيت بنجاح</string>
103 <string name="install_game_content_success_overwrite">%1$d تمت الكتابة Ùوقه بنجاح</string>
104 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
105 <string name="custom_driver_not_supported">برامج التشغيل المخصصة غير مدعومة</string>
106 <string name="custom_driver_not_supported_description">تحميل برنامج التشغيل المخصص غير معتمد حاليًا لهذا الجهاز.\nحدد هذا الخيار مرة أخرى ÙÙŠ المستقبل Ù„Ù…Ø¹Ø±ÙØ© ما إذا تمت Ø¥Ø¶Ø§ÙØ© الدعم!</string>
107 <string name="manage_yuzu_data">إدارة بيانات يوزو</string>
108 <string name="manage_yuzu_data_description">استيراد/تصدير البرامج الثابتة ÙˆØ§Ù„Ù…ÙØ§ØªÙŠØ­ وبيانات المستخدم والمزيد!</string>
109 <string name="share_save_file">مشاركة Ù…Ù„Ù Ø§Ù„Ø­ÙØ¸</string>
110 <string name="export_save_failed">ÙØ´Ù„ تصدير Ø§Ù„Ø­ÙØ¸</string>
111
112 <string name="copied_to_clipboard">نسخ إلى Ø§Ù„Ø­Ø§ÙØ¸Ø©</string>
113 <string name="about_app_description">محاكي سويتش Ù…ÙØªÙˆØ­ المصدر</string>
114 <string name="contributors">المساهمين</string>
115 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
116 <string name="licenses_description">المشاريع التي تجعل تطبيق يوزو لنظام أندرويد ممكنًا</string>
117 <string name="build">البناء</string>
118 <string name="user_data">بيانات المستخدم</string>
119 <string name="exporting_user_data">جار٠تصدير بيانات المستخدم</string>
120 <string name="importing_user_data">جار٠استيراد بيانات المستخدم</string>
121 <string name="import_user_data">استيراد بيانات المستخدم</string>
122 <string name="invalid_yuzu_backup">نسخة احتياطية يوزو غير صالحة</string>
123 <string name="user_data_export_success">تم تصدير بيانات المستخدم بنجاح</string>
124 <string name="user_data_import_success">تم استيراد بيانات المستخدم بنجاح</string>
125 <string name="user_data_export_cancelled">تم إلغاء التصدير</string>
126 <string name="support_link">https://discord.gg/u77vRWY</string>
127 <string name="website_link">https://yuzu-emu.org/</string>
128 <string name="github_link">https://github.com/yuzu-emu</string>
129
130 <!-- Early access upgrade strings -->
131 <string name="early_access">الوصول المبكر</string>
132 <string name="get_early_access">احصل على الوصول المبكر</string>
133 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
134 <string name="get_early_access_description">الميزات المتطورة، والوصول المبكر إلى التحديثات، وأكثر من ذلك</string>
135 <string name="early_access_benefits">مزايا الوصول المبكر</string>
136 <string name="cutting_edge_features">ميزات متطورة</string>
137 <string name="early_access_updates">الوصول المبكر إلى التحديثات</string>
138 <string name="no_manual_installation">لا يوجد التثبيت اليدوي</string>
139 <string name="prioritized_support">الدعم ذو الأولوية</string>
140 <string name="helping_game_preservation">المساعدة ÙÙŠ Ø§Ù„Ø­ÙØ§Ø¸ على اللعبة</string>
141 <string name="our_eternal_gratitude">امتناننا الأبدي</string>
142 <string name="are_you_interested">هل انت مهتم؟</string>
143
144 <!-- General settings strings -->
145 <string name="frame_limit_enable">الحد من السرعة</string>
146 <string name="frame_limit_enable_description">يحد من سرعة المحاكاة بنسبة محددة من السرعة العادية</string>
147 <string name="frame_limit_slider">الحد من السرعة ÙÙŠ المئة</string>
148 <string name="frame_limit_slider_description">يحدد النسبة المئوية للحد من سرعة المحاكاة. 100% هي السرعة الطبيعية. ستؤدي القيم الأعلى أو الأدنى إلى زيادة أو تقليل حد السرعة.</string>
149 <string name="cpu_accuracy">دقة وحدة المعالجة المركزية</string>
150 <string name="value_with_units">%1$s%2$s</string>
151
152 <!-- System settings strings -->
153 <string name="use_docked_mode">وضع الإرساء</string>
154 <string name="use_docked_mode_description">زيادة الدقة، ÙˆØ§Ù†Ø®ÙØ§Ø¶ الأداء. يتم استخدام الوضع المحمول عند تعطيله، مما يؤدي إلى Ø®ÙØ¶ الدقة وزيادة الأداء.</string>
155 <string name="emulated_region">المنطقة التي تمت محاكاتها</string>
156 <string name="emulated_language">لغة المحاكاه</string>
157 <string name="select_rtc_date">حدد التاريخ Ùˆ الساعة ÙÙŠ الوقت الحقيقي</string>
158 <string name="select_rtc_time">حدد وقت الساعة ÙÙŠ الوقت Ø§Ù„ÙØ¹Ù„ÙŠ</string>
159 <string name="use_custom_rtc">ساعة مخصصة ÙÙŠ الوقت الحقيقي</string>
160 <string name="use_custom_rtc_description">يسمح لك بتعيين ساعة مخصصة ÙÙŠ الوقت Ø§Ù„ÙØ¹Ù„ÙŠ Ù…Ù†ÙØµÙ„Ø© عن وقت النظام الحالي لديك</string>
161 <string name="set_custom_rtc">تعيين ساعة مخصصة ÙÙŠ الوقت الحقيقي</string>
162
163 <!-- Graphics settings strings -->
164 <string name="renderer_accuracy">مستوى الدقة</string>
165 <string name="renderer_resolution">(Handheld/Docked) الدقة</string>
166 <string name="renderer_vsync">VSync وضع</string>
167 <string name="renderer_screen_layout">الاتجاه</string>
168 <string name="renderer_aspect_ratio">تناسب الابعاد</string>
169 <string name="renderer_anti_aliasing">طريقة Ù…ÙƒØ§ÙØ­Ø© التعرج</string>
170 <string name="renderer_asynchronous_shaders">استخدم تظليل غير متزامن</string>
171 <string name="renderer_asynchronous_shaders_description">يجمع التظليل بشكل غير متزامن، مما يقلل من التأتأة ولكنه قد يؤدي إلى حدوث بعض الأخطاء.</string>
172 <string name="renderer_reactive_flushing">استخدم Ø§Ù„ØªÙ†Ø¸ÙŠÙ Ø§Ù„ØªÙØ§Ø¹Ù„ÙŠ</string>
173 <string name="renderer_reactive_flushing_description">تحسين دقة العرض ÙÙŠ بعض الألعاب على حساب الأداء</string>
174 <string name="use_disk_shader_cache_description">يقلل من التأتأة عن طريق تخزين وتحميل التظليلات التي تم إنشاؤها محليًا.</string>
175
176 <!-- Debug settings strings -->
177 <string name="cpu">وحدة المعالج المركزية</string>
178 <string name="cpu_debug_mode">تصحيح أخطاء وحدة المعالجة المركزية</string>
179 <string name="cpu_debug_mode_description">يضع وحدة المعالجة المركزية ÙÙŠ وضع التصحيح البطيء.</string>
180 <string name="gpu">GPU</string>
181 <string name="renderer_api">API</string>
182 <string name="renderer_debug">تصحيح الأخطاء الرسومية</string>
183 <string name="renderer_debug_description">يضبط واجهة برمجة تطبيقات الرسومات على وضع تصحيح الأخطاء البطيء.</string>
184 <string name="fastmem">Fastmem</string>
185
186 <!-- Audio settings strings -->
187 <string name="audio_output_engine">محرك الإخراج</string>
188 <string name="audio_volume">حجم</string>
189 <string name="audio_volume_description">يحدد حجم إخراج الصوت</string>
190
191 <!-- Miscellaneous -->
192 <string name="slider_default">Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
193 <string name="ini_saved">الإعدادات المحÙوظة</string>
194 <string name="gameid_saved">الإعدادات المحÙوظة لـ %1$s</string>
195 <string name="unimplemented_menu">القائمة غير Ø§Ù„Ù…Ù†ÙØ°Ø©</string>
196 <string name="loading">جاري تحميل</string>
197 <string name="shutting_down">إيقا٠تشغيل</string>
198 <string name="reset_setting_confirmation">هل تريد إعادة تعيين هذا الإعداد مرة أخرى إلى قيمته Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠØ©ØŸ</string>
199 <string name="reset_to_default">إعادة تعيين إلى Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
200 <string name="reset_all_settings">إعادة تعيين جميع الإعدادات؟</string>
201 <string name="reset_all_settings_description">سيتم إعادة تعيين ÙƒØ§ÙØ© الإعدادات المتقدمة إلى تكوينها Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ. هذا لا يمكن التراجع عنها.</string>
202 <string name="settings_reset">إعادة تعيين الأعدادات</string>
203 <string name="close">إغلاق</string>
204 <string name="learn_more">Ù…Ø¹Ø±ÙØ© المزيد</string>
205 <string name="auto">تلقائي</string>
206 <string name="submit">إرسال</string>
207 <string name="string_null">قيمه خاليه</string>
208 <string name="string_import">استيراد</string>
209 <string name="export">تصدير</string>
210 <string name="export_failed">ÙØ´Ù„ التصدير</string>
211 <string name="import_failed">ÙØ´Ù„ الاستيراد</string>
212 <string name="cancelling">إلغاء</string>
213
214 <!-- GPU driver installation -->
215 <string name="select_gpu_driver">GPU حدد برنامج تشغيل</string>
216 <string name="select_gpu_driver_title">الحالي الخاص بك؟ GPU هل ترغب ÙÙŠ استبدال برنامج تشغيل</string>
217 <string name="select_gpu_driver_install">تثبيت</string>
218 <string name="select_gpu_driver_default">Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
219 <string name="select_gpu_driver_use_default">يستخدم تعري٠معالج الرسوميات Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
220 <string name="select_gpu_driver_error">تم تحديد برنامج تشغيل غير صالح ØŒ باستخدام النظام Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
221 <string name="system_gpu_driver">تعري٠معالج الرسوميات الخاص بالنظام</string>
222 <string name="installing_driver">جار٠تثبيت برنامج التشغيل…</string>
223
224 <!-- Preferences Screen -->
225 <string name="preferences_settings">إعدادات</string>
226 <string name="preferences_general">عام</string>
227 <string name="preferences_system">النظام</string>
228 <string name="preferences_graphics">الرسوميات</string>
229 <string name="preferences_audio">الصوت</string>
230 <string name="preferences_theme">السمة واللون</string>
231 <string name="preferences_debug">تصحيح الأخطاء</string>
232
233 <!-- ROM loading errors -->
234 <string name="loader_error_encrypted">الخاص بك ROM تم تشÙير</string>
235 <string name="loader_error_video_core">حدث خطأ أثناء تهيئة مركز الÙيديو</string>
236 <string name="loader_error_invalid_format">ROM غير قادر على تحميل</string>
237 <string name="loader_error_file_not_found">غير موجود ROM ملÙ</string>
238
239 <!-- Emulation Menu -->
240 <string name="emulation_exit">الخروج من المحاكاة</string>
241 <string name="emulation_done">منجز</string>
242 <string name="emulation_fps_counter">عداد إطار/ثانية</string>
243 <string name="emulation_toggle_controls">تبديل عناصر التحكم</string>
244 <string name="emulation_rel_stick_center">مركز العصا النسبي</string>
245 <string name="emulation_dpad_slide">مزلاق أزرار الاتجاهات</string>
246 <string name="emulation_haptics">الاهتزازات الديناميكية</string>
247 <string name="emulation_show_overlay">عرض التراكب</string>
248 <string name="emulation_toggle_all">تبديل الكل</string>
249 <string name="emulation_control_adjust">ضبط التراكب</string>
250 <string name="emulation_control_scale">حجم</string>
251 <string name="emulation_control_opacity">العتامه</string>
252 <string name="emulation_touch_overlay_reset">إعادة تعيين التراكب</string>
253 <string name="emulation_touch_overlay_edit">تحرير التراكب</string>
254 <string name="emulation_pause">إيقا٠المحاكاة مؤقتًا</string>
255 <string name="emulation_unpause">إلغاء الإيقا٠المؤقت للمضاهاة</string>
256 <string name="emulation_input_overlay">خيارات التراكب</string>
257
258 <string name="load_settings">جار٠تحميل الإعدادات</string>
259
260 <!-- Software keyboard -->
261 <string name="software_keyboard">لوحة Ø§Ù„Ù…ÙØ§ØªÙŠØ­ البرمجية</string>
262
263 <!-- Errors and warnings -->
264 <string name="abort_button">إلغاء</string>
265 <string name="continue_button">استمر</string>
266 <string name="system_archive_not_found">لم يتم العثور على أرشي٠النظام</string>
267 <string name="system_archive_general">أرشي٠النظام</string>
268 <string name="save_load_error">خطأ ÙÙŠ Ø§Ù„Ø­ÙØ¸/التحميل</string>
269 <string name="fatal_error">خطا ÙØ§Ø¯Ø­</string>
270 <string name="performance_warning">سيؤدي إيقا٠تشغيل هذا الإعداد إلى تقليل أداء المحاكاة بشكل ملحوظ! للحصول على Ø£ÙØ¶Ù„ تجربة، يوصى بترك هذا الإعداد ممكنًا.</string>
271 <string name="memory_formatted">%1$s %2$s</string>
272 <string name="no_game_present">لا توجد لعبة قابلة للتمهيد</string>
273
274 <!-- Region Names -->
275 <string name="region_japan">اليابان</string>
276 <string name="region_usa">الولايات المتحدة الأمريكية</string>
277 <string name="region_europe">أوروبا</string>
278 <string name="region_australia">أستراليا</string>
279 <string name="region_china">الصين</string>
280 <string name="region_korea">كوريا</string>
281 <string name="region_taiwan">تايوان</string>
282
283 <!-- Memory Sizes -->
284 <string name="memory_byte">Byte</string>
285 <string name="memory_kilobyte">KB</string>
286 <string name="memory_megabyte">MB</string>
287 <string name="memory_gigabyte">GB</string>
288 <string name="memory_terabyte">TB</string>
289 <string name="memory_petabyte">PB</string>
290 <string name="memory_exabyte">EB</string>
291
292 <!-- Renderer APIs -->
293 <string name="renderer_vulkan">Vulkan</string>
294 <string name="renderer_none">لاشيء</string>
295
296 <!-- Renderer Accuracy -->
297 <string name="renderer_accuracy_normal">عادي</string>
298 <string name="renderer_accuracy_high">عالي</string>
299 <string name="renderer_accuracy_extreme">Extreme (بطيء)</string>
300
301 <!-- Resolutions -->
302 <string name="resolution_half">0.5X (360p/540p)</string>
303 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
304 <string name="resolution_one">1X (720p/1080p)</string>
305 <string name="resolution_two">2X (1440p/2160p) (بطيء)</string>
306 <string name="resolution_three">3X (2160p/3240p) (بطيء)</string>
307 <string name="resolution_four">4X (2880p/4320p) (بطيء)</string>
308
309 <!-- Renderer VSync -->
310 <string name="renderer_vsync_immediate">Immediate (Off)</string>
311 <string name="renderer_vsync_mailbox">Mailbox</string>
312 <string name="renderer_vsync_fifo">FIFO (On)</string>
313 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
314
315 <!-- Scaling Filters -->
316 <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string>
317 <string name="scaling_filter_bilinear">Bilinear</string>
318 <string name="scaling_filter_bicubic">Bicubic</string>
319 <string name="scaling_filter_gaussian">Gaussian</string>
320 <string name="scaling_filter_scale_force">ScaleForce</string>
321 <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string>
322
323 <!-- Anti-Aliasing -->
324 <string name="anti_aliasing_none">لا شيء</string>
325 <string name="anti_aliasing_fxaa">FXAA</string>
326 <string name="anti_aliasing_smaa">SMAA</string>
327
328 <!-- Screen Layouts -->
329 <string name="screen_layout_landscape">اÙقي</string>
330 <string name="screen_layout_portrait">عمودي</string>
331 <string name="screen_layout_auto">تلقائي</string>
332
333 <!-- Aspect Ratios -->
334 <string name="ratio_default">(16:9) Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
335 <string name="ratio_force_four_three">4:3 ÙØ±Ø¶</string>
336 <string name="ratio_force_twenty_one_nine">21:9 ÙØ±Ø¶</string>
337 <string name="ratio_force_sixteen_ten">16:10 ÙØ±Ø¶</string>
338 <string name="ratio_stretch">تمتد إلى Ø§Ù„Ù†Ø§ÙØ°Ø©</string>
339
340 <!-- CPU Accuracy -->
341 <string name="cpu_accuracy_accurate">دقه</string>
342 <string name="cpu_accuracy_unsafe">غير آمن</string>
343 <string name="cpu_accuracy_paranoid">Paranoid (Slow)</string>
344
345 <!-- Gamepad Buttons -->
346 <string name="gamepad_d_pad">أزرار الاتجاهات</string>
347 <string name="gamepad_left_stick">العصا اليسرى</string>
348 <string name="gamepad_right_stick">العصا اليمنى</string>
349 <string name="gamepad_home">شاشة الإستقبال</string>
350 <string name="gamepad_screenshot">لقطة شاشة</string>
351
352 <!-- Disk shader cache -->
353 <string name="preparing_shaders">تحضير التظليل</string>
354 <string name="building_shaders">بناء التظليل</string>
355
356 <!-- Theme options -->
357 <string name="change_app_theme">تغيير سمة التطبيق</string>
358 <string name="theme_default">Ø§ÙØªØ±Ø§Ø¶ÙŠ</string>
359 <string name="theme_material_you">Material You</string>
360
361 <!-- Theme Modes -->
362 <string name="change_theme_mode">تغيير وضع السمة</string>
363 <string name="theme_mode_follow_system">اتبع النظام</string>
364 <string name="theme_mode_light">ÙØ§ØªØ­</string>
365 <string name="theme_mode_dark">غامق</string>
366
367 <!-- Audio output engines -->
368 <string name="cubeb">cubeb</string>
369
370 <!-- Black backgrounds theme -->
371 <string name="use_black_backgrounds">خلÙيات سوداء</string>
372 <string name="use_black_backgrounds_description">عند استخدام المظهر الداكن، قم بتطبيق خلÙيات سوداء.</string>
373
374 <!-- Picture-In-Picture -->
375 <string name="picture_in_picture">صورة داخل صورة</string>
376 <string name="picture_in_picture_description">تصغير Ø§Ù„Ù†Ø§ÙØ°Ø© عند وضعها ÙÙŠ الخلÙية</string>
377 <string name="pause">توقÙ</string>
378 <string name="play">تشغيل</string>
379 <string name="mute">كتم</string>
380 <string name="unmute">إلغاء الكتم</string>
381
382 <!-- Licenses screen strings -->
383 <string name="licenses">التراخيص</string>
384 <string name="license_fidelityfx_fsr_description">AMD ترقية عالية الجودة من</string>
385 </resources>
diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml
new file mode 100644
index 000000000..d2e5fee19
--- /dev/null
+++ b/src/android/app/src/main/res/values-ckb/strings.xml
@@ -0,0 +1,336 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3
4 <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری Ùˆ کلیلی تێدا نییە..&lt;br /&gt;&lt;br /&gt;Ù¾ÛŽØ´ ئەوەی دەست Ù¾ÛŽ بکەیت، تکایە شوێنی ÙØ§ÛŒÙ„ÛŒ <![CDATA[<b> prod.keys </b>]]> دیاریبکە Ù„Û• Ù†ÛŽÙˆ کۆگای ئامێرەکەت.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر Ùێربە</a>]]></string>
5 <string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string>
6 <string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string>
7 <string name="emulation_notification_running">یوزو کاردەکات</string>
8 <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string>
9 <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string>
10 <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">بەخێربێیت!</string>
14 <string name="welcome_description">Ùێربە Ú†Û†Ù† &lt;b>yuzu&lt;/b> ڕێکبخەیت Ùˆ بچییە ناو ئیمولەیشن.</string>
15 <string name="get_started">دەست پێبکە</string>
16 <string name="keys">کلیلەکان</string>
17 <string name="keys_description">ÙØ§ÛŒÙ„ÛŒ &lt;b>prod.keys&lt;/b> هەڵبژێرە بە دوگمەی خوارەوە.</string>
18 <string name="select_keys">کلیلەکان هەڵبژێرە</string>
19 <string name="games">یاریەکان</string>
20 <string name="games_description">Ùۆڵدەری &lt;b>Games&lt;/b> هەڵبژێرە بە دوگمەی خوارەوە.</string>
21 <string name="done">تەواو</string>
22 <string name="done_description">تۆ تەواو ئامادەیت.\nچێژ لە یارییەکانت وەربگرە!</string>
23 <string name="text_continue">بەردەوام بوون</string>
24 <string name="next">دواتر</string>
25 <string name="back">گەڕانەوە</string>
26 <string name="add_games">زیادکردنی یاری</string>
27 <string name="add_games_description">Ùۆڵدەری یارییەکانت هەڵبژێرە</string>
28 <!-- Home strings -->
29 <string name="home_games">یاریەکان</string>
30 <string name="home_search">گەڕان</string>
31 <string name="home_settings">ڕێکخستنەکان</string>
32 <string name="empty_gamelist">تا ئێستا هیچ ÙØ§ÛŒÙ„ÛŽÚ© نەدۆزراوەتەوە یان هیچ ناونیشانێکی یاری هەڵنەبژێردراوە.</string>
33 <string name="search_and_filter_games">گەڕان Ùˆ Ùلتەرکردنی یارییەکان</string>
34 <string name="select_games_folder">Ùۆڵدەری یارییەکان هەڵبژێرە</string>
35 <string name="select_games_folder_description">ڕێگە بە یوزو دەدات بۆ پڕکردنەوەی لیستی یارییەکان</string>
36 <string name="add_games_warning">هەڵبژاردنی Ùۆڵدەری یارییەکان تێپەڕدەکەیت؟</string>
37 <string name="add_games_warning_description">یارییەکان Ù„Û• لیستی یارییەکاندا پیشان نادرێن ئەگەر Ùۆڵدەرێک هەڵنەبژێردرێت.</string>
38 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
39 <string name="home_search_games">گەڕان بەدوای یارییەکاندا</string>
40 <string name="games_dir_selected">ناونیشانی یارییەکان هەڵبژێردرا</string>
41 <string name="install_prod_keys">دابمەزرێنە prod.keys</string>
42 <string name="install_prod_keys_description">پێویستە بۆ کۆدکردنەوەى یارییە تاکەکەسییەکان</string>
43 <string name="install_prod_keys_warning">زیادکردنی کلیلەکان تێپەڕدەکەیت؟</string>
44 <string name="install_prod_keys_warning_description">کلیلی دروست پێویستە بۆ وەرگرتنی یارییەکانی تاکەکەسی. تەنها ئەپەکانی homebrew کاردەکەن ئەگەر بەردەوام بیت.</string>
45 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
46 <string name="notifications">ئاگادارکردنەوەکان</string>
47 <string name="notifications_description">بە دوگمەی خوارەوە مۆڵەتی ئاگادارکردنەوەکە بدە.</string>
48 <string name="give_permission">مۆڵەت بدە</string>
49 <string name="notification_warning">پێدانی مۆڵەتی ئاگادارکردنەوە تێپەڕدەکەیت؟</string>
50 <string name="notification_warning_description">یوزو ناتوانێت لە زانیاری گرنگ ئاگادارت بکاتەوە.</string>
51 <string name="permission_denied">مۆڵەت پێدان ڕەتکرایەوە</string>
52 <string name="permission_denied_description">زۆر جار ئەم مۆڵەتەت ڕەتکردۆتەوە و ئێستا دەبێت بە دەستی ڕێگەپێدان بکەیت لە ڕێکخستنەکانی سیستەمدا.</string>
53 <string name="about">دەربارە</string>
54 <string name="about_description">وەشانی دروستکردن، بیتبێن و زۆر شتیتر</string>
55 <string name="warning_help">یارمەتی</string>
56 <string name="warning_skip">پەڕاندن</string>
57 <string name="warning_cancel">ڕەتکردنەوە</string>
58 <string name="install_amiibo_keys">دامەزراندنی کلیلی Amiibo</string>
59 <string name="install_amiibo_keys_description">پێویستە بۆ بەکارهێنانی Amiibo لە یاریدا</string>
60 <string name="invalid_keys_file">ÙØ§ÛŒÙ„ÛŒ کلیلێکی نادروست هەڵبژێردرا</string>
61 <string name="install_keys_success">کلیلەکان بە سەرکەوتوویی دامەزران</string>
62 <string name="reading_keys_failure">هەڵە لە خوێندنەوەی کۆدکردنی کلیل</string>
63 <string name="install_prod_keys_failure_extension_description">دڵنیابەوە Ú©Û• ÙØ§ÛŒÙ„ÛŒ کلیلەکانت درێژکراوەی .keys ÛŒ هەیە Ùˆ دووبارە هەوڵبدەرەوە.</string>
64 <string name="install_amiibo_keys_failure_extension_description">دڵنیابە Ú©Û• ÙØ§ÛŒÙ„ÛŒ کلیلەکانت درێژکراوەی .bin ÛŒ هەیە Ùˆ دووبارە هەوڵبدەرەوە.</string>
65 <string name="invalid_keys_error">کلیلی کۆدکردنی نادروستە</string>
66 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
67 <string name="install_keys_failure_description">ÙØ§ÛŒÙ„Û• هەڵبژێردراوەکە هەڵەیە یان تێکچووە. تکایە دووبارە کلیلەکانت دەربێنەوە.</string>
68 <string name="install_gpu_driver">دامەزراندنی وەگەڕخەری GPU</string>
69 <string name="install_gpu_driver_description">دامەزراندنی وەگەڕخەری بەدیل بۆ ئەوەی بە ئەگەرێکی زۆرەوە کارایی باشتر یان وردبینی هەبێت</string>
70 <string name="advanced_settings">ڕێکخستنە پێشکەوتووەکان</string>
71 <string name="settings_description">سازدانی ڕێکخستنەکانی ئیمولەیتەر</string>
72 <string name="search_recently_played">بەم دواییە یاری کردووە</string>
73 <string name="search_recently_added">بەم دواییە زیادکرا</string>
74 <string name="search_retail">بەتاک</string>
75 <string name="search_homebrew">هۆم بریو</string>
76 <string name="open_user_folder">کردنەوەی Ùۆڵدەری یوزو</string>
77 <string name="open_user_folder_description">بەڕێوەبردنی ÙØ§ÛŒÙ„Û• ناوخۆییەکانی یوزو</string>
78 <string name="theme_and_color_description">دەستکاری کردنی شێوازی ئەپەکە</string>
79 <string name="no_file_manager">هیچ ÙØ§ÛŒÙ„ بەڕێوەبەرێک نەدۆزرایەوە</string>
80 <string name="notification_no_directory_link">نەتوانرا ناونیشانی یوزو بکرێتەوە</string>
81 <string name="notification_no_directory_link_description">تکایە شوێنی Ùۆڵدەری بەکارهێنەر Ù„Û•Ú¯Û•Úµ پانێڵی لایەنی ÙØ§ÛŒÙ„ بەڕێوەبارەکان بە دەست بدۆزەرەوە.</string>
82 <string name="manage_save_data">بەڕێوەبردنی داتای پاشەکەوتکراو</string>
83 <string name="manage_save_data_description">داتای پاشەکەوتکراو دۆزراوە. تکایە لە خوارەوە بژاردەیەک هەڵبژێرە.</string>
84 <string name="import_export_saves_description">هاوردەکردن یان هەناردەکردنی ÙØ§ÛŒÙ„ÛŒ پاشەکەوتکراو</string>
85 <string name="save_file_imported_success">بە سەرکەوتوویی هاوردە کرا</string>
86 <string name="save_file_invalid_zip_structure">پێکهاتەی شوێنی پاشەکەوتکراو نادروستە</string>
87 <string name="save_file_invalid_zip_structure_description">ناوی یەکەمی Ùۆڵدەر دەبێت ناسنامەی ناونیشانی یارییەکە بێت.</string>
88 <string name="import_saves">هاوردەکردن</string>
89 <string name="export_saves">هەناردەکردن</string>
90 <string name="install_firmware">دامەزراندنی پتەوواڵا</string>
91 <string name="install_firmware_description">پتەوواڵا دەبێت Ù„Û• ئەرشیÙÛŒ زیپدا بێت Ùˆ پێویستە بۆ بووتکردنی هەندێک یاری</string>
92 <string name="firmware_installing">دامەزرانی پتەوواڵا</string>
93 <string name="firmware_installed_success">پتەوواڵا بە سەرکەوتوویی دامەزرا</string>
94 <string name="firmware_installed_failure">دامەزراندنی پتەوواڵا شکستی هێنا</string>
95 <string name="share_log">هاوبەشی پێکردنی لۆگەکانی چاککردنەوە</string>
96 <string name="share_log_description">ÙØ§ÛŒÙ„Û• Ù„Û†Ú¯Û•Ú©Û•ÛŒ یوزو هاوبەش بکە بۆ چاککردنی کێشەکان</string>
97 <string name="share_log_missing">هیچ ÙØ§ÛŒÙ„ÛŽÚ©ÛŒ Ù„Û†Ú¯ نەدۆزراوە</string>
98 <string name="install_game_content">دامەزراندنی ناوەڕۆکی یاری</string>
99 <string name="install_game_content_description">دامەزراندنی نوێکاری یارییەکان یان DLC</string>
100 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
101 <!-- About screen strings -->
102 <string name="gaia_is_not_real">گایا ڕاستەقینە نییە</string>
103 <string name="copied_to_clipboard">کۆپی کرا بۆ تەختەی نووسین</string>
104 <string name="about_app_description">ئیمۆلیتەرێکی سەرچاوە-کراوەی سویچ</string>
105 <string name="contributors">بەشداربووان</string>
106 <string name="contributors_description">دروستکراوە لەگەڵ \u2764 لەلایەن تیمەکەی یوزو</string>
107 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
108 <string name="licenses_description">ئەو پڕۆژانەی کە یوزوی بۆ ئەندرۆید ڕەخساند</string>
109 <string name="build">بونیات</string>
110 <string name="support_link">https://discord.gg/u77vRWY</string>
111 <string name="website_link">https://yuzu-emu.org/</string>
112 <string name="github_link">https://github.com/yuzu-emu</string>
113
114 <!-- Early access upgrade strings -->
115 <string name="early_access">بەزوویی دەسپێگەشتن</string>
116 <string name="get_early_access">بەدەستهێنانی بەزوویی دەسپێگەشتن</string>
117 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
118 <string name="get_early_access_description">تایبەتمەندییە پێشکەوتووەکان، بەزوویی دەستگەیشتن بە نوێکارییەکان و زۆر شتی تر</string>
119 <string name="early_access_benefits">سوودەکانی بەزوویی دەسپێگەشتن</string>
120 <string name="cutting_edge_features">تایبەتمەندییە پێشکەوتووەکان</string>
121 <string name="early_access_updates">زوو دەستگەیشتن بە نوێکارییەکان</string>
122 <string name="no_manual_installation">چیتر دامەزراندنی دەستی نییە</string>
123 <string name="prioritized_support">پشتگیری لە پێشینە</string>
124 <string name="helping_game_preservation">یارمەتیدانی پاراستنی یارییەکان</string>
125 <string name="our_eternal_gratitude">سوپاس و پێزانینی هەمیشەییمان</string>
126 <string name="are_you_interested">ئایا تۆ خوازیاریت؟</string>
127
128 <!-- General settings strings -->
129 <string name="frame_limit_enable">سنووردارکردنی خێرایی</string>
130 <string name="frame_limit_enable_description">خێرایی ئیمولەیشن سنووردار دەکات بۆ ڕێژەیەکی دیاریکراو لە خێرایی ئاسایی.</string>
131 <string name="frame_limit_slider">سنووردارکردنی لەسەدای خێرایی</string>
132 <string name="frame_limit_slider_description">ڕێژەی سەدی دیاری دەکات بۆ سنووردارکردنی خێرایی ئیمولەیشن. 100% خێرایی ئاساییە. بەهایی بەرزتر یان نزمتر دەبێتە هۆی زیاد یان کەمکردنەوەی سنووری خێرایی.</string>
133 <string name="cpu_accuracy">وردی CPU</string>
134 <!-- System settings strings -->
135 <string name="use_docked_mode">دۆخی دۆککراو</string>
136 <string name="use_docked_mode_description">ڕوونی زیاد دەکات، کارایی کەم دەکاتەوە. دۆخی دەستی بەکاردێت کاتێک لەکاردەخرێت، ئەمەش ڕوونی دادەبەزێنێت و کارایی زیاد دەکات.</string>
137 <string name="emulated_region">ناوچەی ئیمولەیشن</string>
138 <string name="emulated_language">زمانی ئیمولەیتەر</string>
139 <string name="select_rtc_date">هەڵبژاردنی بەرواری RTC</string>
140 <string name="select_rtc_time">هەڵبژاردنی کاتی RTC</string>
141 <string name="use_custom_rtc">RTCی تایبەتمەند</string>
142 <string name="use_custom_rtc_description">ڕێگەت پێدەدات کاتژمێرێکی کاتی ڕاستەقینەی تایبەتمەند دابنێیت کە جیاوازە لە کاتی ئێستای سیستەمەکەت.</string>
143 <string name="set_custom_rtc">دانانی RTCی تایبەتمەند</string>
144
145 <!-- Graphics settings strings -->
146 <string name="renderer_accuracy">ئاستی وردبینی</string>
147 <string name="renderer_resolution">ڕوونی (دۆخی دەستی/دۆخی دۆک)</string>
148 <string name="renderer_vsync">دۆخی VSync</string>
149 <string name="renderer_aspect_ratio">ڕێژەی ڕووبەری شاشە</string>
150 <string name="renderer_scaling_filter">Ùلتەری گونجاندنی پەنجەرە</string>
151 <string name="renderer_anti_aliasing">شێوازی دژە-خاوڕۆیی</string>
152 <string name="renderer_force_max_clock">ناچاریکردن بۆ زۆرترین کاتژمێر (تەنها ئەدرینۆ)</string>
153 <string name="renderer_force_max_clock_description">GPU ناچار دەکات بە زۆرترین کاتژمێر کاربکات (هێشتا سنووردارکردنی گەرمی جێبەجێ دەکرێت).</string>
154 <string name="renderer_asynchronous_shaders">بەکارهێنانی سێبەری ناهاوسەنگ</string>
155 <string name="renderer_asynchronous_shaders_description">سێبەرەکان بە شێوەیەکی ناهاوسەنگ کۆدەکاتەوە، پچڕپچڕی کەمدەکاتەوە بەڵام لەوانەیە گلێچ دروستکا.</string>
156 <string name="renderer_reactive_flushing">بەکارهێنانی بەرپێچدەرەوە</string>
157 <string name="renderer_reactive_flushing_description">وردی ڕێندەرکردن لە هەندێک یاریدا باشتر دەکات لەسەر تێچووی کارایی.</string>
158 <string name="use_disk_shader_cache">بیرگەخێرای سێبەری دیسک</string>
159 <string name="use_disk_shader_cache_description">پچڕپچڕی کەمدەکاتەوە بە هەڵگرتن و بارکردنی سێبەری دروستکراو لە ناوخۆدا.</string>
160
161 <!-- Debug settings strings -->
162 <string name="cpu">CPU</string>
163 <string name="renderer_api">API گراÙیک</string>
164 <string name="renderer_debug">چاککردنەوەی گراÙیک</string>
165 <string name="renderer_debug_description">API ÛŒ گراÙیکەکان ڕێکدەخات بۆ دۆخی چاککردنی خاو.</string>
166 <string name="audio_volume">قەبارەی دەنگی</string>
167 <string name="audio_volume_description">دیاریکردنی قەبارەی دەنگی دەرچووی بیستۆک و بزوێنەری دەنگی دەرەکی.</string>
168
169 <!-- Miscellaneous -->
170 <string name="slider_default">بنەڕەت</string>
171 <string name="ini_saved">ڕێکخستنە پاشەکەوتکراوەکان</string>
172 <string name="gameid_saved">ڕێکخستنە پاشەکەوتکراوەکان بۆ %1$s</string>
173 <string name="error_saving">هەڵە لە پاشەکەوتکردن %1$s.ini: %2$s</string>
174 <string name="loading">بارکردن...</string>
175 <string name="reset_setting_confirmation">ئایا دەتەوێت ئەم ڕێکخستنە بگەڕێنیتەوە بۆ بەهای بنەڕەتی خۆی؟</string>
176 <string name="reset_to_default">دوبارە ڕێکخستنەوەی بۆ بنەڕەت</string>
177 <string name="reset_all_settings">هەموو ڕێکخستنەکان دوبارە ڕێک دەخاتەوە؟</string>
178 <string name="reset_all_settings_description">هەموو ڕێکخستنە پێشکەوتووەکان دەگەڕێنەوە بۆ ڕێکخستنی بنەڕەتی خۆیان. پاشگەز بوونەوەی نییه.</string>
179 <string name="settings_reset">دوبارە ڕێککردنەوەی ڕێکخستنەکان</string>
180 <string name="close">داخستن</string>
181 <string name="learn_more">زیاتر Ùێربە</string>
182 <string name="auto">خودکار</string>
183 <string name="submit">پێشکەشکردن</string>
184 <string name="string_import">هاوردەکردن</string>
185 <string name="export">هەناردەکردن</string>
186 <!-- GPU driver installation -->
187 <string name="select_gpu_driver">هەڵبژاردنی وەگەڕخەری GPU</string>
188 <string name="select_gpu_driver_title">حەز دەکەیت وەگەڕخەری GPU ی ئێستات بگۆڕیت؟</string>
189 <string name="select_gpu_driver_install">دامەزراندن</string>
190 <string name="select_gpu_driver_default">بنەڕەت</string>
191 <string name="select_gpu_driver_use_default">بەکارهێنانی وەگەڕخەری GPU ی بنەڕەت</string>
192 <string name="select_gpu_driver_error">وەگەڕخەری نادروست هەڵبژێردرا، بە بەکارهێنانی بنەڕەتی سیستەم!</string>
193 <string name="system_gpu_driver">وەگەڕخەری GPU ی سیستەم</string>
194 <string name="installing_driver">دامەزراندنی وەگەڕخەر...</string>
195
196 <!-- Preferences Screen -->
197 <string name="preferences_settings">ڕێکخستنەکان</string>
198 <string name="preferences_general">گشتی</string>
199 <string name="preferences_system">سیستەم</string>
200 <string name="preferences_graphics">گراÙیک</string>
201 <string name="preferences_audio">دەنگ</string>
202 <string name="preferences_theme">ڕەنگ و ڕووکار</string>
203 <string name="preferences_debug">چاککردنەوە</string>
204
205 <!-- ROM loading errors -->
206 <string name="loader_error_encrypted">ڕۆمەکەت کۆدکراوە</string>
207 <string name="loader_error_encrypted_keys_description"><![CDATA[تکایە دڵنیابەوە لەدامەزراوی <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ÙØ§ÛŒÙ„ەکەت بۆ ئەوەی بتوانرێت یارییەکان کۆد بکرێنەوە.]]></string>
208 <string name="loader_error_video_core">هەڵەیەک لە دەستپێکردنی ناوەکی ڤیدیۆکەدا ڕوویدا</string>
209 <string name="loader_error_video_core_description">ئەمەش بەزۆری بەهۆی وەگەڕخەرێکی ناتەبای GPU ەوەیە. دامەزراندنی وەگەڕخەری GPU ی تایبەتمەندکراو لەوانەیە ئەم کێشەیە چارەسەر بکات.</string>
210 <string name="loader_error_invalid_format">ناتوانرێت ڕۆم باربکرێت</string>
211 <string name="loader_error_file_not_found">ÙØ§ÛŒÙ„ÛŒ Ú•Û†Ù… بوونی نییە</string>
212
213 <!-- Emulation Menu -->
214 <string name="emulation_exit">دەرچوون لە ئیمولەیشن</string>
215 <string name="emulation_done">تەواو</string>
216 <string name="emulation_fps_counter">FPS ژمێر</string>
217 <string name="emulation_toggle_controls">گۆڕینی کۆنتڕۆڵ</string>
218 <string name="emulation_rel_stick_center">ناوەندی گێڕ بەنزیکەیی</string>
219 <string name="emulation_dpad_slide">خلیسکانی 4 دوگمەکە</string>
220 <string name="emulation_haptics">لەرینەوەی پەنجەلێدان</string>
221 <string name="emulation_show_overlay">نیشاندانی داپۆشەر</string>
222 <string name="emulation_toggle_all">گۆڕینی سەرجەم</string>
223 <string name="emulation_control_adjust">ڕێکخستنی داپۆشەر</string>
224 <string name="emulation_control_scale">پێوەر</string>
225 <string name="emulation_control_opacity">ڕوونی</string>
226 <string name="emulation_touch_overlay_reset">دووبارە ڕێکخستنەوەی داپۆشەر</string>
227 <string name="emulation_touch_overlay_edit">دەستکاریکردنی داپۆشەر</string>
228 <string name="emulation_pause">وەستاندنی ئیمولەیشن</string>
229 <string name="emulation_unpause">لادانی وەستاندنی ئیمولەیشن</string>
230 <string name="emulation_input_overlay">هەڵبژاردەکانی داپۆشەر</string>
231
232 <string name="load_settings">بارکردنی ڕێکخستنەکان...</string>
233
234 <!-- Software keyboard -->
235 <string name="software_keyboard">کیبۆردی نەرمەکاڵا</string>
236
237 <!-- Errors and warnings -->
238 <string name="abort_button">دەربارە</string>
239 <string name="continue_button">بەردەوام بوون</string>
240 <string name="system_archive_not_found">ئەرشیÙÛŒ سیستەم نەدۆزراوە</string>
241 <string name="system_archive_not_found_message">%s دیار نییە. تکایە ئەرشیÙÛŒ سیستەمەکەت ÙÚ•ÛŽ بدە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە Ù‡Û†ÛŒ تێکچوون Ùˆ Ùڕێدانەدەرەوە.</string>
242 <string name="system_archive_general">ئەرشیÙÛŽÚ©ÛŒ سیستەم</string>
243 <string name="save_load_error">هەڵەی پاشەکەوتکردن/بارکردن</string>
244 <string name="fatal_error">هەڵەی کوشندە</string>
245 <string name="fatal_error_message">هەڵەیەکی کوشندە ڕوویدا. بۆ وردەکارییەکان Ù„Û†Ú¯Û•Ú©Û• بپشکنە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە Ù‡Û†ÛŒ تێکچوون Ùˆ Ùڕێدانەدەرەوە.</string>
246 <string name="performance_warning">کوژاندنەوەی ئەم ڕێکخستنە دەبێتە هۆی کەمکردنەوەی کارایی ئیمولەیشن! بۆ باشترین ئەزموون، باشترە ئەم ڕێکخستنە چالاک بهێڵیتەوە.</string>
247 <!-- Region Names -->
248 <string name="region_japan">ژاپۆن</string>
249 <string name="region_usa">ئەمریکا</string>
250 <string name="region_europe">ئەورووپا</string>
251 <string name="region_australia">ئوسترالیا</string>
252 <string name="region_china">چین</string>
253 <string name="region_korea">کۆریا</string>
254 <string name="region_taiwan">تایوان</string>
255
256 <string name="memory_gigabyte">GB</string>
257 <!-- Renderer APIs -->
258 <string name="renderer_vulkan">ڤوڵکان</string>
259 <string name="renderer_none">هیچ</string>
260
261 <!-- Renderer Accuracy -->
262 <string name="renderer_accuracy_normal">ئاسایی</string>
263 <string name="renderer_accuracy_high">بەرز</string>
264 <string name="renderer_accuracy_extreme">ئەوپەڕ (خاو)</string>
265
266 <!-- Resolutions -->
267 <string name="resolution_half">0.5X (360p/540p)</string>
268 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
269 <string name="resolution_one">1X (720p/1080p)</string>
270 <string name="resolution_two">2X (1440p/2160p) (خاو)</string>
271 <string name="resolution_three">3X (2160p/3240p) (خاو)</string>
272 <string name="resolution_four">4X (2880p/4320p) (خاو)</string>
273
274 <!-- Renderer VSync -->
275 <string name="renderer_vsync_immediate">دەستبەجێ (کوژاوە)</string>
276 <string name="renderer_vsync_mailbox">سندوقی پۆستە</string>
277 <string name="renderer_vsync_fifo">FIFO (پێکراو)</string>
278 <string name="renderer_vsync_fifo_relaxed">FIFO ئارام</string>
279
280 <!-- Scaling Filters -->
281 <string name="scaling_filter_nearest_neighbor">نزیکترین دراوسێ</string>
282 <string name="scaling_filter_bilinear">دوو هێڵی</string>
283 <string name="scaling_filter_bicubic">دووخشتەکی</string>
284 <string name="scaling_filter_gaussian">گاوسی</string>
285 <string name="scaling_filter_scale_force">پێوەرهێز</string>
286 <string name="scaling_filter_fsr">AMD FidelityFX™ سوپەر ووردبینی</string>
287
288 <!-- Anti-Aliasing -->
289 <string name="anti_aliasing_none">هیچ</string>
290 <string name="anti_aliasing_fxaa">FXAA</string>
291 <string name="anti_aliasing_smaa">SMAA</string>
292
293 <string name="screen_layout_auto">خودکار</string>
294
295 <!-- Aspect Ratios -->
296 <string name="ratio_default">بنەڕەت (16:9)</string>
297 <string name="ratio_force_four_three">ڕووبەری 4:3</string>
298 <string name="ratio_force_twenty_one_nine">ڕووبەری 21:9</string>
299 <string name="ratio_force_sixteen_ten">ڕووبەری 16:10</string>
300 <string name="ratio_stretch">کشانی پڕ بەشاشە</string>
301
302 <!-- CPU Accuracy -->
303 <string name="cpu_accuracy_accurate">وورد</string>
304 <string name="cpu_accuracy_unsafe">ناسەقامگیر</string>
305 <string name="cpu_accuracy_paranoid">بەگومان (خاو)</string>
306
307 <!-- Gamepad Buttons -->
308 <string name="gamepad_d_pad">4 دوگمەکە</string>
309 <string name="gamepad_left_stick">گێڕی چەپ</string>
310 <string name="gamepad_right_stick">گێڕی ڕاست</string>
311 <string name="gamepad_home">ماڵەوە</string>
312 <string name="gamepad_screenshot">وێنەگرتنی شاشە</string>
313
314 <!-- Disk shader cache -->
315 <string name="preparing_shaders">ئامادەکردنی سێبەرەکان</string>
316 <string name="building_shaders">دروستکردنی سێبەرەکان</string>
317
318 <!-- Theme options -->
319 <string name="change_app_theme">گۆڕینی ڕووکاری ئەپەکە</string>
320 <string name="theme_default">بنەڕەت</string>
321 <string name="theme_material_you">کەرەستەی تۆ</string>
322
323 <!-- Theme Modes -->
324 <string name="change_theme_mode">گۆڕینی دۆخی ڕووکار</string>
325 <string name="theme_mode_follow_system">پەیڕەوی کردنی سیستەم</string>
326 <string name="theme_mode_light">ڕوناکی</string>
327 <string name="theme_mode_dark">تاریک</string>
328
329 <!-- Black backgrounds theme -->
330 <string name="use_black_backgrounds">پاشبنەمای ڕەش</string>
331 <string name="use_black_backgrounds_description">لە کاتی بەکارهێنانی ڕووکاری تاریکدا، پاشبنەمای ڕەش دادەنێ.</string>
332
333 <!-- Licenses screen strings -->
334 <string name="licenses">مۆڵەتەکان</string>
335 <string name="license_fidelityfx_fsr_description">بەرزکردنەوەی کوالێتی بەرز لە کۆمپانیای AMD</string>
336 </resources>
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 72a47fbdb..9c6590b5e 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> 4 <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulation ist aktiv</string> 5 <string name="emulation_notification_channel_name">Emulation ist aktiv</string>
@@ -25,6 +25,7 @@
25 <string name="back">Zurück</string> 25 <string name="back">Zurück</string>
26 <string name="add_games">Spiele hinzufügen</string> 26 <string name="add_games">Spiele hinzufügen</string>
27 <string name="add_games_description">Spieleverzeichnis auswählen</string> 27 <string name="add_games_description">Spieleverzeichnis auswählen</string>
28 <string name="step_complete">Fertig!</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Spiele</string> 31 <string name="home_games">Spiele</string>
@@ -38,6 +39,7 @@
38 <string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string> 39 <string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Spiele suchen</string> 41 <string name="home_search_games">Spiele suchen</string>
42 <string name="search_settings">Einstellungen suchen</string>
41 <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string> 43 <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string>
42 <string name="install_prod_keys">prod.keys installieren</string> 44 <string name="install_prod_keys">prod.keys installieren</string>
43 <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string> 45 <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string>
@@ -60,8 +62,11 @@
60 <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string> 62 <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string>
61 <string name="install_keys_success">Schlüssel erfolgreich installiert</string> 63 <string name="install_keys_success">Schlüssel erfolgreich installiert</string>
62 <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string> 64 <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string>
65 <string name="install_prod_keys_failure_extension_description">Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".keys\" hat, und versuchen Sie es erneut.</string>
66 <string name="install_amiibo_keys_failure_extension_description">Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".bin\" hat, und versuchen Sie es erneut.</string>
63 <string name="invalid_keys_error">Ungültige Schlüssel</string> 67 <string name="invalid_keys_error">Ungültige Schlüssel</string>
64 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 68 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
69 <string name="install_keys_failure_description">Die ausgewählte Datei ist falsch oder beschädigt. Bitte kopieren Sie Ihre Schlüssel erneut.</string>
65 <string name="install_gpu_driver">GPU-Treiber installieren</string> 70 <string name="install_gpu_driver">GPU-Treiber installieren</string>
66 <string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string> 71 <string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string>
67 <string name="advanced_settings">Erweiterte Einstellungen</string> 72 <string name="advanced_settings">Erweiterte Einstellungen</string>
@@ -84,7 +89,17 @@
84 <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> 89 <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string>
85 <string name="import_saves">Importieren</string> 90 <string name="import_saves">Importieren</string>
86 <string name="export_saves">Exportieren</string> 91 <string name="export_saves">Exportieren</string>
87 92 <string name="install_firmware">Firmware installieren</string>
93 <string name="install_firmware_description">Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Booten einiger Spiele benötigt</string>
94 <string name="firmware_installing">Firmware wird installiert</string>
95 <string name="firmware_installed_success">Die Firmware wurde erfolgreich installiert!</string>
96 <string name="firmware_installed_failure">Bei der Firmware installation ist etwas fehlgeschlagen.</string>
97 <string name="share_log">Debug-Logs teilen</string>
98 <string name="share_log_description">Debug-Logs an yuzu zur Untersuchung absenden</string>
99 <string name="share_log_missing">Keine Log-Datei gefunden</string>
100 <string name="install_game_content">Spiel installieren</string>
101 <string name="install_game_content_description">Spiel Update oder DLC installieren</string>
102 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
88 <!-- About screen strings --> 103 <!-- About screen strings -->
89 <string name="gaia_is_not_real">Gaia ist nicht real</string> 104 <string name="gaia_is_not_real">Gaia ist nicht real</string>
90 <string name="copied_to_clipboard">In die Zwischenablage kopiert</string> 105 <string name="copied_to_clipboard">In die Zwischenablage kopiert</string>
@@ -92,7 +107,10 @@
92 <string name="contributors">Beitragende</string> 107 <string name="contributors">Beitragende</string>
93 <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string> 108 <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string>
94 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 109 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
110 <string name="licenses_description">Projekte, die yuzu für Android möglich machen </string>
95 <string name="build">Build</string> 111 <string name="build">Build</string>
112 <string name="user_data">Nutzerdaten</string>
113 <string name="user_data_export_cancelled">Export abgebrochen</string>
96 <string name="support_link">https://discord.gg/u77vRWY</string> 114 <string name="support_link">https://discord.gg/u77vRWY</string>
97 <string name="website_link">https://yuzu-emu.org/</string> 115 <string name="website_link">https://yuzu-emu.org/</string>
98 <string name="github_link">https://github.com/yuzu-emu</string> 116 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -107,45 +125,39 @@
107 <string name="early_access_updates">Früherer Zugriff auf Updates</string> 125 <string name="early_access_updates">Früherer Zugriff auf Updates</string>
108 <string name="no_manual_installation">Keine manuelle Installation</string> 126 <string name="no_manual_installation">Keine manuelle Installation</string>
109 <string name="prioritized_support">Priorisierte Unterstützung</string> 127 <string name="prioritized_support">Priorisierte Unterstützung</string>
128 <string name="helping_game_preservation">Beitrag zur Erhaltung der Spiele</string>
110 <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string> 129 <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string>
111 <string name="are_you_interested">Bist du interessiert?</string> 130 <string name="are_you_interested">Bist du interessiert?</string>
112 131
113 <!-- General settings strings --> 132 <!-- General settings strings -->
114 <string name="frame_limit_enable">Geschwindigkeitsbegrenzung aktivieren</string> 133 <string name="frame_limit_enable">Limitierte Geschwindigkeit</string>
115 <string name="frame_limit_enable_description">Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen Prozentsatz der normalen Geschwindigkeit begrenzt.</string> 134 <string name="frame_limit_enable_description">Limitiert die Geschwindigkeit auf einen von dir festgelegten Prozentsatz.</string>
116 <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string> 135 <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string>
117 <string name="frame_limit_slider_description">Legt den Prozentsatz der Bergrenzung der Emulationsgeschwindigkeit fest. Mit dem Standardwert von 100% wird die Emulation auf die normale Geschwindigkeit begrenzt. Höhere oder niedrigere Werte erhöhen oder verringern die Geschwindigkeitsbegrenzung.</string> 136 <string name="frame_limit_slider_description">Gibt die prozentuale Geschwindigkeit der Emulation an. 100% sind normal. Werte darüber oder drunter werden die Geschwindigkeit entsprechend verändern.</string>
118 <string name="cpu_accuracy">CPU-Genauigkeit</string> 137 <string name="cpu_accuracy">CPU-Genauigkeit</string>
119
120 <!-- System settings strings --> 138 <!-- System settings strings -->
121 <string name="use_docked_mode">Dock-Modus</string> 139 <string name="use_docked_mode">Gedockter Modus</string>
122 <string name="use_docked_mode_description">Emuliert im Dock-Modus, was die Auflösung verbessert, aber die Leistung senkt.</string> 140 <string name="use_docked_mode_description">Der Docked Modus erhöht die Auflösung, verringert die aber die Leistung. Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Leistung.</string>
123 <string name="emulated_region">Emulierte Region</string> 141 <string name="emulated_region">Emulierte Region</string>
124 <string name="emulated_language">Emulierte Sprache</string> 142 <string name="emulated_language">Emulierte Sprache</string>
125 <string name="select_rtc_date">RTC-Datum auswählen</string> 143 <string name="select_rtc_date">RTC-Datum auswählen</string>
126 <string name="select_rtc_time">RTC-Zeit auswählen</string> 144 <string name="select_rtc_time">RTC-Zeit auswählen</string>
127 <string name="use_custom_rtc">Benutzerdefinierte RTC aktivieren</string> 145 <string name="use_custom_rtc">Benutzerdefinierte Echtzeituhr</string>
128 <string name="use_custom_rtc_description">Mit dieser Einstellung kann eine benutzerdefinierte Echtzeituhr unabhängig von der aktuellen Systemzeit verwendet werden.</string>
129 <string name="set_custom_rtc">Benutzerdefinierte RTC einstellen</string>
130
131 <!-- Graphics settings strings --> 146 <!-- Graphics settings strings -->
132 <string name="renderer_api">API</string>
133 <string name="renderer_accuracy">Genauigkeitsstufe</string> 147 <string name="renderer_accuracy">Genauigkeitsstufe</string>
134 <string name="renderer_resolution">Auflösung</string>
135 <string name="renderer_vsync">VSync-Modus</string> 148 <string name="renderer_vsync">VSync-Modus</string>
149 <string name="renderer_screen_layout">Orientierung</string>
136 <string name="renderer_aspect_ratio">Seitenverhältnis</string> 150 <string name="renderer_aspect_ratio">Seitenverhältnis</string>
137 <string name="renderer_scaling_filter">Fensteranpassungsfilter</string> 151 <string name="renderer_scaling_filter">Fensteranpassungsfilter</string>
138 <string name="renderer_anti_aliasing">Kantenglättungs-Methode</string>
139 <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string> 152 <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string>
140 <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string> 153 <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string>
141 <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string> 154 <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string>
142 <string name="renderer_asynchronous_shaders_description">Kompiliert Shader asynchron, was Ruckler reduziert, aber zu Glitches führen kann.</string> 155 <!-- Debug settings strings -->
143 <string name="renderer_debug">Grafik-Debugging aktivieren</string> 156 <string name="cpu">CPU</string>
144 <string name="renderer_debug_description">Wenn aktiviert, schaltet die Grafik-API in einen langsameren Debugging-Modus.</string> 157 <string name="cpu_debug_mode">CPU Debugging</string>
145 <string name="use_disk_shader_cache">Nutze Festplatten-Shader-Cache</string> 158 <string name="gpu">GPU</string>
146 <string name="use_disk_shader_cache_description">Ruckeln wird durch das Speichern und Laden von generierten Shadern auf der Festplatte reduziert.</string> 159 <string name="renderer_api">API</string>
147 160 <string name="renderer_debug">Graphik-Debugging</string>
148 <!-- Audio settings strings -->
149 <string name="audio_volume">Lautstärke</string> 161 <string name="audio_volume">Lautstärke</string>
150 <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string> 162 <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string>
151 163
@@ -154,14 +166,22 @@
154 <string name="ini_saved">Einstellungen gespeichert</string> 166 <string name="ini_saved">Einstellungen gespeichert</string>
155 <string name="gameid_saved">Einstellungen für %1$s gespeichert</string> 167 <string name="gameid_saved">Einstellungen für %1$s gespeichert</string>
156 <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string> 168 <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string>
169 <string name="unimplemented_menu">Unimplementiertes Menü</string>
157 <string name="loading">Lädt...</string> 170 <string name="loading">Lädt...</string>
158 <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string> 171 <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string>
159 <string name="reset_to_default">Auf Standard zurücksetzen</string> 172 <string name="reset_to_default">Auf Standard zurücksetzen</string>
160 <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string> 173 <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string>
161 <string name="reset_all_settings_description">Alle erweiterten Einstellungen werden auf ihren Standardwert zurückgesetzt. Dies kann nicht rückgängig gemacht werden.</string>
162 <string name="settings_reset">Einstellungen zurückgesetzt</string> 174 <string name="settings_reset">Einstellungen zurückgesetzt</string>
163 <string name="close">Schließen</string> 175 <string name="close">Schließen</string>
164 <string name="learn_more">Mehr erfahren</string> 176 <string name="learn_more">Mehr erfahren</string>
177 <string name="auto">Auto</string>
178 <string name="submit">Absenden</string>
179 <string name="string_null">Null</string>
180 <string name="string_import">Importieren</string>
181 <string name="export">Exportieren</string>
182 <string name="export_failed">Export fehlgeschlagen</string>
183 <string name="import_failed">Import fehlgeschlagen</string>
184 <string name="cancelling">Abbrechen</string>
165 185
166 <!-- GPU driver installation --> 186 <!-- GPU driver installation -->
167 <string name="select_gpu_driver">GPU-Treiber auswählen</string> 187 <string name="select_gpu_driver">GPU-Treiber auswählen</string>
@@ -169,6 +189,7 @@
169 <string name="select_gpu_driver_install">Installieren</string> 189 <string name="select_gpu_driver_install">Installieren</string>
170 <string name="select_gpu_driver_default">Standard</string> 190 <string name="select_gpu_driver_default">Standard</string>
171 <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> 191 <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
192 <string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
172 <string name="system_gpu_driver">System GPU-Treiber</string> 193 <string name="system_gpu_driver">System GPU-Treiber</string>
173 <string name="installing_driver">Treiber wird installiert...</string> 194 <string name="installing_driver">Treiber wird installiert...</string>
174 195
@@ -179,6 +200,7 @@
179 <string name="preferences_graphics">Grafik</string> 200 <string name="preferences_graphics">Grafik</string>
180 <string name="preferences_audio">Audio</string> 201 <string name="preferences_audio">Audio</string>
181 <string name="preferences_theme">Theme und Farbe</string> 202 <string name="preferences_theme">Theme und Farbe</string>
203 <string name="preferences_debug">Debug</string>
182 204
183 <!-- ROM loading errors --> 205 <!-- ROM loading errors -->
184 <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string> 206 <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string>
@@ -192,22 +214,15 @@
192 <string name="emulation_exit">Emulation beenden</string> 214 <string name="emulation_exit">Emulation beenden</string>
193 <string name="emulation_done">Fertig</string> 215 <string name="emulation_done">Fertig</string>
194 <string name="emulation_fps_counter">FPS Zähler</string> 216 <string name="emulation_fps_counter">FPS Zähler</string>
195 <string name="emulation_toggle_controls">Steuerung umschalten</string>
196 <string name="emulation_rel_stick_center">Relative Stick-Mitte</string>
197 <string name="emulation_dpad_slide">DPad Slide</string>
198 <string name="emulation_haptics">Haptik</string>
199 <string name="emulation_show_overlay">Overlay anzeigen</string>
200 <string name="emulation_toggle_all">Alle umschalten</string> 217 <string name="emulation_toggle_all">Alle umschalten</string>
201 <string name="emulation_control_adjust">Overlay anpassen</string> 218 <string name="emulation_control_adjust">Overlay anpassen</string>
202 <string name="emulation_control_scale">Größe</string> 219 <string name="emulation_control_scale">Größe</string>
203 <string name="emulation_control_opacity">Transparenz</string> 220 <string name="emulation_control_opacity">Transparenz</string>
204 <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string> 221 <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string>
205 <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string> 222 <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string>
206 <string name="emulation_pause">Emulation pausieren</string>
207 <string name="emulation_unpause">Emulation fortsetzen</string>
208 <string name="emulation_input_overlay">Overlay-Optionen</string> 223 <string name="emulation_input_overlay">Overlay-Optionen</string>
209 224
210 <string name="load_settings">Lädt Einstellungen...</string> 225 <string name="load_settings">Lade Einstellungen...</string>
211 226
212 <!-- Software keyboard --> 227 <!-- Software keyboard -->
213 <string name="software_keyboard">Software-Tastatur</string> 228 <string name="software_keyboard">Software-Tastatur</string>
@@ -221,7 +236,7 @@
221 <string name="fatal_error">Schwerwiegender Fehler</string> 236 <string name="fatal_error">Schwerwiegender Fehler</string>
222 <string name="fatal_error_message">Ein schwerwiegender Fehler ist aufgetreten. Einzelheiten wurden im Log protokolliert.\nDas Fortsetzen der Emulation kann zu Abstürzen und Bugs führen.</string> 237 <string name="fatal_error_message">Ein schwerwiegender Fehler ist aufgetreten. Einzelheiten wurden im Log protokolliert.\nDas Fortsetzen der Emulation kann zu Abstürzen und Bugs führen.</string>
223 <string name="performance_warning">Das Deaktivieren dieser Einstellung führt zu erheblichen Leistungsverlusten! Für ein optimales Erlebnis wird empfohlen, sie aktiviert zu lassen.</string> 238 <string name="performance_warning">Das Deaktivieren dieser Einstellung führt zu erheblichen Leistungsverlusten! Für ein optimales Erlebnis wird empfohlen, sie aktiviert zu lassen.</string>
224 239 <string name="memory_formatted">%1$s %2$s</string>
225 <!-- Region Names --> 240 <!-- Region Names -->
226 <string name="region_japan">Japan</string> 241 <string name="region_japan">Japan</string>
227 <string name="region_usa">USA</string> 242 <string name="region_usa">USA</string>
@@ -231,6 +246,15 @@
231 <string name="region_korea">Korea</string> 246 <string name="region_korea">Korea</string>
232 <string name="region_taiwan">Taiwan</string> 247 <string name="region_taiwan">Taiwan</string>
233 248
249 <!-- Memory Sizes -->
250 <string name="memory_byte">Byte</string>
251 <string name="memory_kilobyte">KB</string>
252 <string name="memory_megabyte">MB</string>
253 <string name="memory_gigabyte">GB</string>
254 <string name="memory_terabyte">TB</string>
255 <string name="memory_petabyte">PB</string>
256 <string name="memory_exabyte">EB</string>
257
234 <!-- Renderer APIs --> 258 <!-- Renderer APIs -->
235 <string name="renderer_vulkan">Vulkan</string> 259 <string name="renderer_vulkan">Vulkan</string>
236 <string name="renderer_none">Keiner</string> 260 <string name="renderer_none">Keiner</string>
@@ -267,12 +291,15 @@
267 <string name="anti_aliasing_fxaa">FXAA</string> 291 <string name="anti_aliasing_fxaa">FXAA</string>
268 <string name="anti_aliasing_smaa">SMAA</string> 292 <string name="anti_aliasing_smaa">SMAA</string>
269 293
294 <string name="screen_layout_portrait">Portrait</string>
295 <string name="screen_layout_auto">Auto</string>
296
270 <!-- Aspect Ratios --> 297 <!-- Aspect Ratios -->
271 <string name="ratio_default">Standard (16:9)</string> 298 <string name="ratio_default">Standard (16:9)</string>
272 <string name="ratio_force_four_three">4:3 erzwingen</string> 299 <string name="ratio_force_four_three">4:3 erzwingen</string>
273 <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string> 300 <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string>
274 <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string> 301 <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string>
275 <string name="ratio_stretch">Auf Fenster anpassen</string> 302 <string name="ratio_stretch">Auf Bildschirmgröße anpsassen</string>
276 303
277 <!-- CPU Accuracy --> 304 <!-- CPU Accuracy -->
278 <string name="cpu_accuracy_accurate">Akkurat</string> 305 <string name="cpu_accuracy_accurate">Akkurat</string>
@@ -280,9 +307,9 @@
280 <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string> 307 <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string>
281 308
282 <!-- Gamepad Buttons --> 309 <!-- Gamepad Buttons -->
283 <string name="gamepad_d_pad">Steuerkreuz</string> 310 <string name="gamepad_d_pad">D-Pad</string>
284 <string name="gamepad_left_stick">Linker Analogstick</string> 311 <string name="gamepad_left_stick">Linker Stick</string>
285 <string name="gamepad_right_stick">Rechter Analogstick</string> 312 <string name="gamepad_right_stick">Rechter Stick</string>
286 <string name="gamepad_home">Home</string> 313 <string name="gamepad_home">Home</string>
287 <string name="gamepad_screenshot">Screenshot</string> 314 <string name="gamepad_screenshot">Screenshot</string>
288 315
@@ -291,18 +318,30 @@
291 <string name="building_shaders">Shader werden erstellt</string> 318 <string name="building_shaders">Shader werden erstellt</string>
292 319
293 <!-- Theme options --> 320 <!-- Theme options -->
294 <string name="change_app_theme">App-Theme ändern</string> 321 <string name="change_app_theme">App-Thema ändern</string>
295 <string name="theme_default">Standard</string> 322 <string name="theme_default">Standard</string>
296 <string name="theme_material_you">Material You</string> 323 <string name="theme_material_you">Material You</string>
297 324
298 <!-- Theme Modes --> 325 <!-- Theme Modes -->
299 <string name="change_theme_mode">Theme-Modus ändern</string> 326 <string name="change_theme_mode">Themen-Modus ändern</string>
300 <string name="theme_mode_follow_system">System folgen</string> 327 <string name="theme_mode_follow_system">System folgen</string>
301 <string name="theme_mode_light">Hell</string> 328 <string name="theme_mode_light">Hell</string>
302 <string name="theme_mode_dark">Dunkel</string> 329 <string name="theme_mode_dark">Dunkel</string>
303 330
331 <!-- Audio output engines -->
332 <string name="cubeb">cubeb</string>
333
304 <!-- Black backgrounds theme --> 334 <!-- Black backgrounds theme -->
305 <string name="use_black_backgrounds">Schwarze Hintergünde verwenden</string> 335 <string name="use_black_backgrounds">Schwarze Hintergründe</string>
306 <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string> 336 <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string>
307 337
308</resources> 338 <!-- Picture-In-Picture -->
339 <string name="picture_in_picture">Bild im Bild</string>
340 <string name="pause">Pause</string>
341 <string name="mute">Stummschalten</string>
342 <string name="unmute">Ton aktivieren</string>
343
344 <!-- Licenses screen strings -->
345 <string name="licenses">Lizenzen</string>
346 <string name="license_fidelityfx_fsr_description">Hochwertiges Upscaling von AMD</string>
347 </resources>
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index e5bdd5889..103ac6e65 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -1,7 +1,7 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o keys no vienen incluidos.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> 4 <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulación activa</string> 5 <string name="emulation_notification_channel_name">Emulación activa</string>
6 <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string> 6 <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string>
7 <string name="emulation_notification_running">yuzu esta ejecutándose</string> 7 <string name="emulation_notification_running">yuzu esta ejecutándose</string>
@@ -25,6 +25,7 @@
25 <string name="back">Atrás</string> 25 <string name="back">Atrás</string>
26 <string name="add_games">Añadir Juegos</string> 26 <string name="add_games">Añadir Juegos</string>
27 <string name="add_games_description">Selecciona la carpeta de juegos</string> 27 <string name="add_games_description">Selecciona la carpeta de juegos</string>
28 <string name="step_complete">¡Completado!</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Juegos</string> 31 <string name="home_games">Juegos</string>
@@ -37,7 +38,8 @@
37 <string name="add_games_warning">¿Omitir la selección de la carpeta de juegos?</string> 38 <string name="add_games_warning">¿Omitir la selección de la carpeta de juegos?</string>
38 <string name="add_games_warning_description">No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos.</string> 39 <string name="add_games_warning_description">No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Buscar Juegos</string> 41 <string name="home_search_games">Buscar juegos</string>
42 <string name="search_settings">Buscar configuración</string>
41 <string name="games_dir_selected">Directorio de juegos seleccionado</string> 43 <string name="games_dir_selected">Directorio de juegos seleccionado</string>
42 <string name="install_prod_keys">Instalar prod.keys</string> 44 <string name="install_prod_keys">Instalar prod.keys</string>
43 <string name="install_prod_keys_description">Requerido para descifrar juegos</string> 45 <string name="install_prod_keys_description">Requerido para descifrar juegos</string>
@@ -58,15 +60,18 @@
58 <string name="warning_cancel">Cancelar</string> 60 <string name="warning_cancel">Cancelar</string>
59 <string name="install_amiibo_keys">Instalar clave de Amiiboo</string> 61 <string name="install_amiibo_keys">Instalar clave de Amiiboo</string>
60 <string name="install_amiibo_keys_description">Necesario para usar Amiibo en el juego</string> 62 <string name="install_amiibo_keys_description">Necesario para usar Amiibo en el juego</string>
61 <string name="invalid_keys_file">Archivo de claves inválido seleccionado</string> 63 <string name="invalid_keys_file">Archivo de claves seleccionado inválido</string>
62 <string name="install_keys_success">Claves instaladas correctamente</string> 64 <string name="install_keys_success">Claves instaladas correctamente</string>
63 <string name="reading_keys_failure">Error al leer las claves de cifrado</string> 65 <string name="reading_keys_failure">Error al leer las claves de cifrado</string>
66 <string name="install_prod_keys_failure_extension_description">Compruebe que el archivo de claves tenga una extensión .keys y pruebe otra vez.</string>
67 <string name="install_amiibo_keys_failure_extension_description">Compruebe que el archivo de claves tenga una extensión .bin y pruebe otra vez.</string>
64 <string name="invalid_keys_error">Claves de cifrado no válidas</string> 68 <string name="invalid_keys_error">Claves de cifrado no válidas</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves.</string> 70 <string name="install_keys_failure_description">El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves.</string>
67 <string name="install_gpu_driver">Instalar driver de GPU</string> 71 <string name="install_gpu_driver">Instalar driver de GPU</string>
68 <string name="install_gpu_driver_description">Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores</string> 72 <string name="install_gpu_driver_description">Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores</string>
69 <string name="advanced_settings">Opciones avanzadas</string> 73 <string name="advanced_settings">Opciones avanzadas</string>
74 <string name="advanced_settings_game">Configuración avanzada: %1$s</string>
70 <string name="settings_description">Configurar las opciones del emulador</string> 75 <string name="settings_description">Configurar las opciones del emulador</string>
71 <string name="search_recently_played">Jugado recientemente</string> 76 <string name="search_recently_played">Jugado recientemente</string>
72 <string name="search_recently_added">Añadido recientemente</string> 77 <string name="search_recently_added">Añadido recientemente</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> 91 <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string>
87 <string name="import_saves">Importar</string> 92 <string name="import_saves">Importar</string>
88 <string name="export_saves">Exportar</string> 93 <string name="export_saves">Exportar</string>
94 <string name="install_firmware">Instalar firmware</string>
95 <string name="install_firmware_description">El firmware debe estar en un archivo ZIP y es necesario para ejecutar algunos juegos</string>
96 <string name="firmware_installing">Instalando firmware</string>
97 <string name="firmware_installed_success">Firmware instalado con éxito</string>
98 <string name="firmware_installed_failure">Falló la instalación de firmware</string>
99 <string name="firmware_installed_failure_description">Asegúrese de que los archivos nca del firmware estén en la raíz del zip e inténtelo de nuevo.</string>
100 <string name="share_log">Compartir registros de depuración</string>
101 <string name="share_log_description">Comparta el archivo de registro de yuzu para depurar problemas</string>
102 <string name="share_log_missing">No se encontró ningún archivo de registro</string>
103 <string name="install_game_content">Instalar contenido de juego</string>
104 <string name="install_game_content_description">Instalar actualizaciones o DLC</string>
105 <string name="installing_game_content">Instalando contenido...</string>
106 <string name="install_game_content_failure">Error instalando archivo(s) a la NAND</string>
107 <string name="install_game_content_failure_description">Asegúrese de que el/los contenido(s) son válidos y que el archivo prod.keys esté instalado.</string>
108 <string name="install_game_content_failure_base">La instalación de los juegos base no está permitida para así evitar posibles conflictos.</string>
109 <string name="install_game_content_failure_file_extension">Sólo hay soporte para el contenido en NSP y XCI. Asegúrese de que el/los contenido(s) son válidos.</string>
110 <string name="install_game_content_failed_count">%1$d error(es) de instalación</string>
111 <string name="install_game_content_success">Contenido(s) de juego instalado/s con éxito</string>
112 <string name="install_game_content_success_install">%1$d instalado con éxito</string>
113 <string name="install_game_content_success_overwrite">%1$d sobreescrito con éxito</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">Drivers personalizados no soportados</string>
116 <string name="custom_driver_not_supported_description">En estos momentos, la carga de drivers personalizados no está disponible para este dispositivo..\n¡Comprueba esta opción en el futuro para ver si ya está añadido el soporte a ese dispositivo!</string>
117 <string name="manage_yuzu_data">Administrar datos de yuzu</string>
118 <string name="manage_yuzu_data_description">Importa/exporta el firmware, las keys, los datos de usuario, ¡y más!</string>
119 <string name="share_save_file">Compartir archivo de guardado</string>
120 <string name="export_save_failed">La exportación del guardado falló</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia no es real</string> 123 <string name="gaia_is_not_real">Gaia no es real</string>
@@ -94,7 +126,18 @@
94 <string name="contributors">Contribuidores</string> 126 <string name="contributors">Contribuidores</string>
95 <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string> 127 <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">Proyectos que hacen que yuzu para Android sea una realidad</string>
97 <string name="build">Versión</string> 130 <string name="build">Versión</string>
131 <string name="user_data">Datos de usuario</string>
132 <string name="user_data_description">Importa/exporta todos los datos de usuario.\n\nCuando se importen los datos de usuario, ¡los demás datos de usuario existentes serán borrados!</string>
133 <string name="exporting_user_data">Exportando datos de usuario...</string>
134 <string name="importing_user_data">Importando datos de usuario...</string>
135 <string name="import_user_data">Importar datos de usuario</string>
136 <string name="invalid_yuzu_backup">Backup de válido</string>
137 <string name="user_data_export_success">Datos de usuario exportados con éxito</string>
138 <string name="user_data_import_success">Datos de usuario importados con éxito</string>
139 <string name="user_data_export_cancelled">Exportación cancelada</string>
140 <string name="user_data_import_failed_description">Asegúrese de que las carpetas de datos de usuario estén en la raíz de la carpeta del zip y contengan un archivo config en config/config.ini e inténtelo de nuevo.</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,41 +157,53 @@
114 <string name="are_you_interested">¿Estás interesado?</string> 157 <string name="are_you_interested">¿Estás interesado?</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">Activar limite de velocidad</string> 160 <string name="frame_limit_enable">Limitar velocidad</string>
118 <string name="frame_limit_enable_description">Cuando está habilitado, la velocidad de emulación se limitará a un porcentaje específico de la velocidad normal.</string> 161 <string name="frame_limit_enable_description">Limita la velocidad de emulación a un porcentaje específico de la velocidad normal.</string>
119 <string name="frame_limit_slider">Limitar porcentaje de velocidad</string> 162 <string name="frame_limit_slider">Limitar porcentaje de velocidad</string>
120 <string name="frame_limit_slider_description">Especifica el porcentaje para limitar la velocidad de emulación. Con el valor predeterminado del 100 %, la emulación se limitará a la velocidad normal. Valores más altos o más bajos aumentarán o disminuirán el límite de velocidad.</string> 163 <string name="frame_limit_slider_description">Especifica el porcentaje para limitar la velocidad de emulación. 100% es la velocidad normal. Valores más altos o bajos incrementarán o disminuirán el límite de velocidad.</string>
121 <string name="cpu_accuracy">Precisión de CPU</string> 164 <string name="cpu_accuracy">Precisión de CPU</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">Modo sobremesa</string> 168 <string name="use_docked_mode">Modo Sobremesa</string>
125 <string name="use_docked_mode_description">Emula en modo sobremesa, lo que aumenta la resolución perjudicando el rendimiento.</string> 169 <string name="use_docked_mode_description">Incrementa la resolución al coste de reducir el rendimiento. El Modo Portátil es usado cuando está desactivado, reduciendo la resolución y mejorando así el rendimiento.</string>
126 <string name="emulated_region">Región emulada</string> 170 <string name="emulated_region">Región emulada</string>
127 <string name="emulated_language">Idioma emulado</string> 171 <string name="emulated_language">Idioma emulado</string>
128 <string name="select_rtc_date">Seleccionar Fecha RTC</string> 172 <string name="select_rtc_date">Seleccionar fecha RTC</string>
129 <string name="select_rtc_time">Seleccionar Tiempo RTC</string> 173 <string name="select_rtc_time">Seleccionar tiempo RTC</string>
130 <string name="use_custom_rtc">Habilitar RTC Personalizado</string> 174 <string name="use_custom_rtc">RTC personalizado</string>
131 <string name="use_custom_rtc_description">Esta configuración le permite configurar un reloj de tiempo real personalizado diferente a la hora actual de su sistema</string> 175 <string name="use_custom_rtc_description">Te permite tener un reloj personalizado en tiempo real diferente del tiempo del propio sistema.</string>
132 <string name="set_custom_rtc">Establecer RTC Personalizado</string> 176 <string name="set_custom_rtc">Configurar RTC personalizado</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Nivel de precisión</string> 179 <string name="renderer_accuracy">Nivel de precisión</string>
137 <string name="renderer_resolution">Resolución</string> 180 <string name="renderer_resolution">Resolución (Portátil/Sobremesa)</string>
138 <string name="renderer_vsync">Modo VSync</string> 181 <string name="renderer_vsync">Modo VSync</string>
182 <string name="renderer_screen_layout">Orientación</string>
139 <string name="renderer_aspect_ratio">Relación de aspecto</string> 183 <string name="renderer_aspect_ratio">Relación de aspecto</string>
140 <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string> 184 <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string>
141 <string name="renderer_anti_aliasing">Metodo Anti Aliasing</string> 185 <string name="renderer_anti_aliasing">Método anti-aliasing</string>
142 <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string> 186 <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string>
143 <string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string> 187 <string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
144 <string name="renderer_asynchronous_shaders">Usar shaders asíncronos</string> 188 <string name="renderer_asynchronous_shaders">Usar shaders asíncronos</string>
145 <string name="renderer_asynchronous_shaders_description">Compila shaders de forma asincrónica, lo que reducirá los parones pero puede introducir fallos.</string> 189 <string name="renderer_asynchronous_shaders_description">Compila shaders de manera asíncrona, reduciendo los parones, pero puede introducir fallos.</string>
146 <string name="renderer_debug">Habilitar la depuración de gráficos</string> 190 <string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
147 <string name="renderer_debug_description">Cuando esté marcado, la API de gráficos entra en un modo de depuración más lento.</string> 191 <string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
148 <string name="use_disk_shader_cache">Usar caché de shaders en disco</string> 192 <string name="use_disk_shader_cache">Caché de shaders en disco</string>
149 <string name="use_disk_shader_cache_description">Reduzca los parones almacenando y cargando shaders generados en el disco.</string> 193 <string name="use_disk_shader_cache_description">Reduce los parones almacenando y cargando shaders generados.</string>
194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">Depuración de CPU</string>
198 <string name="cpu_debug_mode_description">Pone la CPU en un modo de depuración lento.</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
201 <string name="renderer_debug">Depuración de gráficos</string>
202 <string name="renderer_debug_description">Configura la API gráfica a un modo de depuración lento.</string>
203 <string name="fastmem">Fastmem</string>
150 204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">Motor de salida</string>
152 <string name="audio_volume">Volumen</string> 207 <string name="audio_volume">Volumen</string>
153 <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string> 208 <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string>
154 209
@@ -157,14 +212,24 @@
157 <string name="ini_saved">Configuración guardada</string> 212 <string name="ini_saved">Configuración guardada</string>
158 <string name="gameid_saved">Configuración guardada para %1$s</string> 213 <string name="gameid_saved">Configuración guardada para %1$s</string>
159 <string name="error_saving">Error guardando %1$s.ini: %2$s</string> 214 <string name="error_saving">Error guardando %1$s.ini: %2$s</string>
215 <string name="unimplemented_menu">Menú sin implementar</string>
160 <string name="loading">Cargando...</string> 216 <string name="loading">Cargando...</string>
217 <string name="shutting_down">Saliendo...</string>
161 <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string> 218 <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string>
162 <string name="reset_to_default">Restablecer a predeterminado</string> 219 <string name="reset_to_default">Restablecer a predeterminado</string>
163 <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string> 220 <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string>
164 <string name="reset_all_settings_description">Todas las configuraciones avanzadas se restablecerán a su configuración predeterminada. Esto no se puede deshacer.</string> 221 <string name="reset_all_settings_description">Todas las opciones avanzadas se restablecerán a su configuración predeterminada. Esta acción no se puede deshacer.</string>
165 <string name="settings_reset">Reiniciar la configuracion</string> 222 <string name="settings_reset">Reiniciar la configuracion</string>
166 <string name="close">Cerrar</string> 223 <string name="close">Cerrar</string>
167 <string name="learn_more">Más información</string> 224 <string name="learn_more">Saber más</string>
225 <string name="auto">Auto</string>
226 <string name="submit">Enviar</string>
227 <string name="string_null">Null</string>
228 <string name="string_import">Importar</string>
229 <string name="export">Exportar</string>
230 <string name="export_failed">La exportación falló</string>
231 <string name="import_failed">La importación falló</string>
232 <string name="cancelling">Cancelando</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Seleccionar driver GPU</string> 235 <string name="select_gpu_driver">Seleccionar driver GPU</string>
@@ -172,6 +237,7 @@
172 <string name="select_gpu_driver_install">Instalar</string> 237 <string name="select_gpu_driver_install">Instalar</string>
173 <string name="select_gpu_driver_default">Predeterminado</string> 238 <string name="select_gpu_driver_default">Predeterminado</string>
174 <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> 239 <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
240 <string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
175 <string name="system_gpu_driver">Driver GPU del sistema</string> 241 <string name="system_gpu_driver">Driver GPU del sistema</string>
176 <string name="installing_driver">Instalando driver...</string> 242 <string name="installing_driver">Instalando driver...</string>
177 243
@@ -182,10 +248,11 @@
182 <string name="preferences_graphics">Gráficos</string> 248 <string name="preferences_graphics">Gráficos</string>
183 <string name="preferences_audio">Audio</string> 249 <string name="preferences_audio">Audio</string>
184 <string name="preferences_theme">Tema y color</string> 250 <string name="preferences_theme">Tema y color</string>
251 <string name="preferences_debug">Depuración</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">Su ROM está encriptada</string> 254 <string name="loader_error_encrypted">Su ROM está encriptada</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga las guías para redumpear <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartuchos de juegos</a> o <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titulos instalados</a>.]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga las guías para redumpear<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartuchos de juegos</a> o <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor, compruebe que su archivo <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado, para que los juegos sean descifrados.]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor, compruebe que su archivo <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado, para que los juegos sean descifrados.]]></string>
190 <string name="loader_error_video_core">Ocurrió un error al inicializar el núcleo de video, posiblemente debido a una incompatibilidad con el driver seleccionado</string> 257 <string name="loader_error_video_core">Ocurrió un error al inicializar el núcleo de video, posiblemente debido a una incompatibilidad con el driver seleccionado</string>
191 <string name="loader_error_video_core_description">Esto suele deberse a un driver de GPU incompatible. La instalación de un controlador de GPU personalizado puede resolver este problema.</string> 258 <string name="loader_error_video_core_description">Esto suele deberse a un driver de GPU incompatible. La instalación de un controlador de GPU personalizado puede resolver este problema.</string>
@@ -196,25 +263,25 @@
196 <string name="emulation_exit">Salir de la emulación</string> 263 <string name="emulation_exit">Salir de la emulación</string>
197 <string name="emulation_done">Hecho</string> 264 <string name="emulation_done">Hecho</string>
198 <string name="emulation_fps_counter">Contador de FPS</string> 265 <string name="emulation_fps_counter">Contador de FPS</string>
199 <string name="emulation_toggle_controls">Alternar Controles</string> 266 <string name="emulation_toggle_controls">Alternar controles</string>
200 <string name="emulation_rel_stick_center">Centro Relativo del Stick</string> 267 <string name="emulation_rel_stick_center">Centro relativo del stick</string>
201 <string name="emulation_dpad_slide">Deslizamiento de la Cruceta</string> 268 <string name="emulation_dpad_slide">Deslizamiento de la cruceta</string>
202 <string name="emulation_haptics">Hápticos</string> 269 <string name="emulation_haptics">Toques hápticos</string>
203 <string name="emulation_show_overlay">Mostrar pantalla</string> 270 <string name="emulation_show_overlay">Mostrar overlay</string>
204 <string name="emulation_toggle_all">Alternar Todo</string> 271 <string name="emulation_toggle_all">Alternar todo</string>
205 <string name="emulation_control_adjust">Ajustar pantalla</string> 272 <string name="emulation_control_adjust">Ajustar overlay</string>
206 <string name="emulation_control_scale">Escala</string> 273 <string name="emulation_control_scale">Escala</string>
207 <string name="emulation_control_opacity">Opacidad</string> 274 <string name="emulation_control_opacity">Opacidad</string>
208 <string name="emulation_touch_overlay_reset">Reiniciar pantalla</string> 275 <string name="emulation_touch_overlay_reset">Reiniciar overlay</string>
209 <string name="emulation_touch_overlay_edit">Editar pantalla</string> 276 <string name="emulation_touch_overlay_edit">Editar overlay</string>
210 <string name="emulation_pause">Pausar Emulación</string> 277 <string name="emulation_pause">Pausar emulación</string>
211 <string name="emulation_unpause">Reanudar Emulación</string> 278 <string name="emulation_unpause">Despausar emulación</string>
212 <string name="emulation_input_overlay">Opciones de pantalla </string> 279 <string name="emulation_input_overlay">Opciones de overlay</string>
213 280
214 <string name="load_settings">Cargando configuración...</string> 281 <string name="load_settings">Cargando configuración...</string>
215 282
216 <!-- Software keyboard --> 283 <!-- Software keyboard -->
217 <string name="software_keyboard">Software del teclado</string> 284 <string name="software_keyboard">Teclado de software</string>
218 285
219 <!-- Errors and warnings --> 286 <!-- Errors and warnings -->
220 <string name="abort_button">Abortar</string> 287 <string name="abort_button">Abortar</string>
@@ -226,6 +293,9 @@
226 <string name="fatal_error">Error fatal</string> 293 <string name="fatal_error">Error fatal</string>
227 <string name="fatal_error_message">Ocurrió un error fatal. Consulte el registro para obtener más detalles.\nContinuar con la emulación puede provocar bloqueos y errores.</string> 294 <string name="fatal_error_message">Ocurrió un error fatal. Consulte el registro para obtener más detalles.\nContinuar con la emulación puede provocar bloqueos y errores.</string>
228 <string name="performance_warning">¡Desactivar esta configuración reducirá significativamente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejar esta configuración habilitada.</string> 295 <string name="performance_warning">¡Desactivar esta configuración reducirá significativamente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejar esta configuración habilitada.</string>
296 <string name="device_memory_inadequate">RAM de dispositivo: %1$s\nRecomendado: %2$s</string>
297 <string name="memory_formatted">%1$s %2$s</string>
298 <string name="no_game_present">¡No hay ningún juego ejecutable presente!</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">Japón</string> 301 <string name="region_japan">Japón</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">Corea</string> 306 <string name="region_korea">Corea</string>
237 <string name="region_taiwan">Taiwán</string> 307 <string name="region_taiwan">Taiwán</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Byte</string>
311 <string name="memory_kilobyte">KB</string>
312 <string name="memory_megabyte">MB</string>
313 <string name="memory_gigabyte">GB</string>
314 <string name="memory_terabyte">TB</string>
315 <string name="memory_petabyte">PB</string>
316 <string name="memory_exabyte">EB</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 319 <string name="renderer_vulkan">Vulkan</string>
@@ -274,6 +351,11 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 351 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 352 <string name="anti_aliasing_smaa">SMAA</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">Paisaje</string>
356 <string name="screen_layout_portrait">Retrato</string>
357 <string name="screen_layout_auto">Auto</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">Predeterminado (16:9)</string> 360 <string name="ratio_default">Predeterminado (16:9)</string>
279 <string name="ratio_force_four_three">Forzar 4:3</string> 361 <string name="ratio_force_four_three">Forzar 4:3</string>
@@ -298,7 +380,7 @@
298 <string name="building_shaders">Construyendo shaders</string> 380 <string name="building_shaders">Construyendo shaders</string>
299 381
300 <!-- Theme options --> 382 <!-- Theme options -->
301 <string name="change_app_theme">Cambiar Tema</string> 383 <string name="change_app_theme">Cambiar tema</string>
302 <string name="theme_default">Predeterminado</string> 384 <string name="theme_default">Predeterminado</string>
303 <string name="theme_material_you">Material You</string> 385 <string name="theme_material_you">Material You</string>
304 386
@@ -308,8 +390,22 @@
308 <string name="theme_mode_light">Claro</string> 390 <string name="theme_mode_light">Claro</string>
309 <string name="theme_mode_dark">Oscuro</string> 391 <string name="theme_mode_dark">Oscuro</string>
310 392
393 <!-- Audio output engines -->
394 <string name="cubeb">cubeb</string>
395
311 <!-- Black backgrounds theme --> 396 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">Usar Fondos Negros</string> 397 <string name="use_black_backgrounds">Fondos oscuros</string>
313 <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string> 398 <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string>
314 399
315</resources> 400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">Picture in Picture</string>
402 <string name="picture_in_picture_description">Minimizar ventana cuando esté en segundo plano</string>
403 <string name="pause">Pausar</string>
404 <string name="play">Jugar</string>
405 <string name="mute">Mutear</string>
406 <string name="unmute">Desmutear</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">Licencias</string>
410 <string name="license_fidelityfx_fsr_description">Upscaling de alta calidad de AMD</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index 1e02828aa..5a827c50b 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> 4 <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string>
5 <string name="emulation_notification_channel_name">L\'émulation est active</string> 5 <string name="emulation_notification_channel_name">L\'émulation est active</string>
@@ -19,12 +19,13 @@
19 <string name="games">Jeux</string> 19 <string name="games">Jeux</string>
20 <string name="games_description">Sélectionnez votre dossier &lt;b>de Jeux&lt;/b> avec le bouton ci-dessous.</string> 20 <string name="games_description">Sélectionnez votre dossier &lt;b>de Jeux&lt;/b> avec le bouton ci-dessous.</string>
21 <string name="done">Terminé</string> 21 <string name="done">Terminé</string>
22 <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string> 22 <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string>
23 <string name="text_continue">Continuer</string> 23 <string name="text_continue">Continuer</string>
24 <string name="next">Suivant</string> 24 <string name="next">Suivant</string>
25 <string name="back">Retour</string> 25 <string name="back">Retour</string>
26 <string name="add_games">Ajouter des jeux</string> 26 <string name="add_games">Ajouter des jeux</string>
27 <string name="add_games_description">Sélectionner votre dossier de jeux</string> 27 <string name="add_games_description">Sélectionner le dossier des jeux</string>
28 <string name="step_complete">Terminé !</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Jeux</string> 31 <string name="home_games">Jeux</string>
@@ -32,12 +33,13 @@
32 <string name="home_settings">Paramètres</string> 33 <string name="home_settings">Paramètres</string>
33 <string name="empty_gamelist">Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné.</string> 34 <string name="empty_gamelist">Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné.</string>
34 <string name="search_and_filter_games">Rechercher et filtrer les jeux</string> 35 <string name="search_and_filter_games">Rechercher et filtrer les jeux</string>
35 <string name="select_games_folder">Sélectionner le dossier de jeux</string> 36 <string name="select_games_folder">Sélectionner le dossier des jeux</string>
36 <string name="select_games_folder_description">Permet à yuzu de remplir la liste des jeux</string> 37 <string name="select_games_folder_description">Permet à yuzu de remplir la liste des jeux</string>
37 <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string> 38 <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string>
38 <string name="add_games_warning_description">Les jeux ne seront pas affichés dans la liste des jeux si aucun dossier n\'est sélectionné.</string> 39 <string name="add_games_warning_description">Les jeux ne seront pas affichés dans la liste des jeux si aucun dossier n\'est sélectionné.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Rechercher des jeux</string> 41 <string name="home_search_games">Rechercher des jeux</string>
42 <string name="search_settings">Rechercher un paramètre</string>
41 <string name="games_dir_selected">Répertoire de jeux sélectionné</string> 43 <string name="games_dir_selected">Répertoire de jeux sélectionné</string>
42 <string name="install_prod_keys">Installer prod.keys</string> 44 <string name="install_prod_keys">Installer prod.keys</string>
43 <string name="install_prod_keys_description">Nécessaire pour décrypter les jeux commerciaux.</string> 45 <string name="install_prod_keys_description">Nécessaire pour décrypter les jeux commerciaux.</string>
@@ -46,7 +48,7 @@
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> 48 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Notifications</string> 49 <string name="notifications">Notifications</string>
48 <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string> 50 <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string>
49 <string name="give_permission">Donner la permission</string> 51 <string name="give_permission">Accorder la permission</string>
50 <string name="notification_warning">Ne pas accorder la permission de notification ?</string> 52 <string name="notification_warning">Ne pas accorder la permission de notification ?</string>
51 <string name="notification_warning_description">yuzu ne pourra pas vous communiquer d\'informations importantes.</string> 53 <string name="notification_warning_description">yuzu ne pourra pas vous communiquer d\'informations importantes.</string>
52 <string name="permission_denied">Permission refusée</string> 54 <string name="permission_denied">Permission refusée</string>
@@ -61,12 +63,15 @@
61 <string name="invalid_keys_file">Fichier de clés sélectionné invalide</string> 63 <string name="invalid_keys_file">Fichier de clés sélectionné invalide</string>
62 <string name="install_keys_success">Clés installées avec succès</string> 64 <string name="install_keys_success">Clés installées avec succès</string>
63 <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string> 65 <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string>
66 <string name="install_prod_keys_failure_extension_description">Vérifiez que votre fichier de clés a une extension .keys et réessayez.</string>
67 <string name="install_amiibo_keys_failure_extension_description">Vérifiez que votre fichier de clés a une extension .bin et réessayez.</string>
64 <string name="invalid_keys_error">Clés de chiffrement invalides</string> 68 <string name="invalid_keys_error">Clés de chiffrement invalides</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés.</string> 70 <string name="install_keys_failure_description">Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés.</string>
67 <string name="install_gpu_driver">Installer le pilote du GPU</string> 71 <string name="install_gpu_driver">Installer le pilote du GPU</string>
68 <string name="install_gpu_driver_description">Installez des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string> 72 <string name="install_gpu_driver_description">Installer des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string>
69 <string name="advanced_settings">Paramètres avancés</string> 73 <string name="advanced_settings">Paramètres avancés</string>
74 <string name="advanced_settings_game">Paramètres avancés : %1$s</string>
70 <string name="settings_description">Configurer les paramètres de l\'émulateur</string> 75 <string name="settings_description">Configurer les paramètres de l\'émulateur</string>
71 <string name="search_recently_played">Joué récemment</string> 76 <string name="search_recently_played">Joué récemment</string>
72 <string name="search_recently_added">Ajouté récemment</string> 77 <string name="search_recently_added">Ajouté récemment</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> 91 <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string>
87 <string name="import_saves">Importer</string> 92 <string name="import_saves">Importer</string>
88 <string name="export_saves">Exporter</string> 93 <string name="export_saves">Exporter</string>
94 <string name="install_firmware">Installer le firmware</string>
95 <string name="install_firmware_description">Le firmware doit être dans une archive ZIP et est nécessaire pour démarrer certains jeux.</string>
96 <string name="firmware_installing">Installation du firmware</string>
97 <string name="firmware_installed_success">Firmware installé avec succès</string>
98 <string name="firmware_installed_failure">L\'installation du firmware a échoué</string>
99 <string name="firmware_installed_failure_description">Assurez-vous que les fichiers NCA du firmware se trouvent à la racine du fichier ZIP, puis réessayez.</string>
100 <string name="share_log">Partager les logs de débogage</string>
101 <string name="share_log_description">Partagez le fichier de log de yuzu pour déboguer les problèmes.</string>
102 <string name="share_log_missing">Aucun fichier de log trouvé</string>
103 <string name="install_game_content">Installer le contenu du jeu</string>
104 <string name="install_game_content_description">Installer une mise à jour ou un DLC</string>
105 <string name="installing_game_content">Installation du contenu en cours...</string>
106 <string name="install_game_content_failure">Erreur lors de l\'installation du fichier dans la NAND</string>
107 <string name="install_game_content_failure_description">Veuillez vous assurer que le contenu est valide et que le fichier prod.keys est installé.</string>
108 <string name="install_game_content_failure_base">L\'installation de jeux de base n\'est pas autorisée afin d\'éviter d\'éventuels conflits.</string>
109 <string name="install_game_content_failure_file_extension">Seuls les contenus NSP et XCI sont pris en charge. Veuillez vérifier que le contenu du jeu est valide.</string>
110 <string name="install_game_content_failed_count">%1$d erreur(s) d\'installation</string>
111 <string name="install_game_content_success">Contenu du jeu installé avec succès</string>
112 <string name="install_game_content_success_install">%1$d installé avec succès</string>
113 <string name="install_game_content_success_overwrite">%1$d écrasé avec succès</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">Pilotes personnalisés non supporté</string>
116 <string name="custom_driver_not_supported_description">Le chargement des pilotes personnalisés ne sont pas actuellement pris en charge pour ce périphérique. Vérifiez à nouveau cette option à l\'avenir pour voir si la prise en charge a été ajoutée !</string>
117 <string name="manage_yuzu_data">Gérer les données de yuzu</string>
118 <string name="manage_yuzu_data_description">Importer/exporter le firmware, les clés, les données utilisateur, et bien plus encore !</string>
119 <string name="share_save_file">Partager le fichier de sauvegarde</string>
120 <string name="export_save_failed">Échec de l\'exportation de la sauvegarde</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia n\'est pas réel</string> 123 <string name="gaia_is_not_real">Gaia n\'est pas réel</string>
@@ -94,7 +126,18 @@
94 <string name="contributors">Contributeurs</string> 126 <string name="contributors">Contributeurs</string>
95 <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string> 127 <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">Des projets qui rendent possible yuzu pour Android</string>
97 <string name="build">Build</string> 130 <string name="build">Build</string>
131 <string name="user_data">Données utilisateur</string>
132 <string name="user_data_description">Importer/exporter toutes les données de l\'application.\n\nLors de l\'importation des données utilisateur, toutes les données utilisateur existantes seront supprimées !</string>
133 <string name="exporting_user_data">Exportation des données utilisateur...</string>
134 <string name="importing_user_data">Importation des données utilisateur...</string>
135 <string name="import_user_data">Importer des données utilisateur</string>
136 <string name="invalid_yuzu_backup">Backup yuzu invalide</string>
137 <string name="user_data_export_success">Les données utilisateur ont été exportés avec succès</string>
138 <string name="user_data_import_success">Les données utilisateur ont été importées avec succès</string>
139 <string name="user_data_export_cancelled">Exportation annulée</string>
140 <string name="user_data_import_failed_description">Assurez-vous que les dossiers de données utilisateur se trouvent à la racine du dossier ZIP et contiennent un fichier de configuration à config/config.ini, puis réessayez.</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,64 +157,87 @@
114 <string name="are_you_interested">Es tu intéressé ?</string> 157 <string name="are_you_interested">Es tu intéressé ?</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">Activer la vitesse limite</string> 160 <string name="frame_limit_enable">Limitation de vitesse</string>
118 <string name="frame_limit_enable_description">Lorsqu\'elle est activée, la vitesse d\'émulation sera limitée à un pourcentage spécifié de la vitesse normale.</string> 161 <string name="frame_limit_enable_description">Limiter la vitesse d\'émulation à un pourcentage spécifié de la vitesse normale</string>
119 <string name="frame_limit_slider">Limite en pourcentage de vitesse</string> 162 <string name="frame_limit_slider">Limite en pourcentage de vitesse</string>
120 <string name="frame_limit_slider_description">Spécifie le pourcentage pour limiter la vitesse d\'émulation. Avec la valeur par défaut de 100%, l\'émulation sera limitée à la vitesse normale. Des valeurs supérieures ou inférieures augmenteront ou diminueront la limite de vitesse.</string> 163 <string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string>
121 <string name="cpu_accuracy">Précision du CPU</string> 164 <string name="cpu_accuracy">Précision du CPU</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">Mode TV</string> 168 <string name="use_docked_mode">Mode TV</string>
125 <string name="use_docked_mode_description">Émuler en mode TV augmente la résolution au détriment des performances.</string> 169 <string name="use_docked_mode_description">Augmenter la résolution, ce qui diminue les performances. Le mode portable est utilisé lorsque la fonction est désactivée, ce qui réduit la résolution et améliore les performances.</string>
126 <string name="emulated_region">Région émulée</string> 170 <string name="emulated_region">Région émulée</string>
127 <string name="emulated_language">Langue émulée</string> 171 <string name="emulated_language">Langue émulée</string>
128 <string name="select_rtc_date">Sélectionner la date RTC</string> 172 <string name="select_rtc_date">Sélectionner la date RTC</string>
129 <string name="select_rtc_time">Sélectionner l\'heure RTC</string> 173 <string name="select_rtc_time">Sélectionner l\'heure RTC</string>
130 <string name="use_custom_rtc">Activer l\'horloge RTC personnalisée</string> 174 <string name="use_custom_rtc">RTC personnalisé</string>
131 <string name="use_custom_rtc_description">Ce paramètre vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string> 175 <string name="use_custom_rtc_description">Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string>
132 <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string> 176 <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Niveau de précision</string> 179 <string name="renderer_accuracy">Niveau de précision</string>
137 <string name="renderer_resolution">Résolution</string> 180 <string name="renderer_resolution">Résolution (Mode Portable/Mode TV)</string>
138 <string name="renderer_vsync">Mode VSync</string> 181 <string name="renderer_vsync">Mode VSync</string>
182 <string name="renderer_screen_layout">Orientation</string>
139 <string name="renderer_aspect_ratio">Format</string> 183 <string name="renderer_aspect_ratio">Format</string>
140 <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string> 184 <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string>
141 <string name="renderer_anti_aliasing">Méthode d\'anticrénelage :</string> 185 <string name="renderer_anti_aliasing">Méthode d\'anticrénelage</string>
142 <string name="renderer_force_max_clock">Forcer la fréquence d\'horloge maximale (Adreno uniquement)</string> 186 <string name="renderer_force_max_clock">Forcer les fréquences maximales (Adreno uniquement)</string>
143 <string name="renderer_force_max_clock_description">Force le GPU à fonctionner au maximum d\'horloges possibles (les contraintes thermiques seront toujours appliquées).</string> 187 <string name="renderer_force_max_clock_description">Forcer le GPU à fonctionner à ses fréquences maximales possibles (les contraintes thermiques seront toujours appliquées).</string>
144 <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string> 188 <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string>
145 <string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone, ce qui réduira les saccades mais peut entraîner des problèmes visuels.</string> 189 <string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone, réduisant les saccades mais pouvant entraîner des problèmes visuels.</string>
146 <string name="renderer_debug">Activer le débogage des graphismes</string> 190 <string name="renderer_reactive_flushing">Utiliser le vidage réactif</string>
147 <string name="renderer_debug_description">Lorsque cette case est cochée, l\'API graphique entre dans un mode de débogage plus lent.</string> 191 <string name="renderer_reactive_flushing_description">Améliore la précision du rendu dans certains jeux au détriment des performances.</string>
148 <string name="use_disk_shader_cache">Utiliser les shader cache de disque</string> 192 <string name="use_disk_shader_cache">Utiliser les shader cache</string>
149 <string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant les shaders générés sur le disque.</string> 193 <string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant localement les shaders générés</string>
194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">Débogage du CPU</string>
198 <string name="cpu_debug_mode_description">Place le CPU en mode lent de débogage.</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
201 <string name="renderer_debug">Débogage des graphismes</string>
202 <string name="renderer_debug_description">Définit l\'API graphique en mode de débogage lent.</string>
203 <string name="fastmem">Fastmem</string>
150 204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">Moteur de sortie</string>
152 <string name="audio_volume">Volume</string> 207 <string name="audio_volume">Volume</string>
153 <string name="audio_volume_description">Spécifie le volume de la sortie audio.</string> 208 <string name="audio_volume_description">Spécifier le volume de la sortie audio.</string>
154 209
155 <!-- Miscellaneous --> 210 <!-- Miscellaneous -->
156 <string name="slider_default">Défaut</string> 211 <string name="slider_default">Par défaut</string>
157 <string name="ini_saved">Paramètres enregistrés</string> 212 <string name="ini_saved">Paramètres enregistrés</string>
158 <string name="gameid_saved">Paramètres enregistrés pour %1$s</string> 213 <string name="gameid_saved">Paramètres enregistrés pour %1$s</string>
159 <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string> 214 <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string>
215 <string name="unimplemented_menu">Menu non implémenté</string>
160 <string name="loading">Chargement...</string> 216 <string name="loading">Chargement...</string>
161 <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string> 217 <string name="shutting_down">Extinction en cours...</string>
218 <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string>
162 <string name="reset_to_default">Réinitialiser par défaut</string> 219 <string name="reset_to_default">Réinitialiser par défaut</string>
163 <string name="reset_all_settings">Réinitialiser tous les réglages ?</string> 220 <string name="reset_all_settings">Réinitialiser tous les réglages ?</string>
164 <string name="reset_all_settings_description">Tous les paramètres avancés seront réinitialisés à leur configuration par défaut. Ça ne peut pas être annulé.</string> 221 <string name="reset_all_settings_description">Tous les paramètres avancés seront réinitialisés à leur configuration par défaut. Ça ne peut pas être annulé.</string>
165 <string name="settings_reset">Paramètres réinitialisés</string> 222 <string name="settings_reset">Paramètres réinitialisés</string>
166 <string name="close">Fermer</string> 223 <string name="close">Fermer</string>
167 <string name="learn_more">Plus d\'informations</string> 224 <string name="learn_more">En savoir plus</string>
225 <string name="auto">Auto</string>
226 <string name="submit">Soumettre</string>
227 <string name="string_null">Nul</string>
228 <string name="string_import">Importer</string>
229 <string name="export">Exporter</string>
230 <string name="export_failed">L\'exportation a échoué</string>
231 <string name="import_failed">L\'importation a échoué</string>
232 <string name="cancelling">Annulation</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Sélectionner le pilote du GPU</string> 235 <string name="select_gpu_driver">Sélectionner le pilote du GPU</string>
171 <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> 236 <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
172 <string name="select_gpu_driver_install">Installer</string> 237 <string name="select_gpu_driver_install">Installer</string>
173 <string name="select_gpu_driver_default">Défaut</string> 238 <string name="select_gpu_driver_default">Par défaut</string>
174 <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> 239 <string name="select_gpu_driver_use_default">Utilisation du pilote du GPU par défaut</string>
240 <string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
175 <string name="system_gpu_driver">Pilote du GPU du système</string> 241 <string name="system_gpu_driver">Pilote du GPU du système</string>
176 <string name="installing_driver">Installation du pilote...</string> 242 <string name="installing_driver">Installation du pilote...</string>
177 243
@@ -182,13 +248,14 @@
182 <string name="preferences_graphics">Vidéo</string> 248 <string name="preferences_graphics">Vidéo</string>
183 <string name="preferences_audio">Audio</string> 249 <string name="preferences_audio">Audio</string>
184 <string name="preferences_theme">Thème et couleur</string> 250 <string name="preferences_theme">Thème et couleur</string>
251 <string name="preferences_debug">Débogage</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">Votre ROM est cryptée</string> 254 <string name="loader_error_encrypted">Votre ROM est cryptée</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Veuillez suivre les guides pour redumper vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartouches de jeu</a> ou <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titres installés</a>.]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[Veuillez suivre les guides pour refaire un dump de vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartouches de jeu</a> ou de vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">titres installés</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Veuillez vous assurer que votre fichier <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> est installé pour que les jeux puissent être déchiffrés.]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[Veuillez vous assurer que votre fichier <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> est installé pour que les jeux puissent être déchiffrés.]]></string>
190 <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string> 257 <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string>
191 <string name="loader_error_video_core_description">Cela est généralement dû à un pilote du GPU incompatible. L\'installation d\'un pilote du GPU personnalisé peut résoudre ce problème.</string> 258 <string name="loader_error_video_core_description">Cela est généralement dû à un pilote GPU incompatible. L\'installation d\'un pilote GPU personnalisé peut résoudre ce problème.</string>
192 <string name="loader_error_invalid_format">Impossible de charger la ROM</string> 259 <string name="loader_error_invalid_format">Impossible de charger la ROM</string>
193 <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string> 260 <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string>
194 261
@@ -198,8 +265,8 @@
198 <string name="emulation_fps_counter">Compteur FPS</string> 265 <string name="emulation_fps_counter">Compteur FPS</string>
199 <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string> 266 <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string>
200 <string name="emulation_rel_stick_center">Centre du stick relatif</string> 267 <string name="emulation_rel_stick_center">Centre du stick relatif</string>
201 <string name="emulation_dpad_slide">Glissement du DPad</string> 268 <string name="emulation_dpad_slide">Glissement du D-pad</string>
202 <string name="emulation_haptics">Haptique</string> 269 <string name="emulation_haptics">Toucher haptique</string>
203 <string name="emulation_show_overlay">Afficher l\'overlay</string> 270 <string name="emulation_show_overlay">Afficher l\'overlay</string>
204 <string name="emulation_toggle_all">Tout basculer</string> 271 <string name="emulation_toggle_all">Tout basculer</string>
205 <string name="emulation_control_adjust">Ajuster l\'overlay</string> 272 <string name="emulation_control_adjust">Ajuster l\'overlay</string>
@@ -225,7 +292,10 @@
225 <string name="save_load_error">Erreur de sauvegarde/chargement</string> 292 <string name="save_load_error">Erreur de sauvegarde/chargement</string>
226 <string name="fatal_error">Erreur fatale</string> 293 <string name="fatal_error">Erreur fatale</string>
227 <string name="fatal_error_message">Une erreur fatale s\'est produite. Consultez les logs pour plus de détails.\nContinuer l\'émulation peut entraîner des plantages et des bogues.</string> 294 <string name="fatal_error_message">Une erreur fatale s\'est produite. Consultez les logs pour plus de détails.\nContinuer l\'émulation peut entraîner des plantages et des bogues.</string>
228 <string name="performance_warning">La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé.</string> 295 <string name="performance_warning">La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé.</string>
296 <string name="device_memory_inadequate">Mémoire RAM de l\'appareil : %1$s\nRecommandé : %2$s</string>
297 <string name="memory_formatted">%1$s %2$s</string>
298 <string name="no_game_present">Aucun jeu démarreable présent !</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">Japon</string> 301 <string name="region_japan">Japon</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">Corée</string> 306 <string name="region_korea">Corée</string>
237 <string name="region_taiwan">Taïwan</string> 307 <string name="region_taiwan">Taïwan</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Octet</string>
311 <string name="memory_kilobyte">Ko</string>
312 <string name="memory_megabyte">Mo</string>
313 <string name="memory_gigabyte">GB</string>
314 <string name="memory_terabyte">To</string>
315 <string name="memory_petabyte">Po</string>
316 <string name="memory_exabyte">Eo</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 319 <string name="renderer_vulkan">Vulkan</string>
@@ -274,6 +351,11 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 351 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 352 <string name="anti_aliasing_smaa">SMAA</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">Paysage</string>
356 <string name="screen_layout_portrait">Portrait</string>
357 <string name="screen_layout_auto">Auto</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">Par défaut (16:9)</string> 360 <string name="ratio_default">Par défaut (16:9)</string>
279 <string name="ratio_force_four_three">Forcer le 4:3</string> 361 <string name="ratio_force_four_three">Forcer le 4:3</string>
@@ -288,8 +370,8 @@
288 370
289 <!-- Gamepad Buttons --> 371 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">Pavé directionnel</string> 372 <string name="gamepad_d_pad">Pavé directionnel</string>
291 <string name="gamepad_left_stick">Stick Gauche</string> 373 <string name="gamepad_left_stick">Stick gauche</string>
292 <string name="gamepad_right_stick">Stick Droit</string> 374 <string name="gamepad_right_stick">Stick droit</string>
293 <string name="gamepad_home">Home</string> 375 <string name="gamepad_home">Home</string>
294 <string name="gamepad_screenshot">Capture d\'écran</string> 376 <string name="gamepad_screenshot">Capture d\'écran</string>
295 377
@@ -299,7 +381,7 @@
299 381
300 <!-- Theme options --> 382 <!-- Theme options -->
301 <string name="change_app_theme">Changer le thème de l\'application</string> 383 <string name="change_app_theme">Changer le thème de l\'application</string>
302 <string name="theme_default">Défaut</string> 384 <string name="theme_default">Par défaut</string>
303 <string name="theme_material_you">Material You</string> 385 <string name="theme_material_you">Material You</string>
304 386
305 <!-- Theme Modes --> 387 <!-- Theme Modes -->
@@ -308,8 +390,22 @@
308 <string name="theme_mode_light">Lumineux</string> 390 <string name="theme_mode_light">Lumineux</string>
309 <string name="theme_mode_dark">Sombre</string> 391 <string name="theme_mode_dark">Sombre</string>
310 392
311 <!-- Black backgrounds theme --> 393 <!-- Audio output engines -->
312 <string name="use_black_backgrounds">Utiliser des arrière-plans noirs</string> 394 <string name="cubeb">cubeb</string>
313 <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer des arrière-plans noirs.</string>
314 395
315</resources> 396 <!-- Black backgrounds theme -->
397 <string name="use_black_backgrounds">Arrière-plan noir</string>
398 <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer un arrière-plan noir.</string>
399
400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">Lecteur réduit</string>
402 <string name="picture_in_picture_description">Réduire la fenêtre lorsqu\'elle est placée en arrière-plan</string>
403 <string name="pause">Pause</string>
404 <string name="play">Jouer</string>
405 <string name="mute">Couper le son</string>
406 <string name="unmute">Remettre le son</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">Licences</string>
410 <string name="license_fidelityfx_fsr_description">Mise à l\'échelle de haute qualité par AMD.</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml
new file mode 100644
index 000000000..0af78a57c
--- /dev/null
+++ b/src/android/app/src/main/res/values-he/strings.xml
@@ -0,0 +1,367 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3
4 <string name="app_disclaimer">התוכנה תריץ ×ž×©×—×§×™× ×œ×§×•× ×¡×•×œ×ª ×” Nintendo Switch. ××£ משחק ×ו ×§×‘×¦×™× ×‘×¢×œ×™ זכויות ×™×•×¦×¨×™× × ×›×œ×œ×™×.&lt;br /&gt;&lt;br /&gt; לפני ש×ת/×” מתחיל בבקשה ×ž×¦× ×ת קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">×§×¨× ×¢×•×“</a>]]></string>
5 <string name="emulation_notification_channel_name">×מולציה פעילה</string>
6 <string name="emulation_notification_channel_description">מציג התר××” מתמשכת ×›×שר ×”×מולציה פועלת.</string>
7 <string name="emulation_notification_running">yuzu רץ</string>
8 <string name="notice_notification_channel_name">התר×ות ותקלות</string>
9 <string name="notice_notification_channel_description">מציג התר×ות ×›×שר משהו הולך ×œ× ×›×©×•×¨×”.</string>
10 <string name="notification_permission_not_granted">הרש×ות התר×ות ×œ× × ×™×ª× ×”!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">×‘×¨×•×›×™× ×”×‘××™×!</string>
14 <string name="welcome_description">למד ×יך להפעיל &lt;b>yuzu&lt;/b> וקפוץ ישר ל×מולציה.</string>
15 <string name="get_started">כדי להתחיל</string>
16 <string name="keys">מפתחות</string>
17 <string name="keys_description">בחר ×ת קובץ ×” &lt;b>prod.keys&lt;/b> שלך ×¢× ×”×›×¤×ª×•×¨ למטה.</string>
18 <string name="select_keys">בחר מפתחות</string>
19 <string name="games">משחקי×</string>
20 <string name="games_description">בחר ×ת התיקיית ×” &lt;b>Games&lt;/b> שלך ×¢× ×”×›×¤×ª×•×¨ למטה.</string>
21 <string name="done">סיו×</string>
22 <string name="done_description">×ת/×” מוכן. \nתהנה/×™ ×ž×”×ž×©×—×§×™× ×©×œ×š </string>
23 <string name="text_continue">המשך</string>
24 <string name="next">הב×</string>
25 <string name="back">×חורה</string>
26 <string name="add_games">הוסף משחקי×</string>
27 <string name="add_games_description">בחר/×™ ×ת תיקיית ×”×ž×©×—×§×™× ×©×œ×š</string>
28 <string name="step_complete">הושל×!</string>
29
30 <!-- Home strings -->
31 <string name="home_games">משחקי×</string>
32 <string name="home_search">חפש</string>
33 <string name="home_settings">הגדרות</string>
34 <string name="empty_gamelist">×œ× × ×ž×¦×ו ×§×‘×¦×™× ×ו לנבחרה ספריית ×§×‘×¦×™× ×‘×™× ×ª×™×™×.</string>
35 <string name="search_and_filter_games">חפש וסנן משחקי×</string>
36 <string name="select_games_folder">בחר תיקיית משחקי×</string>
37 <string name="select_games_folder_description">×פשר ל yuzu ל×כלס ×ת רשימת המשחקי×</string>
38 <string name="add_games_warning">לדלג על בחירת תיקיית המשחקי×?</string>
39 <string name="add_games_warning_description">×ž×©×—×§×™× ×œ× ×™×•×¦×’×• ברשימת ×”×ž×©×—×§×™× ×× ×œ× ×‘×—×¨×” תיקיית משחקי×.</string>
40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
41 <string name="home_search_games">חפש משחקי×</string>
42 <string name="search_settings">חפש בהגדרות</string>
43 <string name="games_dir_selected">ספריית ×ž×©×—×§×™× × ×‘×—×¨×”</string>
44 <string name="install_prod_keys">התקן prod.keys</string>
45 <string name="install_prod_keys_description">הכרחי בכדי לפענח משחקי×</string>
46 <string name="install_prod_keys_warning">לדלג על הוספת מפתחות?</string>
47 <string name="install_prod_keys_warning_description">מפתחות ×—×•×§×™×™× ×”×›×¨×—×™×™× ×›×“×™ לשחק במשחקי×. רק ×פליקציות פירטיות יפעלו ×× ×ª×ž×©×™×š.</string>
48 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
49 <string name="notifications">התר×ות</string>
50 <string name="notifications_description">תן גישה להתר×ות ×¢× ×”×›×¤×ª×•×¨ למטה.</string>
51 <string name="give_permission">תן הרש××”</string>
52 <string name="notification_warning">דלג על מתן הרש××” להתר×ות?</string>
53 <string name="notification_warning_description">yuzu ×œ× ×™×•×›×œ להתריע לך על מידע חשוב.</string>
54 <string name="permission_denied">הרש××” נדחתה</string>
55 <string name="permission_denied_description">×ת/×” דיחת ×ת ההרש××” יותר מדי ×¤×¢×ž×™× ×•×¢×›×©×™×• ×ת/×” צריך/×” לתת גישה ב×ופן ידני בהגדרות.</string>
56 <string name="about">×ודות</string>
57 <string name="about_description">מספר גירסה, ×§×¨×“×™×˜×™× ×•×¢×•×“</string>
58 <string name="warning_help">עזרה</string>
59 <string name="warning_skip">דלג</string>
60 <string name="warning_cancel">ביטול</string>
61 <string name="install_amiibo_keys">התקן מפתחות Amiibo</string>
62 <string name="install_amiibo_keys_description">נחוץ כדי להשתמש ב Amiibo במשחק</string>
63 <string name="invalid_keys_file">קובץ מפתחות ×œ× ×—×•×§×™ נבחר</string>
64 <string name="install_keys_success">מפתחות הותקנו בהצלחה</string>
65 <string name="reading_keys_failure">שגי××” בקרי×ת מפתחות ההצפנה</string>
66 <string name="install_prod_keys_failure_extension_description">×•×“× ×©×œ×§×•×‘×¥ המפתחות שלך יש סיומת של key. ונסה/×™ שוב.</string>
67 <string name="install_amiibo_keys_failure_extension_description">וד×/×™ שלקובץ המפתחות שלך יש סיומת של bin. ונסה/×™ שוב.</string>
68 <string name="invalid_keys_error">מפתחות הצפנה ×œ× ×—×•×§×™×™×</string>
69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
70 <string name="install_keys_failure_description">קבוץ שנבחר מושחת ×ו ×œ× × ×›×•×Ÿ. בבקשה ×”×•×¦× ×ž×—×“×© ×ת המפתחות שלך.</string>
71 <string name="install_gpu_driver">התקן דרייבר למעבד הגרפי</string>
72 <string name="install_gpu_driver_description">התקן ×“×¨×™×™×‘×¨×™× ××—×¨×™× ×‘×©×‘×™×œ סיכוי ×œ×‘×™×¦×•×¢×™× ×ו דיוק ×’×‘×•×”×”×™× ×™×•×ª×¨</string>
73 <string name="advanced_settings">הגדרות מתקדמות</string>
74 <string name="advanced_settings_game">הגדרות מתקדמות: %1$s</string>
75 <string name="settings_description">הדר ×ת הגדרות ×”×מולטור</string>
76 <string name="search_recently_played">שוחק ל×חרונה</string>
77 <string name="search_recently_added">הוסף ל×חרונה</string>
78 <string name="search_retail">קמעונ××™</string>
79 <string name="search_homebrew">Homebrew</string>
80 <string name="open_user_folder">פתח ×ת תיקיית yuzu </string>
81 <string name="open_user_folder_description">× ×” ל ×ת ×”×§×‘×¦×™× ×”×¤× ×™×ž×™×™×Ÿ של yuzu</string>
82 <string name="theme_and_color_description">ערוך ×ת נר×ות ×”×פליקציה</string>
83 <string name="no_file_manager">×œ× × ×ž×¦× ×ž× ×”×œ קבצי×</string>
84 <string name="notification_no_directory_link">×œ× ×™×›×•×œ לפתוח ×ת ספריית yuzu</string>
85 <string name="notification_no_directory_link_description">בבקשה ×ž×§× ×ת תיקיית המשתמש בפנל הצידי של מנהל ×”×§×‘×¦×™× ×‘×ופן ידני.</string>
86 <string name="manage_save_data">נהל מידע שמור</string>
87 <string name="manage_save_data_description">מידע שמור ×œ× × ×ž×¦×. בבקשה בחר/×™ ×ופציה מלמטה</string>
88 <string name="import_export_saves_description">×™×‘× ×ו ×™×¦× ×§×‘×¦×™ שמירה</string>
89 <string name="save_file_imported_success">×™×•×‘× ×‘×”×¦×œ×—×”</string>
90 <string name="save_file_invalid_zip_structure">מבנה ספריית השמירות ×œ× ×—×•×§×™</string>
91 <string name="save_file_invalid_zip_structure_description">התת תיקייה הר×שונה חייב להיות ×” title ID של המשחק</string>
92 <string name="import_saves">ייבו×</string>
93 <string name="export_saves">ייצו×</string>
94 <string name="install_firmware">התקן firmware</string>
95 <string name="install_firmware_description">×” frimware חייב להיות בקובץ zip ×•×”×•× ×”×›×¨×—×™ להפעלת חלק מהמשחקי×</string>
96 <string name="firmware_installing">מתקין frimware</string>
97 <string name="firmware_installed_success">ה frimware הותקן בהצלחה</string>
98 <string name="firmware_installed_failure">התקנת ה frimware נכשלה</string>
99 <string name="firmware_installed_failure_description">×•×“× ×©×§×‘×¦×™ ×” firmware nca נמצ××™× ×‘×©×•×¨×© ×” zip ונסה שוב.</string>
100 <string name="share_log">שתף ×ת יומני ×”×¨×™×©×•× ×©×œ מיפוי הב××’×™×</string>
101 <string name="share_log_description">שתף ×ת קובץ יומני ×”×¨×™×©×•× ×©×œ yuzu בכדי לתקן בעיות</string>
102 <string name="share_log_missing">×œ× × ×ž×¦× ×§×•×‘×¥ יומן רישו×</string>
103 <string name="install_game_content">התקן תוכן משחק</string>
104 <string name="install_game_content_description">התקן עדכוני משחק ×ו DLC</string>
105 <string name="installing_game_content">מתקין תוכן...</string>
106 <string name="install_game_content_failure">תקלה בהתקנת הקובץ (×ו קבצי×) ל NAND</string>
107 <string name="install_game_content_failure_description">בבקשה ×•×“× ×©×”×ª×•×›×Ÿ (×ו תכני×) ×—×•×§×™×™× ×•×©×§×•×‘×¥ ×” prod.keys מותקן.</string>
108 <string name="install_game_content_failure_base">התקנת משחק בסיס נדחת בכדי להימנע ×ž×§×•× ×¤×œ×™×§×˜×™× ×פשריי×.</string>
109 <string name="install_game_content_failure_file_extension">רק קבצי NSP ו XCI נתמכי×. בבקשה ×•×“× ×©×ª×•×›×Ÿ (×ו תכני×) המשחק חוקי.</string>
110 <string name="install_game_content_failed_count">%1$dבעיה (בעיות) התקנה</string>
111 <string name="install_game_content_success">תוכן (×ו תכני) המשחק הותקנו בהצלחה</string>
112 <string name="install_game_content_success_install">%1$d הותקן בהצלחה</string>
113 <string name="install_game_content_success_overwrite">%1$d נדרס/נכתב מעל בהצלחה</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">×“×¨×™×™×‘×¨×™× ×ž×•×ª××ž×™× ×ישית ×œ× × ×ª×ž×›×™×</string>
116 <string name="custom_driver_not_supported_description">הטענת ×“×¨×™×™×‘×™× ×ž×•×ª××ž×™× ×ישית ×œ× × ×ª×ž×š כרגע על מכשיר ×–×”. \nבבקשה בדוק ×ופציה זו בעתיד בכדי לר×ות ×× × ×•×¡×¤×” תמיכה!</string>
117 <string name="manage_yuzu_data">נהל ×ת המידע של yuzu</string>
118 <string name="manage_yuzu_data_description">יב×/×™×¦× firmware, keys, מידע של משתמש ועוד!</string>
119 <string name="share_save_file">שתף קובץ שמירה</string>
120 <string name="export_save_failed">נכשל ×‘×™×™×¦×•× ×©×ž×™×¨×”</string>
121
122 <!-- About screen strings -->
123 <string name="gaia_is_not_real">Gaia ×œ× ×מיתית</string>
124 <string name="copied_to_clipboard">הועתק ללוח</string>
125 <string name="about_app_description">×מולטור Switch ×¢× ×§×•×“ פתוח</string>
126 <string name="contributors">תורמי×</string>
127 <string name="contributors_description">נוצר ×¢× \u2764 מקבוצת yuzu</string>
128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">×¤×¨×•×™×™×§×˜×™× ×©×”×•×¤×›×™× ×ת yuzu ל Android ×פשרי</string>
130 <string name="build">גרסה</string>
131 <string name="user_data">נתוני משתמש</string>
132 <string name="user_data_description">יב×/×™×¦× ×ת כל נתוני ×”×פליקציה.\n\n×›×שר מייב××™× ×ת נתוני המשתמש, כל נתוני המשתמש ×”×§×™×™×ž×™× ×™×ž×—×§×•!</string>
133 <string name="exporting_user_data">×ž×™×™×¦× × ×ª×•× ×™ משתמש...</string>
134 <string name="importing_user_data">×ž×™×™×‘× × ×ª×•× ×™ משתמש...</string>
135 <string name="import_user_data">×™×‘× × ×ª×•× ×™ משתמש</string>
136 <string name="invalid_yuzu_backup">גיבוי yuzu ×œ× ×—×•×§×™</string>
137 <string name="user_data_export_success">נתוני משתמש יוצ×ו בהצלחה</string>
138 <string name="user_data_import_success">נתוני משתמש יוב×ו בהצלחה</string>
139 <string name="user_data_export_cancelled">×™×™×¦×•× ×‘×•×˜×œ</string>
140 <string name="user_data_import_failed_description">×•×“× ×©× ×ª×•× ×™ המשתמש נמצ××™× ×‘×©×•×¨×© קובץ ×” zip ×•×©×”×•× ×ž×›×™×œ קובץ סידור ב config/config.ini ונסה שוב.</string>
141 <string name="support_link">https://discord.gg/u77vRWY</string>
142 <string name="website_link">https://yuzu-emu.org/</string>
143 <string name="github_link">https://github.com/yuzu-emu</string>
144
145 <!-- Early access upgrade strings -->
146 <string name="early_access">גישה מוקדמת</string>
147 <string name="get_early_access">קבל גישה מוקדמת</string>
148 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
149 <string name="get_early_access_description">תכונות חותכות קצה, גישה מוקדמת לעדכוני×, ועוד</string>
150 <string name="early_access_benefits">יתרונות של גישה מקודמת</string>
151 <string name="cutting_edge_features">תכונות חותכות קצה</string>
152 <string name="early_access_updates">גישה מוקדמת לעדכוני×</string>
153 <string name="no_manual_installation">×œ×œ× ×”×ª×§× ×” ידנית</string>
154 <string name="prioritized_support">תמיכה בעדיפות</string>
155 <string name="helping_game_preservation">עוזר בשמירת משחקי×</string>
156 <string name="our_eternal_gratitude">התודה ×”×ינסופית שלנו</string>
157 <string name="are_you_interested">×תה מעוניין?</string>
158
159 <!-- General settings strings -->
160 <string name="frame_limit_enable">הגבל מהירות</string>
161 <string name="frame_limit_enable_description">מגביל ×ת מהירות ×”×מולציה ל×חוז מהירות המבוקש מהמהירות הרגילה.</string>
162 <string name="frame_limit_slider">הגבל ×ת ×חוז המהירות</string>
163 <string name="frame_limit_slider_description">מדייק ×ת ×חוז מהירות ×”×מולציה. 100% ×–×” מהירות רגילה. ×¢×¨×›×™× ×’×“×•×œ×™× ×ו ×§×˜× ×™× ×™×יצו ×ו ×™×טו ×ת מהירות ×”×מולציה.</string>
164 <string name="cpu_accuracy">דיוק המעבד</string>
165 <string name="value_with_units">%1$s%2$s</string>
166
167 <!-- System settings strings -->
168 <string name="use_docked_mode">מצב עגינה</string>
169 <string name="use_docked_mode_description">מעלה ×ת הרזולוציה, פוגע בביצועי×. משתמש במצב נייד ×›×שר מנוטרל, מפחית ×ת הרזולוציה ומעלה ×ת הביצועי×.</string>
170 <string name="emulated_region">×זור ×מולציה</string>
171 <string name="emulated_language">שפת ×מולציה</string>
172 <string name="select_rtc_date">בחר ת×ריך RTC</string>
173 <string name="select_rtc_time">בחר זמן RTC</string>
174 <string name="use_custom_rtc">RTC מות×× ×ישית</string>
175 <string name="use_custom_rtc_description">מ×פשר לך לקבוע שעון זמן ×מת נפרד משעון המערכת שלך.</string>
176 <string name="set_custom_rtc">קבע RTC מות×× ×ישית</string>
177
178 <!-- Graphics settings strings -->
179 <string name="renderer_accuracy">רמת דיוק</string>
180 <string name="renderer_resolution">רזולוציה (מעוגן/נייד)</string>
181 <string name="renderer_vsync">מצב VSync</string>
182 <string name="renderer_screen_layout">כיוון</string>
183 <string name="renderer_aspect_ratio">יחס רוחב גובה</string>
184 <string name="renderer_scaling_filter">פילטר מת×× ×—×œ×•×Ÿ</string>
185 <string name="renderer_anti_aliasing">שיטת Anti-aliasing</string>
186 <string name="renderer_force_max_clock">החזק מהירות שעון מקסימלית (רק ל Adreno)</string>
187 <string name="renderer_force_max_clock_description">מכריח לדחוף ×ת מהירויות המעבד הגרפי ×œ×ž×§×¡×™×ž×•× (הגבלות ×—×•× ×™×ž×©×™×›×• לתפקד).</string>
188 <string name="renderer_reactive_flushing_description">משפר ×ת הדיוק של ×”×מולציה ×‘×ž×©×—×§×™× ×ž×¡×•×™×™×ž×™× ×‘×ž×—×™×¨ של ביצועי×.</string>
189 <!-- Debug settings strings -->
190 <string name="cpu">מעבד</string>
191 <string name="cpu_debug_mode_description">מכניס ×ת המעבד למצב דיב××’ ×יטי</string>
192 <string name="gpu">מעבד גרפי</string>
193 <!-- Audio settings strings -->
194 <string name="audio_output_engine">מנוע פלט</string>
195 <string name="audio_volume">עוצמת שמע</string>
196 <!-- Miscellaneous -->
197 <string name="slider_default">ברירת מחדל</string>
198 <string name="ini_saved">הגדרות שמורות</string>
199 <string name="gameid_saved">הגדרות שמורות עבור %1$s</string>
200 <string name="error_saving">תקלה בשמירת %1$s.ini: %2$s</string>
201 <string name="loading">טוען...</string>
202 <string name="shutting_down">כיבוי...</string>
203 <string name="reset_setting_confirmation">×תה מעוניין ל×פס ×ת ההגדרה הזו חזרה לברירת המחדל?</string>
204 <string name="reset_to_default">×פס לברירת המחדל</string>
205 <string name="reset_all_settings">ל×פס ×ת כל ההגדרות?</string>
206 <string name="reset_all_settings_description">כל ההגדרות המתקדמות ×™×ופסו לברירת המחדל. ×œ× × ×™×ª×Ÿ לבטל פעולה זו.</string>
207 <string name="settings_reset">×פס הגדרות</string>
208 <string name="close">סגור</string>
209 <string name="learn_more">למד עוד</string>
210 <string name="auto">×וטומטי</string>
211 <string name="submit">שלח</string>
212 <string name="string_import">ייבו×</string>
213 <string name="export">ייצו×</string>
214 <string name="export_failed">×™×™×¦×•× × ×›×©×œ</string>
215 <string name="import_failed">×™×™×‘×•× × ×›×©×œ</string>
216 <string name="cancelling">מבטל</string>
217
218 <!-- GPU driver installation -->
219 <string name="select_gpu_driver">בחר דרייבר למעבד הגרפי</string>
220 <string name="select_gpu_driver_title">×תה מעוניין להחליף ×ת הדרייבר של המעבד הגרפי שלך?</string>
221 <string name="select_gpu_driver_install">התקן</string>
222 <string name="select_gpu_driver_default">ברירת מחדל</string>
223 <string name="select_gpu_driver_use_default">משתמש בדרייבר ברירת המחדל של המעבד הגרפי</string>
224 <string name="select_gpu_driver_error">דרייבר ×œ× ×—×•×§×™ נבחר, משתמש בברירת המחדל של המערכת!</string>
225 <string name="system_gpu_driver">דרייבר של המעבד הגרפי של המערכת</string>
226 <string name="installing_driver">מתקין דרייבר...</string>
227
228 <!-- Preferences Screen -->
229 <string name="preferences_settings">הגדרות</string>
230 <string name="preferences_general">כללי</string>
231 <string name="preferences_system">מערכת</string>
232 <string name="preferences_graphics">גרפיקה</string>
233 <string name="preferences_audio">שמע</string>
234 <string name="preferences_theme">צבע ונוש×</string>
235 <!-- ROM loading errors -->
236 <string name="loader_error_encrypted">המשחק שלך מוצפן</string>
237 <string name="loader_error_invalid_format">×ין ×פשרות לטעון ×ת המשחק</string>
238 <string name="loader_error_file_not_found">קובץ המשחק ×œ× ×§×™×™×</string>
239
240 <!-- Emulation Menu -->
241 <string name="emulation_exit">×¦× ×ž×”×מולציה</string>
242 <string name="emulation_done">סיו×</string>
243 <string name="emulation_fps_counter">סופר FPS</string>
244 <string name="emulation_control_scale">קנה מידה</string>
245 <string name="emulation_control_opacity">שקיפות</string>
246 <string name="emulation_pause">עצור ×מולציה</string>
247 <string name="emulation_unpause">המשך ×מולציה</string>
248 <string name="load_settings">טוען הגדרות...</string>
249
250 <!-- Software keyboard -->
251 <string name="software_keyboard">מקלדת תוכנה</string>
252
253 <!-- Errors and warnings -->
254 <string name="abort_button">×ודות</string>
255 <string name="continue_button">המשך</string>
256 <string name="system_archive_not_found">×רכיון מערכת ×œ× × ×ž×¦×</string>
257 <string name="system_archive_not_found_message">%s חסר. בבקשה ×”×•×¦× ×ª× ×רכיוני המערכת שלך./nהמשכת ×”×מולציה עלולה ×œ×’×¨×•× ×œ×§×¨×™×¡×•×ª וב××’×™×.</string>
258 <string name="system_archive_general">×רכיון מערכת</string>
259 <string name="save_load_error">בעיית שמירה/טעינה</string>
260 <string name="fatal_error">שגי××” חמורה</string>
261 <string name="device_memory_inadequate">RAM המכשיר: %1$s/nמומלץ: %2$s</string>
262 <string name="memory_formatted">%1$s%2$s</string>
263 <string name="no_game_present">×ין משחק שניתן להריץ!</string>
264
265 <!-- Region Names -->
266 <string name="region_japan">יפן</string>
267 <string name="region_usa">×רה״ב</string>
268 <string name="region_europe">×ירופה</string>
269 <string name="region_australia">×וסטרליה</string>
270 <string name="region_china">סין</string>
271 <string name="region_korea">קורי××”</string>
272 <string name="region_taiwan">טייוו×ן</string>
273
274 <!-- Memory Sizes -->
275 <string name="memory_byte">בייט</string>
276 <string name="memory_kilobyte">KB</string>
277 <string name="memory_megabyte">MB</string>
278 <string name="memory_gigabyte">GB</string>
279 <string name="memory_terabyte">TB</string>
280 <string name="memory_petabyte">PB</string>
281 <string name="memory_exabyte">EB</string>
282
283 <!-- Renderer APIs -->
284 <string name="renderer_vulkan">Vulkan</string>
285 <string name="renderer_none">×ין ×©×•× ×“×‘×¨</string>
286
287 <!-- Renderer Accuracy -->
288 <string name="renderer_accuracy_normal">רגיל</string>
289 <string name="renderer_accuracy_high">גבוה</string>
290 <string name="renderer_accuracy_extreme">××§×¡×˜×¨×™× (×יטי)</string>
291
292 <!-- Resolutions -->
293 <string name="resolution_half">0.5X (360p/540p)</string>
294 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
295 <string name="resolution_one">1X (720p/1080p)</string>
296 <string name="resolution_two">2X (1440p/2160p) (×יטי)</string>
297 <string name="resolution_three">3X (2160p/3240p) (×יטי)</string>
298 <string name="resolution_four">4X (2880p/4320p) (×יטי)</string>
299
300 <string name="renderer_vsync_mailbox">תיבת דו×ר</string>
301 <string name="renderer_vsync_fifo">FIFO (On)</string>
302 <string name="renderer_vsync_fifo_relaxed">FIFO נינוח</string>
303
304 <!-- Scaling Filters -->
305 <string name="scaling_filter_nearest_neighbor">השכן הקרוב ביותר</string>
306 <string name="scaling_filter_scale_force">ScaleForce</string>
307 <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string>
308
309 <!-- Anti-Aliasing -->
310 <string name="anti_aliasing_none">×ין ×©×•× ×“×‘×¨</string>
311 <string name="anti_aliasing_fxaa">FXAA</string>
312 <string name="anti_aliasing_smaa">SMAA</string>
313
314 <!-- Screen Layouts -->
315 <string name="screen_layout_landscape">לרוחב</string>
316 <string name="screen_layout_portrait">ל×ורך</string>
317 <string name="screen_layout_auto">×וטומטי</string>
318
319 <!-- Aspect Ratios -->
320 <string name="ratio_default">ברירת מחדל (16:9)</string>
321 <string name="ratio_force_four_three">הכרח 4:3</string>
322 <string name="ratio_force_twenty_one_nine">הכרח 21:9</string>
323 <string name="ratio_force_sixteen_ten">הכרח 16:10</string>
324 <string name="ratio_stretch">הרחב לגודל המסך</string>
325
326 <!-- CPU Accuracy -->
327 <string name="cpu_accuracy_accurate">מדויק</string>
328 <string name="cpu_accuracy_unsafe">×œ× ×‘×˜×•×—</string>
329 <string name="cpu_accuracy_paranoid">פר×נו×ידי (×יטי)</string>
330
331 <!-- Gamepad Buttons -->
332 <string name="gamepad_d_pad">D-pad</string>
333 <string name="gamepad_left_stick">ג׳ויסטיק שמ×לי</string>
334 <string name="gamepad_right_stick">ג׳ויסטיק ימני</string>
335 <string name="gamepad_home">בית</string>
336 <string name="gamepad_screenshot">×¦×™×œ×•× ×ž×¡×š</string>
337
338 <!-- Theme options -->
339 <string name="change_app_theme">שנה ×ת × ×•×©× ×”×פליקצייה</string>
340 <string name="theme_default">ברירת מחדל</string>
341 <string name="theme_material_you">חומר ×תה/מ×טירי×ל יו</string>
342
343 <!-- Theme Modes -->
344 <string name="change_theme_mode">שנה ×ת מצב הנוש×</string>
345 <string name="theme_mode_follow_system">עקוב ×חרי המערכת</string>
346 <string name="theme_mode_light">בהיר</string>
347 <string name="theme_mode_dark">×›×”×”</string>
348
349 <!-- Audio output engines -->
350 <string name="cubeb">cubeb</string>
351
352 <!-- Black backgrounds theme -->
353 <string name="use_black_backgrounds">×¨×§×¢×™× ×©×—×•×¨×™×</string>
354 <string name="use_black_backgrounds_description">×›×©×ž×ª×©×ž×©×™× ×‘×ž×¦×‘ ×›×”×”, ×©× ×¨×§×¢×™× ×©×—×•×¨×™×.</string>
355
356 <!-- Picture-In-Picture -->
357 <string name="picture_in_picture">תמונה בתוך תמונה</string>
358 <string name="picture_in_picture_description">הקטן ×ת החלון ×›×שר × ×ž×¦× ×‘×¨×§×¢</string>
359 <string name="pause">עצור</string>
360 <string name="play">שחק</string>
361 <string name="mute">השתק</string>
362 <string name="unmute">בטל השתקה</string>
363
364 <!-- Licenses screen strings -->
365 <string name="licenses">רישיונות</string>
366 <string name="license_fidelityfx_fsr_description">×פסקיילינג ב×יכות גבוהה מ AMD</string>
367 </resources>
diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml
new file mode 100644
index 000000000..6563ba288
--- /dev/null
+++ b/src/android/app/src/main/res/values-hu/strings.xml
@@ -0,0 +1,402 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3
4 <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .&lt;br /&gt;&lt;br /&gt;Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string>
5 <string name="emulation_notification_channel_name">Emuláció aktív</string>
6 <string name="emulation_notification_channel_description">Ãllandó értesítést jelenít meg, amíg az emuláció fut.</string>
7 <string name="emulation_notification_running">A yuzu fut</string>
8 <string name="notice_notification_channel_name">Megjegyzések és hibák</string>
9 <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string>
10 <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Üdvözöljük!</string>
14 <string name="welcome_description">Ismerkedj meg a &lt;b>yuzu&lt;/b> beállításával és ugorj bele az emulációba.</string>
15 <string name="get_started">Vágjunk bele</string>
16 <string name="keys">Kulcsok</string>
17 <string name="keys_description">Válaszd ki a(z) &lt;b>prod.keys&lt;/b> fájlodat az alábbi gombbal.</string>
18 <string name="select_keys">Kulcsok kiválasztása</string>
19 <string name="games">Játékok</string>
20 <string name="games_description">
21Válaszd ki a(z) &lt;b>Games&lt;/b> mappát az alábbi gombbal.</string>
22 <string name="done">Kész</string>
23 <string name="done_description">Minden kész.\nJó szórakozást!</string>
24 <string name="text_continue">Folytatás</string>
25 <string name="next">Következő</string>
26 <string name="back">Vissza</string>
27 <string name="add_games">Játékok hozzáadása</string>
28 <string name="add_games_description">Játékaid mappa kiválasztása</string>
29 <string name="step_complete">Kész!</string>
30
31 <!-- Home strings -->
32 <string name="home_games">Játékok</string>
33 <string name="home_search">Keresés</string>
34 <string name="home_settings">Beállítások</string>
35 <string name="empty_gamelist">Nem található fájl, vagy még nincs kiválasztva könyvtár.</string>
36 <string name="search_and_filter_games">Játékok keresése és szűrése</string>
37 <string name="select_games_folder">Játékmappa kiválasztása</string>
38 <string name="add_games_warning">Kihagyod a játékok mappa kiválasztását?</string>
39 <string name="add_games_warning_description">A játékok nem jelennek meg a Játékok listában, ha egy mappa nincs kijelölve.</string>
40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
41 <string name="home_search_games">Játékok keresése</string>
42 <string name="search_settings">Beállítások keresése</string>
43 <string name="games_dir_selected">Játékok könyvtár kiválasztva</string>
44 <string name="install_prod_keys">prod.keys telepítése</string>
45 <string name="install_prod_keys_description">Kiskereskedelmi játékok dekódolásához szükséges</string>
46 <string name="install_prod_keys_warning">Kihagyod a kulcsok hozzáadását?</string>
47 <string name="install_prod_keys_warning_description">A kiskereskedelmi játékok emulálásához érvényes kulcsokra van szükség. Csak a homebrew alkalmazások fognak működni, ha folytatod.</string>
48 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
49 <string name="notifications">Értesítések</string>
50 <string name="notifications_description">Értesítési engedélyek megadása az alábbi gombbal.</string>
51 <string name="give_permission">Engedély megadása</string>
52 <string name="notification_warning">Kihagyod az értesítési engedély megadását?</string>
53 <string name="notification_warning_description">yuzu nem fog tudni értesíteni a fontos imformációkról</string>
54 <string name="permission_denied">Engedély megtagadva</string>
55 <string name="permission_denied_description">Túl gyakran utasítottad el a hozzáférést, így manuálisan kell jóváhagynod a rendszer beállításokban.</string>
56 <string name="about">A programról</string>
57 <string name="about_description">Build verzió, készítők, és még több</string>
58 <string name="warning_help">Segítség</string>
59 <string name="warning_skip">Kihagyás</string>
60 <string name="warning_cancel">Mégse</string>
61 <string name="install_amiibo_keys">Amiibo kulcsok telepítése</string>
62 <string name="install_amiibo_keys_description">Amiibo használata szükséges a játékhoz</string>
63 <string name="invalid_keys_file">Érvénytelen titkosítófájlok kiválasztva</string>
64 <string name="install_keys_success">Kulcsok sikeresen telepítve</string>
65 <string name="reading_keys_failure">Hiba történt a titkosítókulcsok olvasása során</string>
66 <string name="install_prod_keys_failure_extension_description">Győződj meg róla, hogy a titkosító fájlod .keys kiterjesztéssel rendelkezik, majd próbáld újra.</string>
67 <string name="install_amiibo_keys_failure_extension_description">Győződj meg róla, hogy a titkosító fájlod .bin kiterjesztéssel rendelkezik, majd próbáld újra.</string>
68 <string name="invalid_keys_error">Érvénytelen titkosítókulcsok</string>
69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
70 <string name="install_keys_failure_description">A kiválasztott fájl helytelen, vagy sérült. Ãllíts össze egy új kulcsot.</string>
71 <string name="install_gpu_driver">GPU illesztőprogram telepítése</string>
72 <string name="install_gpu_driver_description">Alternatív illesztőprogramok telepítése az esetlegesen elérhető teljesítmény és pontosság érdekében</string>
73 <string name="advanced_settings">Haladó beállítások</string>
74 <string name="advanced_settings_game">Haladó beállítások: %1$s</string>
75 <string name="settings_description">Emulátorbeállítások konfigurálása</string>
76 <string name="search_recently_played">Nemrég játszva</string>
77 <string name="search_recently_added">Nemrég hozzáadva</string>
78 <string name="search_retail">Kiskereskedelmi</string>
79 <string name="open_user_folder">yuzu mappa megnyitása</string>
80 <string name="open_user_folder_description">yuzu belső fájljainak kezelése</string>
81 <string name="theme_and_color_description">Az alkalmazás megjelenésének módosítása</string>
82 <string name="no_file_manager">Nem található fájlkezelő</string>
83 <string name="notification_no_directory_link">Nem sikerült megnyitni a yuzu könyvtárat</string>
84 <string name="notification_no_directory_link_description">Kérjük, manuálisan keresd meg a felhasználói mappát a fájlkezelő oldalsó paneljével.</string>
85 <string name="manage_save_data">Mentésadatok kezelése</string>
86 <string name="manage_save_data_description">Mentés található. Kérjük, válassz egyet az alábbi opciók közül.</string>
87 <string name="import_export_saves_description">Mentési fájlok importálás vagy exportálása</string>
88 <string name="save_file_imported_success">Sikeresen importálva</string>
89 <string name="save_file_invalid_zip_structure">Érvénytelen mentési könyvtárstruktúra</string>
90 <string name="save_file_invalid_zip_structure_description">Az első almappa neve a játék azonosítója kell, hogy legyen.</string>
91 <string name="import_saves">Importálás</string>
92 <string name="export_saves">Exportálás</string>
93 <string name="install_firmware">Firmware telepítés</string>
94 <string name="install_firmware_description">A firmwarenek ZIP archívumban kell lennie, és szükséges a játékok indításához</string>
95 <string name="firmware_installing">Firmware telepítése</string>
96 <string name="firmware_installed_success">Firmware sikeresen telepítve</string>
97 <string name="firmware_installed_failure">Firmware telepítése sikertelen</string>
98 <string name="firmware_installed_failure_description">Győződj meg róla, hogy a firmware nca fájlok a zip gyökerénél vannak, és próbáld meg újra.</string>
99 <string name="share_log">Hibakereső logok megosztása</string>
100 <string name="share_log_description">A yuzu naplófájl megosztása a problémák elhárításához</string>
101 <string name="share_log_missing">Nem található log fájl</string>
102 <string name="install_game_content">Játéktartalom telepítése</string>
103 <string name="install_game_content_description">Játékfrissítések vagy DLC telepítése</string>
104 <string name="installing_game_content">Tartalom telepítése...</string>
105 <string name="install_game_content_failure">Hiba történt a fájl(ok) NAND-ra telepítése közben</string>
106 <string name="install_game_content_failure_description">Győződj meg róla, hogy a tartalom valós, és a prod.keys fájl telepítve van.</string>
107 <string name="install_game_content_failure_base">Az alapjátékok telepítése nem engedélyezett az esetleges konfliktusok elkerülése érdekében.</string>
108 <string name="install_game_content_failure_file_extension">Csak NSP és XCI tartalom támogatott. Győződj meg róla, hogy a játéktartalom érvényes.</string>
109 <string name="install_game_content_failed_count">%1$d telepítési hiba</string>
110 <string name="install_game_content_success">Játéktartalom sikeresen telepítve</string>
111 <string name="install_game_content_success_install">%1$d sikeresen telepítve</string>
112 <string name="install_game_content_success_overwrite">%1$d sikeresen felülírva</string>
113 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
114 <string name="custom_driver_not_supported">Egyéni illesztőprogramok nem támogatottak</string>
115 <string name="custom_driver_not_supported_description">Egyéni illesztőprogram telepítése jelenleg nem támogatott ezen az eszközön.\nNézz vissza később, hátha hozzáadtuk a támogatását!</string>
116 <string name="manage_yuzu_data">yuzu adatok kezelése</string>
117 <string name="manage_yuzu_data_description">Firmware, kulcsok, felhasználói adatok és egyebek importálása/exportálása</string>
118 <string name="share_save_file">Mentési fájl megosztása</string>
119 <string name="export_save_failed">A mentés exportálása sikertelen</string>
120
121 <!-- About screen strings -->
122 <string name="gaia_is_not_real">Gaia nem valódi</string>
123 <string name="copied_to_clipboard">Másolva a vágólapra</string>
124 <string name="about_app_description">Egy nyílt forráskódú Switch emulátor</string>
125 <string name="contributors">Hozzájárulók</string>
126 <string name="contributors_description">\u2764 által készítve a yuzu csapattól</string>
127 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
128 <string name="licenses_description">Projektek, amik nélkül a yuzu nem jöhetett volna létre Androidra</string>
129 <string name="user_data">Felhasználói adatok</string>
130 <string name="user_data_description">Az összes alkalmazásadat importálása/exportálása.\n\nA felhasználói adatok importálásakor az összes meglévő felhasználói adat törlődik!</string>
131 <string name="exporting_user_data">Felhasználói adatok exportálása...</string>
132 <string name="importing_user_data">Felhasználói adatok importálása...</string>
133 <string name="import_user_data">Felhasználói adatok importálása</string>
134 <string name="invalid_yuzu_backup">Érvénytelen yuzu biztonsági másolat</string>
135 <string name="user_data_export_success">Felhasználói adatok sikeresen exportálva</string>
136 <string name="user_data_import_success">Felhasználói adatok sikeresen importálva</string>
137 <string name="user_data_export_cancelled">Exportálás megszakítva</string>
138 <string name="user_data_import_failed_description">Ellenőrizd, hogy a felhasználói adatok mappái a zip mappa gyökerében vannak, és tartalmaznak egy konfig fájlt a config/config.ini címen, majd próbáld meg újra.</string>
139 <string name="support_link">https://discord.gg/u77vRWY</string>
140 <string name="website_link">https://yuzu-emu.org/</string>
141 <string name="github_link">https://github.com/yuzu-emu</string>
142
143 <!-- Early access upgrade strings -->
144 <string name="early_access">Korai hozzáférés</string>
145 <string name="get_early_access">Szerezz korai hozzáférést</string>
146 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
147 <string name="get_early_access_description">Legújabb funkciók, korai hozzáférés a frissítésekhez, és sok más</string>
148 <string name="early_access_benefits">Korai hozzáférés előnyei</string>
149 <string name="cutting_edge_features">Legújabb funkciók</string>
150 <string name="early_access_updates">Korai hozzáférés a frissítésekhez</string>
151 <string name="no_manual_installation">Automatikus telepítések</string>
152 <string name="prioritized_support">Priorizált támogatás</string>
153 <string name="our_eternal_gratitude">Valamint az örök hálánk</string>
154 <string name="are_you_interested">Érdekel a dolog?</string>
155
156 <!-- General settings strings -->
157 <string name="frame_limit_enable">Sebességkorlát</string>
158 <string name="frame_limit_enable_description">Korlátozza az emuláció sebességét a normál sebesség adott százalékára.</string>
159 <string name="frame_limit_slider">Sebességkorlát százaléka</string>
160 <string name="frame_limit_slider_description">Az emuláció sebességét határozza meg. 100% a normál sebesség. A magasabb értékek növelik, az alacsonyabbak csökkentik a sebességkorlátot.</string>
161 <string name="cpu_accuracy">CPU pontosság</string>
162 <string name="value_with_units">%1$s%2$s</string>
163
164 <!-- System settings strings -->
165 <string name="use_docked_mode">Dokkolt mód</string>
166 <string name="use_docked_mode_description">Növeli a felbontást, de csökkenti a teljesítményt. Kikapcsolás esetén a Kézi mód van használatban, ami kisebb felbontást, de nagyobb teljesítményt eredményez.</string>
167 <string name="emulated_region">Emulált régió</string>
168 <string name="emulated_language">Emulált nyelv</string>
169 <string name="select_rtc_date">Válassz RTC dátumot</string>
170 <string name="select_rtc_time">Válassz RTC időt</string>
171 <string name="use_custom_rtc">Egyéni RTC</string>
172 <string name="use_custom_rtc_description">Megadhatsz egy valós idejű órát, amely eltér a rendszer által használt órától.</string>
173 <string name="set_custom_rtc">Egyéni RTC beállítása</string>
174
175 <!-- Graphics settings strings -->
176 <string name="renderer_accuracy">Pontosság szintje</string>
177 <string name="renderer_resolution">Felbontás (Kézi/Dockolt)</string>
178 <string name="renderer_vsync">VSync mód</string>
179 <string name="renderer_screen_layout">Orientáció</string>
180 <string name="renderer_aspect_ratio">Képarány</string>
181 <string name="renderer_scaling_filter">Ablakhoz alkalmazkodó szűrő</string>
182 <string name="renderer_anti_aliasing">Élsimítási módszer</string>
183 <string name="renderer_force_max_clock">Maximum órajel kényszerítése (csak Adreno)</string>
184 <string name="renderer_force_max_clock_description">Kényszeríti a GPU-t a lehető legnagyobb órajelen működésre (a hőmérséklet korlátozások továbbra is érvényben maradnak).</string>
185 <string name="renderer_asynchronous_shaders">Aszinkron árnyékolók használata</string>
186 <string name="renderer_asynchronous_shaders_description">Aszinkron módon fordítja az árnyékolókat, ami csökkenti az akadozást, de hibákat okozhat.</string>
187 <string name="renderer_reactive_flushing">Reaktív ürítés használata</string>
188 <string name="renderer_reactive_flushing_description">Javítja a renderelési pontosságot néhány játékban a teljesítmény rovására.</string>
189 <string name="use_disk_shader_cache">Lemez árnyékoló gyorsítótár</string>
190 <string name="use_disk_shader_cache_description">Csökkenti az akadásokat azáltal, hogy helyileg tárolja és tölti be a generált árnyékolókat.</string>
191
192 <!-- Debug settings strings -->
193 <string name="cpu">CPU</string>
194 <string name="cpu_debug_mode">CPU hibakeresés</string>
195 <string name="cpu_debug_mode_description">Lassú hibakereső módba állítja a CPU-t.</string>
196 <string name="gpu">GPU</string>
197 <string name="renderer_api">API</string>
198 <string name="renderer_debug">Grafikai hibakeresés</string>
199 <string name="renderer_debug_description">Lassú hibakeresési módba állítja a grafikus API-t .</string>
200 <!-- Audio settings strings -->
201 <string name="audio_output_engine">Kimeneti rendszer</string>
202 <string name="audio_volume">Hangerő</string>
203 <string name="audio_volume_description">Hangkimenet hangerejének megadása</string>
204
205 <!-- Miscellaneous -->
206 <string name="slider_default">Alapértelmezett</string>
207 <string name="ini_saved">Beállítások elmentve</string>
208 <string name="gameid_saved">Beállítások elmentve a következőhöz: %1$s</string>
209 <string name="error_saving">Mentési hiba%1$s .ini: %2$s</string>
210 <string name="unimplemented_menu">Nem implementált menü</string>
211 <string name="loading">Betöltés...</string>
212 <string name="shutting_down">Leállítás...</string>
213 <string name="reset_setting_confirmation">Szeretnéd visszaállítani a beállítások az alapértelmezett értékekre?</string>
214 <string name="reset_to_default">Alaphelyzetbe állítás</string>
215 <string name="reset_all_settings">Alaphelyzetbe állítod a beállításokat?</string>
216 <string name="reset_all_settings_description">Minden haladó beállítás vissza lesz állítva az alapértelmezett konfigurációra. Ez a művelet nem vonható vissza.</string>
217 <string name="settings_reset">Beállítások alaphelyzetbe állítva</string>
218 <string name="close">Bezárás</string>
219 <string name="learn_more">Tudj meg többet</string>
220 <string name="auto">Automatikus</string>
221 <string name="submit">Küldés</string>
222 <string name="string_null">Nulla</string>
223 <string name="string_import">Importálás</string>
224 <string name="export">Exportálás</string>
225 <string name="export_failed">Exportálás sikertelen</string>
226 <string name="import_failed">Importálás sikertelen</string>
227 <string name="cancelling">Megszakítás</string>
228
229 <!-- GPU driver installation -->
230 <string name="select_gpu_driver">Válassz GPU illesztőprogramot</string>
231 <string name="select_gpu_driver_title">Szeretnéd lecserélni a jelenlegi GPU illesztőprogramot?</string>
232 <string name="select_gpu_driver_install">Telepítés</string>
233 <string name="select_gpu_driver_default">Alapértelmezett</string>
234 <string name="select_gpu_driver_use_default">Alapértelmezett GPU illesztőprogram használata</string>
235 <string name="select_gpu_driver_error">Érvénytelen driver kiválasztva, a rendszer alapértelmezett lesz használva!</string>
236 <string name="system_gpu_driver">Rendszer GPU illesztőprogram</string>
237 <string name="installing_driver">Illesztőprogram telepítése...</string>
238
239 <!-- Preferences Screen -->
240 <string name="preferences_settings">Beállítások</string>
241 <string name="preferences_general">Ãltalános</string>
242 <string name="preferences_system">Rendszer</string>
243 <string name="preferences_graphics">Grafika</string>
244 <string name="preferences_audio">Hang</string>
245 <string name="preferences_theme">Téma és színek</string>
246 <string name="preferences_debug">Hibakeresés</string>
247
248 <!-- ROM loading errors -->
249 <string name="loader_error_encrypted">ROM titkosítva</string>
250 <string name="loader_error_encrypted_keys_description"><![CDATA[Győződj meg róla, hogy a <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> fájl telepítve van, hogy a játékok visszafejthetők legyenek.]]></string>
251 <string name="loader_error_video_core">Hiba lépett fel a videómag inicializása során</string>
252 <string name="loader_error_video_core_description">Ezt általában egy nem kompatibilis GPU illesztő okozza. Egyéni GPU illesztőprogram telepítése megoldhatja a problémát.</string>
253 <string name="loader_error_invalid_format">Nem sikerült betölteni a ROM-ot</string>
254 <string name="loader_error_file_not_found">ROM fájl nem létezik</string>
255
256 <!-- Emulation Menu -->
257 <string name="emulation_exit">Emuláció bezárása</string>
258 <string name="emulation_done">Kész</string>
259 <string name="emulation_fps_counter">FPS számláló</string>
260 <string name="emulation_toggle_controls">Irányítás átkapcsolása</string>
261 <string name="emulation_dpad_slide">D-pad csúsztatása</string>
262 <string name="emulation_haptics">Érintés haptikája</string>
263 <string name="emulation_show_overlay">Ãtfedés mutatása</string>
264 <string name="emulation_toggle_all">Össze átkapcsolása</string>
265 <string name="emulation_control_adjust">Ãtfedés testreszabása</string>
266 <string name="emulation_control_scale">Skálázás</string>
267 <string name="emulation_control_opacity">Ãtlátszóság</string>
268 <string name="emulation_touch_overlay_reset">Ãtfedés visszaállítása</string>
269 <string name="emulation_touch_overlay_edit">Ãtfedés módosítása</string>
270 <string name="emulation_pause">Emuláció szünetelése</string>
271 <string name="emulation_unpause">Emuláció folytatása</string>
272 <string name="emulation_input_overlay">Ãtfedés beállításai</string>
273
274 <string name="load_settings">Beállítások betöltése...</string>
275
276 <!-- Software keyboard -->
277 <string name="software_keyboard">Szoftver billenytűzet</string>
278
279 <!-- Errors and warnings -->
280 <string name="abort_button">Megszakítás</string>
281 <string name="continue_button">Folytatás</string>
282 <string name="system_archive_not_found">Nem található rendszerarchívum</string>
283 <string name="system_archive_not_found_message">%s hiányzik. Kérjük, mentsd ki a rendszerarchívumaidat.\nAz emuláció folytatása összeomlásokhoz és hibákhoz vezethet.</string>
284 <string name="system_archive_general">Egy rendszerarchívum</string>
285 <string name="save_load_error">Mentési/betöltési hiba</string>
286 <string name="fatal_error">Végzetes hiba</string>
287 <string name="fatal_error_message">Végzetes hiba történt. Ellenőrizd a logot a részletekért.\nAz emuláció folytatása összeomlást és hibákat eredményzhet.</string>
288 <string name="performance_warning">Ennek a beállításnak a kikapcsolása jelentős mértékben csökkenti a teljesítményt! A legjobb élmény érdekében javasolt a beállítás bekapcsolva tartása.</string>
289 <string name="device_memory_inadequate">Eszköz RAM: %1$s\nAjánlott: %2$s</string>
290 <string name="memory_formatted">%1$s %2$s</string>
291 <string name="no_game_present">Nincs indítható játék!</string>
292
293 <!-- Region Names -->
294 <string name="region_japan">Japán</string>
295 <string name="region_usa">USA</string>
296 <string name="region_europe">Európa</string>
297 <string name="region_australia">Ausztrália</string>
298 <string name="region_china">Kína</string>
299 <string name="region_korea">Korea</string>
300 <string name="region_taiwan">Tajvan</string>
301
302 <!-- Memory Sizes -->
303 <string name="memory_byte">Bájt</string>
304 <string name="memory_kilobyte">KB</string>
305 <string name="memory_megabyte">MB</string>
306 <string name="memory_gigabyte">GB</string>
307 <string name="memory_terabyte">TB</string>
308 <string name="memory_petabyte">PB</string>
309 <string name="memory_exabyte">EB</string>
310
311 <!-- Renderer APIs -->
312 <string name="renderer_vulkan">Vulkan</string>
313 <string name="renderer_none">Nincs</string>
314
315 <!-- Renderer Accuracy -->
316 <string name="renderer_accuracy_normal">Normál</string>
317 <string name="renderer_accuracy_high">Magas</string>
318 <string name="renderer_accuracy_extreme">Extrém (Lassú)</string>
319
320 <!-- Resolutions -->
321 <string name="resolution_half">0.5X (360p/540p)</string>
322 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
323 <string name="resolution_one">1X (720p/1080p)</string>
324 <string name="resolution_two">2X (1440p/2160p) (Lassú)</string>
325 <string name="resolution_three">3X (2160p/3240p) (Lassú)</string>
326 <string name="resolution_four">4X (2880p/4320p) (Lassú)</string>
327
328 <!-- Renderer VSync -->
329 <string name="renderer_vsync_immediate">Azonnali (Ki)</string>
330 <string name="renderer_vsync_mailbox">Postaláda</string>
331 <string name="renderer_vsync_fifo">FIFO (Be)</string>
332 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxált</string>
333
334 <!-- Scaling Filters -->
335 <string name="scaling_filter_nearest_neighbor">Legközelebbi szomszéd</string>
336 <string name="scaling_filter_bilinear">Bilineáris</string>
337 <string name="scaling_filter_bicubic">Bikubikus</string>
338 <string name="scaling_filter_gaussian">Gauss-féle</string>
339 <string name="scaling_filter_scale_force">ScaleForce</string>
340 <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string>
341
342 <!-- Anti-Aliasing -->
343 <string name="anti_aliasing_none">Nincs</string>
344 <string name="anti_aliasing_fxaa">FXAA</string>
345 <string name="anti_aliasing_smaa">SMAA</string>
346
347 <!-- Screen Layouts -->
348 <string name="screen_layout_landscape">Fekvő</string>
349 <string name="screen_layout_portrait">Ãlló</string>
350 <string name="screen_layout_auto">Automatikus</string>
351
352 <!-- Aspect Ratios -->
353 <string name="ratio_default">Alapértelmezett (16:9)</string>
354 <string name="ratio_force_four_three">4:3 kényszerítése</string>
355 <string name="ratio_force_twenty_one_nine">21:9 kényszerítése</string>
356 <string name="ratio_force_sixteen_ten">16:10 kényszerítése</string>
357 <string name="ratio_stretch">Ablakhoz nyújtás</string>
358
359 <!-- CPU Accuracy -->
360 <string name="cpu_accuracy_accurate">Pontos</string>
361 <string name="cpu_accuracy_unsafe">Nem biztonságos</string>
362 <string name="cpu_accuracy_paranoid">Paranoid (Lassú)</string>
363
364 <!-- Gamepad Buttons -->
365 <string name="gamepad_d_pad">D-pad</string>
366 <string name="gamepad_left_stick">Bal kar</string>
367 <string name="gamepad_right_stick">Jobb kar</string>
368 <string name="gamepad_home">Home</string>
369 <string name="gamepad_screenshot">Képernyőmentés</string>
370
371 <!-- Disk shader cache -->
372 <string name="preparing_shaders">Ãrnyékolók elÅ‘készítése</string>
373 <string name="building_shaders">Ãrnyékolók létrehozása</string>
374
375 <!-- Theme options -->
376 <string name="change_app_theme">Alkalmazás témájának módosítása</string>
377 <string name="theme_default">Alapértelmezett</string>
378 <!-- Theme Modes -->
379 <string name="change_theme_mode">Téma váltása</string>
380 <string name="theme_mode_follow_system">Rendszerbeállítások használata</string>
381 <string name="theme_mode_light">Világos</string>
382 <string name="theme_mode_dark">Sötét</string>
383
384 <!-- Audio output engines -->
385 <string name="cubeb">cubeb</string>
386
387 <!-- Black backgrounds theme -->
388 <string name="use_black_backgrounds">Fekete háttér</string>
389 <string name="use_black_backgrounds_description">Sötét téma használatakor fekete háttér használata.</string>
390
391 <!-- Picture-In-Picture -->
392 <string name="picture_in_picture">Kép a képben</string>
393 <string name="picture_in_picture_description">Ablak minimalizálása, amikor háttérbe kerül</string>
394 <string name="pause">Szünet</string>
395 <string name="play">Lejátszás</string>
396 <string name="mute">Némítás</string>
397 <string name="unmute">Némítás feloldása</string>
398
399 <!-- Licenses screen strings -->
400 <string name="licenses">Licenszek</string>
401 <string name="license_fidelityfx_fsr_description">Magas minőségű felskálázás az AMD-től</string>
402 </resources>
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index 09c9345b0..5afebb4c4 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> 4 <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string>
5 <string name="emulation_notification_channel_name">L\'emulatore è attivo</string> 5 <string name="emulation_notification_channel_name">L\'emulatore è attivo</string>
@@ -13,9 +13,9 @@
13 <string name="welcome">Benvenuto!</string> 13 <string name="welcome">Benvenuto!</string>
14 <string name="welcome_description">Scopri come configurare &lt;b>yuzu&lt;/b> e passare all\'emulazione.</string> 14 <string name="welcome_description">Scopri come configurare &lt;b>yuzu&lt;/b> e passare all\'emulazione.</string>
15 <string name="get_started">Iniziare</string> 15 <string name="get_started">Iniziare</string>
16 <string name="keys">Pulsanti</string> 16 <string name="keys">Chiavi</string>
17 <string name="keys_description">Seleziona il tuo file &lt;b>prod.keys&lt;/b> con il pulsante in basso.</string> 17 <string name="keys_description">Seleziona il tuo file &lt;b>prod.keys&lt;/b> con il pulsante in basso.</string>
18 <string name="select_keys">Selezione Pulsanti</string> 18 <string name="select_keys">Seleziona le chiavi</string>
19 <string name="games">Giochi</string> 19 <string name="games">Giochi</string>
20 <string name="games_description">Seleziona la cartella &lt;b>Games&lt;/b> con il pulsante in basso.</string> 20 <string name="games_description">Seleziona la cartella &lt;b>Games&lt;/b> con il pulsante in basso.</string>
21 <string name="done">Fatto</string> 21 <string name="done">Fatto</string>
@@ -25,6 +25,7 @@
25 <string name="back">Indietro</string> 25 <string name="back">Indietro</string>
26 <string name="add_games">Aggiungi giochi</string> 26 <string name="add_games">Aggiungi giochi</string>
27 <string name="add_games_description">Seleziona la cartella dei giochi</string> 27 <string name="add_games_description">Seleziona la cartella dei giochi</string>
28 <string name="step_complete">Completato!</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Giochi</string> 31 <string name="home_games">Giochi</string>
@@ -38,6 +39,7 @@
38 <string name="add_games_warning_description">I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata.</string> 39 <string name="add_games_warning_description">I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Cerca giochi</string> 41 <string name="home_search_games">Cerca giochi</string>
42 <string name="search_settings">Cerca impostazione</string>
41 <string name="games_dir_selected">Cartella dei giochi selezionata</string> 43 <string name="games_dir_selected">Cartella dei giochi selezionata</string>
42 <string name="install_prod_keys">Installa prod.keys</string> 44 <string name="install_prod_keys">Installa prod.keys</string>
43 <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string> 45 <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string>
@@ -61,15 +63,18 @@
61 <string name="invalid_keys_file">Selezionate chiavi non valide</string> 63 <string name="invalid_keys_file">Selezionate chiavi non valide</string>
62 <string name="install_keys_success">Chiavi installate correttamente</string> 64 <string name="install_keys_success">Chiavi installate correttamente</string>
63 <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string> 65 <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string>
66 <string name="install_prod_keys_failure_extension_description">Controlla che le tue chiavi abbiano l\'estensione .keys e prova di nuovo.</string>
67 <string name="install_amiibo_keys_failure_extension_description">Controlla che le tue chiavi abbiano l\'estensione .bin e prova di nuovo</string>
64 <string name="invalid_keys_error">Chiavi di crittografia non valide</string> 68 <string name="invalid_keys_error">Chiavi di crittografia non valide</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi.</string> 70 <string name="install_keys_failure_description">Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi.</string>
67 <string name="install_gpu_driver">Installa i driver GPU</string> 71 <string name="install_gpu_driver">Installa i driver GPU</string>
68 <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string> 72 <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string>
69 <string name="advanced_settings">Impostazioni avanzate</string> 73 <string name="advanced_settings">Impostazioni avanzate</string>
74 <string name="advanced_settings_game">Impostazioni Avanzate: %1$s</string>
70 <string name="settings_description">Configura le impostazioni dell\'emulatore</string> 75 <string name="settings_description">Configura le impostazioni dell\'emulatore</string>
71 <string name="search_recently_played">Giocato recentemente</string> 76 <string name="search_recently_played">Giocati recentemente</string>
72 <string name="search_recently_added">Aggiunto recentemente</string> 77 <string name="search_recently_added">Aggiunti recentemente</string>
73 <string name="search_retail">Rivenditore</string> 78 <string name="search_retail">Rivenditore</string>
74 <string name="search_homebrew">Homebrew</string> 79 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Apri la cartella di yuzu</string> 80 <string name="open_user_folder">Apri la cartella di yuzu</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> 91 <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string>
87 <string name="import_saves">Importa</string> 92 <string name="import_saves">Importa</string>
88 <string name="export_saves">Esporta</string> 93 <string name="export_saves">Esporta</string>
94 <string name="install_firmware">Installa firmware</string>
95 <string name="install_firmware_description">Il firmware deve essere in un archivio ZIP ed è necessario per avviare alcuni giochi</string>
96 <string name="firmware_installing">Installando il firmware</string>
97 <string name="firmware_installed_success">Firmware installato con successo</string>
98 <string name="firmware_installed_failure">L\'installazione del firmware è fallita</string>
99 <string name="firmware_installed_failure_description">Accertati che i file .nca del firmware siano contenuti direttamente nella radice dello .zip e riprova.</string>
100 <string name="share_log">Condividi log di debug</string>
101 <string name="share_log_description">Condividi i log di yuzu per ricevere supporto</string>
102 <string name="share_log_missing">Nessun file di log trovato</string>
103 <string name="install_game_content">Installa contenuti di gioco</string>
104 <string name="install_game_content_description">Installa aggiornamenti o DLC</string>
105 <string name="installing_game_content">Installazione dei contenuti...</string>
106 <string name="install_game_content_failure">Errore durante l\'installazione del contenuto in NAND.</string>
107 <string name="install_game_content_failure_description">Accertati che i contenuti da installare siano validi e che le prod.keys siano presenti.</string>
108 <string name="install_game_content_failure_base">Installare i giochi base in NAND non è permesso, perché potrebbe causare dei conflitti con altri tipi di contenuti(Aggiornamenti e DLC)</string>
109 <string name="install_game_content_failure_file_extension">Solo i tipi NSP e XCI sono supportati. Verifica che i contenuti di gioco siano validi.</string>
110 <string name="install_game_content_failed_count">Errori di installazione: %1$d</string>
111 <string name="install_game_content_success">Contenuto/i di gioco installato/i con successo.</string>
112 <string name="install_game_content_success_install">%1$dinstallato con successo.</string>
113 <string name="install_game_content_success_overwrite">%1$dsovrascritto con successo</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">I driver personalizzati non sono supportati.</string>
116 <string name="custom_driver_not_supported_description">I driver personalizzati non sono attualmente supportati su questo dispositivo.\n Ricontrolla in futuro.</string>
117 <string name="manage_yuzu_data">Gestisci i dati di Yuzu</string>
118 <string name="manage_yuzu_data_description">Importa/Esporta il firmware, le keys, i dati utente, e altro!</string>
119 <string name="share_save_file">Condividi i tuoi dati di salvataggio</string>
120 <string name="export_save_failed">Errore durante l\'esportazione del salvataggio</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia non è reale</string> 123 <string name="gaia_is_not_real">Gaia non è reale</string>
@@ -94,7 +126,18 @@
94 <string name="contributors">Collaboratori</string> 126 <string name="contributors">Collaboratori</string>
95 <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string> 127 <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">Progetti che rendono yuzu per Android possibile</string>
97 <string name="build">Compilazione</string> 130 <string name="build">Compilazione</string>
131 <string name="user_data">Dati Utente</string>
132 <string name="user_data_description">Importa/Esporta tutti i dati dell\'applicazione.\n\nDurante l\'importazione dei Dati Utente, quelli già esistenti verranno ELIMINATI.</string>
133 <string name="exporting_user_data">Esportazione dei Dati Utente...</string>
134 <string name="importing_user_data">Importazione dei Dati Utente...</string>
135 <string name="import_user_data">Importa i Dati Utente</string>
136 <string name="invalid_yuzu_backup">Backup di Yuzu Invalido</string>
137 <string name="user_data_export_success">Dati Utente esportati con successo</string>
138 <string name="user_data_import_success">Dati Utente importati con successo.</string>
139 <string name="user_data_export_cancelled">Esportazione annullata</string>
140 <string name="user_data_import_failed_description">Assicurati che la cartella dei Dati dell\'utente stiano nella radice del file.zip e che sia presente una cartella config in config/config.ini, poi, riprova.</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,41 +157,53 @@
114 <string name="are_you_interested">Sei interessato?</string> 157 <string name="are_you_interested">Sei interessato?</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">Abilita il limite di velocità</string> 160 <string name="frame_limit_enable">Limita velocità</string>
118 <string name="frame_limit_enable_description">Quando abilitato, la velocità di emulazione verrà limitata a una specifica percentuale della velocità normale.</string> 161 <string name="frame_limit_enable_description">Limita la velocità dell\'emulazione a una specifica percentuale della velocità normale.</string>
119 <string name="frame_limit_slider">Limite velocità percentuale</string> 162 <string name="frame_limit_slider">Limite velocità percentuale</string>
120 <string name="frame_limit_slider_description">Specifica la percentuale del limite della velocità di emulazione. Con quella preimpostata al 100% l\'emulazione verrà limitata alla velocità normale. Valori più alti o bassi aumenteranno o diminuiranno il limite di velocità.</string> 163 <string name="frame_limit_slider_description">Specifica la percentuale per limitare la velocità di emulazione. 100% è la velocità normale. Valori maggiori o minori aumenteranno o diminuiranno il limite di velocità</string>
121 <string name="cpu_accuracy">Accuratezza della CPU</string> 164 <string name="cpu_accuracy">Accuratezza della CPU</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">Modalità docked</string> 168 <string name="use_docked_mode">Modalità Docked</string>
125 <string name="use_docked_mode_description">Emula in modalità docked, questo aumenta la risoluzione a spese delle performance.</string> 169 <string name="use_docked_mode_description">Aumenta la risoluzione, diminuendo le performance. La modalità portatile è usata quando disabilitato, diminuendo la risoluzione e aumentando le performance.</string>
126 <string name="emulated_region">Regione emulata</string> 170 <string name="emulated_region">Regione emulata</string>
127 <string name="emulated_language">Lingua emulata</string> 171 <string name="emulated_language">Lingua emulata</string>
128 <string name="select_rtc_date">Seleziona la data dall\'orologio in tempo reale</string> 172 <string name="select_rtc_date">Imposta la data </string>
129 <string name="select_rtc_time">Seleziona il tempo dall\'orologio in tempo reale</string> 173 <string name="select_rtc_time">Imposta l\'ora, i minuti e i secondi.</string>
130 <string name="use_custom_rtc">Abilità l\'orologio in tempo reale personalizzato</string> 174 <string name="use_custom_rtc">RTC Personalizzato</string>
131 <string name="use_custom_rtc_description">Questa impostazione ti permette di impostare un orologio in tempo reale personalizzato separato da quello del tuo sistema corrente.</string> 175 <string name="use_custom_rtc_description">Ti permette di impostare un orologio in tempo reale personalizzato, completamente separato da quello di sistema.</string>
132 <string name="set_custom_rtc">Imposta l\'orologio in tempo reale personalizzato</string> 176 <string name="set_custom_rtc">Imposta un orologio in tempo reale personalizzato</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Livello di accuratezza</string> 179 <string name="renderer_accuracy">Livello di accuratezza</string>
137 <string name="renderer_resolution">Risoluzione</string> 180 <string name="renderer_resolution">Risoluzione (Portatile/Docked)</string>
138 <string name="renderer_vsync">Modalità VSync</string> 181 <string name="renderer_vsync">Modalità VSync</string>
139 <string name="renderer_aspect_ratio">Rapporto d\'aspetto</string> 182 <string name="renderer_screen_layout">Orientamento</string>
140 <string name="renderer_scaling_filter">Filtro di adattamento alla finestra</string> 183 <string name="renderer_aspect_ratio">Rapporto d\'aspetto: </string>
184 <string name="renderer_scaling_filter">Filtro adattivo della finestra </string>
141 <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string> 185 <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string>
142 <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string> 186 <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string>
143 <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string> 187 <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string>
144 <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string> 188 <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string>
145 <string name="renderer_asynchronous_shaders_description">Compila le shaders asincronamente, questo riduce lo shutter ma potrebbe introdurre dei glitch. </string> 189 <string name="renderer_asynchronous_shaders_description">Compila le shader in modo asincrone, riducendo lo stutter. Può causare glitch grafici.</string>
146 <string name="renderer_debug">Abilità il debug grafico</string> 190 <string name="renderer_reactive_flushing">Abilita il Reactive Flushing</string>
147 <string name="renderer_debug_description">Quando l\'opzione è selezionata, l\'API grafica entra in una modalità di debug più lenta</string> 191 <string name="renderer_reactive_flushing_description">Migliora l\'accuratezza della grafica in alcuni giochi, al costo delle performance.</string>
148 <string name="use_disk_shader_cache">Usa cache shader su disco</string> 192 <string name="use_disk_shader_cache">Usa la cache delle shader</string>
149 <string name="use_disk_shader_cache_description">Riduce lo stuttering salvando e caricando le shader generate sul disco.</string> 193 <string name="use_disk_shader_cache_description">Riduce lo stuttering caricando le shader già compilate all\'avvio.</string>
194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">Debug della CPU</string>
198 <string name="cpu_debug_mode_description">Imposta la CPU in modalità Debug (Più lento)</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
201 <string name="renderer_debug">Debug GPU</string>
202 <string name="renderer_debug_description">Imposta l\'API grafica in uno stato dedicato al Debugging. Impatta di molto sulle performance.</string>
203 <string name="fastmem">Fastmem</string>
150 204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">Motore di Output</string>
152 <string name="audio_volume">Volume</string> 207 <string name="audio_volume">Volume</string>
153 <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string> 208 <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string>
154 209
@@ -157,14 +212,24 @@
157 <string name="ini_saved">Impostazioni salvate</string> 212 <string name="ini_saved">Impostazioni salvate</string>
158 <string name="gameid_saved">Impostazioni salvate per %1$s</string> 213 <string name="gameid_saved">Impostazioni salvate per %1$s</string>
159 <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string> 214 <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string>
215 <string name="unimplemented_menu">Menu non implementato</string>
160 <string name="loading">Caricamento…</string> 216 <string name="loading">Caricamento…</string>
217 <string name="shutting_down">Spegnimento...</string>
161 <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string> 218 <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string>
162 <string name="reset_to_default">Riportare alle impostazioni originali</string> 219 <string name="reset_to_default">Riportare alle impostazioni originali</string>
163 <string name="reset_all_settings">Resettare tutte le impostazioni?</string> 220 <string name="reset_all_settings">Resettare tutte le impostazioni?</string>
164 <string name="reset_all_settings_description">Tutte le Impostazioni Avanzate saranno ripristinate a quelle originali. Questa operazione non è reversibile</string> 221 <string name="reset_all_settings_description">Le impostazione avanzate verranno completamente reimpostate. Questa operazione è IRREVERSIBILE.</string>
165 <string name="settings_reset">Reimposta le impostazioni</string> 222 <string name="settings_reset">Reimposta le impostazioni</string>
166 <string name="close">Chiudi</string> 223 <string name="close">Chiudi</string>
167 <string name="learn_more">Per saperne di più</string> 224 <string name="learn_more">Per saperne di più</string>
225 <string name="auto">Automatico</string>
226 <string name="submit">Invia</string>
227 <string name="string_null">Nullo</string>
228 <string name="string_import">Importa</string>
229 <string name="export">Esporta</string>
230 <string name="export_failed">Esportazione Fallita</string>
231 <string name="import_failed">Importazione Fallita</string>
232 <string name="cancelling">Cancellazione</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Seleziona il driver della GPU</string> 235 <string name="select_gpu_driver">Seleziona il driver della GPU</string>
@@ -172,6 +237,7 @@
172 <string name="select_gpu_driver_install">Installa</string> 237 <string name="select_gpu_driver_install">Installa</string>
173 <string name="select_gpu_driver_default">Predefinito</string> 238 <string name="select_gpu_driver_default">Predefinito</string>
174 <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> 239 <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
240 <string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
175 <string name="system_gpu_driver">Driver GPU del sistema</string> 241 <string name="system_gpu_driver">Driver GPU del sistema</string>
176 <string name="installing_driver">Installando i driver...</string> 242 <string name="installing_driver">Installando i driver...</string>
177 243
@@ -182,10 +248,11 @@
182 <string name="preferences_graphics">Grafica</string> 248 <string name="preferences_graphics">Grafica</string>
183 <string name="preferences_audio">Audio</string> 249 <string name="preferences_audio">Audio</string>
184 <string name="preferences_theme">Tema e colori</string> 250 <string name="preferences_theme">Tema e colori</string>
251 <string name="preferences_debug">Debug</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">La tua ROM è criptata</string> 254 <string name="loader_error_encrypted">La tua ROM è criptata</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Per favore segui la guida per eseguire il dump della <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartuccia di gioco</a> o i <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titoli installati</a>.]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[Segui la nostra guida per fare il <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">dump delle tue cartucce di gioco</a>oppure <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">dei titoli già installati</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Per favore assicurati che il file <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> sia installato in modo che i giochi possano essere decrittati.]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[Per favore assicurati che il file <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> sia installato in modo che i giochi possano essere decrittati.]]></string>
190 <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string> 257 <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string>
191 <string name="loader_error_video_core_description">Questo è causato solitamente dal driver incompatibile di una GPU. L\'installazione di driver GPU personalizzati potrebbe risolvere questo problema.</string> 258 <string name="loader_error_video_core_description">Questo è causato solitamente dal driver incompatibile di una GPU. L\'installazione di driver GPU personalizzati potrebbe risolvere questo problema.</string>
@@ -193,28 +260,28 @@
193 <string name="loader_error_file_not_found">Il file della ROM non esiste</string> 260 <string name="loader_error_file_not_found">Il file della ROM non esiste</string>
194 261
195 <!-- Emulation Menu --> 262 <!-- Emulation Menu -->
196 <string name="emulation_exit">Uscire dall\'emulazione</string> 263 <string name="emulation_exit">Arresta emulazione</string>
197 <string name="emulation_done">Fatto</string> 264 <string name="emulation_done">Fatto</string>
198 <string name="emulation_fps_counter">Contatore degli FPS</string> 265 <string name="emulation_fps_counter">Contatore FPS</string>
199 <string name="emulation_toggle_controls">Controlli a interruttore</string> 266 <string name="emulation_toggle_controls">Controlli a interruttore</string>
200 <string name="emulation_rel_stick_center">Centro relativo degli Stick</string> 267 <string name="emulation_rel_stick_center">Centro relativo degli Stick</string>
201 <string name="emulation_dpad_slide">Slittamento del Pad Direzionale</string> 268 <string name="emulation_dpad_slide">DPad A Scorrimento</string>
202 <string name="emulation_haptics">Aptico</string> 269 <string name="emulation_haptics">Feedback Aptico</string>
203 <string name="emulation_show_overlay">Mostra Overlay</string> 270 <string name="emulation_show_overlay">Mostra l\'Overlay</string>
204 <string name="emulation_toggle_all">Attiva/disattiva tutto</string> 271 <string name="emulation_toggle_all">Attiva/Disattiva tutto</string>
205 <string name="emulation_control_adjust">Aggiusta Overlay</string> 272 <string name="emulation_control_adjust">Modifica l\'Overlay</string>
206 <string name="emulation_control_scale">Scala</string> 273 <string name="emulation_control_scale">Scala</string>
207 <string name="emulation_control_opacity">Opacità</string> 274 <string name="emulation_control_opacity">Opacità</string>
208 <string name="emulation_touch_overlay_reset">Reimposta Overlay</string> 275 <string name="emulation_touch_overlay_reset">Reimposta l\'Overlay</string>
209 <string name="emulation_touch_overlay_edit">Modifica Overlay</string> 276 <string name="emulation_touch_overlay_edit">Modifica l\'Overlay</string>
210 <string name="emulation_pause">Metti in pausa l\'emulazione</string> 277 <string name="emulation_pause">Sospendi l\'emulazione</string>
211 <string name="emulation_unpause">Riprendi Emulazione</string> 278 <string name="emulation_unpause">Riprendi l\'emulazione</string>
212 <string name="emulation_input_overlay">Impostazioni Overlay</string> 279 <string name="emulation_input_overlay">Opzioni overlay</string>
213 280
214 <string name="load_settings">Caricamento delle impostazioni...</string> 281 <string name="load_settings">Carico le impostazioni...</string>
215 282
216 <!-- Software keyboard --> 283 <!-- Software keyboard -->
217 <string name="software_keyboard">Tastiera software</string> 284 <string name="software_keyboard">Tastiera Software</string>
218 285
219 <!-- Errors and warnings --> 286 <!-- Errors and warnings -->
220 <string name="abort_button">Interrompi</string> 287 <string name="abort_button">Interrompi</string>
@@ -226,6 +293,9 @@
226 <string name="fatal_error">Errore Fatale</string> 293 <string name="fatal_error">Errore Fatale</string>
227 <string name="fatal_error_message">Un errore fatale è accaduto. Controlla i log per i dettagli.\nContinuare ad emulare potrebbe portare bug o causare crash.</string> 294 <string name="fatal_error_message">Un errore fatale è accaduto. Controlla i log per i dettagli.\nContinuare ad emulare potrebbe portare bug o causare crash.</string>
228 <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string> 295 <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string>
296 <string name="device_memory_inadequate">RAM Totale:%1$s\nRaccomandati: %2$s</string>
297 <string name="memory_formatted">%1$s%2$s</string>
298 <string name="no_game_present">Non è presente alcun gioco avviabile.</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">Giappone</string> 301 <string name="region_japan">Giappone</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">Corea</string> 306 <string name="region_korea">Corea</string>
237 <string name="region_taiwan">Taiwan</string> 307 <string name="region_taiwan">Taiwan</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Byte</string>
311 <string name="memory_kilobyte">Kb</string>
312 <string name="memory_megabyte">Mb</string>
313 <string name="memory_gigabyte">GB</string>
314 <string name="memory_terabyte">Tb</string>
315 <string name="memory_petabyte">Pb</string>
316 <string name="memory_exabyte">Eb</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 319 <string name="renderer_vulkan">Vulkan</string>
@@ -274,12 +351,17 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 351 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 352 <string name="anti_aliasing_smaa">SMAA</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">Layout Orizzontale</string>
356 <string name="screen_layout_portrait">Layout Verticale</string>
357 <string name="screen_layout_auto">Automatico</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">Predefinito (16:9)</string> 360 <string name="ratio_default">Predefinito (16:9)</string>
279 <string name="ratio_force_four_three">Forza 4:3</string> 361 <string name="ratio_force_four_three">Forza 4:3</string>
280 <string name="ratio_force_twenty_one_nine">Forza 21:9</string> 362 <string name="ratio_force_twenty_one_nine">Forza 21:9</string>
281 <string name="ratio_force_sixteen_ten">Forza 16:10</string> 363 <string name="ratio_force_sixteen_ten">Forza 16:10</string>
282 <string name="ratio_stretch">Allunga a finestra</string> 364 <string name="ratio_stretch">Adatta alla finestra</string>
283 365
284 <!-- CPU Accuracy --> 366 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">Accurata</string> 367 <string name="cpu_accuracy_accurate">Accurata</string>
@@ -287,9 +369,9 @@
287 <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string> 369 <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string>
288 370
289 <!-- Gamepad Buttons --> 371 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">D-Pad</string> 372 <string name="gamepad_d_pad">D-pad</string>
291 <string name="gamepad_left_stick">Levetta sinistra</string> 373 <string name="gamepad_left_stick">Analogico sinistro</string>
292 <string name="gamepad_right_stick">Levetta destra</string> 374 <string name="gamepad_right_stick">Analogico destro</string>
293 <string name="gamepad_home">Home</string> 375 <string name="gamepad_home">Home</string>
294 <string name="gamepad_screenshot">Screenshot</string> 376 <string name="gamepad_screenshot">Screenshot</string>
295 377
@@ -298,7 +380,7 @@
298 <string name="building_shaders">Costruendo gli shaders</string> 380 <string name="building_shaders">Costruendo gli shaders</string>
299 381
300 <!-- Theme options --> 382 <!-- Theme options -->
301 <string name="change_app_theme">Cambia il tema dell\'app</string> 383 <string name="change_app_theme">Cambia tema dell\'app</string>
302 <string name="theme_default">Predefinito</string> 384 <string name="theme_default">Predefinito</string>
303 <string name="theme_material_you">Material You</string> 385 <string name="theme_material_you">Material You</string>
304 386
@@ -308,8 +390,22 @@
308 <string name="theme_mode_light">Chiaro</string> 390 <string name="theme_mode_light">Chiaro</string>
309 <string name="theme_mode_dark">Scuro</string> 391 <string name="theme_mode_dark">Scuro</string>
310 392
393 <!-- Audio output engines -->
394 <string name="cubeb">cubeb</string>
395
311 <!-- Black backgrounds theme --> 396 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">Usa sfondi neri</string> 397 <string name="use_black_backgrounds">Sfondi neri</string>
313 <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string> 398 <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string>
314 399
315</resources> 400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">Picture in Picture</string>
402 <string name="picture_in_picture_description">Minimizza la finestra quando viene impostata in background</string>
403 <string name="pause">Pausa</string>
404 <string name="play">Gioca</string>
405 <string name="mute">Silenzia</string>
406 <string name="unmute">Riattiva</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">Licenze</string>
410 <string name="license_fidelityfx_fsr_description">Upscaling di alta qualità da parte di AMD</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index a0ea78bef..3be4e7d26 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -1,11 +1,12 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">ã“ã®ã‚½ãƒ•トウェアã¯ã€Nintendo Switch用ã®ã‚²ãƒ¼ãƒ ã‚’実行ã—ã¾ã™ã€‚ ゲームソフトやキーã¯å«ã¾ã‚Œã¾ã›ã‚“。&lt;br /&gt;&lt;br /&gt;事å‰ã«ã€ <![CDATA[<b> prod.keys </b>]]> ファイルをデãƒã‚¤ã‚¹ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã«é…ç½®ã—ã¦ãŠã„ã¦ãã ã•ã„。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> 4 <string name="app_disclaimer">ã“ã®ã‚½ãƒ•トウェアã§ã¯ã€Nintendo Switchã®ã‚²ãƒ¼ãƒ ã‚’実行ã§ãã¾ã™ã€‚ ゲームソフトやキーã¯å«ã¾ã‚Œã¾ã›ã‚“。&lt;br /&gt;&lt;br /&gt;事å‰ã«ã€ <![CDATA[<b> prod.keys </b>]]> ファイルをストレージã«é…ç½®ã—ã¦ãŠã„ã¦ãã ã•ã„。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string>
5 <string name="emulation_notification_channel_name">ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒæœ‰åйã§ã™</string> 5 <string name="emulation_notification_channel_name">ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒæœ‰åйã§ã™</string>
6 <string name="emulation_notification_channel_description">エミュレーションã®å®Ÿè¡Œä¸­ã«å¸¸è¨­é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string> 6 <string name="emulation_notification_channel_description">エミュレーションã®å®Ÿè¡Œä¸­ã«å¸¸è¨­é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string>
7 <string name="emulation_notification_running">yuzu ã¯å®Ÿè¡Œä¸­ã§ã™</string> 7 <string name="emulation_notification_running">yuzu ã¯å®Ÿè¡Œä¸­ã§ã™</string>
8 <string name="notice_notification_channel_description">å•題ãŒç™ºç”Ÿã—ãŸã¨ãã«é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string> 8 <string name="notice_notification_channel_name">通知ã¨ã‚¨ãƒ©ãƒ¼</string>
9 <string name="notice_notification_channel_description">å•題ã®ç™ºç”Ÿæ™‚ã«é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string>
9 <string name="notification_permission_not_granted">通知ãŒè¨±å¯ã•れã¦ã„ã¾ã›ã‚“!</string> 10 <string name="notification_permission_not_granted">通知ãŒè¨±å¯ã•れã¦ã„ã¾ã›ã‚“!</string>
10 11
11 <!-- Setup strings --> 12 <!-- Setup strings -->
@@ -16,7 +17,7 @@
16 <string name="keys_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰ &lt;b>prod.keys&lt;/b> ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> 17 <string name="keys_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰ &lt;b>prod.keys&lt;/b> ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string>
17 <string name="select_keys">ã‚­ãƒ¼ã‚’é¸æŠž</string> 18 <string name="select_keys">ã‚­ãƒ¼ã‚’é¸æŠž</string>
18 <string name="games">ゲーム</string> 19 <string name="games">ゲーム</string>
19 <string name="games_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰&lt;b>ゲーム&lt;/b>ãŒã‚ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> 20 <string name="games_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰&lt;b>ゲーム&lt;/b>ã®ã‚ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string>
20 <string name="done">完了</string> 21 <string name="done">完了</string>
21 <string name="done_description">準備ãŒå®Œäº†ã—ã¾ã—ãŸã€‚\nã‚²ãƒ¼ãƒ ã‚’ãŠæ¥½ã—ã¿ãã ã•ã„!</string> 22 <string name="done_description">準備ãŒå®Œäº†ã—ã¾ã—ãŸã€‚\nã‚²ãƒ¼ãƒ ã‚’ãŠæ¥½ã—ã¿ãã ã•ã„!</string>
22 <string name="text_continue">続行</string> 23 <string name="text_continue">続行</string>
@@ -24,48 +25,53 @@
24 <string name="back">戻る</string> 25 <string name="back">戻る</string>
25 <string name="add_games">ゲームを追加</string> 26 <string name="add_games">ゲームを追加</string>
26 <string name="add_games_description">ã‚²ãƒ¼ãƒ ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž</string> 27 <string name="add_games_description">ã‚²ãƒ¼ãƒ ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž</string>
28 <string name="step_complete">完了!</string>
27 29
28 <!-- Home strings --> 30 <!-- Home strings -->
29 <string name="home_games">ゲーム</string> 31 <string name="home_games">ゲーム</string>
30 <string name="home_search">検索</string> 32 <string name="home_search">検索</string>
31 <string name="home_settings">設定</string> 33 <string name="home_settings">設定</string>
32 <string name="empty_gamelist">ファイルãŒè¦‹ã¤ã‹ã‚‰ãªã„ã‹ã€ã‚²ãƒ¼ãƒ ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãŒã¾ã é¸æŠžã•れã¦ã„ã¾ã›ã‚“。</string> 34 <string name="empty_gamelist">ファイルãŒå­˜åœ¨ã—ãªã„ã‹ã‚²ãƒ¼ãƒ ãƒ•ォルダãŒé¸æŠžã•れã¦ã„ã¾ã›ã‚“。</string>
33 <string name="search_and_filter_games">ã‚²ãƒ¼ãƒ ã®æ¤œç´¢ã¨çµžã‚Šè¾¼ã¿</string> 35 <string name="search_and_filter_games">ã‚²ãƒ¼ãƒ ã®æ¤œç´¢ã¨çµžã‚Šè¾¼ã¿</string>
34 <string name="select_games_folder">ã‚²ãƒ¼ãƒ ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž</string> 36 <string name="select_games_folder">ゲームフォルダ</string>
35 <string name="select_games_folder_description">yuzu ãŒã‚²ãƒ¼ãƒ ãƒªã‚¹ãƒˆã«è¿½åŠ ã§ãるよã†ã«ã—ã¾ã™</string> 37 <string name="select_games_folder_description">ゲームをyuzuã®ã‚²ãƒ¼ãƒ ãƒªã‚¹ãƒˆã«è¿½åŠ ã—ã¾ã™</string>
36 <string name="add_games_warning">ゲームフォルダã®é¸æŠžã‚’スキップã—ã¾ã™ã‹?</string> 38 <string name="add_games_warning">ゲームフォルダã®é¸æŠžã‚’スキップã—ã¾ã™ã‹?</string>
37 <string name="add_games_warning_description">ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ãªã„å ´åˆã€ã‚²ãƒ¼ãƒ ã¯ã‚²ãƒ¼ãƒ ãƒªã‚¹ãƒˆã«è¡¨ç¤ºã•れã¾ã›ã‚“。</string> 39 <string name="add_games_warning_description">ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ãªã„ã¨ã€ã‚²ãƒ¼ãƒ ãŒãƒªã‚¹ãƒˆã«è¡¨ç¤ºã•れã¾ã›ã‚“。</string>
38 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
39 <string name="home_search_games">ゲームを検索</string> 41 <string name="home_search_games">ゲームを検索</string>
40 <string name="games_dir_selected">ゲームディレクトリãŒé¸æŠžã•れã¾ã—ãŸ</string> 42 <string name="search_settings">検索設定</string>
41 <string name="install_prod_keys">prod.keys をインストール</string> 43 <string name="games_dir_selected">ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¾ã—ãŸ</string>
42 <string name="install_prod_keys_description">ゲームã®å¾©å·åŒ–ã«å¿…è¦</string> 44 <string name="install_prod_keys">prod.keys</string>
45 <string name="install_prod_keys_description">製å“版ゲームã®å¾©å·åŒ–ã«å¿…è¦ã§ã™</string>
43 <string name="install_prod_keys_warning">キーã®è¿½åŠ ã‚’ã‚¹ã‚­ãƒƒãƒ—ã—ã¾ã™ã‹ï¼Ÿ</string> 46 <string name="install_prod_keys_warning">キーã®è¿½åŠ ã‚’ã‚¹ã‚­ãƒƒãƒ—ã—ã¾ã™ã‹ï¼Ÿ</string>
44 <string name="install_prod_keys_warning_description">製å“版ゲームã®ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã«ã¯ã€æœ‰åйãªã‚­ãƒ¼ãŒå¿…è¦ã§ã™ã€‚続行ã™ã‚‹ã¨è‡ªä½œã‚¢ãƒ—リã—ã‹æ©Ÿèƒ½ã—ã¾ã›ã‚“。</string> 47 <string name="install_prod_keys_warning_description">製å“版ゲームã®ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã«ã¯ã€æœ‰åйãªã‚­ãƒ¼ãŒå¿…è¦ã§ã™ã€‚続行ã™ã‚‹ã¨è‡ªä½œã‚¢ãƒ—リã—ã‹æ©Ÿèƒ½ã—ã¾ã›ã‚“。</string>
45 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> 48 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
46 <string name="notifications">通知</string> 49 <string name="notifications">通知</string>
47 <string name="notifications_description">下ã®ãƒœã‚¿ãƒ³ã§é€šçŸ¥ã®æ¨©é™ã‚’許å¯ã—ã¦ãã ã•ã„。</string> 50 <string name="notifications_description">下ã®ãƒœã‚¿ãƒ³ã§é€šçŸ¥ã‚’許å¯ã—ã¦ãã ã•ã„。</string>
48 <string name="give_permission">許å¯</string> 51 <string name="give_permission">許å¯</string>
49 <string name="notification_warning">通知ã®è¨±å¯ã‚’スキップã—ã¾ã™ã‹ï¼Ÿ</string> 52 <string name="notification_warning">通知ã®è¨±å¯ã‚’スキップã—ã¾ã™ã‹ï¼Ÿ</string>
50 <string name="notification_warning_description">yuzuã¯é‡è¦ãªãŠçŸ¥ã‚‰ã›ã‚’通知ã§ãã¾ã›ã‚“。</string> 53 <string name="notification_warning_description">yuzuã¯é‡è¦ãªãŠçŸ¥ã‚‰ã›ã‚’通知ã§ãã¾ã›ã‚“。</string>
51 <string name="permission_denied">権é™ãŒæ‹’å¦ã•れã¾ã—ãŸ</string> 54 <string name="permission_denied">権é™ãŒæ‹’å¦ã•れã¾ã—ãŸ</string>
52 <string name="permission_denied_description">ã“ã®æ¨©é™ã‚’複数回拒å¦ã—ãŸãŸã‚ã€ã‚·ã‚¹ãƒ†ãƒ è¨­å®šã§æ‰‹å‹•ã§è¨±å¯ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string> 55 <string name="permission_denied_description">ã“ã®æ¨©é™ã‚’複数回拒å¦ã—ãŸãŸã‚ã€è¨­å®šã‹ã‚‰æ‰‹å‹•ã§è¨±å¯ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string>
53 <string name="about">情報</string> 56 <string name="about">情報</string>
54 <string name="about_description">ビルドãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆãªã©</string> 57 <string name="about_description">ビルドãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆãªã©</string>
55 <string name="warning_help">ヘルプ</string> 58 <string name="warning_help">ヘルプ</string>
56 <string name="warning_skip">スキップ</string> 59 <string name="warning_skip">スキップ</string>
57 <string name="warning_cancel">キャンセル</string> 60 <string name="warning_cancel">キャンセル</string>
58 <string name="install_amiibo_keys">Amiibo キーをインストール</string> 61 <string name="install_amiibo_keys">Amiibo</string>
59 <string name="install_amiibo_keys_description">ゲーム内ã§ã® Amiibo ã®ä½¿ç”¨ã«å¿…è¦</string> 62 <string name="install_amiibo_keys_description">ゲーム内ã§ã® Amiibo ã®ä½¿ç”¨ã«å¿…è¦ã§ã™</string>
60 <string name="invalid_keys_file">無効ãªã‚­ãƒ¼ãƒ•ァイルãŒé¸æŠžã•れã¾ã—ãŸ</string> 63 <string name="invalid_keys_file">無効ãªã‚­ãƒ¼ãƒ•ァイルã§ã</string>
61 <string name="install_keys_success">正常ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸ</string> 64 <string name="install_keys_success">正常ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸ</string>
62 <string name="reading_keys_failure">æš—å·åŒ–キーã®èª­ã¿å–りエラー</string> 65 <string name="reading_keys_failure">æš—å·åŒ–キーã®èª­ã¿è¾¼ã¿å¤±æ•—</string>
63 <string name="invalid_keys_error">æš—å·åŒ–キーãŒç„¡åйã§ã™</string> 66 <string name="install_prod_keys_failure_extension_description">ã‚­ãƒ¼ã®æ‹¡å¼µå­ãŒ.keysã§ã‚ã‚‹ã“ã¨ã‚’確èªã—ã€å†åº¦ãŠè©¦ã—ãã ã•ã„。</string>
67 <string name="install_amiibo_keys_failure_extension_description">ã‚­ãƒ¼ã®æ‹¡å¼µå­ãŒ.binã§ã‚ã‚‹ã“ã¨ã‚’確èªã—ã€å†åº¦ãŠè©¦ã—ãã ã•ã„。</string>
68 <string name="invalid_keys_error">æš—å·åŒ–キーãŒç„¡åй</string>
64 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
65 <string name="install_keys_failure_description">é¸æŠžã•れãŸãƒ•ァイルãŒä¸æ­£ã¾ãŸã¯ç ´æã—ã¦ã„ã¾ã™ã€‚キーをå†ãƒ€ãƒ³ãƒ—ã—ã¦ãã ã•ã„。</string> 70 <string name="install_keys_failure_description">ファイルãŒé–“é•ã£ã¦ã„ã‚‹ã‹ç ´æã—ã¦ã„ã¾ã™ã€‚キーをå†ãƒ€ãƒ³ãƒ—ã—ã¦ãã ã•ã„。</string>
66 <string name="install_gpu_driver">GPUドライãƒãƒ¼ã‚’インストール</string> 71 <string name="install_gpu_driver">GPUドライãƒãƒ¼</string>
67 <string name="install_gpu_driver_description">代替ドライãƒãƒ¼ã‚’インストールã—ã¦ãƒ‘フォーマンスや精度をå‘上ã•ã›ã¾ã™</string> 72 <string name="install_gpu_driver_description">代替ドライãƒãƒ¼ã‚’インストールã—ã¦ãƒ‘フォーマンスや精度をå‘上ã•ã›ã¾ã™</string>
68 <string name="advanced_settings">高度ãªè¨­å®š</string> 73 <string name="advanced_settings">高度ãªè¨­å®š</string>
74 <string name="advanced_settings_game">高度ãªè¨­å®š: %1$s</string>
69 <string name="settings_description">エミュレーターã®è¨­å®šã‚’æ§‹æˆã—ã¾ã™</string> 75 <string name="settings_description">エミュレーターã®è¨­å®šã‚’æ§‹æˆã—ã¾ã™</string>
70 <string name="search_recently_played">最近プレイã—ãŸ</string> 76 <string name="search_recently_played">最近プレイã—ãŸ</string>
71 <string name="search_recently_added">最近追加ã•れãŸ</string> 77 <string name="search_recently_added">最近追加ã•れãŸ</string>
@@ -77,15 +83,34 @@
77 <string name="no_file_manager">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</string> 83 <string name="no_file_manager">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</string>
78 <string name="notification_no_directory_link">yuzuã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é–‹ã‘ã¾ã›ã‚“</string> 84 <string name="notification_no_directory_link">yuzuã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é–‹ã‘ã¾ã›ã‚“</string>
79 <string name="notification_no_directory_link_description">ファイルマãƒãƒ¼ã‚¸ãƒ£ã®ã‚µã‚¤ãƒ‰ãƒ‘ãƒãƒ«ã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒ•ã‚©ãƒ«ãƒ€ã‚’æ‰‹å‹•ã§æŽ¢ã—ã¦ãã ã•ã„。</string> 85 <string name="notification_no_directory_link_description">ファイルマãƒãƒ¼ã‚¸ãƒ£ã®ã‚µã‚¤ãƒ‰ãƒ‘ãƒãƒ«ã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒ•ã‚©ãƒ«ãƒ€ã‚’æ‰‹å‹•ã§æŽ¢ã—ã¦ãã ã•ã„。</string>
80 <string name="manage_save_data">セーブデータを管ç†</string> 86 <string name="manage_save_data">セーブデータ</string>
81 <string name="manage_save_data_description">セーブデータãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚以下ã®ã‚ªãƒ—ションã‹ã‚‰é¸æŠžã—ã¦ãã ã•ã„。</string> 87 <string name="manage_save_data_description">セーブデータãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚æ“ä½œã‚’é¸æŠžã—ã¦ãã ã•ã„。</string>
82 <string name="import_export_saves_description">セーブファイルをインãƒãƒ¼ãƒˆ/エクスãƒãƒ¼ãƒˆ</string> 88 <string name="import_export_saves_description">セーブファイルをインãƒãƒ¼ãƒˆ/エクスãƒãƒ¼ãƒˆ</string>
83 <string name="save_file_imported_success">インãƒãƒ¼ãƒˆãŒå®Œäº†ã—ã¾ã—ãŸ</string> 89 <string name="save_file_imported_success">インãƒãƒ¼ãƒˆãŒå®Œäº†ã—ã¾ã—ãŸ</string>
84 <string name="save_file_invalid_zip_structure">セーブデータã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªæ§‹é€ ãŒç„¡åйã§ã™</string> 90 <string name="save_file_invalid_zip_structure">セーブデータã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªæ§‹é€ ãŒç„¡åй</string>
85 <string name="save_file_invalid_zip_structure_description">最åˆã®ã‚µãƒ–フォルダåã¯ã€ã‚²ãƒ¼ãƒ ã®ã‚¿ã‚¤ãƒˆãƒ«IDã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string> 91 <string name="save_file_invalid_zip_structure_description">最åˆã®ã‚µãƒ–フォルダåã¯ã€ã‚²ãƒ¼ãƒ ã®ã‚¿ã‚¤ãƒˆãƒ«IDã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string>
86 <string name="import_saves">インãƒãƒ¼ãƒˆ</string> 92 <string name="import_saves">インãƒãƒ¼ãƒˆ</string>
87 <string name="export_saves">エクスãƒãƒ¼ãƒˆ</string> 93 <string name="export_saves">エクスãƒãƒ¼ãƒˆ</string>
88 94 <string name="install_firmware">ファームウェア</string>
95 <string name="install_firmware_description">ファームウェアã¯ZIPアーカイブã§ã‚ã‚‹å¿…è¦ãŒã‚りã€ä¸€éƒ¨ã®ã‚²ãƒ¼ãƒ ã‚’èµ·å‹•ã™ã‚‹ã®ã«å¿…è¦ã§ã™</string>
96 <string name="firmware_installing">ファームウェアをインストール中</string>
97 <string name="firmware_installed_success">インストールãŒå®Œäº†ã—ã¾ã—ãŸ</string>
98 <string name="firmware_installed_failure">インストール失敗</string>
99 <string name="share_log">デãƒãƒƒã‚°ãƒ­ã‚°</string>
100 <string name="share_log_description">yuzuã®ãƒ­ã‚°ãƒ•ァイルを共有ã—ã¦å•題をデãƒãƒƒã‚°ã—ã¾ã™</string>
101 <string name="share_log_missing">ログãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
102 <string name="install_game_content">追加コンテンツ</string>
103 <string name="install_game_content_description">更新データやDLCをインストールã—ã¾ã™</string>
104 <string name="installing_game_content">コンテンツをインストール中...</string>
105 <string name="install_game_content_failure_file_extension">NSPã¨XCIå½¢å¼ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ã¿ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ã‚²ãƒ¼ãƒ ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒæœ‰åйãªã‚‚ã®ã§ã‚ã‚‹ã‹ã”確èªãã ã•ã„。</string>
106 <string name="install_game_content_failed_count">%1$d ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã‚¨ãƒ©ãƒ¼</string>
107 <string name="install_game_content_success">ゲームコンテンツã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«æˆåŠŸã—ã¾ã—ãŸ</string>
108 <string name="install_game_content_success_install">%1$d ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«æˆåŠŸã—ã¾ã—ãŸ</string>
109 <string name="install_game_content_success_overwrite">%1$d ã®ä¸Šæ›¸ãã«æˆåŠŸã—ã¾ã—ãŸ</string>
110 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
111 <string name="custom_driver_not_supported">カスタムドライãƒã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“</string>
112 <string name="manage_yuzu_data">yuzu データを管ç†</string>
113 <string name="share_save_file">セーブファイルを共有</string>
89 <!-- About screen strings --> 114 <!-- About screen strings -->
90 <string name="gaia_is_not_real">ガイアã¯å®Ÿåœ¨ã—ãªã„</string> 115 <string name="gaia_is_not_real">ガイアã¯å®Ÿåœ¨ã—ãªã„</string>
91 <string name="copied_to_clipboard">クリップボードã«ã‚³ãƒ”ーã—ã¾ã—ãŸ</string> 116 <string name="copied_to_clipboard">クリップボードã«ã‚³ãƒ”ーã—ã¾ã—ãŸ</string>
@@ -93,7 +118,15 @@
93 <string name="contributors">貢献者</string> 118 <string name="contributors">貢献者</string>
94 <string name="contributors_description">yuzuãƒãƒ¼ãƒ ã®\u2764ã§ä½œã‚‰ã‚ŒãŸ</string> 119 <string name="contributors_description">yuzuãƒãƒ¼ãƒ ã®\u2764ã§ä½œã‚‰ã‚ŒãŸ</string>
95 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 120 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
121 <string name="licenses_description">yuzu for Androidã®ä½œæˆã‚’å¯èƒ½ã«ã—ãŸãƒ—ロジェクト</string>
96 <string name="build">ビルド</string> 122 <string name="build">ビルド</string>
123 <string name="user_data">ユーザデータ</string>
124 <string name="exporting_user_data">ユーザデータをエクスãƒãƒ¼ãƒˆä¸­...</string>
125 <string name="importing_user_data">ユーザデータをインãƒãƒ¼ãƒˆä¸­...</string>
126 <string name="import_user_data">ユーザデータをインãƒãƒ¼ãƒˆ</string>
127 <string name="user_data_export_success">ユーザデータã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã«æˆåŠŸã—ã¾ã—ãŸ</string>
128 <string name="user_data_import_success">ユーザデータã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã«æˆåŠŸã—ã¾ã—ãŸ</string>
129 <string name="user_data_export_cancelled">エクスãƒãƒ¼ãƒˆã‚’キャンセルã—ã¾ã—ãŸ</string>
97 <string name="support_link">https://discord.gg/u77vRWY</string> 130 <string name="support_link">https://discord.gg/u77vRWY</string>
98 <string name="website_link">https://yuzu-emu.org/</string> 131 <string name="website_link">https://yuzu-emu.org/</string>
99 <string name="github_link">https://github.com/yuzu-emu</string> 132 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -105,72 +138,91 @@
105 <string name="get_early_access_description">æœ€å…ˆç«¯ã®æ©Ÿèƒ½ã€ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹ãªã©</string> 138 <string name="get_early_access_description">æœ€å…ˆç«¯ã®æ©Ÿèƒ½ã€ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹ãªã©</string>
106 <string name="early_access_benefits">早期アクセスã®ãƒ¡ãƒªãƒƒãƒˆ</string> 139 <string name="early_access_benefits">早期アクセスã®ãƒ¡ãƒªãƒƒãƒˆ</string>
107 <string name="cutting_edge_features">æœ€å…ˆç«¯ã®æ©Ÿèƒ½</string> 140 <string name="cutting_edge_features">æœ€å…ˆç«¯ã®æ©Ÿèƒ½</string>
108 <string name="early_access_updates">ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹</string> 141 <string name="early_access_updates">アップデートã¸ã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹</string>
109 <string name="no_manual_installation">手動インストールãŒä¸è¦</string> 142 <string name="no_manual_installation">手動インストールãŒä¸è¦</string>
110 <string name="prioritized_support">優先的ãªã‚µãƒãƒ¼ãƒˆ</string> 143 <string name="prioritized_support">優先サãƒãƒ¼ãƒˆ</string>
111 <string name="helping_game_preservation">ゲームã®ä¿å­˜ã«è²¢çŒ®</string> 144 <string name="helping_game_preservation">ゲームã®ä¿å­˜ã«è²¢çŒ®</string>
112 <string name="our_eternal_gratitude">ç§ãŸã¡ã®æ°¸é ã®æ„Ÿè¬</string> 145 <string name="our_eternal_gratitude">ç§ãŸã¡ã‹ã‚‰æ°¸é ã®æ„Ÿè¬</string>
113 <string name="are_you_interested">興味ãŒã‚りã¾ã™ã‹ï¼Ÿ</string> 146 <string name="are_you_interested">興味ãŒã‚りã¾ã™ã‹ï¼Ÿ</string>
114 147
115 <!-- General settings strings --> 148 <!-- General settings strings -->
116 <string name="frame_limit_enable">速度制é™ã‚’有効化</string> 149 <string name="frame_limit_enable">エミュレーション速度を制é™</string>
117 <string name="frame_limit_enable_description">有効ã«ã™ã‚‹ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³é€Ÿåº¦ãŒä»»æ„ã®å‰²åˆã«åˆ¶é™ã•れã¾ã™ã€‚</string> 150 <string name="frame_limit_enable_description">エミュレーション速度を指定ã—ãŸå‰²åˆã«åˆ¶é™ã—ã¾ã™ã€‚</string>
118 <string name="frame_limit_slider">エミュレーション速度ã®åˆ¶é™</string> 151 <string name="frame_limit_slider">エミュレーション速度</string>
119 <string name="frame_limit_slider_description">エミュレーション速度を制é™ã™ã‚‹å‰²åˆã‚’指定ã—ã¾ã™ã€‚デフォルトã®100%ã§ã¯ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã¯é€šå¸¸ã®é€Ÿåº¦ã«åˆ¶é™ã•れã¾ã™ã€‚値ãŒé«˜ã„ã¾ãŸã¯ä½Žã„ã»ã©ã€é€Ÿåº¦åˆ¶é™ãŒå¢—加ã¾ãŸã¯æ¸›å°‘ã—ã¾ã™ã€‚</string> 152 <string name="frame_limit_slider_description">エミュレーション速度を制é™ã™ã‚‹ãƒ‘ーセンテージを指定ã—ã¾ã™ã€‚100%ã¯é€šå¸¸é€Ÿåº¦ã§ã™ã€‚値ã®å¢—減ã§é€Ÿåº¦ã‚‚増減ã—ã¾ã™ã€‚</string>
120 <string name="cpu_accuracy">CPU精度</string> 153 <string name="cpu_accuracy">CPU精度</string>
121
122 <!-- System settings strings --> 154 <!-- System settings strings -->
123 <string name="use_docked_mode">TVモード</string> 155 <string name="use_docked_mode">TVモード</string>
124 <string name="use_docked_mode_description">TVモードã§ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ãƒˆã—ã¾ã™ã€‚パフォーマンスãŒçŠ ç‰²ã«ãªã‚Šã¾ã™ãŒã€è§£åƒåº¦ãŒå‘上ã—ã¾ã™ã€‚</string> 156 <string name="use_docked_mode_description">高解åƒåº¦ã€ä½Žãƒ‘フォーマンス。無効時ã«ã¯æºå¸¯ãƒ¢ãƒ¼ãƒ‰ãŒä½¿ç”¨ã•れã¾ã™ï¼ˆä½Žè§£åƒåº¦ã€é«˜ãƒ‘フォーマンス)。</string>
125 <string name="emulated_region">地域</string> 157 <string name="emulated_region">地域</string>
126 <string name="emulated_language">言語</string> 158 <string name="emulated_language">言語</string>
127 <string name="select_rtc_date">RTCã®æ—¥ä»˜ã‚’é¸æŠž</string> 159 <string name="select_rtc_date">RTCã®æ—¥ä»˜ã‚’é¸æŠž</string>
128 <string name="select_rtc_time">RTCã®æ™‚åˆ»ã‚’é¸æŠž</string> 160 <string name="select_rtc_time">RTCã®æ™‚åˆ»ã‚’é¸æŠž</string>
129 <string name="use_custom_rtc">カスタムRTC</string> 161 <string name="use_custom_rtc">カスタム RTC</string>
130 <string name="use_custom_rtc_description">ç¾åœ¨ã®ã‚·ã‚¹ãƒ†ãƒ æ™‚é–“ã¨ã¯åˆ¥ã«ã‚«ã‚¹ã‚¿ãƒ ã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã‚¯ãƒ­ãƒƒã‚¯ã‚’設定ã§ãã¾ã™ã€‚</string> 162 <string name="use_custom_rtc_description">ç¾åœ¨ã®ã‚·ã‚¹ãƒ†ãƒ æ™‚é–“ã¨ã¯åˆ¥ã«ã€ä»»æ„ã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã‚¯ãƒ­ãƒƒã‚¯ã‚’設定ã§ãã¾ã™ã€‚</string>
131 <string name="set_custom_rtc">カスタムRTCを設定</string> 163 <string name="set_custom_rtc">カスタムRTCを設定</string>
132 164
133 <!-- Graphics settings strings --> 165 <!-- Graphics settings strings -->
134 <string name="renderer_api">API</string>
135 <string name="renderer_accuracy">精度</string> 166 <string name="renderer_accuracy">精度</string>
136 <string name="renderer_resolution">è§£åƒåº¦</string> 167 <string name="renderer_resolution">è§£åƒåº¦ï¼ˆæºå¸¯ãƒ¢ãƒ¼ãƒ‰/TVモード)</string>
137 <string name="renderer_vsync">åž‚ç›´åŒæœŸãƒ¢ãƒ¼ãƒ‰</string> 168 <string name="renderer_vsync">åž‚ç›´åŒæœŸãƒ¢ãƒ¼ãƒ‰</string>
169 <string name="renderer_screen_layout">ç”»é¢ã®å‘ã</string>
138 <string name="renderer_aspect_ratio">アスペクト比</string> 170 <string name="renderer_aspect_ratio">アスペクト比</string>
139 <string name="renderer_scaling_filter">ウィンドウé©å¿œãƒ•ィルター</string> 171 <string name="renderer_scaling_filter">ウィンドウé©å¿œãƒ•ィルター</string>
140 <string name="renderer_anti_aliasing">アンãƒã‚¨ã‚¤ãƒªã‚¢ã‚¹æ–¹å¼</string> 172 <string name="renderer_anti_aliasing">アンãƒã‚¨ã‚¤ãƒªã‚¢ã‚¹æ–¹å¼</string>
141 <string name="renderer_force_max_clock">最大クロックを強制 (Adrenoã®ã¿)</string> 173 <string name="renderer_force_max_clock">最大クロックを強制 (Adrenoã®ã¿)</string>
142 <string name="renderer_force_max_clock_description">GPUã‚’å¯èƒ½ãªé™ã‚Šæœ€å¤§ã‚¯ãƒ­ãƒƒã‚¯ã§å‹•作ã•ã›ã¾ã™ (éŽç†±åˆ¶é™ã¯å¼•ãç¶šãé©ç”¨ã•れã¾ã™)。</string> 174 <string name="renderer_force_max_clock_description">GPUを最大é™å¯èƒ½ãªå‘¨æ³¢æ•°ã§å‹•作ã•ã›ã¾ã™ (éŽç†±åˆ¶é™ã¯å¼•ãç¶šãé©ç”¨ã•れã¾ã™)。</string>
143 <string name="renderer_asynchronous_shaders">éžåŒæœŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼</string> 175 <string name="renderer_asynchronous_shaders">éžåŒæœŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼</string>
144 <string name="renderer_asynchronous_shaders_description">シェーダーをéžåŒæœŸã§ã‚³ãƒ³ãƒ‘イルã—ã¾ã™ã€‚コマè½ã¡ãŒè»½æ¸›ã•れã¾ã™ãŒã€ä¸å…·åˆãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> 176 <string name="renderer_asynchronous_shaders_description">シェーダーをéžåŒæœŸã§ã‚³ãƒ³ãƒ‘イルã—ã¾ã™ã€‚コマè½ã¡ãŒè»½æ¸›ã•れã¾ã™ãŒã€ä¸å…·åˆãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string>
177 <string name="renderer_reactive_flushing">峿™‚書ãè¾¼ã¿</string>
178 <string name="renderer_reactive_flushing_description">一部ã®ã‚²ãƒ¼ãƒ ã«ãŠã„ã¦ã€ãƒ‘フォーマンスを犠牲ã«ã—ãªãŒã‚‰ã‚‚ã€ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ç²¾åº¦ã‚’å‘上ã•ã›ã¾ã™ã€‚</string>
179 <string name="use_disk_shader_cache">ディスクシェーダーキャッシュ</string>
180 <string name="use_disk_shader_cache_description">生æˆã—ãŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼ã‚’端末ã«ä¿å­˜ã—ã¦èª­ã¿è¾¼ã¿ã€ã‚³ãƒžè½ã¡ã‚’軽減ã—ã¾ã™ã€‚</string>
181
182 <!-- Debug settings strings -->
183 <string name="cpu">CPU</string>
184 <string name="cpu_debug_mode">CPU デãƒãƒƒã‚®ãƒ³ã‚°</string>
185 <string name="gpu">GPU</string>
186 <string name="renderer_api">API</string>
145 <string name="renderer_debug">グラフィックデãƒãƒƒã‚°</string> 187 <string name="renderer_debug">グラフィックデãƒãƒƒã‚°</string>
146 <string name="renderer_debug_description">オンã«ã™ã‚‹ã¨ã€ã‚°ãƒ©ãƒ•ィックAPI ã¯ä½Žé€Ÿã®ãƒ‡ãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰ã«å…¥ã‚Šã¾ã™ã€‚</string> 188 <string name="renderer_debug_description">グラフィックAPIを低速デãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰ã«è¨­å®šã—ã¾ã™ã€‚</string>
147 <string name="use_disk_shader_cache">シェーダーキャッシュを使用</string> 189 <string name="fastmem">Fastmem</string>
148 <string name="use_disk_shader_cache_description">生æˆã—ãŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼ã‚’ディスクã«ä¿å­˜ã—ã¦èª­ã¿è¾¼ã‚€ã“ã¨ã§ã€ã‚³ãƒžè½ã¡ã‚’軽減ã—ã¾ã™ã€‚</string>
149 190
150 <!-- Audio settings strings --> 191 <!-- Audio settings strings -->
192 <string name="audio_output_engine">出力エンジン</string>
151 <string name="audio_volume">音é‡</string> 193 <string name="audio_volume">音é‡</string>
152 <string name="audio_volume_description">オーディオ出力ã®éŸ³é‡ã‚’指定ã—ã¾ã™</string> 194 <string name="audio_volume_description">オーディオ出力ã®éŸ³é‡ã‚’指定ã—ã¾ã™</string>
153 195
154 <!-- Miscellaneous --> 196 <!-- Miscellaneous -->
155 <string name="slider_default">デフォルト</string> 197 <string name="slider_default">デフォルト</string>
156 <string name="ini_saved">設定をä¿å­˜ã—ã¾ã—ãŸ</string> 198 <string name="ini_saved">設定をä¿å­˜ã—ã¾ã—ãŸ</string>
157 <string name="gameid_saved">%1$sã®è¨­å®šã‚’ä¿å­˜ã—ã¾ã—ãŸ</string> 199 <string name="gameid_saved">%1$s ã®è¨­å®šã‚’ä¿å­˜ã—ã¾ã—ãŸ</string>
158 <string name="error_saving">%1$s.ini ã®ä¿å­˜ã‚¨ãƒ©ãƒ¼: %2$s</string> 200 <string name="error_saving">%1$s.ini ã®ä¿å­˜ã‚¨ãƒ©ãƒ¼: %2$s</string>
201 <string name="unimplemented_menu">未実装ã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼</string>
159 <string name="loading">読ã¿è¾¼ã¿ä¸­â€¦</string> 202 <string name="loading">読ã¿è¾¼ã¿ä¸­â€¦</string>
203 <string name="shutting_down">終了中...</string>
160 <string name="reset_setting_confirmation">ã“ã®è¨­å®šã‚’åˆæœŸå€¤ã«ãƒªã‚»ãƒƒãƒˆã—ã¾ã™ã‹?</string> 204 <string name="reset_setting_confirmation">ã“ã®è¨­å®šã‚’åˆæœŸå€¤ã«ãƒªã‚»ãƒƒãƒˆã—ã¾ã™ã‹?</string>
161 <string name="reset_to_default">åˆæœŸè¨­å®šã«æˆ»ã™</string> 205 <string name="reset_to_default">åˆæœŸè¨­å®šã«æˆ»ã™</string>
162 <string name="reset_all_settings">ã™ã¹ã¦ã®è¨­å®šã‚’リセットã—ã¾ã™ã‹ï¼Ÿ</string> 206 <string name="reset_all_settings">ã™ã¹ã¦ã®è¨­å®šã‚’リセットã—ã¾ã™ã‹ï¼Ÿ</string>
163 <string name="reset_all_settings_description">ã™ã¹ã¦ã®è©³ç´°è¨­å®šãŒåˆæœŸè¨­å®šã«æˆ»ã•れã¾ã™ã€‚ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。</string> 207 <string name="reset_all_settings_description">ã™ã¹ã¦ã®è©³ç´°è¨­å®šãŒåˆæœŸå€¤ã«æˆ»ã•れã¾ã™ã€‚ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。</string>
164 <string name="settings_reset">設定をリセットã—ã¾ã—ãŸ</string> 208 <string name="settings_reset">設定をリセットã—ã¾ã—ãŸ</string>
165 <string name="close">é–‰ã˜ã‚‹</string> 209 <string name="close">é–‰ã˜ã‚‹</string>
166 <string name="learn_more">詳細情報</string> 210 <string name="learn_more">詳細情報</string>
211 <string name="auto">自動</string>
212 <string name="submit">é€ä¿¡</string>
213 <string name="string_import">インãƒãƒ¼ãƒˆ</string>
214 <string name="export">エクスãƒãƒ¼ãƒˆ</string>
215 <string name="export_failed">エクスãƒãƒ¼ãƒˆå¤±æ•—</string>
216 <string name="import_failed">インãƒãƒ¼ãƒˆå¤±æ•—</string>
217 <string name="cancelling">キャンセル中</string>
167 218
168 <!-- GPU driver installation --> 219 <!-- GPU driver installation -->
169 <string name="select_gpu_driver">GPUドライãƒã‚’é¸æŠž</string> 220 <string name="select_gpu_driver">GPUドライãƒã‚’é¸æŠž</string>
170 <string name="select_gpu_driver_title">ç¾åœ¨ã®GPUドライãƒãƒ¼ã‚’ç½®ãæ›ãˆã¾ã™ã‹ï¼Ÿ</string> 221 <string name="select_gpu_driver_title">ç¾åœ¨ã®GPUドライãƒã‚’ç½®ãæ›ãˆã¾ã™ã‹ï¼Ÿ</string>
171 <string name="select_gpu_driver_install">インストール</string> 222 <string name="select_gpu_driver_install">インストール</string>
172 <string name="select_gpu_driver_default">デフォルト</string> 223 <string name="select_gpu_driver_default">デフォルト</string>
173 <string name="select_gpu_driver_use_default">デフォルトã®GPUドライãƒãƒ¼ã‚’使用ã—ã¾ã™</string> 224 <string name="select_gpu_driver_use_default">デフォルトã®ãƒ‰ãƒ©ã‚¤ãƒã‚’使用ã—ã¾ã™</string>
225 <string name="select_gpu_driver_error">é¸æŠžã•れãŸãƒ‰ãƒ©ã‚¤ãƒãŒç„¡åйã€ã‚·ã‚¹ãƒ†ãƒ ã®ãƒ‡ãƒ•ォルトを使用ã—ã¾ã™!</string>
174 <string name="system_gpu_driver">システムã®GPUドライãƒ</string> 226 <string name="system_gpu_driver">システムã®GPUドライãƒ</string>
175 <string name="installing_driver">インストール中…</string> 227 <string name="installing_driver">インストール中…</string>
176 228
@@ -181,33 +233,34 @@
181 <string name="preferences_graphics">グラフィック</string> 233 <string name="preferences_graphics">グラフィック</string>
182 <string name="preferences_audio">サウンド</string> 234 <string name="preferences_audio">サウンド</string>
183 <string name="preferences_theme">テーマã¨è‰²</string> 235 <string name="preferences_theme">テーマã¨è‰²</string>
236 <string name="preferences_debug">デãƒãƒƒã‚°</string>
184 237
185 <!-- ROM loading errors --> 238 <!-- ROM loading errors -->
186 <string name="loader_error_encrypted">ROMãŒæš—å·åŒ–ã•れã¦ã„ã¾ã™</string> 239 <string name="loader_error_encrypted">ROMãŒæš—å·åŒ–ã•れã¦ã„ã¾ã™</string>
187 <string name="loader_error_encrypted_roms_description"><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">ゲームカートリッジ</a>ã‚„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">インストール済ã¿ã®ã‚¿ã‚¤ãƒˆãƒ«</a>ã‚’å†åº¦ãƒ€ãƒ³ãƒ—ã™ã‚‹ãŸã‚ã®ã‚¬ã‚¤ãƒ‰ã«å¾“ã£ã¦ãã ã•ã„。]]></string> 240 <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームã®å¾©å·åŒ–ã«å¿…è¦ãª <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。]]></string>
188 <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームを復å·åŒ–ã™ã‚‹ãŸã‚ã« <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。]]></string>
189 <string name="loader_error_video_core">ビデオコアã®åˆæœŸåŒ–中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ</string> 241 <string name="loader_error_video_core">ビデオコアã®åˆæœŸåŒ–中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ</string>
190 <string name="loader_error_video_core_description">ã“れã¯é€šå¸¸ã€äº’æ›æ€§ã®ãªã„GPUドライãƒãƒ¼ãŒåŽŸå› ã§ç™ºç”Ÿã—ã¾ã™ã€‚ カスタムGPUドライãƒãƒ¼ã‚’インストールã™ã‚‹ã¨ã€å•題ãŒè§£æ±ºã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> 242 <string name="loader_error_video_core_description">ã“れã¯é€šå¸¸ã€äº’æ›æ€§ã®ãªã„GPUドライãƒãƒ¼ãŒåŽŸå› ã§ç™ºç”Ÿã—ã¾ã™ã€‚ カスタムGPUドライãƒãƒ¼ã‚’インストールã™ã‚‹ã¨ã€å•題ãŒè§£æ±ºã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string>
191 <string name="loader_error_invalid_format">ROMã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ</string> 243 <string name="loader_error_invalid_format">ROMã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ</string>
192 <string name="loader_error_file_not_found">ROMファイルãŒå­˜åœ¨ã—ã¾ã›ã‚“</string> 244 <string name="loader_error_file_not_found">ROMファイルãŒå­˜åœ¨ã—ã¾ã›ã‚“</string>
193 245
194 <!-- Emulation Menu --> 246 <!-- Emulation Menu -->
195 <string name="emulation_exit">エミュレーションを終了</string> 247 <string name="emulation_exit">終了</string>
196 <string name="emulation_done">完了</string> 248 <string name="emulation_done">完了</string>
197 <string name="emulation_fps_counter">FPSカウンター</string> 249 <string name="emulation_fps_counter">FPSカウンター</string>
198 <string name="emulation_toggle_controls">コントロールを切り替ãˆ</string> 250 <string name="emulation_toggle_controls">ボタンã®è¡¨ç¤ºè¨­å®š</string>
199 <string name="emulation_dpad_slide">å字キーã®ã‚¹ãƒ©ã‚¤ãƒ‰æ“作</string> 251 <string name="emulation_rel_stick_center">スティックを固定ã—ãªã„</string>
200 <string name="emulation_haptics">振動</string> 252 <string name="emulation_dpad_slide">å字キーをスライドæ“作</string>
201 <string name="emulation_show_overlay">オーãƒãƒ¼ãƒ¬ã‚¤ã‚’表示</string> 253 <string name="emulation_haptics">ã‚¿ãƒƒãƒæŒ¯å‹•</string>
202 <string name="emulation_toggle_all">ã™ã¹ã¦é¸æŠž</string> 254 <string name="emulation_show_overlay">ボタンを表示</string>
203 <string name="emulation_control_adjust">オーãƒãƒ¼ãƒ¬ã‚¤ã‚’調整</string> 255 <string name="emulation_toggle_all">ã™ã¹ã¦åˆ‡æ›¿</string>
256 <string name="emulation_control_adjust">見ãŸç›®ã‚’調整</string>
204 <string name="emulation_control_scale">大ãã•</string> 257 <string name="emulation_control_scale">大ãã•</string>
205 <string name="emulation_control_opacity">ä¸é€æ˜Žåº¦</string> 258 <string name="emulation_control_opacity">ä¸é€æ˜Žåº¦</string>
206 <string name="emulation_touch_overlay_reset">リセット</string> 259 <string name="emulation_touch_overlay_reset">リセット</string>
207 <string name="emulation_touch_overlay_edit">オーãƒãƒ¼ãƒ¬ã‚¤ã‚’編集</string> 260 <string name="emulation_touch_overlay_edit">ä½ç½®ã‚’編集</string>
208 <string name="emulation_pause">ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’ä¸€æ™‚åœæ­¢</string> 261 <string name="emulation_pause">ä¸€æ™‚åœæ­¢</string>
209 <string name="emulation_unpause">エミュレーションをå†é–‹</string> 262 <string name="emulation_unpause">å†é–‹</string>
210 <string name="emulation_input_overlay">オーãƒãƒ¼ãƒ¬ã‚¤ã‚ªãƒ—ション</string> 263 <string name="emulation_input_overlay">表示オプション</string>
211 264
212 <string name="load_settings">設定をロード中…</string> 265 <string name="load_settings">設定をロード中…</string>
213 266
@@ -220,10 +273,13 @@
220 <string name="system_archive_not_found">システムアーカイブãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string> 273 <string name="system_archive_not_found">システムアーカイブãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string>
221 <string name="system_archive_not_found_message">%s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。システムアーカイブをダンプã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã€ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> 274 <string name="system_archive_not_found_message">%s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。システムアーカイブをダンプã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã€ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string>
222 <string name="system_archive_general">システムアーカイブ</string> 275 <string name="system_archive_general">システムアーカイブ</string>
223 <string name="save_load_error">セーブ/ロード エラー</string> 276 <string name="save_load_error">セーブ/ロードエラー</string>
224 <string name="fatal_error">致命的ãªã‚¨ãƒ©ãƒ¼</string> 277 <string name="fatal_error">致命的ãªã‚¨ãƒ©ãƒ¼</string>
225 <string name="fatal_error_message">致命的ãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚詳細ã¯ãƒ­ã‚°ã‚’確èªã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> 278 <string name="fatal_error_message">致命的ãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚詳細ã¯ãƒ­ã‚°ã‚’確èªã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string>
226 <string name="performance_warning">ã“ã®è¨­å®šã‚’オフã«ã™ã‚‹ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ‘フォーマンスãŒè‘—ã—ã低下ã—ã¾ã™ï¼æœ€é«˜ã®ä½“験を得るãŸã‚ã«ã¯ã€ã“ã®è¨­å®šã‚’有効ã«ã—ã¦ãŠãã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚</string> 279 <string name="performance_warning">ã“ã®è¨­å®šã‚’オフã«ã™ã‚‹ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ‘フォーマンスãŒè‘—ã—ã低下ã—ã¾ã™ï¼æœ€é«˜ã®ä½“験を得るãŸã‚ã«ã¯ã€ã“ã®è¨­å®šã‚’有効ã«ã—ã¦ãŠãã“ã¨ã‚’推奨ã—ã¾ã™ã€‚</string>
280 <string name="device_memory_inadequate">デãƒã‚¤ã‚¹ RAM: %1$s\n推奨: %2$s</string>
281 <string name="memory_formatted">%1$s %2$s</string>
282 <string name="no_game_present">èµ·å‹•ã§ãるゲームãŒã‚りã¾ã›ã‚“!</string>
227 283
228 <!-- Region Names --> 284 <!-- Region Names -->
229 <string name="region_japan">日本</string> 285 <string name="region_japan">日本</string>
@@ -234,7 +290,14 @@
234 <string name="region_korea">韓国</string> 290 <string name="region_korea">韓国</string>
235 <string name="region_taiwan">å°æ¹¾</string> 291 <string name="region_taiwan">å°æ¹¾</string>
236 292
237 <!-- Language Names --> 293 <!-- Memory Sizes -->
294 <string name="memory_byte">Byte</string>
295 <string name="memory_kilobyte">KB</string>
296 <string name="memory_megabyte">MB</string>
297 <string name="memory_gigabyte">GB</string>
298 <string name="memory_terabyte">TB</string>
299 <string name="memory_petabyte">PB</string>
300 <string name="memory_exabyte">EB</string>
238 301
239 <!-- Renderer APIs --> 302 <!-- Renderer APIs -->
240 <string name="renderer_vulkan">Vulkan</string> 303 <string name="renderer_vulkan">Vulkan</string>
@@ -242,7 +305,7 @@
242 305
243 <!-- Renderer Accuracy --> 306 <!-- Renderer Accuracy -->
244 <string name="renderer_accuracy_normal">標準</string> 307 <string name="renderer_accuracy_normal">標準</string>
245 <string name="renderer_accuracy_high">高ã„</string> 308 <string name="renderer_accuracy_high">高</string>
246 <string name="renderer_accuracy_extreme">最高 (低速)</string> 309 <string name="renderer_accuracy_extreme">最高 (低速)</string>
247 310
248 <!-- Resolutions --> 311 <!-- Resolutions -->
@@ -272,12 +335,17 @@
272 <string name="anti_aliasing_fxaa">FXAA</string> 335 <string name="anti_aliasing_fxaa">FXAA</string>
273 <string name="anti_aliasing_smaa">SMAA</string> 336 <string name="anti_aliasing_smaa">SMAA</string>
274 337
338 <!-- Screen Layouts -->
339 <string name="screen_layout_landscape">横長</string>
340 <string name="screen_layout_portrait">縦長</string>
341 <string name="screen_layout_auto">自動</string>
342
275 <!-- Aspect Ratios --> 343 <!-- Aspect Ratios -->
276 <string name="ratio_default">デフォルト (16:9)</string> 344 <string name="ratio_default">デフォルト (16:9)</string>
277 <string name="ratio_force_four_three">強制 4:3</string> 345 <string name="ratio_force_four_three">強制 4:3</string>
278 <string name="ratio_force_twenty_one_nine">強制 21:9</string> 346 <string name="ratio_force_twenty_one_nine">強制 21:9</string>
279 <string name="ratio_force_sixteen_ten">強制 16:10</string> 347 <string name="ratio_force_sixteen_ten">強制 16:10</string>
280 <string name="ratio_stretch">ウィンドウã«åˆã‚ã›ã‚‹</string> 348 <string name="ratio_stretch">ç”»é¢ã«åˆã‚ã›ã‚‹</string>
281 349
282 <!-- CPU Accuracy --> 350 <!-- CPU Accuracy -->
283 <string name="cpu_accuracy_accurate">正確</string> 351 <string name="cpu_accuracy_accurate">正確</string>
@@ -289,7 +357,7 @@
289 <string name="gamepad_left_stick">Lスティック</string> 357 <string name="gamepad_left_stick">Lスティック</string>
290 <string name="gamepad_right_stick">Rスティック</string> 358 <string name="gamepad_right_stick">Rスティック</string>
291 <string name="gamepad_home">HOMEボタン</string> 359 <string name="gamepad_home">HOMEボタン</string>
292 <string name="gamepad_screenshot">スクリーンショット</string> 360 <string name="gamepad_screenshot">キャãƒãƒãƒ£ãƒ¼ãƒœã‚¿ãƒ³</string>
293 361
294 <!-- Disk shader cache --> 362 <!-- Disk shader cache -->
295 <string name="preparing_shaders">シェーダーを準備ã—ã¦ã„ã¾ã™</string> 363 <string name="preparing_shaders">シェーダーを準備ã—ã¦ã„ã¾ã™</string>
@@ -306,8 +374,22 @@
306 <string name="theme_mode_light">ライト</string> 374 <string name="theme_mode_light">ライト</string>
307 <string name="theme_mode_dark">ダーク</string> 375 <string name="theme_mode_dark">ダーク</string>
308 376
309 <!-- Black backgrounds theme --> 377 <!-- Audio output engines -->
310 <string name="use_black_backgrounds">黒色ã®èƒŒæ™¯ã‚’使用</string> 378 <string name="cubeb">cubeb</string>
311 <string name="use_black_backgrounds_description">ダークテーマã®ä½¿ç”¨æ™‚ã¯ã€é»’色ã®èƒŒæ™¯ã‚’有効ã«ã—ã¦ãã ã•ã„。</string>
312 379
313</resources> 380 <!-- Black backgrounds theme -->
381 <string name="use_black_backgrounds">完全ãªé»’を使用</string>
382 <string name="use_black_backgrounds_description">ダークテーマã®èƒŒæ™¯è‰²ã«é»’ãŒé©ç”¨ã•れã¾ã™ã€‚</string>
383
384 <!-- Picture-In-Picture -->
385 <string name="picture_in_picture">ピクãƒãƒ£ãƒ¼ã‚¤ãƒ³ãƒ”クãƒãƒ£ãƒ¼</string>
386 <string name="picture_in_picture_description">ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰æ™‚ã«ã‚¦ã‚¤ãƒ³ãƒ‰ã‚¦ã‚’最å°åŒ–ã™ã‚‹</string>
387 <string name="pause">中断</string>
388 <string name="play">プレイ</string>
389 <string name="mute">消音</string>
390 <string name="unmute">消音解除</string>
391
392 <!-- Licenses screen strings -->
393 <string name="licenses">ライセンス</string>
394 <string name="license_fidelityfx_fsr_description">AMDã®é«˜å“質アップスケーリング</string>
395 </resources>
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index 214f95706..1b9160a23 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -1,9 +1,9 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">ì´ ì†Œí”„íŠ¸ì›¨ì–´ëŠ” 닌í…ë„ ìŠ¤ìœ„ì¹˜ 게임 콘솔용 ê²Œìž„ì„ ì‹¤í–‰í•©ë‹ˆë‹¤. 게임 타ì´í‹€ì´ë‚˜ keys는 í¬í•¨ë˜ì–´ 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 ì „ì— ìž¥ì¹˜ 저장소ì—서 <![CDATA[<b> prod.keys </b>]]> 파ì¼ì„ 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ìžì„¸ížˆ 알아보기</a>]]></string> 4 <string name="app_disclaimer">ì´ ì†Œí”„íŠ¸ì›¨ì–´ëŠ” Nintendo Switch ê²Œìž„ì„ ì‹¤í–‰í•©ë‹ˆë‹¤. 게임 타ì´í‹€ì´ë‚˜ 키는 í¬í•¨ë˜ì–´ 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 ì „ì— ìž¥ì¹˜ 저장소ì—서 <![CDATA[<b> prod.keys </b>]]> 파ì¼ì„ 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ìžì„¸ížˆ 알아보기</a>]]></string>
5 <string name="emulation_notification_channel_name">ì—뮬레ì´ì…˜ì´ 활성화ë¨</string> 5 <string name="emulation_notification_channel_name">ì—뮬레ì´ì…˜ì´ 활성화ë¨</string>
6 <string name="emulation_notification_channel_description">ì—뮬레ì´ì…˜ì´ 실행 ì¤‘ì¼ ë•Œ ì˜êµ¬ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string> 6 <string name="emulation_notification_channel_description">ì—뮬레ì´ì…˜ì´ 실행 ì¤‘ì¼ ë•Œ ì§€ì†ì ìœ¼ë¡œ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string>
7 <string name="emulation_notification_running">yuzu가 실행 중입니다.</string> 7 <string name="emulation_notification_running">yuzu가 실행 중입니다.</string>
8 <string name="notice_notification_channel_name">알림 ë° ì˜¤ë¥˜</string> 8 <string name="notice_notification_channel_name">알림 ë° ì˜¤ë¥˜</string>
9 <string name="notice_notification_channel_description">문제가 ë°œìƒí•˜ë©´ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string> 9 <string name="notice_notification_channel_description">문제가 ë°œìƒí•˜ë©´ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string>
@@ -11,26 +11,25 @@
11 11
12 <!-- Setup strings --> 12 <!-- Setup strings -->
13 <string name="welcome">환ì˜í•©ë‹ˆë‹¤!</string> 13 <string name="welcome">환ì˜í•©ë‹ˆë‹¤!</string>
14 <string name="welcome_description">&lt;b>yuzu&lt;/b> 를 설정하고 ì—뮬레ì´ì…˜ìœ¼ë¡œ ì´ë™í•˜ëŠ” ë°©ë²•ì„ ì•Œì•„ë³´ì„¸ìš”.</string> 14 <string name="welcome_description">&lt;b>yuzu&lt;/b>를 설정하고 ì—뮬레ì´ì…˜ì„ 시작하세요.</string>
15 <string name="get_started">시작하기</string> 15 <string name="get_started">시작하기</string>
16 <string name="keys">Keys</string> 16 <string name="keys">키 설정</string>
17 <string name="keys_description">아래 버튼ì 사용하여 &lt;b>prod.keys&lt;/b> 파ì¼ì„ ì„ íƒí•©ë‹ˆë‹¤.</string> 17 <string name="keys_description">아래 버튼으로 &lt;b>prod.keys&lt;/b> 파ì¼ì„ ì„ íƒí•©ë‹ˆë‹¤.</string>
18 <string name="select_keys">keys ì„ íƒ</string> 18 <string name="select_keys">키 ì„ íƒ</string>
19 <string name="games">게임</string> 19 <string name="games">게임</string>
20 <string name="games_description">아래 버튼으로 &lt;b>게임&lt;/b> í´ë”를 ì„ íƒí•©ë‹ˆë‹¤.</string> 20 <string name="games_description">아래 버튼으로 &lt;b>게임&lt;/b> í´ë”를 ì„ íƒí•©ë‹ˆë‹¤.</string>
21 <string name="done">완료</string> 21 <string name="done">완료</string>
22 <string name="done_description">모든 준비가 완료ë˜ì—ˆìŠµë‹ˆë‹¤.\nê²Œìž„ì„ ì¦ê¸°ì„¸ìš”!</string> 22 <string name="done_description">모ë 준비ë˜ì—ˆìŠµë‹ˆë‹¤.\nê²Œìž„ì„ ì¦ê¸°ì„¸ìš”!</string>
23 <string name="text_continue">계ì†</string> 23 <string name="text_continue">계ì†</string>
24 <string name="next">다ìŒ</string> 24 <string name="next">다ìŒ</string>
25 <string name="back">뒤로</string> 25 <string name="back">ì´ì „</string>
26 <string name="add_games">게임 추가</string> 26 <string name="add_games">게임 추가</string>
27 <string name="add_games_description">게임 í´ë” ì„ íƒ</string> 27 <string name="add_games_description">게임 í´ë” ì„ íƒ</string>
28
29 <!-- Home strings --> 28 <!-- Home strings -->
30 <string name="home_games">게임</string> 29 <string name="home_games">게임</string>
31 <string name="home_search">검색</string> 30 <string name="home_search">검색</string>
32 <string name="home_settings">설정</string> 31 <string name="home_settings">설정</string>
33 <string name="empty_gamelist">파ì¼ì„ ì°¾ì„ ìˆ˜ 없거나 ì•„ì§ ê²Œìž„ 디렉토리를 ì„ íƒí•˜ì§€ 않았습니다.</string> 32 <string name="empty_gamelist">파ì¼ì„ ì°¾ì„ ìˆ˜ 없거나 ì•„ì§ ê²Œìž„ 디렉터리를 ì„ íƒí•˜ì§€ 않았습니다.</string>
34 <string name="search_and_filter_games">게임 검색 ë° í•„í„°ë§</string> 33 <string name="search_and_filter_games">게임 검색 ë° í•„í„°ë§</string>
35 <string name="select_games_folder">게임 í´ë” ì„ íƒ</string> 34 <string name="select_games_folder">게임 í´ë” ì„ íƒ</string>
36 <string name="select_games_folder_description">yuzuê°€ 게임 목ë¡ì„ 채울 수 있ë„ë¡ í—ˆìš©</string> 35 <string name="select_games_folder_description">yuzuê°€ 게임 목ë¡ì„ 채울 수 있ë„ë¡ í—ˆìš©</string>
@@ -38,140 +37,160 @@
38 <string name="add_games_warning_description">í´ë”를 ì„ íƒí•˜ì§€ 않으면 게임 목ë¡ì— ê²Œìž„ì´ í‘œì‹œë˜ì§€ 않습니다.</string> 37 <string name="add_games_warning_description">í´ë”를 ì„ íƒí•˜ì§€ 않으면 게임 목ë¡ì— ê²Œìž„ì´ í‘œì‹œë˜ì§€ 않습니다.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 38 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">게임 검색</string> 39 <string name="home_search_games">게임 검색</string>
41 <string name="games_dir_selected">게임 디렉터리 ì„ íƒ</string> 40 <string name="games_dir_selected">게임 디렉터리를 설ì í–ˆìŠµë‹ˆë‹¤.</string>
42 <string name="install_prod_keys">prod.keys 설치</string> 41 <string name="install_prod_keys">prod.keys 설치</string>
43 <string name="install_prod_keys_description">íŒë§¤ìš© 게임 암호 í•´ë…ì— ìš”êµ¬</string> 42 <string name="install_prod_keys_description">패키지 게임 암호 í•´ë…ì— í•„ìš”</string>
44 <string name="install_prod_keys_warning">keys 추가를 건너뛰겠습니까?</string> 43 <string name="install_prod_keys_warning">키 추가를 건너뛰겠습니까?</string>
45 <string name="install_prod_keys_warning_description">ì •í’ˆ ê²Œìž„ì„ ì—뮬레ì´íŠ¸í•˜ë ¤ë©´ 유효한 keysê°€ 필요합니다. 계ì†í•˜ë©´ ìžì²´ 제작 앱만 ìž‘ë™í•©ë‹ˆë‹¤.</string> 44 <string name="install_prod_keys_warning_description">패키지 ê²Œìž„ì„ ì—뮬레ì´íŠ¸í•˜ë ¤ë©´ 유효한 키 ê°’ì´ í•„ìš”í•©ë‹ˆë‹¤. ì´ ë‹¨ê³„ë¥¼ 건너뛰면 홈브류 게임만 실행할 수 있습니다.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> 45 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">알림</string> 46 <string name="notifications">알림</string>
48 <string name="notifications_description">아래 버튼으로 알림 ê¶Œí•œì„ ë¶€ì—¬í•©ë‹ˆë‹¤.</string> 47 <string name="notifications_description">아래 버튼으로 알림 ê¶Œí•œì„ ë¶€ì—¬í•©ë‹ˆë‹¤.</string>
49 <string name="give_permission">ê¶Œíœ ë¶€ì—¬</string> 48 <string name="give_permission">ìŒë¦¼ 켜기</string>
50 <string name="notification_warning">알림 권한 부여를 건너뛰겠습니까?</string> 49 <string name="notification_warning">알림ì ë„겠습니까?</string>
51 <string name="notification_warning_description">yuzu는 중요한 정보를 알려드리지 않습니다.</string> 50 <string name="notification_warning_description">yuzu가 중요한 정보를 알려드리지 않습니다.</string>
52 <string name="permission_denied">권한 ê±°ë¶€ë¨</string> 51 <string name="permission_denied">권한 ê±°ë¶€ë¨</string>
53 <string name="permission_denied_description">ì´ ê¶Œí•œì„ ë„ˆë¬´ ë§Žì´ ê±°ë¶€í–ˆìœ¼ë¯€ë¡œ ì´ì œ 시스템 설정ì—서 수ë™ìœ¼ë¡œ ê¶Œí•œì„ ë¶€ì—¬í•´ì•¼ 합니다.</string> 52 <string name="permission_denied_description">권한 í—ˆìš©ì„ ë„ˆë¬´ ë§Žì´ ê±°ë¶€í•˜ì—¬ 시스템 설정ì—서 수ë™ìœ¼ë¡œ ê¶Œí•œì„ ë¶€ì—¬í•´ì•¼ 합니다.</string>
54 <string name="about">ì •ë³´</string> 53 <string name="about">ì •ë³´</string>
55 <string name="about_description">빌드 버전, í¬ë ˆë”§ 등</string> 54 <string name="about_description">빌드 버전, í¬ë ˆë”§ 등</string>
56 <string name="warning_help">ë„움ë§</string> 55 <string name="warning_help">ë„움ë§</string>
57 <string name="warning_skip">건너뛰기</string> 56 <string name="warning_skip">건너뛰기</string>
58 <string name="warning_cancel">취소</string> 57 <string name="warning_cancel">취소</string>
59 <string name="install_amiibo_keys">Amiibo keys 설치</string> 58 <string name="install_amiibo_keys">amiibo 키 설치</string>
60 <string name="install_amiibo_keys_description">게임ì—서 아미보 사용 시 í•„ìš”</string> 59 <string name="install_amiibo_keys_description">게임ì—서 amiibo 사용 시 í•„ìš”</string>
61 <string name="invalid_keys_file">ìž˜ëª»ëœ keys íŒŒì¼ ì„ íƒ</string> 60 <string name="invalid_keys_file">ìž˜ëª»ëœ í‚¤ 파ì¼ì´ ì„ íƒë¨</string>
62 <string name="install_keys_success">keysê°€ 성공ì ìœ¼ë¡œ 설치ë¨</string> 61 <string name="install_keys_success">키 ê°’ì„ ì„¤ì¹˜í–ˆìŠµë‹ˆë‹¤.</string>
63 <string name="reading_keys_failure">암호화 keys ì½ê¸° 오류</string> 62 <string name="reading_keys_failure">암호화 키 ì½ê¸° 오류</string>
64 <string name="invalid_keys_error">ìž˜ëª»ëœ ì•”í˜¸í™” keys</string> 63 <string name="install_prod_keys_failure_extension_description">키 파ì¼ì˜ 확장ìžê°€ .keysì¸ì§€ 확ì¸í•˜ê³  다시 시ë„하세요.</string>
64 <string name="install_amiibo_keys_failure_extension_description">키 파ì¼ì˜ 확장ìžê°€ .binì¸ì§€ 확ì¸í•˜ê³  다시 시ë„하세요.</string>
65 <string name="invalid_keys_error">암호화 키가 올바르지 않ìŒ</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 66 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">ì„ íƒí•œ 파ì¼ì´ 잘못ë˜ì—ˆê±°ë‚˜ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. keys를 다시 ë¤í”„하세요.</string> 67 <string name="install_keys_failure_description">ì„ íƒí•œ 파ì¼ì´ 잘못ë˜ì—ˆê±°ë‚˜ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. 키를 다시 ë¤í”„하세요.</string>
67 <string name="install_gpu_driver">GPU 드ë¼ì´ë²„ 설치</string> 68 <string name="install_gpu_driver">GPU 드ë¼ì´ë²„ 설치</string>
68 <string name="install_gpu_driver_description">잠재ì ìœ¼ë¡œ ë” ë‚˜ì€ ì„±ëŠ¥ ë˜ëŠ” ì •í™•ì„±ì„ ìœ„í•´ 대체 드ë¼ì´ë²„를 설치하세요.</string> 69 <string name="install_gpu_driver_description">잠재ì ìœ¼ë¡œ ë” ë‚˜ì€ ì„±ëŠ¥ ë˜ëŠ” ì •í™•ì„±ì„ ìœ„í•´ 대체 드ë¼ì´ë²„를 설치하세요.</string>
69 <string name="advanced_settings">고급 설정</string> 70 <string name="advanced_settings">고급 설정</string>
70 <string name="settings_description">ì—뮬레ì´í„° 설정 구성</string> 71 <string name="settings_description">ì—뮬레ì´í„° 설정 구성</string>
71 <string name="search_recently_played">최근 플레ì´í•œ 게임</string> 72 <string name="search_recently_played">최근 플레ì´</string>
72 <string name="search_recently_added">최근 추가한 게임</string> 73 <string name="search_recently_added">최근 추가</string>
73 <string name="search_retail">íŒë§¤ìš©</string> 74 <string name="search_retail">패키지</string>
74 <string name="search_homebrew">홈브류</string> 75 <string name="search_homebrew">홈브류</string>
75 <string name="open_user_folder">yuzu í´ë” 열기</string> 76 <string name="open_user_folder">yuzu í´ë” 열기</string>
76 <string name="open_user_folder_description">yuzuì˜ ë‚´ë¶€ íŒŒì¼ ê´€ë¦¬</string> 77 <string name="open_user_folder_description">yuzuì˜ ë‚´ë¶€ íŒŒì¼ ê´€ë¦¬</string>
77 <string name="theme_and_color_description">앱 모양 수정</string> 78 <string name="theme_and_color_description">앱 ë””ìžì¸ 편집</string>
78 <string name="no_file_manager">íŒŒì¼ ê´€ë¦¬ìžë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string> 79 <string name="no_file_manager">íŒŒì¼ ê´€ë¦¬ìžë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string>
79 <string name="notification_no_directory_link">yuzu 디렉토리를 ì—´ 수 ì—†ìŒ</string> 80 <string name="notification_no_directory_link">yuzu 디렉터리를 ì—´ 수 ì—†ìŒ</string>
80 <string name="notification_no_directory_link_description">íŒŒì¼ ê´€ë¦¬ìžì˜ 사ì´ë“œ 패ë„ì—서 ì‚¬ìš©ìž í´ë”를 수ë™ìœ¼ë¡œ 찾아주세요.</string> 81 <string name="notification_no_directory_link_description">íŒŒì¼ ê´€ë¦¬ìžì˜ 사ì´ë“œ 패ë„ì—서 ì‚¬ìš©ìž í´ë”를 수ë™ìœ¼ë¡œ 찾아주세요.</string>
81 <string name="manage_save_data">저장 ë°ì´í„° 관리</string> 82 <string name="manage_save_data">저장 ë°ì´í„° 관리</string>
82 <string name="manage_save_data_description">ë°ì´í„°ë¥¼ 저장했습니다. 아래ì—서 ì˜µì…˜ì„ ì„ íƒí•˜ì„¸ìš”.</string> 83 <string name="manage_save_data_description">저장 ë°ì´í„°ë¥¼ 발견했습니다. 아래ì—서 ì˜µì…˜ì„ ì„ íƒí•˜ì„¸ìš”.</string>
83 <string name="import_export_saves_description">저장 íŒŒì¼ ê°€ì ¸ì˜¤ê¸° ë˜ëŠ” 내보내기</string> 84 <string name="import_export_saves_description">저장 íŒŒì¼ ê°€ì ¸ì˜¤ê¸° ë˜ëŠ” 내보내기</string>
84 <string name="save_file_imported_success">ê°ì ¸ì˜¤ê¸° 성공</string> 85 <string name="save_file_imported_success">ë°ì´í„°ë¥¼ 불러왔습니다.</string>
85 <string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못ë¨</string> 86 <string name="save_file_invalid_zip_structure">올바르지 ì•Šì€ ì €ìž¥ 디렉터리 구조</string>
86 <string name="save_file_invalid_zip_structure_description">첫 번째 하위 í´ë” ì´ë¦„ì€ ê²Œìž„ì˜ íƒ€ì´í‹€ ID여야 합니다.</string> 87 <string name="save_file_invalid_zip_structure_description">첫 번째 하위 í´ë” ì´ë¦„ì€ ê²Œìž„ì˜ íƒ€ì´í‹€ ID여야 합니다.</string>
87 <string name="import_saves">가져오기</string> 88 <string name="import_saves">가져오기</string>
88 <string name="export_saves">내보내기</string> 89 <string name="export_saves">내보내기</string>
89 90 <string name="install_firmware">펌웨어 설치</string>
91 <string name="install_firmware_description">펌웨어는 ZIP 파ì¼ì´ë©° ì¼ë¶€ ê²Œìž„ì„ ë¶€íŒ…í•˜ëŠ” ë° í•„ìš”í•©ë‹ˆë‹¤.</string>
92 <string name="firmware_installing">펌웨어 설치</string>
93 <string name="firmware_installed_success">펌웨어를 설치했습니다.</string>
94 <string name="firmware_installed_failure">펌웨어 설치 실패</string>
95 <string name="share_log">디버그 로그 공유</string>
96 <string name="share_log_description">yuzuì˜ ë¡œê·¸ 파ì¼ì„ 공유하여 문제 디버깅하기</string>
97 <string name="share_log_missing">로그 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다.</string>
98 <string name="install_game_content">게임 콘í…츠 설치</string>
99 <string name="install_game_content_description">게임 ì—…ë°ì´íЏ ë˜ëŠ” DLC 설치</string>
100 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
90 <!-- About screen strings --> 101 <!-- About screen strings -->
91 <string name="gaia_is_not_real">ê°€ì´ì•„는 진짜가 아님</string> 102 <string name="gaia_is_not_real">ê°€ì´ì•„는 진짜가 아님</string>
92 <string name="copied_to_clipboard">í´ë¦½ë³´ë“œì— 복사</string> 103 <string name="copied_to_clipboard">í´ë¦½ë³´ë“œì— 복사ë˜ì—ˆìŠµë‹ˆë‹¤.</string>
93 <string name="about_app_description">오픈 소스 스위치 ì—뮬레ì´í„°</string> 104 <string name="about_app_description">오픈 소스 Switch ì—뮬레ì´í„°</string>
94 <string name="contributors">기여ìž</string> 105 <string name="contributors">기여ìž</string>
95 <string name="contributors_description">yuzu íŒ€ì˜ \u2764로 제작</string> 106 <string name="contributors_description">yuzu íŒ€ì˜ \u2764로 제작</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 107 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
108 <string name="licenses_description">Androidìš© yuzu를 가능하게 하는 프로ì íЏ</string>
97 <string name="build">빌드</string> 109 <string name="build">빌드</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 110 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 111 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 112 <string name="github_link">https://github.com/yuzu-emu</string>
101 113
102 <!-- Early access upgrade strings --> 114 <!-- Early access upgrade strings -->
103 <string name="early_access">미리 ì²´í—˜í˜ê¸°</string> 115 <string name="early_access">앞서 해보기</string>
104 <string name="get_early_access">미리 ì²´í—˜í˜ê¸° ì‹ ì²­</string> 116 <string name="get_early_access">앞서 해보기 ì‹ ì²­</string>
105 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> 117 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
106 <string name="get_early_access_description">ìµœì²¨ë¨ ê¸°ëŠ¥, 미리 체험하기 ì—…ë°ì´íЏ 등</string> 118 <string name="get_early_access_description">최신 기능, ì—…ë°ì´íЏ 미리 ì²´í—˜ 등</string>
107 <string name="early_access_benefits">미리 ì²´í—˜í˜ê¸° 혜íƒ</string> 119 <string name="early_access_benefits">앞서 해보기 혜íƒ</string>
108 <string name="cutting_edge_features">ìµœì²¨ë¨ ê¸°ëŠ¥</string> 120 <string name="cutting_edge_features">최신 기능</string>
109 <string name="early_access_updates">미리 체험하기 ì—…ë°ì´íЏ</string> 121 <string name="early_access_updates">ì—…ë°ì´íЏ 미리 ì²´í—˜</string>
110 <string name="no_manual_installation">ìˆ˜ë™ ì„¤ì¹˜ 불필요</string> 122 <string name="no_manual_installation">ìˆ˜ë™ ì„¤ì¹˜ 불필요</string>
111 <string name="prioritized_support">ìš°ì„  ì§€ì›</string> 123 <string name="prioritized_support">ìš°ì„  ì§€ì›</string>
112 <string name="helping_game_preservation">게임 ë³´ì¡´ ë„ì€ì£¼ê¸°</string> 124 <string name="helping_game_preservation">게임 ë³´ì¡´ ì§€ì</string>
113 <string name="our_eternal_gratitude">ì˜ì›í•œ ê°ì‚¬ì˜ 마ìŒì„ 전합니다</string> 125 <string name="our_eternal_gratitude">ìš°ë¦¬ì˜ ì˜ì›í•œ ê°ì‚¬ì˜ 마ìŒ</string>
114 <string name="are_you_interested">관심 있으세요?</string> 126 <string name="are_you_interested">관심 있으세요?</string>
115 127
116 <!-- General settings strings --> 128 <!-- General settings strings -->
117 <string name="frame_limit_enable">제한 ì†ë„ 활성화</string> 129 <string name="frame_limit_enable">ì†ë„ 제한</string>
118 <string name="frame_limit_enable_description">활성화하면 ì—뮬레ì´ì…˜ ì†ë„ê°€ ì •ìƒ ì†ë„ì˜ ì§€ì •ëœ ë¹„ìœ¨ë¡œ 제한ë©ë‹ˆë‹¤.</string> 130 <string name="frame_limit_enable_description">ì—뮬레ì´ì…˜ ì†ë„를 ì •ìƒ ì†ë„ì˜ ì§€ì •ëœ ë¹„ìœ¨ë¡œ 제한합니다.</string>
119 <string name="frame_limit_slider">ì†ë„ 제한 비율</string> 131 <string name="frame_limit_slider">ì†ë„ 제한 비율</string>
120 <string name="frame_limit_slider_description">ì—뮬레ì´ì…˜ ì†ë„를 제한할 ë¹„ìœ¨ì„ ì§€ì •í•©ë‹ˆë‹¤. ê¸°ë³¸ê°’ì¸ 100%로 설정하면 ì—뮬레ì´ì…˜ì´ ì •ìƒ ì†ë„로 제한ë©ë‹ˆë‹¤. ê°’ì´ ë†’ê±°ë‚˜ 낮으면 ì†ë„ ì œí•œì´ ì¦ê°€í•˜ê±°ë‚˜ ê°ì†Œí•©ë‹ˆë‹¤.</string> 132 <string name="frame_limit_slider_description">ì—뮬레ì´ì…˜ ì†ë„ì˜ ì œí•œ ë¹„ìœ¨ì„ ì§€ì •í•©ë‹ˆë‹¤. 100%ê°€ ì •ìƒ ì†ë„입니다. ê°’ì´ ë†’ê±°ë‚˜ 낮으면 ì†ë„ ì œí•œì´ ì¦ê°€í•˜ê±°ë‚˜ ê°ì†Œí•©ë‹ˆë‹¤.</string>
121 <string name="cpu_accuracy">CPU 정확ë„</string> 133 <string name="cpu_accuracy">CPU 정확ë„</string>
122
123 <!-- System settings strings --> 134 <!-- System settings strings -->
124 <string name="use_docked_mode">ë„킹 모드</string> 135 <string name="use_docked_mode">ë 모드</string>
125 <string name="use_docked_mode_description">ë„킹 모드ì—서 ì—뮬레ì´ì…˜í•˜ë©´ ì„±ëŠ¥ì´ ì €í•˜ë˜ëŠ” 대신 í•´ìƒë„ê°€ í–¥ìƒë©ë‹ˆë‹¤.</string> 136 <string name="use_docked_mode_description">í•´ìƒë„를 높ì´ë©° ì„±ëŠ¥ì´ ì €í•˜ë©ë‹ˆë‹¤. 비활성화시 휴대 모드가 사용ë˜ë©° í•´ìƒë„는 낮아지고 ì„±ëŠ¥ì€ í–¥ìƒë©ë‹ˆë‹¤.</string>
126 <string name="emulated_region">ì—뮬레ì´íŠ¸ëœ ì§€ì—­</string> 137 <string name="emulated_region">ì—뮬레ì´íЏ 지역</string>
127 <string name="emulated_language">ì—뮬레ì´íŠ¸ëœ ì–¸ì–´</string> 138 <string name="emulated_language">ì—뮬레ì´íЏ 언어</string>
128 <string name="select_rtc_date">RTC ë‚ ì§œ ì„ íƒ</string> 139 <string name="select_rtc_date">RTC ë‚ ì§œ ì„ íƒ</string>
129 <string name="select_rtc_time">RTC 시간 ì„ íƒ</string> 140 <string name="select_rtc_time">RTC 시간 ì„ íƒ</string>
130 <string name="use_custom_rtc">커스텀 RTC 활성화</string> 141 <string name="use_custom_rtc">ì‚¬ìš©ìž ì§ì • RTC</string>
131 <string name="use_custom_rtc_description">ì´ ì„¤ì •ì„ ì‚¬ìš©í•˜ë©´ 현재 시스템 시간과 별ë„로 ì‚¬ìš©ìž ì§€ì • 실시간 시계를 설정할 수 있ìŒ</string> 142 <string name="use_custom_rtc_description">현재 시스템 시간과 별ë„로 ì‚¬ìš©ìž ì§€ì • 실시간 시계를 설정할 수 있습니다.</string>
132 <string name="set_custom_rtc">커스텀 RTC 설정</string> 143 <string name="set_custom_rtc">ì‚¬ìš©ìž ì§ì • RTC 설정</string>
133 144
134 <!-- Graphics settings strings --> 145 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">ì •í™•ë„ ìˆ˜ì¤€</string> 146 <string name="renderer_accuracy">ì •í™•ë„ ìˆ˜ì¤€</string>
137 <string name="renderer_resolution">í•´ìƒë„</string> 147 <string name="renderer_resolution">í•´ìƒë„ (휴대 모드/ë… ëª¨ë“œ)</string>
138 <string name="renderer_vsync">수ì§ë™ê¸°í™” 모드</string> 148 <string name="renderer_vsync">수ì§ë™ê¸°í™” 모드</string>
139 <string name="renderer_aspect_ratio">화면비</string> 149 <string name="renderer_aspect_ratio">화면비</string>
140 <string name="renderer_scaling_filter">ì°½ ì ì‘ í•„í„°</string> 150 <string name="renderer_scaling_filter">윈ë„ìš° ì ì‘ í•„í„°</string>
141 <string name="renderer_anti_aliasing">안티-ì—ì¼ë¦¬ì–´ì‹± 방법</string> 151 <string name="renderer_anti_aliasing">안티ì—ì¼ë¦¬ì–´ì‹± 방법</string>
142 <string name="renderer_force_max_clock">최대 í´ëŸ­ ê°•ì œ 설정 (아드레노만 해당)</string> 152 <string name="renderer_force_max_clock">최대 í´ëŸ­ ê°•ì œ 설정 (아드레노 ì „ìš©)</string>
143 <string name="renderer_force_max_clock_description">GPUê°€ 가능한 최대 í´ëŸ­ìœ¼ë¡œ 실행ë˜ë„ë¡ ê°•ì œí•©ë‹ˆë‹¤ (ì—´ 제약 ì¡°ê±´ì€ ì—¬ì „ížˆ ì ìš©ë©ë‹ˆë‹¤).</string> 153 <string name="renderer_force_max_clock_description">GPUê°€ 가능한 최대 í´ëŸ­ìœ¼ë¡œ 실행ë˜ë„ë¡ ê°•ì œí•©ë‹ˆë‹¤ (ì—´ 제약 ì¡°ê±´ì€ ì—¬ì „ížˆ ì ìš©ë©ë‹ˆë‹¤).</string>
144 <string name="renderer_asynchronous_shaders">비ë™ê¸° ì…°ì´ë” 사용</string> 154 <string name="renderer_asynchronous_shaders">비ë™ê¸° ì…°ì´ë” 사용</string>
145 <string name="renderer_asynchronous_shaders_description">ì…°ì´ë”를 비ë™ê¸°ì‹ìœ¼ë¡œ 컴파ì¼í•˜ë¯€ë¡œ ëŠê¹€ 현ìƒì´ 줄어들지만 글리치가 ë°œìƒí•  수 있습니다.</string> 155 <string name="renderer_asynchronous_shaders_description">ì…°ì´ë”를 비ë™ê¸°ì‹ìœ¼ë¡œ 컴파ì¼í•˜ì—¬ ëŠê¹€ 현ìƒì„ 줄ì´ì§€ë§Œ 글리치가 ë°œìƒí•  수 있습니다.</string>
146 <string name="renderer_debug">그래픽 디버깅 활성화</string> 156 <string name="renderer_reactive_flushing">ë°˜ì‘형 플러싱 사용</string>
147 <string name="renderer_debug_description">ì´ ì˜µì…˜ì„ ì„ íƒí•˜ë©´ 그래픽 APIê°€ ëŠë¦° 디버깅 모드로 전환ë©ë‹ˆë‹¤.</string> 157 <string name="renderer_reactive_flushing_description">ì¼ë¶€ 게임ì—서 성능 저하를 ê°ìˆ˜í•˜ê³  ë Œë”ë§ ì •í™•ë„를 í–¥ìƒí•©ë‹ˆë‹¤.</string>
148 <string name="use_disk_shader_cache">ë””ìŠ¤í¬ ì…°ì´ë” ìºì‹œ 사용</string> 158 <string name="use_disk_shader_cache">ë””ìŠ¤í¬ ì…°ì´ë” ìºì‹œ</string>
149 <string name="use_disk_shader_cache_description">ìƒì„±ëœ ì…°ì´ë”를 디스í¬ì— 저장하고 불러오기하여 ëŠê¹€ 현ìƒì„ 줄입니다.</string> 159 <string name="use_disk_shader_cache_description">ìƒì„±ëœ ì…°ì´ë”를 ë¡œì»¬ì— ì €ìž¥í•˜ê³  로드하여 ëŠê¹€ 현ìƒì„ 줄입니다.</string>
150 160
151 <!-- Audio settings strings --> 161 <!-- Debug settings strings -->
162 <string name="cpu">CPU</string>
163 <string name="renderer_api">API</string>
164 <string name="renderer_debug">그래픽 디버깅</string>
165 <string name="renderer_debug_description">그래픽 API를 ëŠë¦° 디버깅 모드로 설정합니다.</string>
152 <string name="audio_volume">볼륨</string> 166 <string name="audio_volume">볼륨</string>
153 <string name="audio_volume_description">오디오 ì¶œë ¥ì˜ ë³¼ë¥¨ì„ ì§€ì •í•©ë‹ˆë‹¤.</string> 167 <string name="audio_volume_description">오디오 ì¶œë ¥ì˜ ë³¼ë¥¨ì„ ì§€ì •í•©ë‹ˆë‹¤.</string>
154 168
155 <!-- Miscellaneous --> 169 <!-- Miscellaneous -->
156 <string name="slider_default">기본값</string> 170 <string name="slider_default">기본값</string>
157 <string name="ini_saved">ì €ìž¥ëœ ì„¤ì •</string> 171 <string name="ini_saved">ì„¤ì •ì´ ì €ìž¥ë˜ì—ˆìŠµë‹ˆë‹¤.</string>
158 <string name="gameid_saved">%1$s를 ìœí•´ ì €ìž¥ëœ ì„¤ì •</string> 172 <string name="gameid_saved">%1$s ì ìš© 설ì ì´ 저장ë˜ì—ˆìŠµë‹ˆë‹¤.</string>
159 <string name="error_saving">%1$s.ini 저장 중 오류: %2$s</string> 173 <string name="error_saving">%1$s.ini 저장 중 오류 ë°œìƒ: %2$s</string>
160 <string name="loading">불러오기 중...</string> 174 <string name="loading">불러오는 중...</string>
161 <string name="reset_setting_confirmation">ì´ ì„¤ì •ì„ ê¸°ë³¸ê°’ìœ¼ë¡œ ë˜ëŒë¦¬ê² ìŠµë‹ˆê¹Œ?</string> 175 <string name="reset_setting_confirmation">ì´ ì„¤ì •ì„ ê¸°ë³¸ê°’ìœ¼ë¡œ 재설정하겠습니까?</string>
162 <string name="reset_to_default">기본값으로 재설정</string> 176 <string name="reset_to_default">기본값으로 재설정</string>
163 <string name="reset_all_settings">모든 ì„¤ì •ì„ ì´ˆê¸°í™”í•˜ê² ìŠµë‹ˆê¹Œ?</string> 177 <string name="reset_all_settings">모든 ì„¤ì •ì„ ì´ˆê¸°í™”í•˜ê² ìŠµë‹ˆê¹Œ?</string>
164 <string name="reset_all_settings_description">모든 고급 ì„¤ì •ì´ ê¸°ë³¸ 구성으로 재설정ë©ë‹ˆë‹¤. ì´ ì„¤ì •ì€ ë˜ëŒë¦´ 수 없습니다.</string> 178 <string name="reset_all_settings_description">모든 고급 ì„¤ì •ì´ ê¸°ë³¸ 구성으로 재설정ë©ë‹ˆë‹¤. ì´ ìž‘ì—…ì€ ë˜ëŒë¦´ 수 없습니다.</string>
165 <string name="settings_reset">설정 초기화</string> 179 <string name="settings_reset">설정 초기화</string>
166 <string name="close">닫기</string> 180 <string name="close">닫기</string>
167 <string name="learn_more">ìžì„¸ížˆ 알아보기</string> 181 <string name="learn_more">ìžì„¸ížˆ</string>
168 182 <string name="auto">ìžë™</string>
183 <string name="submit">제출</string>
184 <string name="string_null">Null</string>
185 <string name="string_import">가져오기</string>
186 <string name="export">내보내기</string>
169 <!-- GPU driver installation --> 187 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">GPU 드ë¼ì´ë²„ ì„ íƒ</string> 188 <string name="select_gpu_driver">GPU 드ë¼ì´ë²„ ì„ íƒ</string>
171 <string name="select_gpu_driver_title">현재 사용 ì¤‘ì¸ GPU 드ë¼ì´ë²„를 êµì²´í•˜ê² ìŠµë‹ˆê¹Œ?</string> 189 <string name="select_gpu_driver_title">현재 ì‚¬ìš©ì¤‘ì¸ GPU 드ë¼ì´ë²„를 변경하겠습니까?</string>
172 <string name="select_gpu_driver_install">설치</string> 190 <string name="select_gpu_driver_install">설치</string>
173 <string name="select_gpu_driver_default">기본값</string> 191 <string name="select_gpu_driver_default">기본값</string>
174 <string name="select_gpu_driver_use_default">기본 GPU 드ë¼ì´ë²„ 사용</string> 192 <string name="select_gpu_driver_use_default">기본 GPU 드ë¼ì´ë²„를 사용합니다.</string>
193 <string name="select_gpu_driver_error">ìž˜ëª»ëœ ë“œë¼ì´ë¸Œê°€ ì„ íƒë˜ì—ˆìŠµë‹ˆë‹¤. 시스템 ê¸°ë³¸ê°’ì„ ì‚¬ìš©í•©ë‹ˆë‹¤.</string>
175 <string name="system_gpu_driver">시스템 GPU 드ë¼ì´ë²„</string> 194 <string name="system_gpu_driver">시스템 GPU 드ë¼ì´ë²„</string>
176 <string name="installing_driver">드ë¼ì´ë²„ 설치 중...</string> 195 <string name="installing_driver">드ë¼ì´ë²„ 설치 중...</string>
177 196
@@ -182,51 +201,50 @@
182 <string name="preferences_graphics">그래픽</string> 201 <string name="preferences_graphics">그래픽</string>
183 <string name="preferences_audio">오디오</string> 202 <string name="preferences_audio">오디오</string>
184 <string name="preferences_theme">테마 ë° ìƒ‰ìƒ</string> 203 <string name="preferences_theme">테마 ë° ìƒ‰ìƒ</string>
204 <string name="preferences_debug">디버그</string>
185 205
186 <!-- ROM loading errors --> 206 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">ë¡¬ì´ ì•”í˜¸í™”ë˜ì—ˆìŒ</string> 207 <string name="loader_error_encrypted">롬 파ì¼ì´ 암호화ë˜ì–´ìžˆìŒ</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[ê°€ì´ë“œì— ë”°ë¼ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">게임 카트리지</a> ë˜ëŠ” <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">ì„¤ì¹˜ëœ íƒ€ì´í‹€</a>를 다시 ë¤í”„하세요.]]></string> 208 <string name="loader_error_encrypted_keys_description"><![CDATA[ê²Œìž„ì„ í•´ë…í•  수 있ë„ë¡ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 파ì¼ì´ 설치ë˜ì–´ 있는지 확ì¸í•˜ì„¸ìš”.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Pê²Œìž„ì„ í•´ë…í•  수 있ë„ë¡ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 파ì¼ì´ 설치ë˜ì–´ 있는지 확ì¸í•˜ì„¸ìš”.]]></string>
190 <string name="loader_error_video_core">비디오 코어를 초기화하는 ë™ì•ˆ 오류 ë°œìƒ</string> 209 <string name="loader_error_video_core">비디오 코어를 초기화하는 ë™ì•ˆ 오류 ë°œìƒ</string>
191 <string name="loader_error_video_core_description">ì´ ë¬¸ì œëŠ” ì¼ë°˜ì ìœ¼ë¡œ 호환ë˜ì§€ 않는 GPU 드ë¼ì´ë²„로 ì¸í•´ ë°œìƒí•©ë‹ˆë‹¤. ì‚¬ìš©ìž ì§€ì • GPU 드ë¼ì´ë²„를 설치하면 ì´ ë¬¸ì œê°€ í•´ê²°ë  ìˆ˜ 있습니다.</string> 210 <string name="loader_error_video_core_description">ì¼ë°˜ì ìœ¼ë¡œ ì´ ë¬¸ì œëŠ” 호환ë˜ì§€ 않는 GPU 드ë¼ì´ë²„로 ì¸í•´ ë°œìƒí•©ë‹ˆë‹¤. ì‚¬ìš©ìž ì§€ì • GPU 드ë¼ì´ë²„를 설치하면 ì´ ë¬¸ì œê°€ í•´ê²°ë  ìˆ˜ 있습니다.</string>
192 <string name="loader_error_invalid_format">ë¡¬ì„ ë¶ˆëŸ¬ì˜¬ 수 ì—†ìŒ</string> 211 <string name="loader_error_invalid_format">롬 파ì¼ì„ 불러올 수 ì—†ìŒ</string>
193 <string name="loader_error_file_not_found">롬 파ì¼ì´ 존재하지 않ìŒ</string> 212 <string name="loader_error_file_not_found">롬 파ì¼ì´ 존재하지 않ìŒ</string>
194 213
195 <!-- Emulation Menu --> 214 <!-- Emulation Menu -->
196 <string name="emulation_exit">ì—뮬레ì´ì…˜ 종료</string> 215 <string name="emulation_exit">ì—뮬레ì´ì…˜ 종료</string>
197 <string name="emulation_done">완료</string> 216 <string name="emulation_done">완료</string>
198 <string name="emulation_fps_counter">FPS 카운터</string> 217 <string name="emulation_fps_counter">FPS 표시</string>
199 <string name="emulation_toggle_controls">í† ê¸ ì œì–´</string> 218 <string name="emulation_toggle_controls">컨트롤러 ì íƒ</string>
200 <string name="emulation_rel_stick_center">ìƒëŒ€ 스틱 센터</string> 219 <string name="emulation_rel_stick_center">ìŠ¤í‹±ì˜ ì¤‘ì‹¬ ì´ë™</string>
201 <string name="emulation_dpad_slide">ì‹­ìžíŒ¨ë“œ 슬ë¼ì´ë“œ</string> 220 <string name="emulation_dpad_slide">ì‹­ìží‚¤ 슬ë¼ì´ë“œ</string>
202 <string name="emulation_haptics">햅틱</string> 221 <string name="emulation_haptics">터치 햅틱</string>
203 <string name="emulation_show_overlay">ì˜¤ë²„ë ˆì´ í‘œì‹œ</string> 222 <string name="emulation_show_overlay">컨트롤러 표시</string>
204 <string name="emulation_toggle_all">ëª¨ë‘ í† ê¸€</string> 223 <string name="emulation_toggle_all">ëª¨ë‘ ì„ íƒ</string>
205 <string name="emulation_control_adjust">ì˜¤ë²„ë ˆì´ ì¡°ì •</string> 224 <string name="emulation_control_adjust">컨트롤러 ì¡°ì •</string>
206 <string name="emulation_control_scale">스케ì¼</string> 225 <string name="emulation_control_scale">í¬ê¸°</string>
207 <string name="emulation_control_opacity">불투명ë„</string> 226 <string name="emulation_control_opacity">불투명ë„</string>
208 <string name="emulation_touch_overlay_reset">ì˜¤ë²„ë ˆì´ ìž¬ì„¤ì •</string> 227 <string name="emulation_touch_overlay_reset">컨트롤러 설정 초기화</string>
209 <string name="emulation_touch_overlay_edit">오ë²ë ˆì´ 편집</string> 228 <string name="emulation_touch_overlay_edit">컨트롤러 위치 편집</string>
210 <string name="emulation_pause">ì—뮬레ì´ì…˜ ì¼ì‹œ 중지</string> 229 <string name="emulation_pause">ì—뮬레ì´ì…˜ ì¼ì‹œ 중지</string>
211 <string name="emulation_unpause">ì—뮬레ì´ì…˜ ì¼ì‹œ 중지 í•´ì œ</string> 230 <string name="emulation_unpause">ì—뮬레ì´ì…˜ ì¼ì‹œ 중지 í•´ì œ</string>
212 <string name="emulation_input_overlay">ì˜¤ë²„ë ˆì´ ì˜µì…˜</string> 231 <string name="emulation_input_overlay">화면 ì˜¤ë²„ë ˆì´ ì„¤ì •</string>
213 232
214 <string name="load_settings">설정 불러오기 중...</string> 233 <string name="load_settings">설정 불러오는 중...</string>
215 234
216 <!-- Software keyboard --> 235 <!-- Software keyboard -->
217 <string name="software_keyboard">ê°€ìƒ í‚¤ë³´ë“œ</string> 236 <string name="software_keyboard">소프트웨어 키보드</string>
218 237
219 <!-- Errors and warnings --> 238 <!-- Errors and warnings -->
220 <string name="abort_button">정보</string> 239 <string name="abort_button">중단</string>
221 <string name="continue_button">계ì†</string> 240 <string name="continue_button">계ì†</string>
222 <string name="system_archive_not_found">시스템 ì•„ì¹´ì´ë¸Œë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string> 241 <string name="system_archive_not_found">시스템 ì•„ì¹´ì´ë¸Œë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string>
223 <string name="system_archive_not_found_message">%sê°€ 누ë½ë˜ì—ˆìŠµë‹ˆë‹¤. 시스템 ì•„ì¹´ì´ë¸Œë¥¼ ë¤í”„하세요.\nì—뮬레ì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí•  수 있습니다.</string> 242 <string name="system_archive_not_found_message">%sê°€ 누ë½ë˜ì—ˆìŠµë‹ˆë‹¤. 시스템 ì•„ì¹´ì´ë¸Œë¥¼ ë¤í”„하세요.\nì—뮬레ì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí•  수 있습니다.</string>
224 <string name="system_archive_general">시스템 ì•„ì¹´ì´ë¸Œ</string> 243 <string name="system_archive_general">시스템 ì•„ì¹´ì´ë¸Œ</string>
225 <string name="save_load_error">저장하기/불러오기 오류</string> 244 <string name="save_load_error">저장하기/불러오기 오류</string>
226 <string name="fatal_error">치명ì ì¸ 오류</string> 245 <string name="fatal_error">ì¹˜ëª…ì  ì˜¤ë¥˜</string>
227 <string name="fatal_error_message">치명ì ì¸ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ ë¡œê·¸ë¥¼ 확ì¸í•˜ì‹­ì‹œì˜¤.\nì—뮬레ì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí•  수 있습니다.</string> 246 <string name="fatal_error_message">ì¹˜ëª…ì  ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ ë¡œê·¸ë¥¼ 확ì¸í•˜ì‹­ì‹œì˜¤.\nì—뮬레ì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí•  수 있습니다.</string>
228 <string name="performance_warning">ì´ ì„¤ì •ì„ ë„ë©´ ì—뮬레ì´ì…˜ ì„±ëŠ¥ì´ í¬ê²Œ 저하ë©ë‹ˆë‹¤! 최ìƒì˜ í™˜ê²½ì„ ìœ„í•´ ì´ ì„¤ì •ì„ í™œì„±í™”ëœ ìƒíƒœë¡œ ë‘는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤.</string> 247 <string name="performance_warning">ì´ ì„¤ì •ì„ ë„ë©´ ì—뮬레ì´ì…˜ ì„±ëŠ¥ì´ í¬ê²Œ 저하ë©ë‹ˆë‹¤! 최ìƒì˜ í™˜ê²½ì„ ìœ„í•´ ì´ ì„¤ì •ì„ í™œì„±í™”ëœ ìƒíƒœë¡œ ë‘는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤.</string>
229
230 <!-- Region Names --> 248 <!-- Region Names -->
231 <string name="region_japan">ì¼ë³¸</string> 249 <string name="region_japan">ì¼ë³¸</string>
232 <string name="region_usa">미국</string> 250 <string name="region_usa">미국</string>
@@ -234,12 +252,11 @@
234 <string name="region_australia">호주</string> 252 <string name="region_australia">호주</string>
235 <string name="region_china">중국</string> 253 <string name="region_china">중국</string>
236 <string name="region_korea">대한민국</string> 254 <string name="region_korea">대한민국</string>
237 <string name="region_taiwan">타ì´ì™„</string> 255 <string name="region_taiwan">대만</string>
238
239 <!-- Language Names -->
240 256
257 <string name="memory_gigabyte">ì˜êµ­ 하계 표준시(GB)</string>
241 <!-- Renderer APIs --> 258 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">불칸</string> 259 <string name="renderer_vulkan">Vulcan</string>
243 <string name="renderer_none">ì—†ìŒ</string> 260 <string name="renderer_none">ì—†ìŒ</string>
244 261
245 <!-- Renderer Accuracy --> 262 <!-- Renderer Accuracy -->
@@ -256,17 +273,17 @@
256 <string name="resolution_four">4X (2880p/4320p) (ëŠë¦¼)</string> 273 <string name="resolution_four">4X (2880p/4320p) (ëŠë¦¼)</string>
257 274
258 <!-- Renderer VSync --> 275 <!-- Renderer VSync -->
259 <string name="renderer_vsync_immediate">즉시 (ë)</string> 276 <string name="renderer_vsync_immediate">ì¦‰ê° í‘œì‹œ (ë„기)</string>
260 <string name="renderer_vsync_mailbox">ë©”ì¼ë°•스</string> 277 <string name="renderer_vsync_mailbox">ë©”ì¼ë°•스</string>
261 <string name="renderer_vsync_fifo">FIFO (켬)</string> 278 <string name="renderer_vsync_fifo">FIFO (켜기)</string>
262 <string name="renderer_vsync_fifo_relaxed">FIFO 릴랙스</string> 279 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
263 280
264 <!-- Scaling Filters --> 281 <!-- Scaling Filters -->
265 <string name="scaling_filter_nearest_neighbor">가장 가까운 ì´ì›ƒ</string> 282 <string name="scaling_filter_nearest_neighbor">최근접 ë³´ê°„</string>
266 <string name="scaling_filter_bilinear">ì´ì¤‘선형</string> 283 <string name="scaling_filter_bilinear">ìŒì„ í˜• ë³´ê°„</string>
267 <string name="scaling_filter_bicubic">고등차수보간</string> 284 <string name="scaling_filter_bicubic">ìŒìž…ë°© ë³´ê°„</string>
268 <string name="scaling_filter_gaussian">가우시안</string> 285 <string name="scaling_filter_gaussian">가우시안</string>
269 <string name="scaling_filter_scale_force">스케ì¼í¬ìФ</string> 286 <string name="scaling_filter_scale_force">ScaleForce</string>
270 <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ 초고해ìƒë„</string> 287 <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ 초고해ìƒë„</string>
271 288
272 <!-- Anti-Aliasing --> 289 <!-- Anti-Aliasing -->
@@ -274,27 +291,29 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 291 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 292 <string name="anti_aliasing_smaa">SMAA</string>
276 293
294 <string name="screen_layout_auto">ìžë™</string>
295
277 <!-- Aspect Ratios --> 296 <!-- Aspect Ratios -->
278 <string name="ratio_default">기본 (16:9)</string> 297 <string name="ratio_default">기본 (16:9)</string>
279 <string name="ratio_force_four_three">강제 4:3</string> 298 <string name="ratio_force_four_three">강제 4:3</string>
280 <string name="ratio_force_twenty_one_nine">강제 21:9</string> 299 <string name="ratio_force_twenty_one_nine">강제 21:9</string>
281 <string name="ratio_force_sixteen_ten">강제 16:10</string> 300 <string name="ratio_force_sixteen_ten">강제 16:10</string>
282 <string name="ratio_stretch">ì°½ì— ë§žê²Œ 늘림</string> 301 <string name="ratio_stretch">í™”ë©´ì— ë§žì¶¤</string>
283 302
284 <!-- CPU Accuracy --> 303 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">정확함</string> 304 <string name="cpu_accuracy_accurate">정확함</string>
286 <string name="cpu_accuracy_unsafe">안전하지 않ìŒ</string> 305 <string name="cpu_accuracy_unsafe">최ì í™” (안전하지 않ìŒ)</string>
287 <string name="cpu_accuracy_paranoid">편ì§ì¦ (ëŠë¦¼)</string> 306 <string name="cpu_accuracy_paranoid">최ì í™”하ì§ì•ŠìŒ (ëŠë¦¼)</string>
288 307
289 <!-- Gamepad Buttons --> 308 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">ì‹­ìžíŒ¨ë“œ</string> 309 <string name="gamepad_d_pad">ì‹­ìží‚¤</string>
291 <string name="gamepad_left_stick">L 스틱</string> 310 <string name="gamepad_left_stick">L 스틱</string>
292 <string name="gamepad_right_stick">R 스틱</string> 311 <string name="gamepad_right_stick">R 스틱</string>
293 <string name="gamepad_home">홈</string> 312 <string name="gamepad_home">홈</string>
294 <string name="gamepad_screenshot">스í¬ë¦°ìƒ·</string> 313 <string name="gamepad_screenshot">스í¬ë¦°ìƒ·</string>
295 314
296 <!-- Disk shader cache --> 315 <!-- Disk shader cache -->
297 <string name="preparing_shaders">ì…°ì´ë” 준비하기</string> 316 <string name="preparing_shaders">ì…°ì´ë” 준비하는 중</string>
298 <string name="building_shaders">ì…°ì´ë” 빌드 중</string> 317 <string name="building_shaders">ì…°ì´ë” 빌드 중</string>
299 318
300 <!-- Theme options --> 319 <!-- Theme options -->
@@ -303,13 +322,19 @@
303 <string name="theme_material_you">Material You</string> 322 <string name="theme_material_you">Material You</string>
304 323
305 <!-- Theme Modes --> 324 <!-- Theme Modes -->
306 <string name="change_theme_mode">테마 모드 변경</string> 325 <string name="change_theme_mode">ë‹¤í¬ ëª¨ë“œ 설정</string>
307 <string name="theme_mode_follow_system">팔로우 시스템</string> 326 <string name="theme_mode_follow_system">시스템 값 사용</string>
308 <string name="theme_mode_light">ë°ìŒ</string> 327 <string name="theme_mode_light">ë¼ì´íЏ 모드</string>
309 <string name="theme_mode_dark">ì–´ë‘움</string> 328 <string name="theme_mode_dark">ë‹¤í¬ ëª¨ë“œ</string>
310 329
311 <!-- Black backgrounds theme --> 330 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">ê²€ì€ìƒ‰ ë°°ê²½ 사용</string> 331 <string name="use_black_backgrounds">검정 ë°°ê²½</string>
313 <string name="use_black_backgrounds_description">ì–´ë‘ìš´ 테마를 사용할 때는 ê²€ì€ìƒ‰ ë°°ê²½ì„ ì ìš©í•©ë‹ˆë‹¤.</string> 332 <string name="use_black_backgrounds_description">ì–´ë‘ìš´ 테마를 사용할 때는 검정 ë°°ê²½ì„ ì ìš©í•©ë‹ˆë‹¤.</string>
333
334 <string name="mute">ìŒì†Œê±°</string>
335 <string name="unmute">ìŒì†Œê±° í•´ì œ</string>
314 336
315</resources> 337 <!-- Licenses screen strings -->
338 <string name="licenses">ë¼ì´ì„¼ìФ</string>
339 <string name="license_fidelityfx_fsr_description">AMDì˜ ê³ í’ˆì§ˆ 업스케ì¼ë§</string>
340 </resources>
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index 5443cef42..3162a9d41 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> 4 <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulering er aktiv</string> 5 <string name="emulation_notification_channel_name">Emulering er aktiv</string>
@@ -25,7 +25,6 @@
25 <string name="back">Tilbake</string> 25 <string name="back">Tilbake</string>
26 <string name="add_games">Legg til spill</string> 26 <string name="add_games">Legg til spill</string>
27 <string name="add_games_description">Velg din spillmappe</string> 27 <string name="add_games_description">Velg din spillmappe</string>
28
29 <!-- Home strings --> 28 <!-- Home strings -->
30 <string name="home_games">Spill</string> 29 <string name="home_games">Spill</string>
31 <string name="home_search">Søk</string> 30 <string name="home_search">Søk</string>
@@ -37,7 +36,7 @@
37 <string name="add_games_warning">Hoppe over valg av spillmappe?</string> 36 <string name="add_games_warning">Hoppe over valg av spillmappe?</string>
38 <string name="add_games_warning_description">Spill vises ikke i Spill-listen hvis en mappe ikke er valgt.</string> 37 <string name="add_games_warning_description">Spill vises ikke i Spill-listen hvis en mappe ikke er valgt.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 38 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Søk i spill</string> 39 <string name="home_search_games">Søk i spill|</string>
41 <string name="games_dir_selected">Spillkatalogen er valgt</string> 40 <string name="games_dir_selected">Spillkatalogen er valgt</string>
42 <string name="install_prod_keys">Installer prod.keys</string> 41 <string name="install_prod_keys">Installer prod.keys</string>
43 <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string> 42 <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string>
@@ -61,6 +60,8 @@
61 <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string> 60 <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string>
62 <string name="install_keys_success">Nøkler vellykket installert</string> 61 <string name="install_keys_success">Nøkler vellykket installert</string>
63 <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string> 62 <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string>
63 <string name="install_prod_keys_failure_extension_description">Kontroller at nøkkelfilen har filtypen .keys, og prøv igjen.</string>
64 <string name="install_amiibo_keys_failure_extension_description">Kontroller at nøkkelfilen har filtypen .bin, og prøv igjen.</string>
64 <string name="invalid_keys_error">Ugyldige krypteringsnøkler</string> 65 <string name="invalid_keys_error">Ugyldige krypteringsnøkler</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 66 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string> 67 <string name="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string>
@@ -86,7 +87,17 @@
86 <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> 87 <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string>
87 <string name="import_saves">Importer</string> 88 <string name="import_saves">Importer</string>
88 <string name="export_saves">Eksporter</string> 89 <string name="export_saves">Eksporter</string>
89 90 <string name="install_firmware">Installer fastvare</string>
91 <string name="install_firmware_description">Fastvaren må være i et ZIP-arkiv og er nødvendig for å starte noen spill.</string>
92 <string name="firmware_installing">Installering av fastvare</string>
93 <string name="firmware_installed_success">Fastvaren er vellykket installert</string>
94 <string name="firmware_installed_failure">Installasjon av fastvare mislyktes</string>
95 <string name="share_log">Del feilsøkingslogger</string>
96 <string name="share_log_description">Del yuzus loggfil for å feilsøke problemer</string>
97 <string name="share_log_missing">Ingen loggfil funnet</string>
98 <string name="install_game_content">Installer spillinnhold</string>
99 <string name="install_game_content_description">Installer spilloppdateringer eller DLC</string>
100 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
90 <!-- About screen strings --> 101 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia er ikke ekte</string> 102 <string name="gaia_is_not_real">Gaia er ikke ekte</string>
92 <string name="copied_to_clipboard">Kopiert til utklippstavlen</string> 103 <string name="copied_to_clipboard">Kopiert til utklippstavlen</string>
@@ -94,6 +105,7 @@
94 <string name="contributors">Bidragsytere</string> 105 <string name="contributors">Bidragsytere</string>
95 <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string> 106 <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 107 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
108 <string name="licenses_description">Prosjekter som gjør yuzu for Android mulig</string>
97 <string name="build">Bygg</string> 109 <string name="build">Bygg</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 110 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 111 <string name="website_link">https://yuzu-emu.org/</string>
@@ -114,41 +126,43 @@
114 <string name="are_you_interested">Er du interessert?</string> 126 <string name="are_you_interested">Er du interessert?</string>
115 127
116 <!-- General settings strings --> 128 <!-- General settings strings -->
117 <string name="frame_limit_enable">Aktiver hastighetsbegrensning</string> 129 <string name="frame_limit_enable">Begrense hastigheten</string>
118 <string name="frame_limit_enable_description">NÃ¥r aktivert, begrenses emuleringshastigheten til en angitt prosentandel av normal hastighet.</string> 130 <string name="frame_limit_enable_description">Begrenser emuleringshastigheten til en spesifisert prosentandel av normal hastighet.</string>
119 <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string> 131 <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string>
120 <string name="frame_limit_slider_description">Angir prosentandelen som skal begrense emuleringshastigheten. Med standardverdien 100 % vil emuleringen være begrenset til normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsbegrensningen.</string> 132 <string name="frame_limit_slider_description">Angir prosentandelen som skal begrense emuleringshastigheten. 100 % er normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsgrensen.</string>
121 <string name="cpu_accuracy">CPU-nøyaktighet</string> 133 <string name="cpu_accuracy">CPU-nøyaktighet</string>
122
123 <!-- System settings strings --> 134 <!-- System settings strings -->
124 <string name="use_docked_mode">Dokket modus</string> 135 <string name="use_docked_mode">Dokket modus</string>
125 <string name="use_docked_mode_description">Emulerer i dokket modus, noe som øker oppløsningen på bekostning av ytelsen.</string> 136 <string name="use_docked_mode_description">Øker oppløsningen, men reduserer ytelsen. Håndholdt modus brukes når den er deaktivert, noe som reduserer oppløsningen og øker ytelsen.</string>
126 <string name="emulated_region">Emulert region</string> 137 <string name="emulated_region">Emulert region</string>
127 <string name="emulated_language">Emulert språk</string> 138 <string name="emulated_language">Emulert språk</string>
128 <string name="select_rtc_date">Velg RTC-dato</string> 139 <string name="select_rtc_date">Velg RTC-dato</string>
129 <string name="select_rtc_time">Velg RTC-tid</string> 140 <string name="select_rtc_time">Velg RTC-tid</string>
130 <string name="use_custom_rtc">Aktiver egendefinert RTC</string> 141 <string name="use_custom_rtc">Tilpasset Sannhetstidsklokke</string>
131 <string name="use_custom_rtc_description">Med denne innstillingen kan du stille inn en egendefinert sanntidsklokke som er atskilt fra gjeldende systemtid.</string> 142 <string name="use_custom_rtc_description">Gjør det mulig å stille inn en egendefinert sanntidsklokke separat fra den gjeldende systemtiden.</string>
132 <string name="set_custom_rtc">Angi egendefinert RTC</string> 143 <string name="set_custom_rtc">Angi tilpasset RTC</string>
133 144
134 <!-- Graphics settings strings --> 145 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Nøyaktighetsnivå</string> 146 <string name="renderer_accuracy">Nøyaktighetsnivå</string>
137 <string name="renderer_resolution">Oppløsning</string> 147 <string name="renderer_resolution">Oppløsning (håndholdt/dokket)</string>
138 <string name="renderer_vsync">VSync-modus</string> 148 <string name="renderer_vsync">VSync-modus</string>
139 <string name="renderer_aspect_ratio">Størrelsesforhold</string> 149 <string name="renderer_aspect_ratio">Størrelsesforhold</string>
140 <string name="renderer_scaling_filter">Filter for vindustilpasning</string> 150 <string name="renderer_scaling_filter">Filter for vindustilpasning</string>
141 <string name="renderer_anti_aliasing">Anti-Aliasing-metode</string> 151 <string name="renderer_anti_aliasing">Anti-aliasing-metode</string>
142 <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string> 152 <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string>
143 <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string> 153 <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string>
144 <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string> 154 <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string>
145 <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakkingen, men kan føre til feil.</string> 155 <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakking, men kan føre til feil.</string>
146 <string name="renderer_debug">Aktiver feilsøking av grafikk</string> 156 <string name="renderer_reactive_flushing">Bruk reaktiv spyling</string>
147 <string name="renderer_debug_description">Når dette er merket av, går grafikk-API-et inn i en langsommere feilsøkingsmodus.</string> 157 <string name="renderer_reactive_flushing_description">Forbedrer gjengivelsesnøyaktigheten i enkelte spill på bekostning av ytelsen.</string>
148 <string name="use_disk_shader_cache">Bruk disk shader-cache</string> 158 <string name="use_disk_shader_cache">Disk shader-hurtigbuffer</string>
149 <string name="use_disk_shader_cache_description">Reduser hakking ved å lagre og laste inn genererte shaders på disken.</string> 159 <string name="use_disk_shader_cache_description">Reduserer hakking ved å lagre og laste inn genererte shaders lokalt.</string>
150 160
151 <!-- Audio settings strings --> 161 <!-- Debug settings strings -->
162 <string name="cpu">CPU</string>
163 <string name="renderer_api">API</string>
164 <string name="renderer_debug">Feilsøking av grafikk</string>
165 <string name="renderer_debug_description">Setter grafikk-API-et til en langsom feilsøkingsmodus.</string>
152 <string name="audio_volume">Volum</string> 166 <string name="audio_volume">Volum</string>
153 <string name="audio_volume_description">Angir volumet på lydutgangen.</string> 167 <string name="audio_volume_description">Angir volumet på lydutgangen.</string>
154 168
@@ -164,14 +178,19 @@
164 <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string> 178 <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string>
165 <string name="settings_reset">Tilbakestilling av innstillinger</string> 179 <string name="settings_reset">Tilbakestilling av innstillinger</string>
166 <string name="close">Lukk</string> 180 <string name="close">Lukk</string>
167 <string name="learn_more">Lær Mer</string> 181 <string name="learn_more">Lær mer</string>
168 182 <string name="auto">Auto</string>
183 <string name="submit">Send inn</string>
184 <string name="string_null">Null</string>
185 <string name="string_import">Importer</string>
186 <string name="export">Eksporter</string>
169 <!-- GPU driver installation --> 187 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Velg GPU-driver</string> 188 <string name="select_gpu_driver">Velg GPU-driver</string>
171 <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> 189 <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
172 <string name="select_gpu_driver_install">Installer</string> 190 <string name="select_gpu_driver_install">Installer</string>
173 <string name="select_gpu_driver_default">Standard</string> 191 <string name="select_gpu_driver_default">Standard</string>
174 <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> 192 <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
193 <string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
175 <string name="system_gpu_driver">Systemets GPU-driver</string> 194 <string name="system_gpu_driver">Systemets GPU-driver</string>
176 <string name="installing_driver">Installerer driver...</string> 195 <string name="installing_driver">Installerer driver...</string>
177 196
@@ -182,10 +201,10 @@
182 <string name="preferences_graphics">Grafikk</string> 201 <string name="preferences_graphics">Grafikk</string>
183 <string name="preferences_audio">Lyd</string> 202 <string name="preferences_audio">Lyd</string>
184 <string name="preferences_theme">Tema og farge</string> 203 <string name="preferences_theme">Tema og farge</string>
204 <string name="preferences_debug">Feilsøk</string>
185 205
186 <!-- ROM loading errors --> 206 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">ROM-en din er kryptert</string> 207 <string name="loader_error_encrypted">ROM-en din er kryptert</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Følg veiledningene for å redumpe dine <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">spillkassetter</a> eller <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">installerte titler</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Vennligst sørg for at <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> filen er installert slik at spillene kan dekrypteres.]]></string> 208 <string name="loader_error_encrypted_keys_description"><![CDATA[Vennligst sørg for at <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> filen er installert slik at spillene kan dekrypteres.]]></string>
190 <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string> 209 <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string>
191 <string name="loader_error_video_core_description">Dette skyldes vanligvis en inkompatibel GPU-driver. Installering av en tilpasset GPU-driver kan løse problemet.</string> 210 <string name="loader_error_video_core_description">Dette skyldes vanligvis en inkompatibel GPU-driver. Installering av en tilpasset GPU-driver kan løse problemet.</string>
@@ -196,25 +215,25 @@
196 <string name="emulation_exit">Avslutt emulering</string> 215 <string name="emulation_exit">Avslutt emulering</string>
197 <string name="emulation_done">Ferdig</string> 216 <string name="emulation_done">Ferdig</string>
198 <string name="emulation_fps_counter">FPS-teller</string> 217 <string name="emulation_fps_counter">FPS-teller</string>
199 <string name="emulation_toggle_controls">Veksle kontroller</string> 218 <string name="emulation_toggle_controls">Veksle mellom kontrollene</string>
200 <string name="emulation_rel_stick_center">Relativt senter for stikken</string> 219 <string name="emulation_rel_stick_center">Relativt pinnesenter</string>
201 <string name="emulation_dpad_slide">DPad-skyveplate</string> 220 <string name="emulation_dpad_slide">D-pad-skyving</string>
202 <string name="emulation_haptics">Haptikk</string> 221 <string name="emulation_haptics">Berøringshaptikk</string>
203 <string name="emulation_show_overlay">Vis overlegg</string> 222 <string name="emulation_show_overlay">Vis overlegg</string>
204 <string name="emulation_toggle_all">Slå av alt</string> 223 <string name="emulation_toggle_all">Veksle mellom alle</string>
205 <string name="emulation_control_adjust">Juster overlegg</string> 224 <string name="emulation_control_adjust">Juster overlegg</string>
206 <string name="emulation_control_scale">Skaler</string> 225 <string name="emulation_control_scale">Skaler</string>
207 <string name="emulation_control_opacity">Gjennomsiktighet</string> 226 <string name="emulation_control_opacity">Gjennomsiktighet</string>
208 <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string> 227 <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string>
209 <string name="emulation_touch_overlay_edit">Rediger overlegg</string> 228 <string name="emulation_touch_overlay_edit">Rediger overlegg</string>
210 <string name="emulation_pause">Pause Emulering</string> 229 <string name="emulation_pause">Pause emulering</string>
211 <string name="emulation_unpause">Opphev pausing av emulering</string> 230 <string name="emulation_unpause">Ta emuleringen ut av pause</string>
212 <string name="emulation_input_overlay">Alternativer for overlegg</string> 231 <string name="emulation_input_overlay">Overlay-alternativer</string>
213 232
214 <string name="load_settings">Laster inn innstillinger...</string> 233 <string name="load_settings">Laster inn innstillinger...</string>
215 234
216 <!-- Software keyboard --> 235 <!-- Software keyboard -->
217 <string name="software_keyboard">Programvare Tastatur</string> 236 <string name="software_keyboard">Programvaretastatur</string>
218 237
219 <!-- Errors and warnings --> 238 <!-- Errors and warnings -->
220 <string name="abort_button">Avbryt</string> 239 <string name="abort_button">Avbryt</string>
@@ -226,7 +245,6 @@
226 <string name="fatal_error">Fatal Feil</string> 245 <string name="fatal_error">Fatal Feil</string>
227 <string name="fatal_error_message">Det oppstod en fatal feil. Sjekk loggen for mer informasjon.\nFortsatt emulering kan føre til krasj og feil.</string> 246 <string name="fatal_error_message">Det oppstod en fatal feil. Sjekk loggen for mer informasjon.\nFortsatt emulering kan føre til krasj og feil.</string>
228 <string name="performance_warning">Hvis du slår av denne innstillingen, reduseres emuleringsytelsen betydelig! Vi anbefaler at du lar denne innstillingen være aktivert for å få den beste opplevelsen.</string> 247 <string name="performance_warning">Hvis du slår av denne innstillingen, reduseres emuleringsytelsen betydelig! Vi anbefaler at du lar denne innstillingen være aktivert for å få den beste opplevelsen.</string>
229
230 <!-- Region Names --> 248 <!-- Region Names -->
231 <string name="region_japan">Japan</string> 249 <string name="region_japan">Japan</string>
232 <string name="region_usa">USA</string> 250 <string name="region_usa">USA</string>
@@ -236,8 +254,7 @@
236 <string name="region_korea">Korea</string> 254 <string name="region_korea">Korea</string>
237 <string name="region_taiwan">Taiwan</string> 255 <string name="region_taiwan">Taiwan</string>
238 256
239 <!-- Language Names --> 257 <string name="memory_gigabyte">GB</string>
240
241 <!-- Renderer APIs --> 258 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 259 <string name="renderer_vulkan">Vulkan</string>
243 <string name="renderer_none">Ingen</string> 260 <string name="renderer_none">Ingen</string>
@@ -274,12 +291,14 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 291 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 292 <string name="anti_aliasing_smaa">SMAA</string>
276 293
294 <string name="screen_layout_auto">Auto</string>
295
277 <!-- Aspect Ratios --> 296 <!-- Aspect Ratios -->
278 <string name="ratio_default">Standard (16:9)</string> 297 <string name="ratio_default">Standard (16:9)</string>
279 <string name="ratio_force_four_three">Tving 4:3</string> 298 <string name="ratio_force_four_three">Tving 4:3</string>
280 <string name="ratio_force_twenty_one_nine">Tving 21:9</string> 299 <string name="ratio_force_twenty_one_nine">Tving 21:9</string>
281 <string name="ratio_force_sixteen_ten">Tving 16:10</string> 300 <string name="ratio_force_sixteen_ten">Tving 16:10</string>
282 <string name="ratio_stretch">Strekk til Vindu</string> 301 <string name="ratio_stretch">Strekk til vindu</string>
283 302
284 <!-- CPU Accuracy --> 303 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">Nøyaktig</string> 304 <string name="cpu_accuracy_accurate">Nøyaktig</string>
@@ -287,9 +306,9 @@
287 <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string> 306 <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string>
288 307
289 <!-- Gamepad Buttons --> 308 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">D-Pad</string> 309 <string name="gamepad_d_pad">D-pad</string>
291 <string name="gamepad_left_stick">Venstre Pinne</string> 310 <string name="gamepad_left_stick">Venstre spak</string>
292 <string name="gamepad_right_stick">Høyre Pinne</string> 311 <string name="gamepad_right_stick">Høyre spak</string>
293 <string name="gamepad_home">Hjem</string> 312 <string name="gamepad_home">Hjem</string>
294 <string name="gamepad_screenshot">Skjermbilde</string> 313 <string name="gamepad_screenshot">Skjermbilde</string>
295 314
@@ -298,7 +317,7 @@
298 <string name="building_shaders">Bygging av shaders</string> 317 <string name="building_shaders">Bygging av shaders</string>
299 318
300 <!-- Theme options --> 319 <!-- Theme options -->
301 <string name="change_app_theme">Endre appens tema</string> 320 <string name="change_app_theme">Endre app-tema</string>
302 <string name="theme_default">Standard</string> 321 <string name="theme_default">Standard</string>
303 <string name="theme_material_you">Material You</string> 322 <string name="theme_material_you">Material You</string>
304 323
@@ -309,7 +328,13 @@
309 <string name="theme_mode_dark">Mørk</string> 328 <string name="theme_mode_dark">Mørk</string>
310 329
311 <!-- Black backgrounds theme --> 330 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">Bruk svart bakgrunn</string> 331 <string name="use_black_backgrounds">Svart bakgrunn</string>
313 <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string> 332 <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string>
314 333
315</resources> 334 <string name="mute">Lydløs</string>
335 <string name="unmute">Slå på lyden</string>
336
337 <!-- Licenses screen strings -->
338 <string name="licenses">Lisenser</string>
339 <string name="license_fidelityfx_fsr_description">Oppskalering av høy kvalitet fra AMD</string>
340 </resources>
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index 899e233d0..f4d9920c2 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> 4 <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string> 5 <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string>
@@ -25,7 +25,6 @@
25 <string name="back">Wstecz</string> 25 <string name="back">Wstecz</string>
26 <string name="add_games">Dodaj gry</string> 26 <string name="add_games">Dodaj gry</string>
27 <string name="add_games_description">Wybierz folder zawierajÄ…cy Twoje gry</string> 27 <string name="add_games_description">Wybierz folder zawierajÄ…cy Twoje gry</string>
28
29 <!-- Home strings --> 28 <!-- Home strings -->
30 <string name="home_games">Gry</string> 29 <string name="home_games">Gry</string>
31 <string name="home_search">Szukaj</string> 30 <string name="home_search">Szukaj</string>
@@ -61,6 +60,8 @@
61 <string name="invalid_keys_file">Wybrano niepoprawne klucze</string> 60 <string name="invalid_keys_file">Wybrano niepoprawne klucze</string>
62 <string name="install_keys_success">Klucze zainstalowane pomyślnie</string> 61 <string name="install_keys_success">Klucze zainstalowane pomyślnie</string>
63 <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string> 62 <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string>
63 <string name="install_prod_keys_failure_extension_description">Upewnij się że twoje klucze mają rozszerzenie .keys i spróbuj ponownie.</string>
64 <string name="install_amiibo_keys_failure_extension_description">Upewnij się że twoje klucze mają rozszerzenie .bin i spróbuj ponownie.</string>
64 <string name="invalid_keys_error">Niepoprawne klucze</string> 65 <string name="invalid_keys_error">Niepoprawne klucze</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 66 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string> 67 <string name="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string>
@@ -86,7 +87,17 @@
86 <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> 87 <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string>
87 <string name="import_saves">Importuj</string> 88 <string name="import_saves">Importuj</string>
88 <string name="export_saves">Eksportuj</string> 89 <string name="export_saves">Eksportuj</string>
89 90 <string name="install_firmware">Zainstaluj firmware</string>
91 <string name="install_firmware_description">Firmware musi być w postaci archiwum ZIP, niektóre gry wymagają go do uruchomienia/prawidłowego działania</string>
92 <string name="firmware_installing">InstalujÄ™ firmware</string>
93 <string name="firmware_installed_success">Zainstalowano pomyślnie</string>
94 <string name="firmware_installed_failure">Błąd podczas instalacji firmware</string>
95 <string name="share_log">Udostępnij logi debugowania</string>
96 <string name="share_log_description">Podziel się logami yuzu, pomoże to twórcom w poprawie działania emulatora</string>
97 <string name="share_log_missing">Nie znaleziono plików logów</string>
98 <string name="install_game_content">Zainstaluj zawartość gry</string>
99 <string name="install_game_content_description">Zainstaluj aktualizacjÄ™ gry lub dodatek DLC</string>
100 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
90 <!-- About screen strings --> 101 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia isn\'t real</string> 102 <string name="gaia_is_not_real">Gaia isn\'t real</string>
92 <string name="copied_to_clipboard">Skopiowano do schowka</string> 103 <string name="copied_to_clipboard">Skopiowano do schowka</string>
@@ -94,6 +105,7 @@
94 <string name="contributors">Współtwórcy</string> 105 <string name="contributors">Współtwórcy</string>
95 <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string> 106 <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 107 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
108 <string name="licenses_description">Projekty dzięki którym yuzu mógł zostać stworzony</string>
97 <string name="build">Wersja</string> 109 <string name="build">Wersja</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 110 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 111 <string name="website_link">https://yuzu-emu.org/</string>
@@ -114,27 +126,25 @@
114 <string name="are_you_interested">JesteÅ› zainteresowany?</string> 126 <string name="are_you_interested">JesteÅ› zainteresowany?</string>
115 127
116 <!-- General settings strings --> 128 <!-- General settings strings -->
117 <string name="frame_limit_enable">Włącz limit szybkości emulacji</string> 129 <string name="frame_limit_enable">Limit szybkość</string>
118 <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string> 130 <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string>
119 <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string> 131 <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string>
120 <string name="frame_limit_slider_description">Określa limit szybkości emulacji gier. Domyślna wartość 100% oznacza normalną szybkość z jaką działa gra. Wartości niższe lub wyższe zmniejszą lub zwiększą limit szybkości.</string> 132 <string name="frame_limit_slider_description">Określa limit szybkości emulacji gier. Domyślna wartość 100% oznacza normalną szybkość z jaką działa gra. Wartości niższe lub wyższe zmniejszą lub zwiększą limit szybkości.</string>
121 <string name="cpu_accuracy">Dokładność procesora CPU</string> 133 <string name="cpu_accuracy">Dokładność procesora CPU</string>
122
123 <!-- System settings strings --> 134 <!-- System settings strings -->
124 <string name="use_docked_mode">Tryb zadokowany</string> 135 <string name="use_docked_mode">Tryb zadokowany</string>
125 <string name="use_docked_mode_description">Emulacja w trybie stacji dokującej, zwiększa rozdzielczość kosztem wydajności.</string> 136 <string name="use_docked_mode_description">Zwiększa rozdzielczość kosztem wydajności. Kiedy wyłączone, używany jest tryb Handheld, który obniża rozdzielczość i dzięki temu zwiększa wydajność.</string>
126 <string name="emulated_region">Region emulacji</string> 137 <string name="emulated_region">Region emulacji</string>
127 <string name="emulated_language">Język emulacji</string> 138 <string name="emulated_language">Język emulacji</string>
128 <string name="select_rtc_date">Ustaw datÄ™ RTC</string> 139 <string name="select_rtc_date">Ustaw datÄ™ RTC</string>
129 <string name="select_rtc_time">Ustaw czas RTC</string> 140 <string name="select_rtc_time">Ustaw czas RTC</string>
130 <string name="use_custom_rtc">Włącz niestandardowy zegar RTC</string> 141 <string name="use_custom_rtc">Niestandardowy RTC</string>
131 <string name="use_custom_rtc_description">Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android.</string> 142 <string name="use_custom_rtc_description">Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android.</string>
132 <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string> 143 <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string>
133 144
134 <!-- Graphics settings strings --> 145 <!-- Graphics settings strings -->
135 <string name="renderer_api">Interfejs graficzny</string>
136 <string name="renderer_accuracy">Poziom precyzji emulacji</string> 146 <string name="renderer_accuracy">Poziom precyzji emulacji</string>
137 <string name="renderer_resolution">Rozdzielczość</string> 147 <string name="renderer_resolution">Rozdzielczość (Handheld/Zadokowany)</string>
138 <string name="renderer_vsync">Synchronizacja pionowa VSync</string> 148 <string name="renderer_vsync">Synchronizacja pionowa VSync</string>
139 <string name="renderer_aspect_ratio">Proporcje ekranu</string> 149 <string name="renderer_aspect_ratio">Proporcje ekranu</string>
140 <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string> 150 <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string>
@@ -143,12 +153,16 @@
143 <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string> 153 <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string>
144 <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string> 154 <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string>
145 <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string> 155 <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string>
146 <string name="renderer_debug">Włącz debugowanie grafiki</string> 156 <string name="renderer_reactive_flushing">Użyj spłukiwania reaktywnego - reactive flushing</string>
147 <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string> 157 <string name="renderer_reactive_flushing_description">Poprawia jakość renderowania w kilku grach, kosztem wydajności.</string>
148 <string name="use_disk_shader_cache">Użyj pamięci podręcznej shaderów na dysku</string> 158 <string name="use_disk_shader_cache">Pamięć podręczna shaderów</string>
149 <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string> 159 <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string>
150 160
151 <!-- Audio settings strings --> 161 <!-- Debug settings strings -->
162 <string name="cpu">CPU</string>
163 <string name="renderer_api">Interfejs graficzny</string>
164 <string name="renderer_debug">Debugowanie grafiki</string>
165 <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string>
152 <string name="audio_volume">Głośność</string> 166 <string name="audio_volume">Głośność</string>
153 <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string> 167 <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string>
154 168
@@ -161,17 +175,21 @@
161 <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string> 175 <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string>
162 <string name="reset_to_default">Przywróć ustawienia domyślne</string> 176 <string name="reset_to_default">Przywróć ustawienia domyślne</string>
163 <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string> 177 <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string>
164 <string name="reset_all_settings_description">Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć.</string> 178 <string name="reset_all_settings_description">Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć</string>
165 <string name="settings_reset">Reset ustawień</string> 179 <string name="settings_reset">Reset ustawień</string>
166 <string name="close">Zamknij</string> 180 <string name="close">Zamknij</string>
167 <string name="learn_more">Dowiedz się więcej</string> 181 <string name="learn_more">Dowiedz się więcej</string>
168 182 <string name="auto">Automatyczny</string>
183 <string name="submit">Zatwierdź</string>
184 <string name="string_import">Importuj</string>
185 <string name="export">Eksportuj</string>
169 <!-- GPU driver installation --> 186 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Wybierz sterownik GPU </string> 187 <string name="select_gpu_driver">Wybierz sterownik GPU </string>
171 <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> 188 <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
172 <string name="select_gpu_driver_install">Zainstaluj</string> 189 <string name="select_gpu_driver_install">Zainstaluj</string>
173 <string name="select_gpu_driver_default">Domyślne</string> 190 <string name="select_gpu_driver_default">Domyślne</string>
174 <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> 191 <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
192 <string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
175 <string name="system_gpu_driver">Systemowy sterownik GPU</string> 193 <string name="system_gpu_driver">Systemowy sterownik GPU</string>
176 <string name="installing_driver">Instalowanie sterownika...</string> 194 <string name="installing_driver">Instalowanie sterownika...</string>
177 195
@@ -182,10 +200,10 @@
182 <string name="preferences_graphics">Grafika</string> 200 <string name="preferences_graphics">Grafika</string>
183 <string name="preferences_audio">Dźwięk</string> 201 <string name="preferences_audio">Dźwięk</string>
184 <string name="preferences_theme">Motyw i kolor</string> 202 <string name="preferences_theme">Motyw i kolor</string>
203 <string name="preferences_debug">Debug</string>
185 204
186 <!-- ROM loading errors --> 205 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">Twój ROM jest zakodowany</string> 206 <string name="loader_error_encrypted">Twój ROM jest zakodowany</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Użyj przewodnika aby wykonać zrzuty <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">kardridży</a> lub <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">zainstalowanych gier</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Upewnij się że plik kluczy <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> jest zainstalowany aby gry mogły zostać odczytane.]]></string> 207 <string name="loader_error_encrypted_keys_description"><![CDATA[Upewnij się że plik kluczy <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> jest zainstalowany aby gry mogły zostać odczytane.]]></string>
190 <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string> 208 <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string>
191 <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string> 209 <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string>
@@ -198,23 +216,23 @@
198 <string name="emulation_fps_counter">Licznik FPS</string> 216 <string name="emulation_fps_counter">Licznik FPS</string>
199 <string name="emulation_toggle_controls">Wybierz przyciski</string> 217 <string name="emulation_toggle_controls">Wybierz przyciski</string>
200 <string name="emulation_rel_stick_center">Wycentruj gałki</string> 218 <string name="emulation_rel_stick_center">Wycentruj gałki</string>
201 <string name="emulation_dpad_slide">Ruchomy DPad</string> 219 <string name="emulation_dpad_slide">Ruchomy D-pad</string>
202 <string name="emulation_haptics">Wibracje haptyczne</string> 220 <string name="emulation_haptics">Wibracje haptyczne</string>
203 <string name="emulation_show_overlay">Pokaż przyciski</string> 221 <string name="emulation_show_overlay">Pokaż przyciski</string>
204 <string name="emulation_toggle_all">Zaznacz wszystkie</string> 222 <string name="emulation_toggle_all">Włącz wszystkie</string>
205 <string name="emulation_control_adjust">Dostosuj nakładkę</string> 223 <string name="emulation_control_adjust">Dostosuj nakładkę</string>
206 <string name="emulation_control_scale">Skala</string> 224 <string name="emulation_control_scale">Skala</string>
207 <string name="emulation_control_opacity">Przeźroczystość</string> 225 <string name="emulation_control_opacity">Przeźroczystość</string>
208 <string name="emulation_touch_overlay_reset">Resetuj</string> 226 <string name="emulation_touch_overlay_reset">Resetuj nakładkę</string>
209 <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string> 227 <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string>
210 <string name="emulation_pause">Wstrzymaj emulacjÄ™</string> 228 <string name="emulation_pause">Wstrzymaj emulacjÄ™</string>
211 <string name="emulation_unpause">Wznów emulację</string> 229 <string name="emulation_unpause">Wznów emulację</string>
212 <string name="emulation_input_overlay">Opcje nakładki</string> 230 <string name="emulation_input_overlay">Opcje nakładki</string>
213 231
214 <string name="load_settings">Wczytywanie ustawień...</string> 232 <string name="load_settings">Wczytuję ustawienia...</string>
215 233
216 <!-- Software keyboard --> 234 <!-- Software keyboard -->
217 <string name="software_keyboard">Klawiatura systemowa</string> 235 <string name="software_keyboard">Klawiatura programowa</string>
218 236
219 <!-- Errors and warnings --> 237 <!-- Errors and warnings -->
220 <string name="abort_button">Przerwij</string> 238 <string name="abort_button">Przerwij</string>
@@ -226,7 +244,6 @@
226 <string name="fatal_error">Błąd krytyczny</string> 244 <string name="fatal_error">Błąd krytyczny</string>
227 <string name="fatal_error_message">Wystąpił błąd krytyczny. Szczegóły znajdziesz w pliku log.\nKontynuowanie może spowodować błędy lub przerwanie emulacji. </string> 245 <string name="fatal_error_message">Wystąpił błąd krytyczny. Szczegóły znajdziesz w pliku log.\nKontynuowanie może spowodować błędy lub przerwanie emulacji. </string>
228 <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string> 246 <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string>
229
230 <!-- Region Names --> 247 <!-- Region Names -->
231 <string name="region_japan">Japonia</string> 248 <string name="region_japan">Japonia</string>
232 <string name="region_usa">USA</string> 249 <string name="region_usa">USA</string>
@@ -236,8 +253,7 @@
236 <string name="region_korea">Korea</string> 253 <string name="region_korea">Korea</string>
237 <string name="region_taiwan">Tajwan</string> 254 <string name="region_taiwan">Tajwan</string>
238 255
239 <!-- Language Names --> 256 <string name="memory_gigabyte">GB</string>
240
241 <!-- Renderer APIs --> 257 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 258 <string name="renderer_vulkan">Vulkan</string>
243 <string name="renderer_none">Żadny</string> 259 <string name="renderer_none">Żadny</string>
@@ -274,12 +290,14 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 290 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 291 <string name="anti_aliasing_smaa">SMAA</string>
276 292
293 <string name="screen_layout_auto">Automatyczny</string>
294
277 <!-- Aspect Ratios --> 295 <!-- Aspect Ratios -->
278 <string name="ratio_default">Domyślne (16:9)</string> 296 <string name="ratio_default">Domyślne (16:9)</string>
279 <string name="ratio_force_four_three">WymuÅ› 4:3</string> 297 <string name="ratio_force_four_three">WymuÅ› 4:3</string>
280 <string name="ratio_force_twenty_one_nine">WymuÅ› 21:9</string> 298 <string name="ratio_force_twenty_one_nine">WymuÅ› 21:9</string>
281 <string name="ratio_force_sixteen_ten">WymuÅ› 16:10</string> 299 <string name="ratio_force_sixteen_ten">WymuÅ› 16:10</string>
282 <string name="ratio_stretch">RozciÄ…gnij do Okna</string> 300 <string name="ratio_stretch">RozciÄ…gnij do okna</string>
283 301
284 <!-- CPU Accuracy --> 302 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">Dokładny</string> 303 <string name="cpu_accuracy_accurate">Dokładny</string>
@@ -287,7 +305,7 @@
287 <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string> 305 <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string>
288 306
289 <!-- Gamepad Buttons --> 307 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">D-Pad</string> 308 <string name="gamepad_d_pad">D-pad</string>
291 <string name="gamepad_left_stick">Lewa gałka</string> 309 <string name="gamepad_left_stick">Lewa gałka</string>
292 <string name="gamepad_right_stick">Prawa gałka</string> 310 <string name="gamepad_right_stick">Prawa gałka</string>
293 <string name="gamepad_home">Home</string> 311 <string name="gamepad_home">Home</string>
@@ -298,18 +316,21 @@
298 <string name="building_shaders">Budowanie shaderów</string> 316 <string name="building_shaders">Budowanie shaderów</string>
299 317
300 <!-- Theme options --> 318 <!-- Theme options -->
301 <string name="change_app_theme">Zmień motyw aplikacji</string> 319 <string name="change_app_theme">Ustaw motyw aplikacji</string>
302 <string name="theme_default">Domyślny</string> 320 <string name="theme_default">Domyślny</string>
303 <string name="theme_material_you">Material You</string> 321 <string name="theme_material_you">Material You</string>
304 322
305 <!-- Theme Modes --> 323 <!-- Theme Modes -->
306 <string name="change_theme_mode">Zmiana trybu motywu</string> 324 <string name="change_theme_mode">Zmień tryb motywu</string>
307 <string name="theme_mode_follow_system">Podążaj za systemowym</string> 325 <string name="theme_mode_follow_system">Podążaj za systemowym</string>
308 <string name="theme_mode_light">Jasny</string> 326 <string name="theme_mode_light">Jasny</string>
309 <string name="theme_mode_dark">Ciemny</string> 327 <string name="theme_mode_dark">Ciemny</string>
310 328
311 <!-- Black backgrounds theme --> 329 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">Używaj czarnego tła</string> 330 <string name="use_black_backgrounds">Czarne tła</string>
313 <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string> 331 <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string>
314 332
315</resources> 333 <!-- Licenses screen strings -->
334 <string name="licenses">Licencje</string>
335 <string name="license_fidelityfx_fsr_description">Rozciąganie wysokiej jakości od AMD</string>
336 </resources>
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index caa095364..8888fc750 100644
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -1,30 +1,31 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string> 4 <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começar, por favor localize o arquivo <![CDATA[1 prod.keys 1]]> no armazenamento de seu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Saiba mais2]]></string>
5 <string name="emulation_notification_channel_name">Emulação está Ativa</string> 5 <string name="emulation_notification_channel_name">Emulação está Ativa</string>
6 <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string> 6 <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string>
7 <string name="emulation_notification_running">Yuzu está em execução </string> 7 <string name="emulation_notification_running">Yuzu está em execução </string>
8 <string name="notice_notification_channel_name">Notificações e erros</string> 8 <string name="notice_notification_channel_name">Notificações e erros</string>
9 <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> 9 <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string>
10 <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> 10 <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string>
11 11
12 <!-- Setup strings --> 12 <!-- Setup strings -->
13 <string name="welcome">Bemvindo! </string> 13 <string name="welcome">Bem-vindo! </string>
14 <string name="welcome_description">Aprende como configurar &lt;b>yuzu&lt;/b> e arranca a emulação.</string> 14 <string name="welcome_description">Aprenda como configurar o &lt;b>yuzu&lt;/b> e mergulhe na emulação.</string>
15 <string name="get_started">Começa</string> 15 <string name="get_started">Primeiros passos</string>
16 <string name="keys">Chaves</string> 16 <string name="keys">Keys</string>
17 <string name="keys_description">Seleciona o teu ficheiro &lt;b>prod.keys&lt;/b> com o botão abaixo.</string> 17 <string name="keys_description">Selecione seu arquivo &lt;b>prod.keys&lt;/b> com o botão abaixo.</string>
18 <string name="select_keys">Seleciona as Chaves</string> 18 <string name="select_keys">Selecione as Keys</string>
19 <string name="games">Jogos</string> 19 <string name="games">Jogos</string>
20 <string name="games_description">Seleciona a tua pasta &lt;b>Games&lt;/b> com o botão abaixo.</string> 20 <string name="games_description">Seleciona sua pasta &lt;b>Jogos&lt;/b> com o botão abaixo.</string>
21 <string name="done">Feito</string> 21 <string name="done">Feito</string>
22 <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string> 22 <string name="done_description">Tudo pronto.\nAproveite seus jogos!</string>
23 <string name="text_continue">Continuar</string> 23 <string name="text_continue">Continuar</string>
24 <string name="next">Próximo</string> 24 <string name="next">Próximo</string>
25 <string name="back">Voltar</string> 25 <string name="back">Voltar</string>
26 <string name="add_games">Adiciona Jogos</string> 26 <string name="add_games">Adicionar Jogos</string>
27 <string name="add_games_description">Seleciona a tua pasta de Jogos</string> 27 <string name="add_games_description">Selecione sua pasta de Jogos</string>
28 <string name="step_complete">Completo!</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Jogos</string> 31 <string name="home_games">Jogos</string>
@@ -37,7 +38,8 @@
37 <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> 38 <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string>
38 <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string> 39 <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Procurar Jogos</string> 41 <string name="home_search_games">Procurar jogos</string>
42 <string name="search_settings">Procurar nas definições</string>
41 <string name="games_dir_selected">Pasta de Jogos selecionada</string> 43 <string name="games_dir_selected">Pasta de Jogos selecionada</string>
42 <string name="install_prod_keys">Instala prod.keys</string> 44 <string name="install_prod_keys">Instala prod.keys</string>
43 <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> 45 <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string>
@@ -61,15 +63,18 @@
61 <string name="invalid_keys_file">Ficheiro de chaves inválido</string> 63 <string name="invalid_keys_file">Ficheiro de chaves inválido</string>
62 <string name="install_keys_success">Chaves instaladas com sucesso</string> 64 <string name="install_keys_success">Chaves instaladas com sucesso</string>
63 <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> 65 <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string>
66 <string name="install_prod_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .keys e tente novamente.</string>
67 <string name="install_amiibo_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .bin e tente novamente.</string>
64 <string name="invalid_keys_error">Chaves de encriptação inválidas</string> 68 <string name="invalid_keys_error">Chaves de encriptação inválidas</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> 70 <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string>
67 <string name="install_gpu_driver">Instala driver para GPU</string> 71 <string name="install_gpu_driver">Instala driver para GPU</string>
68 <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> 72 <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string>
69 <string name="advanced_settings">Definições avançadas</string> 73 <string name="advanced_settings">Definições avançadas</string>
74 <string name="advanced_settings_game">Definições avançadas: %1$s</string>
70 <string name="settings_description">Configura definições do emulador</string> 75 <string name="settings_description">Configura definições do emulador</string>
71 <string name="search_recently_played">Jogos recentes</string> 76 <string name="search_recently_played">Jogado recentemente</string>
72 <string name="search_recently_added">Adicionados recentemente</string> 77 <string name="search_recently_added">Adicionado recentemente</string>
73 <string name="search_retail">Jogos comerciais</string> 78 <string name="search_retail">Jogos comerciais</string>
74 <string name="search_homebrew">Homebrew</string> 79 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Abre a pasta Yuzu</string> 80 <string name="open_user_folder">Abre a pasta Yuzu</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> 91 <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>
87 <string name="import_saves">Importar</string> 92 <string name="import_saves">Importar</string>
88 <string name="export_saves">Exportar</string> 93 <string name="export_saves">Exportar</string>
94 <string name="install_firmware">Instalar firmware</string>
95 <string name="install_firmware_description">O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos.</string>
96 <string name="firmware_installing">Instalando firmware</string>
97 <string name="firmware_installed_success">Firmware instalado com sucesso.</string>
98 <string name="firmware_installed_failure">Falha na instalação do firmware</string>
99 <string name="firmware_installed_failure_description">Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo.</string>
100 <string name="share_log">Compartilhe registros de debug.</string>
101 <string name="share_log_description">Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas</string>
102 <string name="share_log_missing">Arquivo de registro não encontrado</string>
103 <string name="install_game_content">Instalar conteúdo de jogos</string>
104 <string name="install_game_content_description">Instalar atualizações de jogos ou DLC</string>
105 <string name="installing_game_content">A instalar conteúdo...</string>
106 <string name="install_game_content_failure">Erro ao instalar ficheiro(s) para NAND</string>
107 <string name="install_game_content_failure_description">Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas.</string>
108 <string name="install_game_content_failure_base">A instalação de jogos base não é permitida para evitar possíveis conflitos.</string>
109 <string name="install_game_content_failure_file_extension">Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos.</string>
110 <string name="install_game_content_failed_count">%1$d erro(s) de instalação</string>
111 <string name="install_game_content_success">Conteúdo(s) de jogo instalados com sucesso</string>
112 <string name="install_game_content_success_install">%1$d instalado com sucesso</string>
113 <string name="install_game_content_success_overwrite">%1$d substituída com êxito</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">Drivers personalizados não suportados</string>
116 <string name="custom_driver_not_supported_description">Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado!</string>
117 <string name="manage_yuzu_data">Administrar dados yuzu</string>
118 <string name="manage_yuzu_data_description">Importa/exporta firmware, chaves, dados do usuário e mais!</string>
119 <string name="share_save_file">Partilha ficheiro duardado</string>
120 <string name="export_save_failed">Erro ao exportar dados guardados</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia não é real</string> 123 <string name="gaia_is_not_real">Gaia não é real</string>
@@ -94,7 +126,18 @@
94 <string name="contributors">Contribuidores</string> 126 <string name="contributors">Contribuidores</string>
95 <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> 127 <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">Projetos que tornam o yuzu para Android possível</string>
97 <string name="build">Versão</string> 130 <string name="build">Versão</string>
131 <string name="user_data">Dado de utilizados</string>
132 <string name="user_data_description">Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluídos!</string>
133 <string name="exporting_user_data">A exportar dados de utilizados...</string>
134 <string name="importing_user_data">A importar dados de utilizador...</string>
135 <string name="import_user_data">Importar dados de utilizados...</string>
136 <string name="invalid_yuzu_backup">Backup yuzu inválido</string>
137 <string name="user_data_export_success">Dados de utilizados exportados com sucesso</string>
138 <string name="user_data_import_success">Dados de utilizador importado com sucesso</string>
139 <string name="user_data_export_cancelled">Exportação cancelada</string>
140 <string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,41 +157,53 @@
114 <string name="are_you_interested">Estás interessado?</string> 157 <string name="are_you_interested">Estás interessado?</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">Ativar limite de velocidade</string> 160 <string name="frame_limit_enable">Limite de velocidade</string>
118 <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> 161 <string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem específica da velocidade normal.</string>
119 <string name="frame_limit_slider">Percentagem do limite de velocidade</string> 162 <string name="frame_limit_slider">Percentagem do limite de velocidade</string>
120 <string name="frame_limit_slider_description">Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade.</string> 163 <string name="frame_limit_slider_description">Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade.</string>
121 <string name="cpu_accuracy">Precisão do CPU</string> 164 <string name="cpu_accuracy">Precisão do CPU</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">Modo ancorado</string> 168 <string name="use_docked_mode">Modo Ancorado</string>
125 <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> 169 <string name="use_docked_mode_description">Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho.</string>
126 <string name="emulated_region">Região da emulação</string> 170 <string name="emulated_region">Região da emulação</string>
127 <string name="emulated_language">Idioma da emulação</string> 171 <string name="emulated_language">Idioma da emulação</string>
128 <string name="select_rtc_date">Seleciona a data RTC</string> 172 <string name="select_rtc_date">Selecione a data do sistema</string>
129 <string name="select_rtc_time">Seleciona a hora RTC</string> 173 <string name="select_rtc_time">Selecione a hora do sistema</string>
130 <string name="use_custom_rtc">Ativa RTC personalizado</string> 174 <string name="use_custom_rtc">Data e hora personalizada</string>
131 <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> 175 <string name="use_custom_rtc_description">Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.</string>
132 <string name="set_custom_rtc">Define RTC personalizado</string> 176 <string name="set_custom_rtc">Defina um relógio em tempo real personalizado</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Nível de precisão</string> 179 <string name="renderer_accuracy">Nível de precisão</string>
137 <string name="renderer_resolution">Resolução</string> 180 <string name="renderer_resolution">Resolução (Portátil/Ancorado)</string>
138 <string name="renderer_vsync">Modo VSync</string> 181 <string name="renderer_vsync">Modo VSync</string>
139 <string name="renderer_aspect_ratio">Proporção do ecrã</string> 182 <string name="renderer_screen_layout">Oriantação</string>
183 <string name="renderer_aspect_ratio">Proporção da tela</string>
140 <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> 184 <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string>
141 <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> 185 <string name="renderer_anti_aliasing">Método de Anti-Serrilhado</string>
142 <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> 186 <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string>
143 <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> 187 <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string>
144 <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string> 188 <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string>
145 <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> 189 <string name="renderer_asynchronous_shaders_description">Compila os shaders de forma assíncrona, reduzindo travamentos, mas pode apresentar problemas.</string>
190 <string name="renderer_reactive_flushing">Usar flushing reativo</string>
191 <string name="renderer_reactive_flushing_description">Melhora a precisão da renderização em alguns jogos ao custo de desempenho.</string>
192 <string name="use_disk_shader_cache">Cache de shaders em disco</string>
193 <string name="use_disk_shader_cache_description">Reduz travamentos ao armazenar e carregar localmente os shaders.</string>
194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">Depuração da CPU</string>
198 <string name="cpu_debug_mode_description">Coloca a CPU em um modo de depuração lento.</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
146 <string name="renderer_debug">Ativar depuração de gráficos</string> 201 <string name="renderer_debug">Ativar depuração de gráficos</string>
147 <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> 202 <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string>
148 <string name="use_disk_shader_cache">Usar cache de shaders em disco</string> 203 <string name="fastmem">Fastmem</string>
149 <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string>
150 204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">Motor de saída</string>
152 <string name="audio_volume">Volume</string> 207 <string name="audio_volume">Volume</string>
153 <string name="audio_volume_description">Especifica o volume de saída.</string> 208 <string name="audio_volume_description">Especifica o volume de saída.</string>
154 209
@@ -157,14 +212,24 @@
157 <string name="ini_saved">Definições guardadas</string> 212 <string name="ini_saved">Definições guardadas</string>
158 <string name="gameid_saved">Definições guardadas para %1$s</string> 213 <string name="gameid_saved">Definições guardadas para %1$s</string>
159 <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> 214 <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string>
215 <string name="unimplemented_menu">Menu não implementado</string>
160 <string name="loading">A carregar...</string> 216 <string name="loading">A carregar...</string>
217 <string name="shutting_down">A desligar...</string>
161 <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> 218 <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string>
162 <string name="reset_to_default">Reverter para padrão</string> 219 <string name="reset_to_default">Reverter para padrão</string>
163 <string name="reset_all_settings">Redefinir todas as definições?</string> 220 <string name="reset_all_settings">Redefinir todas as definições?</string>
164 <string name="reset_all_settings_description">Todas as definições avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido.</string> 221 <string name="reset_all_settings_description">Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito.</string>
165 <string name="settings_reset">Redefinir definições</string> 222 <string name="settings_reset">Redefinir definições</string>
166 <string name="close">Fechar</string> 223 <string name="close">Fechar</string>
167 <string name="learn_more">Saiba mais</string> 224 <string name="learn_more">Saiba mais</string>
225 <string name="auto">Automático</string>
226 <string name="submit">Enviar</string>
227 <string name="string_null">Nenhum (desativado)</string>
228 <string name="string_import">Importar</string>
229 <string name="export">Exportar</string>
230 <string name="export_failed">Exportação falhada</string>
231 <string name="import_failed">IMportação falhada</string>
232 <string name="cancelling">A cancelar</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Seleciona a driver para o GPU</string> 235 <string name="select_gpu_driver">Seleciona a driver para o GPU</string>
@@ -172,6 +237,7 @@
172 <string name="select_gpu_driver_install">Instalar</string> 237 <string name="select_gpu_driver_install">Instalar</string>
173 <string name="select_gpu_driver_default">Padrão</string> 238 <string name="select_gpu_driver_default">Padrão</string>
174 <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> 239 <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
240 <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
175 <string name="system_gpu_driver">Driver do GPU padrão</string> 241 <string name="system_gpu_driver">Driver do GPU padrão</string>
176 <string name="installing_driver">A instalar o Driver...</string> 242 <string name="installing_driver">A instalar o Driver...</string>
177 243
@@ -182,10 +248,11 @@
182 <string name="preferences_graphics">Gráficos</string> 248 <string name="preferences_graphics">Gráficos</string>
183 <string name="preferences_audio">Ãudio</string> 249 <string name="preferences_audio">Ãudio</string>
184 <string name="preferences_theme">Cor e tema.</string> 250 <string name="preferences_theme">Cor e tema.</string>
251 <string name="preferences_debug">Depuração</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">A tua ROM está encriptada</string> 254 <string name="loader_error_encrypted">A tua ROM está encriptada</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor segue os guias para fazer redump das tuas<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">Cartidges de Jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">Jogos Instalados</a>.]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga os guias para despejar novamente o seu <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartucho de jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string>
190 <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string> 257 <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string>
191 <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string> 258 <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string>
@@ -193,25 +260,25 @@
193 <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> 260 <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string>
194 261
195 <!-- Emulation Menu --> 262 <!-- Emulation Menu -->
196 <string name="emulation_exit">Sair da emulação</string> 263 <string name="emulation_exit">Parar emulação</string>
197 <string name="emulation_done">Feito</string> 264 <string name="emulation_done">Feito</string>
198 <string name="emulation_fps_counter">Contador de FPS</string> 265 <string name="emulation_fps_counter">Contador de FPS</string>
199 <string name="emulation_toggle_controls">Alterar Controlos</string> 266 <string name="emulation_toggle_controls">Alterar controles</string>
200 <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> 267 <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string>
201 <string name="emulation_dpad_slide">Deslizar do DPad</string> 268 <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string>
202 <string name="emulation_haptics">Hápticos </string> 269 <string name="emulation_haptics">Vibração ao tocar</string>
203 <string name="emulation_show_overlay">Mostrar sobreposição </string> 270 <string name="emulation_show_overlay">Mostrar overlay</string>
204 <string name="emulation_toggle_all">Alterar todos</string> 271 <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string>
205 <string name="emulation_control_adjust">Ajustar a sobreposição </string> 272 <string name="emulation_control_adjust">Ajustar overlay</string>
206 <string name="emulation_control_scale">Escala</string> 273 <string name="emulation_control_scale">Escala</string>
207 <string name="emulation_control_opacity">Opacidade</string> 274 <string name="emulation_control_opacity">Opacidade</string>
208 <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> 275 <string name="emulation_touch_overlay_reset">Restaurar overlay padrão</string>
209 <string name="emulation_touch_overlay_edit">Editar sobreposição </string> 276 <string name="emulation_touch_overlay_edit">Editar overlay</string>
210 <string name="emulation_pause">Pausa emulação</string> 277 <string name="emulation_pause">Pausar emulação</string>
211 <string name="emulation_unpause">Retomar emulação</string> 278 <string name="emulation_unpause">Retomar emulação</string>
212 <string name="emulation_input_overlay">Opções de sobreposição </string> 279 <string name="emulation_input_overlay">Opções de overlay</string>
213 280
214 <string name="load_settings">Configurações a carregar...</string> 281 <string name="load_settings">Carregando configurações...</string>
215 282
216 <!-- Software keyboard --> 283 <!-- Software keyboard -->
217 <string name="software_keyboard">Teclado de software</string> 284 <string name="software_keyboard">Teclado de software</string>
@@ -226,6 +293,9 @@
226 <string name="fatal_error">Erro fatal</string> 293 <string name="fatal_error">Erro fatal</string>
227 <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> 294 <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string>
228 <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string> 295 <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string>
296 <string name="device_memory_inadequate">RAM do dispositivo: %1$s\nRecommended: %2$s</string>
297 <string name="memory_formatted">%1$s %2$s</string>
298 <string name="no_game_present">Nenhum jogo inicializável presente!</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">Japão</string> 301 <string name="region_japan">Japão</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">Coréia</string> 306 <string name="region_korea">Coréia</string>
237 <string name="region_taiwan">Taiwan</string> 307 <string name="region_taiwan">Taiwan</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Byte</string>
311 <string name="memory_kilobyte">KB</string>
312 <string name="memory_megabyte">MB</string>
313 <string name="memory_gigabyte">GB</string>
314 <string name="memory_terabyte">TB</string>
315 <string name="memory_petabyte">PB</string>
316 <string name="memory_exabyte">EB</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulcano</string> 319 <string name="renderer_vulkan">Vulcano</string>
@@ -274,12 +351,17 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 351 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 352 <string name="anti_aliasing_smaa">SMAA</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">Landscape</string>
356 <string name="screen_layout_portrait">Portrait</string>
357 <string name="screen_layout_auto">Automático</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">Padrão (16:9)</string> 360 <string name="ratio_default">Padrão (16:9)</string>
279 <string name="ratio_force_four_three">Forçar 4:3</string> 361 <string name="ratio_force_four_three">Forçar 4:3</string>
280 <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> 362 <string name="ratio_force_twenty_one_nine">Forçar 21:9</string>
281 <string name="ratio_force_sixteen_ten">Forçar 16:10</string> 363 <string name="ratio_force_sixteen_ten">Forçar 16:10</string>
282 <string name="ratio_stretch">Esticar para a janela</string> 364 <string name="ratio_stretch">Esticar à janela</string>
283 365
284 <!-- CPU Accuracy --> 366 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">Preciso</string> 367 <string name="cpu_accuracy_accurate">Preciso</string>
@@ -287,7 +369,7 @@
287 <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> 369 <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string>
288 370
289 <!-- Gamepad Buttons --> 371 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">D-pad</string> 372 <string name="gamepad_d_pad">Botões Direcionais</string>
291 <string name="gamepad_left_stick">Analógico esquerdo</string> 373 <string name="gamepad_left_stick">Analógico esquerdo</string>
292 <string name="gamepad_right_stick">Analógico direito</string> 374 <string name="gamepad_right_stick">Analógico direito</string>
293 <string name="gamepad_home">Botão Home</string> 375 <string name="gamepad_home">Botão Home</string>
@@ -298,18 +380,32 @@
298 <string name="building_shaders">A criar shaders</string> 380 <string name="building_shaders">A criar shaders</string>
299 381
300 <!-- Theme options --> 382 <!-- Theme options -->
301 <string name="change_app_theme">Muda o Tema da App</string> 383 <string name="change_app_theme">Mudar o tema do aplicativo</string>
302 <string name="theme_default">Padrão</string> 384 <string name="theme_default">Padrão</string>
303 <string name="theme_material_you">Material You</string> 385 <string name="theme_material_you">Material You</string>
304 386
305 <!-- Theme Modes --> 387 <!-- Theme Modes -->
306 <string name="change_theme_mode">Altera o Modo do Tema</string> 388 <string name="change_theme_mode">Alterar o tema</string>
307 <string name="theme_mode_follow_system">Igual ao Sistema</string> 389 <string name="theme_mode_follow_system">Igual ao Sistema</string>
308 <string name="theme_mode_light">Claro</string> 390 <string name="theme_mode_light">Claro</string>
309 <string name="theme_mode_dark">Escuro</string> 391 <string name="theme_mode_dark">Escuro</string>
310 392
393 <!-- Audio output engines -->
394 <string name="cubeb">cubeb</string>
395
311 <!-- Black backgrounds theme --> 396 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">Usa Fundos Negros</string> 397 <string name="use_black_backgrounds">Plano de fundo preto</string>
313 <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> 398 <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string>
314 399
315</resources> 400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">Picture in Picture</string>
402 <string name="picture_in_picture_description">Minimizar a janela quando colocada em segundo plano</string>
403 <string name="pause">Pausa</string>
404 <string name="play">Correr</string>
405 <string name="mute">Mudo</string>
406 <string name="unmute">Unmute</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">Licenças</string>
410 <string name="license_fidelityfx_fsr_description">Upscaling de alta qualidade da AMD</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index 0a1a47fbb..6afea9b03 100644
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string> 4 <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string>
5 <string name="emulation_notification_channel_name">Emulação está Ativa</string> 5 <string name="emulation_notification_channel_name">Emulação está Ativa</string>
@@ -25,6 +25,7 @@
25 <string name="back">Voltar</string> 25 <string name="back">Voltar</string>
26 <string name="add_games">Adiciona Jogos</string> 26 <string name="add_games">Adiciona Jogos</string>
27 <string name="add_games_description">Seleciona a tua pasta de Jogos</string> 27 <string name="add_games_description">Seleciona a tua pasta de Jogos</string>
28 <string name="step_complete">Completo!</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Jogos</string> 31 <string name="home_games">Jogos</string>
@@ -37,7 +38,8 @@
37 <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> 38 <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string>
38 <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string> 39 <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Procurar Jogos</string> 41 <string name="home_search_games">Procurar jogos</string>
42 <string name="search_settings">Procurar nas definições</string>
41 <string name="games_dir_selected">Pasta de Jogos selecionada</string> 43 <string name="games_dir_selected">Pasta de Jogos selecionada</string>
42 <string name="install_prod_keys">Instala prod.keys</string> 44 <string name="install_prod_keys">Instala prod.keys</string>
43 <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> 45 <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string>
@@ -61,15 +63,18 @@
61 <string name="invalid_keys_file">Ficheiro de chaves inválido</string> 63 <string name="invalid_keys_file">Ficheiro de chaves inválido</string>
62 <string name="install_keys_success">Chaves instaladas com sucesso</string> 64 <string name="install_keys_success">Chaves instaladas com sucesso</string>
63 <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> 65 <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string>
66 <string name="install_prod_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .keys e tente novamente.</string>
67 <string name="install_amiibo_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .bin e tente novamente.</string>
64 <string name="invalid_keys_error">Chaves de encriptação inválidas</string> 68 <string name="invalid_keys_error">Chaves de encriptação inválidas</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> 70 <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string>
67 <string name="install_gpu_driver">Instala driver para GPU</string> 71 <string name="install_gpu_driver">Instala driver para GPU</string>
68 <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> 72 <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string>
69 <string name="advanced_settings">Configurações avançadas</string> 73 <string name="advanced_settings">Configurações avançadas</string>
74 <string name="advanced_settings_game">Definições avançadas: %1$s</string>
70 <string name="settings_description">Configura configurações do emulador</string> 75 <string name="settings_description">Configura configurações do emulador</string>
71 <string name="search_recently_played">Jogos recentes</string> 76 <string name="search_recently_played">Jogado recentemente</string>
72 <string name="search_recently_added">Adicionados recentemente</string> 77 <string name="search_recently_added">Adicionado recentemente</string>
73 <string name="search_retail">Jogos comerciais</string> 78 <string name="search_retail">Jogos comerciais</string>
74 <string name="search_homebrew">Homebrew</string> 79 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Abre a pasta Yuzu</string> 80 <string name="open_user_folder">Abre a pasta Yuzu</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> 91 <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>
87 <string name="import_saves">Importar</string> 92 <string name="import_saves">Importar</string>
88 <string name="export_saves">Exportar</string> 93 <string name="export_saves">Exportar</string>
94 <string name="install_firmware">Instalar firmware</string>
95 <string name="install_firmware_description">O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos.</string>
96 <string name="firmware_installing">Instalando firmware</string>
97 <string name="firmware_installed_success">Firmware instalado com sucesso.</string>
98 <string name="firmware_installed_failure">Falha na instalação do firmware</string>
99 <string name="firmware_installed_failure_description">Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo.</string>
100 <string name="share_log">Compartilhe registros de debug.</string>
101 <string name="share_log_description">Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas</string>
102 <string name="share_log_missing">Arquivo de registro não encontrado</string>
103 <string name="install_game_content">Instalar conteúdo adicional</string>
104 <string name="install_game_content_description">Instale atualizações de jogos ou DLC</string>
105 <string name="installing_game_content">A instalar conteúdo...</string>
106 <string name="install_game_content_failure">Erro ao instalar ficheiro(s) para NAND</string>
107 <string name="install_game_content_failure_description">Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas.</string>
108 <string name="install_game_content_failure_base">A instalação de jogos base não é permitida para evitar possíveis conflitos.</string>
109 <string name="install_game_content_failure_file_extension">Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos.</string>
110 <string name="install_game_content_failed_count">%1$d erro(s) de instalação</string>
111 <string name="install_game_content_success">Conteúdo(s) de jogo instalados com sucesso</string>
112 <string name="install_game_content_success_install">%1$d instalado com sucesso</string>
113 <string name="install_game_content_success_overwrite">%1$d substituída com êxito</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">Drivers personalizados não suportados</string>
116 <string name="custom_driver_not_supported_description">Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado!</string>
117 <string name="manage_yuzu_data">Administrar dados yuzu</string>
118 <string name="manage_yuzu_data_description">Importa/exporta firmware, chaves, dados do usuário e mais!</string>
119 <string name="share_save_file">Partilha ficheiro duardado</string>
120 <string name="export_save_failed">Erro ao exportar dados guardados</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia não é real</string> 123 <string name="gaia_is_not_real">Gaia não é real</string>
@@ -94,7 +126,18 @@
94 <string name="contributors">Contribuidores</string> 126 <string name="contributors">Contribuidores</string>
95 <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> 127 <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">Projetos que tornam o yuzu para Android possível</string>
97 <string name="build">Versão</string> 130 <string name="build">Versão</string>
131 <string name="user_data">Dado de utilizados</string>
132 <string name="user_data_description">Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluídos!</string>
133 <string name="exporting_user_data">A exportar dados de utilizados...</string>
134 <string name="importing_user_data">A importar dados de utilizador...</string>
135 <string name="import_user_data">Importar dados de utilizados...</string>
136 <string name="invalid_yuzu_backup">Backup yuzu inválido</string>
137 <string name="user_data_export_success">Dados de utilizados exportados com sucesso</string>
138 <string name="user_data_import_success">Dados de utilizador importado com sucesso</string>
139 <string name="user_data_export_cancelled">Exportação cancelada</string>
140 <string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,41 +157,53 @@
114 <string name="are_you_interested">Estás interessado?</string> 157 <string name="are_you_interested">Estás interessado?</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">Ativar limite de velocidade</string> 160 <string name="frame_limit_enable">Limite de velocidade</string>
118 <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> 161 <string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem específica da velocidade normal.</string>
119 <string name="frame_limit_slider">Percentagem do limite de velocidade</string> 162 <string name="frame_limit_slider">Percentagem do limite de velocidade</string>
120 <string name="frame_limit_slider_description">Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade.</string> 163 <string name="frame_limit_slider_description">Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade.</string>
121 <string name="cpu_accuracy">Precisão do CPU</string> 164 <string name="cpu_accuracy">Precisão do CPU</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">Modo ancorado</string> 168 <string name="use_docked_mode">Modo Ancorado</string>
125 <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> 169 <string name="use_docked_mode_description">Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho.</string>
126 <string name="emulated_region">Região da emulação</string> 170 <string name="emulated_region">Região da emulação</string>
127 <string name="emulated_language">Idioma da emulação</string> 171 <string name="emulated_language">Idioma da emulação</string>
128 <string name="select_rtc_date">Seleciona a data RTC</string> 172 <string name="select_rtc_date">Selecione a data do sistema</string>
129 <string name="select_rtc_time">Seleciona a hora RTC</string> 173 <string name="select_rtc_time">Selecione a hora do sistema</string>
130 <string name="use_custom_rtc">Ativa RTC personalizado</string> 174 <string name="use_custom_rtc">RTC personalizado</string>
131 <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> 175 <string name="use_custom_rtc_description">Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.</string>
132 <string name="set_custom_rtc">Define RTC personalizado</string> 176 <string name="set_custom_rtc">Defina um relógio em tempo real personalizado</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Nível de precisão</string> 179 <string name="renderer_accuracy">Nível de precisão</string>
137 <string name="renderer_resolution">Resolução</string> 180 <string name="renderer_resolution">Resolução (Portátil/Ancorado)</string>
138 <string name="renderer_vsync">Modo VSync</string> 181 <string name="renderer_vsync">Modo VSync</string>
139 <string name="renderer_aspect_ratio">Proporção do ecrã</string> 182 <string name="renderer_screen_layout">Oriantação</string>
183 <string name="renderer_aspect_ratio">Proporção da tela</string>
140 <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> 184 <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string>
141 <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> 185 <string name="renderer_anti_aliasing">Método de Anti-Serrilhado</string>
142 <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> 186 <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string>
143 <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> 187 <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string>
144 <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string> 188 <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string>
145 <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> 189 <string name="renderer_asynchronous_shaders_description">Compila os shaders de forma assíncrona, reduzindo travamentos, mas pode apresentar problemas.</string>
190 <string name="renderer_reactive_flushing">Usar flushing reativo</string>
191 <string name="renderer_reactive_flushing_description">Melhora a precisão da renderização em alguns jogos ao custo de desempenho.</string>
192 <string name="use_disk_shader_cache">Cache de shaders em disco</string>
193 <string name="use_disk_shader_cache_description">Reduz travamentos ao armazenar e carregar localmente os shaders.</string>
194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">Depuração da CPU</string>
198 <string name="cpu_debug_mode_description">Coloca a CPU em um modo de depuração lento.</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
146 <string name="renderer_debug">Ativar depuração de gráficos</string> 201 <string name="renderer_debug">Ativar depuração de gráficos</string>
147 <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> 202 <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string>
148 <string name="use_disk_shader_cache">Usar cache do disk shader</string> 203 <string name="fastmem">Fastmem</string>
149 <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string>
150 204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">Motor de saída</string>
152 <string name="audio_volume">Volume</string> 207 <string name="audio_volume">Volume</string>
153 <string name="audio_volume_description">Especifica o volume de saída.</string> 208 <string name="audio_volume_description">Especifica o volume de saída.</string>
154 209
@@ -157,14 +212,24 @@
157 <string name="ini_saved">Configurações guardadas</string> 212 <string name="ini_saved">Configurações guardadas</string>
158 <string name="gameid_saved">Configurações guardadas para %1$s</string> 213 <string name="gameid_saved">Configurações guardadas para %1$s</string>
159 <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> 214 <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string>
215 <string name="unimplemented_menu">Menu não implementado</string>
160 <string name="loading">A carregar...</string> 216 <string name="loading">A carregar...</string>
217 <string name="shutting_down">A desligar...</string>
161 <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> 218 <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string>
162 <string name="reset_to_default">Reverter para padrão</string> 219 <string name="reset_to_default">Reverter para padrão</string>
163 <string name="reset_all_settings">Redefinir todas as configurações?</string> 220 <string name="reset_all_settings">Redefinir todas as configurações?</string>
164 <string name="reset_all_settings_description">Todas as configurações avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido.</string> 221 <string name="reset_all_settings_description">Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito.</string>
165 <string name="settings_reset">Redefinir configurações </string> 222 <string name="settings_reset">Redefinir configurações </string>
166 <string name="close">Fechar</string> 223 <string name="close">Fechar</string>
167 <string name="learn_more">Saber Mais</string> 224 <string name="learn_more">Saber mais</string>
225 <string name="auto">Automático</string>
226 <string name="submit">Enviar</string>
227 <string name="string_null">Nenhum (desativado)</string>
228 <string name="string_import">Importar</string>
229 <string name="export">Exportar</string>
230 <string name="export_failed">Exportação falhada</string>
231 <string name="import_failed">IMportação falhada</string>
232 <string name="cancelling">A cancelar</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Seleciona a driver para o GPU</string> 235 <string name="select_gpu_driver">Seleciona a driver para o GPU</string>
@@ -172,6 +237,7 @@
172 <string name="select_gpu_driver_install">Instalar</string> 237 <string name="select_gpu_driver_install">Instalar</string>
173 <string name="select_gpu_driver_default">Padrão</string> 238 <string name="select_gpu_driver_default">Padrão</string>
174 <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> 239 <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
240 <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
175 <string name="system_gpu_driver">Driver do GPU padrão</string> 241 <string name="system_gpu_driver">Driver do GPU padrão</string>
176 <string name="installing_driver">A instalar o Driver...</string> 242 <string name="installing_driver">A instalar o Driver...</string>
177 243
@@ -182,10 +248,11 @@
182 <string name="preferences_graphics">Gráficos</string> 248 <string name="preferences_graphics">Gráficos</string>
183 <string name="preferences_audio">Audio</string> 249 <string name="preferences_audio">Audio</string>
184 <string name="preferences_theme">Cor e tema.</string> 250 <string name="preferences_theme">Cor e tema.</string>
251 <string name="preferences_debug">Depurar</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">A tua ROM está encriptada</string> 254 <string name="loader_error_encrypted">A tua ROM está encriptada</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor segue os guias para fazer redump das tuas<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">Cartidges de Jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">Jogos Instalados</a>.]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga os guias para despejar novamente o seu <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartucho de jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string>
190 <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string> 257 <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string>
191 <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string> 258 <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string>
@@ -193,28 +260,28 @@
193 <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> 260 <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string>
194 261
195 <!-- Emulation Menu --> 262 <!-- Emulation Menu -->
196 <string name="emulation_exit">Sair da emulação</string> 263 <string name="emulation_exit">Parar emulação</string>
197 <string name="emulation_done">Feito</string> 264 <string name="emulation_done">Feito</string>
198 <string name="emulation_fps_counter">Contador de FPS</string> 265 <string name="emulation_fps_counter">Contador de FPS</string>
199 <string name="emulation_toggle_controls">Alterar Controlos</string> 266 <string name="emulation_toggle_controls">Alterar controles</string>
200 <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> 267 <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string>
201 <string name="emulation_dpad_slide">Deslizar do DPad</string> 268 <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string>
202 <string name="emulation_haptics">Hápticos </string> 269 <string name="emulation_haptics">Vibração ao tocar</string>
203 <string name="emulation_show_overlay">Mostrar sobreposição </string> 270 <string name="emulation_show_overlay">Mostrar overlay</string>
204 <string name="emulation_toggle_all">Alterar todos</string> 271 <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string>
205 <string name="emulation_control_adjust">Ajustar a sobreposição </string> 272 <string name="emulation_control_adjust">Ajustar overlay</string>
206 <string name="emulation_control_scale">Escala</string> 273 <string name="emulation_control_scale">Escala</string>
207 <string name="emulation_control_opacity">Opacidade</string> 274 <string name="emulation_control_opacity">Opacidade</string>
208 <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> 275 <string name="emulation_touch_overlay_reset">Restaurar overlay padrão</string>
209 <string name="emulation_touch_overlay_edit">Editar sobreposição </string> 276 <string name="emulation_touch_overlay_edit">Editar overlay</string>
210 <string name="emulation_pause">Pausa emulação</string> 277 <string name="emulation_pause">Pausar emulação</string>
211 <string name="emulation_unpause">Retomar emulação</string> 278 <string name="emulation_unpause">Despausar emulação</string>
212 <string name="emulation_input_overlay">Opções de sobreposição </string> 279 <string name="emulation_input_overlay">Opções de overlay</string>
213 280
214 <string name="load_settings">Configurações a carregar...</string> 281 <string name="load_settings">Carregando configurações...</string>
215 282
216 <!-- Software keyboard --> 283 <!-- Software keyboard -->
217 <string name="software_keyboard">Teclado de Software</string> 284 <string name="software_keyboard">Teclado de software</string>
218 285
219 <!-- Errors and warnings --> 286 <!-- Errors and warnings -->
220 <string name="abort_button">Abortar</string> 287 <string name="abort_button">Abortar</string>
@@ -226,6 +293,9 @@
226 <string name="fatal_error">Erro fatal</string> 293 <string name="fatal_error">Erro fatal</string>
227 <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> 294 <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string>
228 <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string> 295 <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string>
296 <string name="device_memory_inadequate">RAM do dispositivo: %1$s\nRecommended: %2$s</string>
297 <string name="memory_formatted">%1$s %2$s</string>
298 <string name="no_game_present">Nenhum jogo inicializável presente!</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">Japão</string> 301 <string name="region_japan">Japão</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">Coreia</string> 306 <string name="region_korea">Coreia</string>
237 <string name="region_taiwan">Taiwan</string> 307 <string name="region_taiwan">Taiwan</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Byte</string>
311 <string name="memory_kilobyte">KB</string>
312 <string name="memory_megabyte">MB</string>
313 <string name="memory_gigabyte">GB</string>
314 <string name="memory_terabyte">TB</string>
315 <string name="memory_petabyte">PB</string>
316 <string name="memory_exabyte">EB</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulcano</string> 319 <string name="renderer_vulkan">Vulcano</string>
@@ -274,12 +351,17 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 351 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 352 <string name="anti_aliasing_smaa">SMAA</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">Landscape</string>
356 <string name="screen_layout_portrait">Portrait</string>
357 <string name="screen_layout_auto">Automático</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">Padrão (16:9)</string> 360 <string name="ratio_default">Padrão (16:9)</string>
279 <string name="ratio_force_four_three">Forçar 4:3</string> 361 <string name="ratio_force_four_three">Forçar 4:3</string>
280 <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> 362 <string name="ratio_force_twenty_one_nine">Forçar 21:9</string>
281 <string name="ratio_force_sixteen_ten">Forçar 16:10</string> 363 <string name="ratio_force_sixteen_ten">Forçar 16:10</string>
282 <string name="ratio_stretch">Esticar à Janela</string> 364 <string name="ratio_stretch">Esticar à janela</string>
283 365
284 <!-- CPU Accuracy --> 366 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">Preciso</string> 367 <string name="cpu_accuracy_accurate">Preciso</string>
@@ -287,9 +369,9 @@
287 <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> 369 <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string>
288 370
289 <!-- Gamepad Buttons --> 371 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">D-Pad</string> 372 <string name="gamepad_d_pad">Botões Direcionais</string>
291 <string name="gamepad_left_stick">Analógico Esquerdo</string> 373 <string name="gamepad_left_stick">Analógico esquerdo</string>
292 <string name="gamepad_right_stick">Analógico Direito</string> 374 <string name="gamepad_right_stick">Analógico direito</string>
293 <string name="gamepad_home">Home</string> 375 <string name="gamepad_home">Home</string>
294 <string name="gamepad_screenshot">Captura de ecrã</string> 376 <string name="gamepad_screenshot">Captura de ecrã</string>
295 377
@@ -298,18 +380,32 @@
298 <string name="building_shaders">A criar shaders</string> 380 <string name="building_shaders">A criar shaders</string>
299 381
300 <!-- Theme options --> 382 <!-- Theme options -->
301 <string name="change_app_theme">Muda o Tema da App</string> 383 <string name="change_app_theme">Mudar o tema do aplicativo</string>
302 <string name="theme_default">Padrão</string> 384 <string name="theme_default">Padrão</string>
303 <string name="theme_material_you">Material You</string> 385 <string name="theme_material_you">Material You</string>
304 386
305 <!-- Theme Modes --> 387 <!-- Theme Modes -->
306 <string name="change_theme_mode">Altera o Modo do Tema</string> 388 <string name="change_theme_mode">Alterar o tema</string>
307 <string name="theme_mode_follow_system">Igual ao Sistema</string> 389 <string name="theme_mode_follow_system">Igual ao Sistema</string>
308 <string name="theme_mode_light">Claro</string> 390 <string name="theme_mode_light">Claro</string>
309 <string name="theme_mode_dark">Escuro</string> 391 <string name="theme_mode_dark">Escuro</string>
310 392
393 <!-- Audio output engines -->
394 <string name="cubeb">cubeb</string>
395
311 <!-- Black backgrounds theme --> 396 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">Usa Fundos Escuros</string> 397 <string name="use_black_backgrounds">Plano de fundo preto</string>
313 <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> 398 <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string>
314 399
315</resources> 400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">Picture in Picture</string>
402 <string name="picture_in_picture_description">Minimizar a janela quando colocada em segundo plano</string>
403 <string name="pause">Pausa</string>
404 <string name="play">Correr</string>
405 <string name="mute">Mute</string>
406 <string name="unmute">Unmute</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">Licenças</string>
410 <string name="license_fidelityfx_fsr_description">Upscaling de alta qualidade da AMD</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index 0bef035d6..c614257a8 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Это программное обеÑпечение позволÑет запуÑкать игры Ð´Ð»Ñ Ð¸Ð³Ñ€Ð¾Ð²Ð¾Ð¹ конÑоли Nintendo Switch. Мы не предоÑтавлÑем Ñами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище уÑтройÑтва..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> 4 <string name="app_disclaimer">Это программное обеÑпечение позволÑет запуÑкать игры Ð´Ð»Ñ Ð¸Ð³Ñ€Ð¾Ð²Ð¾Ð¹ конÑоли Nintendo Switch. Мы не предоÑтавлÑем Ñами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище уÑтройÑтва..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string>
5 <string name="emulation_notification_channel_name">ЭмулÑÑ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string> 5 <string name="emulation_notification_channel_name">ЭмулÑÑ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string>
@@ -7,7 +7,7 @@
7 <string name="emulation_notification_running">yuzu запущен</string> 7 <string name="emulation_notification_running">yuzu запущен</string>
8 <string name="notice_notification_channel_name">Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸ ошибки</string> 8 <string name="notice_notification_channel_name">Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸ ошибки</string>
9 <string name="notice_notification_channel_description">Показывать уведомлениÑ, когда что-то пошло не так</string> 9 <string name="notice_notification_channel_description">Показывать уведомлениÑ, когда что-то пошло не так</string>
10 <string name="notification_permission_not_granted">Ð’Ñ‹ не предоÑтавили разрешение уведомлений!</string> 10 <string name="notification_permission_not_granted">Ð’Ñ‹ не предоÑтавили разрешение на уведомлениÑ!</string>
11 11
12 <!-- Setup strings --> 12 <!-- Setup strings -->
13 <string name="welcome">Добро пожаловать!</string> 13 <string name="welcome">Добро пожаловать!</string>
@@ -25,6 +25,7 @@
25 <string name="back">Ðазад</string> 25 <string name="back">Ðазад</string>
26 <string name="add_games">Добавить игры</string> 26 <string name="add_games">Добавить игры</string>
27 <string name="add_games_description">Выберите папку Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string> 27 <string name="add_games_description">Выберите папку Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string>
28 <string name="step_complete">Выполнено!</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">Игры</string> 31 <string name="home_games">Игры</string>
@@ -38,6 +39,7 @@
38 <string name="add_games_warning_description">Игры не будут отображатьÑÑ Ð² ÑпиÑке Игры, еÑли папка не выбрана.</string> 39 <string name="add_games_warning_description">Игры не будут отображатьÑÑ Ð² ÑпиÑке Игры, еÑли папка не выбрана.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Ðайти игры</string> 41 <string name="home_search_games">Ðайти игры</string>
42 <string name="search_settings">ÐаÑтройки поиÑка</string>
41 <string name="games_dir_selected">Выбрана папка Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string> 43 <string name="games_dir_selected">Выбрана папка Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string>
42 <string name="install_prod_keys">УÑтановить prod.keys</string> 44 <string name="install_prod_keys">УÑтановить prod.keys</string>
43 <string name="install_prod_keys_description">ТребуетÑÑ Ð´Ð»Ñ Ñ€Ð°Ñшифровки розничных игр</string> 45 <string name="install_prod_keys_description">ТребуетÑÑ Ð´Ð»Ñ Ñ€Ð°Ñшифровки розничных игр</string>
@@ -61,14 +63,17 @@
61 <string name="invalid_keys_file">Выбран неверный файл ключей</string> 63 <string name="invalid_keys_file">Выбран неверный файл ключей</string>
62 <string name="install_keys_success">Ключи уÑпешно уÑтановлены</string> 64 <string name="install_keys_success">Ключи уÑпешно уÑтановлены</string>
63 <string name="reading_keys_failure">Ошибка при чтении ключей шифрованиÑ</string> 65 <string name="reading_keys_failure">Ошибка при чтении ключей шифрованиÑ</string>
66 <string name="install_prod_keys_failure_extension_description">УбедитеÑÑŒ, что файл ключей имеет раÑширение .keys, и повторите попытку.</string>
67 <string name="install_amiibo_keys_failure_extension_description">УбедитеÑÑŒ, что файл ключей имеет раÑширение .bin, и повторите попытку.</string>
64 <string name="invalid_keys_error">Ðеверные ключи шифрованиÑ</string> 68 <string name="invalid_keys_error">Ðеверные ключи шифрованиÑ</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. ПожалуйÑта, пере-дампите ваши ключи.</string> 70 <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. ПожалуйÑта, пере-дампите ваши ключи.</string>
67 <string name="install_gpu_driver">УÑтановить драйвер ГП</string> 71 <string name="install_gpu_driver">УÑтановить драйвер ГП</string>
68 <string name="install_gpu_driver_description">УÑтановите альтернативные драйверы Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾ лучшей производительноÑти и/или точноÑти</string> 72 <string name="install_gpu_driver_description">УÑтановите альтернативные драйверы Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾ лучшей производительноÑти и/или точноÑти</string>
69 <string name="advanced_settings">РаÑширенные наÑтройки</string> 73 <string name="advanced_settings">РаÑширенные наÑтройки</string>
74 <string name="advanced_settings_game">РаÑширенные наÑтройки: %1$s</string>
70 <string name="settings_description">ÐаÑтройка параметров ÑмулÑтора</string> 75 <string name="settings_description">ÐаÑтройка параметров ÑмулÑтора</string>
71 <string name="search_recently_played">Ðедавно Ñыграно</string> 76 <string name="search_recently_played">Ðедавно Ñыгранные</string>
72 <string name="search_recently_added">Ðедавно добавлено</string> 77 <string name="search_recently_added">Ðедавно добавлено</string>
73 <string name="search_retail">Розничные</string> 78 <string name="search_retail">Розничные</string>
74 <string name="search_homebrew">Homebrew</string> 79 <string name="search_homebrew">Homebrew</string>
@@ -86,6 +91,34 @@
86 <string name="save_file_invalid_zip_structure_description">Ðазвание первой вложенной папки должно быть идентификатором игры.</string> 91 <string name="save_file_invalid_zip_structure_description">Ðазвание первой вложенной папки должно быть идентификатором игры.</string>
87 <string name="import_saves">Импорт</string> 92 <string name="import_saves">Импорт</string>
88 <string name="export_saves">ЭкÑпорт</string> 93 <string name="export_saves">ЭкÑпорт</string>
94 <string name="install_firmware">УÑтановить прошивку</string>
95 <string name="install_firmware_description">Прошивка должна находитьÑÑ Ð² ZIP-архиве и необходима Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ некоторых игр</string>
96 <string name="firmware_installing">УÑтановка прошивки</string>
97 <string name="firmware_installed_success">Прошивка уÑпешно уÑтановлена</string>
98 <string name="firmware_installed_failure">Ðе удалоÑÑŒ уÑтановить прошивку</string>
99 <string name="firmware_installed_failure_description">УбедитеÑÑŒ что файлы прошивки nca находÑÑ‚ÑÑ Ð² корне zip-архива и повторите попытку.</string>
100 <string name="share_log">ПоделитьÑÑ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð¾Ð¼ отладки</string>
101 <string name="share_log_description">ПоделитьÑÑ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð¾Ð¼ отладки yuzu Ð´Ð»Ñ ÑƒÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼</string>
102 <string name="share_log_missing">Файл журнала не найден</string>
103 <string name="install_game_content">УÑтановить игровой контент</string>
104 <string name="install_game_content_description">УÑтановить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð³Ñ€Ñ‹ или дополнений</string>
105 <string name="installing_game_content">УÑтановка контента...</string>
106 <string name="install_game_content_failure">Ошибка уÑтановки файл(ов) в NAND.</string>
107 <string name="install_game_content_failure_description">УбедитеÑÑŒ что Ñодержимое допуÑтимо и что файл prod.keys уÑтановлен.</string>
108 <string name="install_game_content_failure_base">УÑтановка базовых игр запрещена во избежание возможных конфликтов.</string>
109 <string name="install_game_content_failure_file_extension">ПоддерживаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ контент NSP и XCI. ПожалуйÑта убедитеÑÑŒ что игровой контент дейÑтвителен.</string>
110 <string name="install_game_content_failed_count">%1$d ошибка уÑтановки</string>
111 <string name="install_game_content_success">Игровой контент уÑпешно уÑтановлен</string>
112 <string name="install_game_content_success_install">%1$d УÑпешно уÑтановлено</string>
113 <string name="install_game_content_success_overwrite">%1$d УÑпешно перезапиÑано</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">ПользовательÑкие драйверы не поддерживаютÑÑ</string>
116 <string name="custom_driver_not_supported_description">Загрузка пользовательÑкого драйвера в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ поддерживаетÑÑ Ð´Ð»Ñ Ñтого уÑтройÑтва.\nПроверьте Ñтот параметр еще раз в будущем чтобы узнать была ли добавлена ​​поддержка!
117 </string>
118 <string name="manage_yuzu_data">Управление данными yuzu</string>
119 <string name="manage_yuzu_data_description">Импортируйте/ÑкÑпортируйте прошивку, ключи, пользовательÑкие данные и многое другое!</string>
120 <string name="share_save_file">ПоделитьÑÑ Ñ„Ð°Ð¹Ð»Ð¾Ð¼ ÑохранениÑ</string>
121 <string name="export_save_failed">Ðе удалоÑÑŒ ÑкÑпортировать Ñохранение</string>
89 122
90 <!-- About screen strings --> 123 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia не ÑущеÑтвует</string> 124 <string name="gaia_is_not_real">Gaia не ÑущеÑтвует</string>
@@ -94,7 +127,18 @@
94 <string name="contributors">Контрибьюторы</string> 127 <string name="contributors">Контрибьюторы</string>
95 <string name="contributors_description">Сделано Ñ \u2764 от команды yuzu</string> 128 <string name="contributors_description">Сделано Ñ \u2764 от команды yuzu</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 129 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
130 <string name="licenses_description">Проекты, которые Ñделали yuzu Ð´Ð»Ñ Android возможным</string>
97 <string name="build">Сборка</string> 131 <string name="build">Сборка</string>
132 <string name="user_data">Данные пользователÑ</string>
133 <string name="user_data_description">Импортируйте/ÑкÑпортируйте вÑе данные приложениÑ.\n\nПри импорте пользовательÑких данных вÑе ÑущеÑтвующие пользовательÑкие данные будут удалены!</string>
134 <string name="exporting_user_data">ЭкÑпорт пользовательÑких данных…</string>
135 <string name="importing_user_data">Импорт пользовательÑких данных…</string>
136 <string name="import_user_data">Импортировать пользовательÑкие данные</string>
137 <string name="invalid_yuzu_backup">ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ Ñ€ÐµÐ·ÐµÑ€Ð²Ð½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ yuzu</string>
138 <string name="user_data_export_success">ПользовательÑкие данные уÑпешно ÑкÑпортированы</string>
139 <string name="user_data_import_success">ПользовательÑкие данные уÑпешно импортированы</string>
140 <string name="user_data_export_cancelled">ЭкÑпорт отменен</string>
141 <string name="user_data_import_failed_description">УбедитеÑÑŒ что папки пользовательÑких данных находÑÑ‚ÑÑ Ð² корне zip-папки и Ñодержат файл конфигурации config/config.ini и повторите попытку.</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 142 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 143 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 144 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,41 +158,51 @@
114 <string name="are_you_interested">Ð’Ñ‹ заинтереÑованы?</string> 158 <string name="are_you_interested">Ð’Ñ‹ заинтереÑованы?</string>
115 159
116 <!-- General settings strings --> 160 <!-- General settings strings -->
117 <string name="frame_limit_enable">Ðключить ограничение ÑкороÑти</string> 161 <string name="frame_limit_enable">Ограничить ÑкороÑть</string>
118 <string name="frame_limit_enable_description">ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, ÑкороÑть ÑмулÑции будет ограничена указанным процентом от нормальной ÑкороÑти.</string> 162 <string name="frame_limit_enable_description">Ограничивает ÑкороÑть ÑмулÑции указанным процентом от нормальной ÑкороÑти.</string>
119 <string name="frame_limit_slider">Ограничение процента cкороÑти</string> 163 <string name="frame_limit_slider">Ограничение процента cкороÑти</string>
120 <string name="frame_limit_slider_description">Указывает процент Ð´Ð»Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ ÑкороÑти ÑмулÑции. При значении по умолчанию 100% ÑмулÑÑ†Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ ограничена нормальной ÑкороÑтью. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñ‹ÑˆÐµ или ниже будут увеличивать или уменьшать ограничение ÑкороÑти.</string> 164 <string name="frame_limit_slider_description">Указывает процент Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ ÑкороÑти ÑмулÑции. 100% - Ñто Ð½Ð¾Ñ€Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ ÑкороÑть. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð±Ð¾Ð»ÑŒÑˆÐµ или меньше увеличивают или уменьшают ограничение ÑкороÑти.</string>
121 <string name="cpu_accuracy">ТочноÑть ЦП</string> 165 <string name="cpu_accuracy">ТочноÑть ЦП</string>
166 <string name="value_with_units">%1$s%2$s</string>
122 167
123 <!-- System settings strings --> 168 <!-- System settings strings -->
124 <string name="use_docked_mode">Режим док-Ñтанции</string> 169 <string name="use_docked_mode">Режим док-Ñтанции</string>
125 <string name="use_docked_mode_description">ЭмулÑÑ†Ð¸Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ð° док-Ñтанции, что увеличивает разрешение за Ñчет ÑÐ½Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти.</string> 170 <string name="use_docked_mode_description">Увеличивает разрешение, ÑÐ½Ð¸Ð¶Ð°Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть. Портативный режим иÑпользуетÑÑ Ð¿Ñ€Ð¸ отключении, ÑÐ½Ð¸Ð¶Ð°Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ и Ð¿Ð¾Ð²Ñ‹ÑˆÐ°Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть.</string>
126 <string name="emulated_region">Эмулируемый регион</string> 171 <string name="emulated_region">Регион конÑоли</string>
127 <string name="emulated_language">ЭмулируемÑй Ñзык</string> 172 <string name="emulated_language">ЯзÑк конÑоли</string>
128 <string name="select_rtc_date">Выберите дату RTC</string> 173 <string name="select_rtc_date">Выберите дату RTC</string>
129 <string name="select_rtc_time">Выберите Ð²Ñ€ÐµÐ¼Ñ RTC</string> 174 <string name="select_rtc_time">Выберите Ð²Ñ€ÐµÐ¼Ñ RTC</string>
130 <string name="use_custom_rtc">Ðключить пользовательÑкий RTC</string> 175 <string name="use_custom_rtc">ПользовательÑкий RTC</string>
131 <string name="use_custom_rtc_description">Этот параметр позволÑет уÑтановить пользовательÑкие чаÑÑ‹ реального времени отдельно от текущего ÑиÑтемного времени</string> 176 <string name="use_custom_rtc_description">ПозволÑет уÑтановить пользовательÑкие чаÑÑ‹ реального времени отдельно от текущего ÑиÑтемного времени.</string>
132 <string name="set_custom_rtc">УÑтановить пользовательÑкий RTC</string> 177 <string name="set_custom_rtc">УÑтановить пользовательÑкий RTC</string>
133 178
134 <!-- Graphics settings strings --> 179 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Уровень точноÑти</string> 180 <string name="renderer_accuracy">Уровень точноÑти</string>
137 <string name="renderer_resolution">Разрешение</string> 181 <string name="renderer_resolution">Разрешение (портативное/в док-Ñтанции)</string>
138 <string name="renderer_vsync">Режим верт. Ñинхронизации</string> 182 <string name="renderer_vsync">Режим верт. Ñинхронизации</string>
183 <string name="renderer_screen_layout">ОриентациÑ</string>
139 <string name="renderer_aspect_ratio">Соотношение Ñторон</string> 184 <string name="renderer_aspect_ratio">Соотношение Ñторон</string>
140 <string name="renderer_scaling_filter">Фильтр адаптации окна</string> 185 <string name="renderer_scaling_filter">Фильтр адаптации окна</string>
141 <string name="renderer_anti_aliasing">Метод ÑглаживаниÑ</string> 186 <string name="renderer_anti_aliasing">Метод ÑглаживаниÑ</string>
142 <string name="renderer_force_max_clock">Принудительно заÑтавить макÑимальную тактовую чаÑтоту (только Ð´Ð»Ñ Adreno)</string> 187 <string name="renderer_force_max_clock">Принудительно заÑтавить макÑимальную тактовую чаÑтоту (только Ð´Ð»Ñ Adreno)</string>
143 <string name="renderer_force_max_clock_description">ЗаÑтавлÑет ГП работать на макÑимально возможных тактовых чаÑтотах (тепловые Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñе равно будут применÑтьÑÑ).</string> 188 <string name="renderer_force_max_clock_description">ЗаÑтавлÑет ГП работать на макÑимально возможных тактовых чаÑтотах (тепловые Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñе равно будут применÑтьÑÑ).</string>
144 <string name="renderer_asynchronous_shaders">ИÑпользовать аÑинхронные шейдеры</string> 189 <string name="renderer_asynchronous_shaders">ИÑпользовать аÑинхронные шейдеры</string>
145 <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры аÑинхронно, что уменьшает завиÑаниÑ, но может взамен предоÑтавить визуальные баги.</string> 190 <string name="renderer_asynchronous_shaders_description">КомпилÑÑ†Ð¸Ñ ÑˆÐµÐ¹Ð´ÐµÑ€Ð¾Ð² проиÑходит аÑинхронно, что уменьшает завиÑаниÑ, но может привеÑти к поÑвлению багов.</string>
146 <string name="renderer_debug">Включить отладку графики</string> 191 <string name="renderer_reactive_flushing">Ð ÐµÐ°ÐºÑ‚Ð¸Ð²Ð½Ð°Ñ Ð¾Ñ‡Ð¸Ñтка</string>
147 <string name="renderer_debug_description">ЕÑли включено, графичеÑкий API переходит в более медленный режим отладки</string> 192 <string name="renderer_reactive_flushing_description">Повышение точноÑти рендеринга в некоторых играх за Ñчет ÑÐ½Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти.</string>
148 <string name="use_disk_shader_cache">ИÑпользовать кÑш шейдеров на диÑке</string> 193 <string name="use_disk_shader_cache">КÑш шейдеров на диÑке</string>
149 <string name="use_disk_shader_cache_description">Уменьшение завиÑаний за Ñчет Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸ загрузки Ñгенерированных шейдеров на хранилище.</string> 194 <string name="use_disk_shader_cache_description">Уменьшение завиÑаний за Ñчет Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸ загрузки Ñгенерированных шейдеров.</string>
195
196 <!-- Debug settings strings -->
197 <string name="cpu">ЦП</string>
198 <string name="cpu_debug_mode">Отладка ЦП</string>
199 <string name="cpu_debug_mode_description">Переводит ЦП в режим медленной отладки.</string>
200 <string name="gpu">графичеÑкий процеÑÑор</string>
201 <string name="renderer_api">API</string>
202 <string name="renderer_debug">Отладка графики</string>
203 <string name="renderer_debug_description">Переводит графичеÑкий API в режим медленной отладки.</string>
204 <string name="fastmem">Fastmem</string>
150 205
151 <!-- Audio settings strings -->
152 <string name="audio_volume">ГромкоÑть</string> 206 <string name="audio_volume">ГромкоÑть</string>
153 <string name="audio_volume_description">Задает громкоÑть аудиовыхода.</string> 207 <string name="audio_volume_description">Задает громкоÑть аудиовыхода.</string>
154 208
@@ -157,7 +211,9 @@
157 <string name="ini_saved">Сохраненные наÑтройки</string> 211 <string name="ini_saved">Сохраненные наÑтройки</string>
158 <string name="gameid_saved">ÐаÑтройки Ñохранены Ð´Ð»Ñ %1$s</string> 212 <string name="gameid_saved">ÐаÑтройки Ñохранены Ð´Ð»Ñ %1$s</string>
159 <string name="error_saving">Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ %1$s.ini: %2$s</string> 213 <string name="error_saving">Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ %1$s.ini: %2$s</string>
214 <string name="unimplemented_menu">Ðереализованное меню</string>
160 <string name="loading">Загрузка...</string> 215 <string name="loading">Загрузка...</string>
216 <string name="shutting_down">Выключение…</string>
161 <string name="reset_setting_confirmation">Хотите ли вы вернуть Ñтот параметр к значению по умолчанию?</string> 217 <string name="reset_setting_confirmation">Хотите ли вы вернуть Ñтот параметр к значению по умолчанию?</string>
162 <string name="reset_to_default">Ð¡Ð±Ñ€Ð¾Ñ Ðº наÑтройкам по умолчанию</string> 218 <string name="reset_to_default">Ð¡Ð±Ñ€Ð¾Ñ Ðº наÑтройкам по умолчанию</string>
163 <string name="reset_all_settings">СброÑить вÑе наÑтройки?</string> 219 <string name="reset_all_settings">СброÑить вÑе наÑтройки?</string>
@@ -165,6 +221,14 @@
165 <string name="settings_reset">ÐаÑтройки Ñброшены</string> 221 <string name="settings_reset">ÐаÑтройки Ñброшены</string>
166 <string name="close">Закрыть</string> 222 <string name="close">Закрыть</string>
167 <string name="learn_more">Узнать больше</string> 223 <string name="learn_more">Узнать больше</string>
224 <string name="auto">Ðвто</string>
225 <string name="submit">Отправить</string>
226 <string name="string_null">Null</string>
227 <string name="string_import">Импорт</string>
228 <string name="export">ЭкÑпорт</string>
229 <string name="export_failed">Ошибка ÑкÑпорта</string>
230 <string name="import_failed">Ошибка импортированиÑ</string>
231 <string name="cancelling">ОтменÑÑŽ</string>
168 232
169 <!-- GPU driver installation --> 233 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Выбрать драйвер ГП</string> 234 <string name="select_gpu_driver">Выбрать драйвер ГП</string>
@@ -172,6 +236,7 @@
172 <string name="select_gpu_driver_install">УÑтановить</string> 236 <string name="select_gpu_driver_install">УÑтановить</string>
173 <string name="select_gpu_driver_default">По умолчанию</string> 237 <string name="select_gpu_driver_default">По умолчанию</string>
174 <string name="select_gpu_driver_use_default">ИÑпользуетÑÑ Ñтандартный драйвер ГП </string> 238 <string name="select_gpu_driver_use_default">ИÑпользуетÑÑ Ñтандартный драйвер ГП </string>
239 <string name="select_gpu_driver_error">Выбран неверный драйвер, иÑпользуетÑÑ Ñтандартный ÑиÑтемный!</string>
175 <string name="system_gpu_driver">СиÑтемный драйвер ГП</string> 240 <string name="system_gpu_driver">СиÑтемный драйвер ГП</string>
176 <string name="installing_driver">УÑтановка драйвера...</string> 241 <string name="installing_driver">УÑтановка драйвера...</string>
177 242
@@ -182,10 +247,11 @@
182 <string name="preferences_graphics">Графика</string> 247 <string name="preferences_graphics">Графика</string>
183 <string name="preferences_audio">Ðудио</string> 248 <string name="preferences_audio">Ðудио</string>
184 <string name="preferences_theme">Тема и цвет</string> 249 <string name="preferences_theme">Тема и цвет</string>
250 <string name="preferences_debug">Отладка</string>
185 251
186 <!-- ROM loading errors --> 252 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">Ваш ROM зашифрованный</string> 253 <string name="loader_error_encrypted">Ваш ROM зашифрованный</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[ПожалуйÑта, Ñледуйте инÑтрукциÑм, чтобы пере-дампить ваши <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">игровые картриджи</a> или <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">уÑтановленные игры</a>.]]></string> 254 <string name="loader_error_encrypted_roms_description"><![CDATA[Следуйте инÑтрукциÑм, чтобы пере-дампить игровые картриджи <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\"> или <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\"> уÑтановленные игры</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[ПожалуйÑта, убедитеÑÑŒ, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> уÑтановлен, чтобы игры можно было раÑшифровать.]]></string> 255 <string name="loader_error_encrypted_keys_description"><![CDATA[ПожалуйÑта, убедитеÑÑŒ, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> уÑтановлен, чтобы игры можно было раÑшифровать.]]></string>
190 <string name="loader_error_video_core">Произошла ошибка при инициализации видеоÑдра.</string> 256 <string name="loader_error_video_core">Произошла ошибка при инициализации видеоÑдра.</string>
191 <string name="loader_error_video_core_description">Обычно Ñто вызвано неÑовмеÑтимым драйвером ГП. УÑтановка пользовательÑкого драйвера ГП может решить Ñту проблему.</string> 257 <string name="loader_error_video_core_description">Обычно Ñто вызвано неÑовмеÑтимым драйвером ГП. УÑтановка пользовательÑкого драйвера ГП может решить Ñту проблему.</string>
@@ -199,17 +265,17 @@
199 <string name="emulation_toggle_controls">Переключение управлениÑ</string> 265 <string name="emulation_toggle_controls">Переключение управлениÑ</string>
200 <string name="emulation_rel_stick_center">ОтноÑительный центр Ñтика</string> 266 <string name="emulation_rel_stick_center">ОтноÑительный центр Ñтика</string>
201 <string name="emulation_dpad_slide">Слайд креÑтовиной</string> 267 <string name="emulation_dpad_slide">Слайд креÑтовиной</string>
202 <string name="emulation_haptics">Ð¢Ð°ÐºÑ‚Ð¸Ð»ÑŒÐ½Ð°Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð°Ñ ÑвÑзь</string> 268 <string name="emulation_haptics">ÐžÐ±Ñ€Ð°Ñ‚Ð½Ð°Ñ ÑвÑзь от нажатий</string>
203 <string name="emulation_show_overlay">Показать оверлей</string> 269 <string name="emulation_show_overlay">Показать оверлей</string>
204 <string name="emulation_toggle_all">Переключить вÑÑ‘</string> 270 <string name="emulation_toggle_all">Переключить вÑÑ‘</string>
205 <string name="emulation_control_adjust">ÐаÑтроить оверлей</string> 271 <string name="emulation_control_adjust">Регулировка оверлеÑ</string>
206 <string name="emulation_control_scale">МаÑштаб</string> 272 <string name="emulation_control_scale">МаÑштаб</string>
207 <string name="emulation_control_opacity">ÐепрозрачноÑть</string> 273 <string name="emulation_control_opacity">ÐепрозрачноÑть</string>
208 <string name="emulation_touch_overlay_reset">СброÑить оверлей</string> 274 <string name="emulation_touch_overlay_reset">СброÑить оверлей</string>
209 <string name="emulation_touch_overlay_edit">Изменить оверлей</string> 275 <string name="emulation_touch_overlay_edit">Редактировать оверлей</string>
210 <string name="emulation_pause">Пауза ÑмулÑции</string> 276 <string name="emulation_pause">Пауза ÑмулÑции</string>
211 <string name="emulation_unpause">Возобновление ÑмулÑции</string> 277 <string name="emulation_unpause">Возобновить ÑмулÑцию</string>
212 <string name="emulation_input_overlay">ÐаÑтройки оверлеÑ</string> 278 <string name="emulation_input_overlay">ÐаÑтройка оверлеÑ</string>
213 279
214 <string name="load_settings">Загрузка наÑтроек...</string> 280 <string name="load_settings">Загрузка наÑтроек...</string>
215 281
@@ -226,6 +292,9 @@
226 <string name="fatal_error">Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°</string> 292 <string name="fatal_error">Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°</string>
227 <string name="fatal_error_message">Произошла Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Проверьте журнал Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации.\nПродолжение ÑмулÑции может привеÑти к ÑбоÑм и ошибкам.</string> 293 <string name="fatal_error_message">Произошла Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Проверьте журнал Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации.\nПродолжение ÑмулÑции может привеÑти к ÑбоÑм и ошибкам.</string>
228 <string name="performance_warning">Отключение Ñтой наÑтройки значительно Ñнизит производительноÑть ÑмулÑции! Ð”Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð»ÑƒÑ‡ÑˆÐ¸Ñ… результатов рекомендуетÑÑ Ð¾Ñтавить Ñту наÑтройку включенной.</string> 294 <string name="performance_warning">Отключение Ñтой наÑтройки значительно Ñнизит производительноÑть ÑмулÑции! Ð”Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð»ÑƒÑ‡ÑˆÐ¸Ñ… результатов рекомендуетÑÑ Ð¾Ñтавить Ñту наÑтройку включенной.</string>
295 <string name="device_memory_inadequate">ÐžÐ¿ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ Ð¿Ð°Ð¼Ñть уÑтройÑтва: %1$s\nРекомендовано: %2$s</string>
296 <string name="memory_formatted">%1$s%2$s</string>
297 <string name="no_game_present">Загрузочной игры нету!</string>
229 298
230 <!-- Region Names --> 299 <!-- Region Names -->
231 <string name="region_japan">ЯпониÑ</string> 300 <string name="region_japan">ЯпониÑ</string>
@@ -236,7 +305,14 @@
236 <string name="region_korea">КореÑ</string> 305 <string name="region_korea">КореÑ</string>
237 <string name="region_taiwan">Тайвань</string> 306 <string name="region_taiwan">Тайвань</string>
238 307
239 <!-- Language Names --> 308 <!-- Memory Sizes -->
309 <string name="memory_byte">Байт</string>
310 <string name="memory_kilobyte">КБ</string>
311 <string name="memory_megabyte">МБ</string>
312 <string name="memory_gigabyte">GB</string>
313 <string name="memory_terabyte">ТБ</string>
314 <string name="memory_petabyte">ПБ</string>
315 <string name="memory_exabyte">ЕВ</string>
240 316
241 <!-- Renderer APIs --> 317 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 318 <string name="renderer_vulkan">Vulkan</string>
@@ -274,6 +350,11 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 350 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 351 <string name="anti_aliasing_smaa">SMAA</string>
276 352
353 <!-- Screen Layouts -->
354 <string name="screen_layout_landscape">Пейзаж</string>
355 <string name="screen_layout_portrait">Портрет</string>
356 <string name="screen_layout_auto">Ðвто</string>
357
277 <!-- Aspect Ratios --> 358 <!-- Aspect Ratios -->
278 <string name="ratio_default">Стандартное (16:9)</string> 359 <string name="ratio_default">Стандартное (16:9)</string>
279 <string name="ratio_force_four_three">ЗаÑтавить 4:3</string> 360 <string name="ratio_force_four_three">ЗаÑтавить 4:3</string>
@@ -288,8 +369,8 @@
288 369
289 <!-- Gamepad Buttons --> 370 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">КреÑтовина</string> 371 <string name="gamepad_d_pad">КреÑтовина</string>
291 <string name="gamepad_left_stick">Левый мини-джойÑтик</string> 372 <string name="gamepad_left_stick">Левый Ñтик</string>
292 <string name="gamepad_right_stick">Правый мини-джойÑтик</string> 373 <string name="gamepad_right_stick">Правый Ñтик</string>
293 <string name="gamepad_home">Home</string> 374 <string name="gamepad_home">Home</string>
294 <string name="gamepad_screenshot">Скриншот</string> 375 <string name="gamepad_screenshot">Скриншот</string>
295 376
@@ -298,18 +379,32 @@
298 <string name="building_shaders">ПоÑтройка шейдеров</string> 379 <string name="building_shaders">ПоÑтройка шейдеров</string>
299 380
300 <!-- Theme options --> 381 <!-- Theme options -->
301 <string name="change_app_theme">Изменить тему приложениÑ</string> 382 <string name="change_app_theme">Сменить тему</string>
302 <string name="theme_default">По умолчанию</string> 383 <string name="theme_default">По умолчанию</string>
303 <string name="theme_material_you">Material You</string> 384 <string name="theme_material_you">Material You</string>
304 385
305 <!-- Theme Modes --> 386 <!-- Theme Modes -->
306 <string name="change_theme_mode">Изменить режим темы</string> 387 <string name="change_theme_mode">Сменить режим темы</string>
307 <string name="theme_mode_follow_system">СиÑтемнаÑ</string> 388 <string name="theme_mode_follow_system">СиÑтемнаÑ</string>
308 <string name="theme_mode_light">СветлаÑ</string> 389 <string name="theme_mode_light">СветлаÑ</string>
309 <string name="theme_mode_dark">ТемнаÑ</string> 390 <string name="theme_mode_dark">ТемнаÑ</string>
310 391
392 <!-- Audio output engines -->
393 <string name="cubeb">cubeb</string>
394
311 <!-- Black backgrounds theme --> 395 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">ИÑпользовать черный фон</string> 396 <string name="use_black_backgrounds">ЧÑрный фон</string>
313 <string name="use_black_backgrounds_description">При иÑпользовании темной темы применÑйте черный фон.</string> 397 <string name="use_black_backgrounds_description">При иÑпользовании темной темы применÑйте черный фон.</string>
314 398
315</resources> 399 <!-- Picture-In-Picture -->
400 <string name="picture_in_picture">Картинка в картинке</string>
401 <string name="picture_in_picture_description">Свернуть окно при размещении в фоновом режиме</string>
402 <string name="pause">Пауза</string>
403 <string name="play">Играть</string>
404 <string name="mute">Выключить звук</string>
405 <string name="unmute">Включить звук</string>
406
407 <!-- Licenses screen strings -->
408 <string name="licenses">Лицензии</string>
409 <string name="license_fidelityfx_fsr_description">Ð’Ñ‹ÑококачеÑтвенное маÑштабирование от AMD</string>
410 </resources>
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 5b789ee98..34809dbb8 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Це програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÑÑ” запуÑкати ігри Ð´Ð»Ñ Ñ–Ð³Ñ€Ð¾Ð²Ð¾Ñ— конÑолі Nintendo Switch. Ми не надаємо Ñамі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у Ñховищі приÑтрою.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</a>]]></string> 4 <string name="app_disclaimer">Це програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÑÑ” запуÑкати ігри Ð´Ð»Ñ Ñ–Ð³Ñ€Ð¾Ð²Ð¾Ñ— конÑолі Nintendo Switch. Ми не надаємо Ñамі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у Ñховищі приÑтрою.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</a>]]></string>
5 <string name="emulation_notification_channel_name">ЕмулÑÑ†Ñ–Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string> 5 <string name="emulation_notification_channel_name">ЕмулÑÑ†Ñ–Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string>
@@ -25,7 +25,6 @@
25 <string name="back">Ðазад</string> 25 <string name="back">Ðазад</string>
26 <string name="add_games">Додати ігри</string> 26 <string name="add_games">Додати ігри</string>
27 <string name="add_games_description">Виберіть папку з іграми</string> 27 <string name="add_games_description">Виберіть папку з іграми</string>
28
29 <!-- Home strings --> 28 <!-- Home strings -->
30 <string name="home_games">Ігри</string> 29 <string name="home_games">Ігри</string>
31 <string name="home_search">Пошук</string> 30 <string name="home_search">Пошук</string>
@@ -61,6 +60,7 @@
61 <string name="invalid_keys_file">Вибрано неправильний файл ключів</string> 60 <string name="invalid_keys_file">Вибрано неправильний файл ключів</string>
62 <string name="install_keys_success">Ключі уÑпішно вÑтановлено</string> 61 <string name="install_keys_success">Ключі уÑпішно вÑтановлено</string>
63 <string name="reading_keys_failure">Помилка під Ñ‡Ð°Ñ Ð·Ñ‡Ð¸Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»ÑŽÑ‡Ñ–Ð² шифруваннÑ</string> 62 <string name="reading_keys_failure">Помилка під Ñ‡Ð°Ñ Ð·Ñ‡Ð¸Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»ÑŽÑ‡Ñ–Ð² шифруваннÑ</string>
63 <string name="install_prod_keys_failure_extension_description">ПереконайтеÑÑ, що файл ключів має Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ .keys, Ñ– повторіть Ñпробу.</string>
64 <string name="invalid_keys_error">Ðевірні ключі шифруваннÑ</string> 64 <string name="invalid_keys_error">Ðевірні ключі шифруваннÑ</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь лаÑка, пере-дампіть ваші ключі.</string> 66 <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь лаÑка, пере-дампіть ваші ключі.</string>
@@ -68,8 +68,6 @@
68 <string name="install_gpu_driver_description">Ð’Ñтановіть альтернативні драйвери Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ кращої продуктивноÑті та/або точноÑті</string> 68 <string name="install_gpu_driver_description">Ð’Ñтановіть альтернативні драйвери Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ кращої продуктивноÑті та/або точноÑті</string>
69 <string name="advanced_settings">Розширені налаштуваннÑ</string> 69 <string name="advanced_settings">Розширені налаштуваннÑ</string>
70 <string name="settings_description">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² емулÑтора</string> 70 <string name="settings_description">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² емулÑтора</string>
71 <string name="search_recently_played">Ðещодавно зіграно</string>
72 <string name="search_recently_added">Ðещодавно додано</string>
73 <string name="search_retail">Роздрібні</string> 71 <string name="search_retail">Роздрібні</string>
74 <string name="search_homebrew">Homebrew</string> 72 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Відкрити папку yuzu</string> 73 <string name="open_user_folder">Відкрити папку yuzu</string>
@@ -86,7 +84,6 @@
86 <string name="save_file_invalid_zip_structure_description">Ðазва першої вкладеної папки має бути ідентифікатором гри.</string> 84 <string name="save_file_invalid_zip_structure_description">Ðазва першої вкладеної папки має бути ідентифікатором гри.</string>
87 <string name="import_saves">Імпорт</string> 85 <string name="import_saves">Імпорт</string>
88 <string name="export_saves">ЕкÑпорт</string> 86 <string name="export_saves">ЕкÑпорт</string>
89
90 <!-- About screen strings --> 87 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia не Ñ–Ñнує</string> 88 <string name="gaia_is_not_real">Gaia не Ñ–Ñнує</string>
92 <string name="copied_to_clipboard">Скопійовано в буфер обміну</string> 89 <string name="copied_to_clipboard">Скопійовано в буфер обміну</string>
@@ -113,42 +110,20 @@
113 <string name="our_eternal_gratitude">Ðаша неÑкінченна вдÑчніÑть</string> 110 <string name="our_eternal_gratitude">Ðаша неÑкінченна вдÑчніÑть</string>
114 <string name="are_you_interested">Ви зацікавлені?</string> 111 <string name="are_you_interested">Ви зацікавлені?</string>
115 112
116 <!-- General settings strings -->
117 <string name="frame_limit_enable">Увімкнути Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті</string>
118 <string name="frame_limit_enable_description">Якщо цю функцію ввімкнено, швидкіÑть емулÑції буде обмежена зазначеним відÑотком від нормальної швидкоÑті.</string>
119 <string name="frame_limit_slider">ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñотка швидкоÑті</string> 113 <string name="frame_limit_slider">ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñотка швидкоÑті</string>
120 <string name="frame_limit_slider_description">Вказує відÑоток Ð´Ð»Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті емулÑції. При значенні за замовчуваннÑм 100% емулÑÑ†Ñ–Ñ Ð±ÑƒÐ´Ðµ обмежена нормальною швидкіÑтю. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¸Ñ‰Ðµ або нижче збільшуватимуть або зменшуватимуть Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті.</string>
121 <string name="cpu_accuracy">ТочніÑть ЦП</string> 114 <string name="cpu_accuracy">ТочніÑть ЦП</string>
122
123 <!-- System settings strings -->
124 <string name="use_docked_mode">Режим док-Ñтанції</string>
125 <string name="use_docked_mode_description">ЕмулÑÑ†Ñ–Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ док-Ñтанції, що збільшує роздільну здатніÑть за рахунок Ð·Ð½Ð¸Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñті.</string>
126 <string name="emulated_region">Емульований регіон</string> 115 <string name="emulated_region">Емульований регіон</string>
127 <string name="emulated_language">Емульована мова</string> 116 <string name="emulated_language">Емульована мова</string>
128 <string name="select_rtc_date">Оберіть дату RTC</string> 117 <string name="use_custom_rtc">КориÑтувацький RTC</string>
129 <string name="select_rtc_time">Оберіть Ñ‡Ð°Ñ RTC</string>
130 <string name="use_custom_rtc">Увімкнути кориÑтувацький RTC</string>
131 <string name="use_custom_rtc_description">Цей параметр дає змогу вÑтановити кориÑтувацький годинник реального чаÑу окремо від поточного ÑиÑтемного чаÑу</string>
132 <string name="set_custom_rtc">Ð’Ñтановити кориÑтувацький RTC</string>
133
134 <!-- Graphics settings strings --> 118 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">Рівень точноÑті</string> 119 <string name="renderer_accuracy">Рівень точноÑті</string>
137 <string name="renderer_resolution">Роздільна здатніÑть</string>
138 <string name="renderer_vsync">Режим верт. Ñинхронізації</string> 120 <string name="renderer_vsync">Режим верт. Ñинхронізації</string>
139 <string name="renderer_aspect_ratio">Ð¡Ð¿Ñ–Ð²Ð²Ñ–Ð´Ð½Ð¾ÑˆÐµÐ½Ð½Ñ Ñторін</string>
140 <string name="renderer_scaling_filter">Фільтр адаптації вікна</string>
141 <string name="renderer_anti_aliasing">Метод згладжуваннÑ</string>
142 <string name="renderer_force_max_clock">ПримуÑово змуÑити макÑимальну тактову чаÑтоту (тільки Ð´Ð»Ñ Adreno)</string> 121 <string name="renderer_force_max_clock">ПримуÑово змуÑити макÑимальну тактову чаÑтоту (тільки Ð´Ð»Ñ Adreno)</string>
143 <string name="renderer_force_max_clock_description">Змушує ГП працювати на макÑимально можливих тактових чаÑтотах (теплові Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñе одно будуть заÑтоÑовуватиÑÑ).</string> 122 <string name="renderer_force_max_clock_description">Змушує ГП працювати на макÑимально можливих тактових чаÑтотах (теплові Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñе одно будуть заÑтоÑовуватиÑÑ).</string>
144 <string name="renderer_asynchronous_shaders">ВикориÑтовувати аÑинхронні шейдери</string> 123 <string name="renderer_asynchronous_shaders">ВикориÑтовувати аÑинхронні шейдери</string>
145 <string name="renderer_asynchronous_shaders_description">Компілює шейдери аÑинхронно, що зменшує завиÑаннÑ, але може натоміÑть надати візуальні баги.</string> 124 <!-- Debug settings strings -->
146 <string name="renderer_debug">Увімкнути Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð³Ñ€Ð°Ñ„Ñ–ÐºÐ¸</string> 125 <string name="cpu">ЦП</string>
147 <string name="renderer_debug_description">Якщо увімкнено, графічний API переходить у повільніший режим налагодженнÑ</string> 126 <string name="renderer_api">API</string>
148 <string name="use_disk_shader_cache">ВикориÑтовувати кеш шейдерів на диÑку</string>
149 <string name="use_disk_shader_cache_description">Ð—Ð¼ÐµÐ½ÑˆÐµÐ½Ð½Ñ Ð·Ð°Ð²Ð¸Ñань завдÑки зберіганню та завантаженню згенерованих шейдерів на Ñховище.</string>
150
151 <!-- Audio settings strings -->
152 <string name="audio_volume">ГучніÑть</string> 127 <string name="audio_volume">ГучніÑть</string>
153 <string name="audio_volume_description">Вказує гучніÑть аудіовиходу.</string> 128 <string name="audio_volume_description">Вказує гучніÑть аудіовиходу.</string>
154 129
@@ -161,17 +136,20 @@
161 <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм?</string> 136 <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм?</string>
162 <string name="reset_to_default">Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð´Ð¾ налаштувань за замовчуваннÑм</string> 137 <string name="reset_to_default">Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð´Ð¾ налаштувань за замовчуваннÑм</string>
163 <string name="reset_all_settings">Скинути вÑÑ– налаштуваннÑ</string> 138 <string name="reset_all_settings">Скинути вÑÑ– налаштуваннÑ</string>
164 <string name="reset_all_settings_description">УÑÑ– додаткові Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ Ñкинуто до Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° замовчуваннÑм. Це неможливо ÑкаÑувати.</string>
165 <string name="settings_reset">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñкинуто</string> 139 <string name="settings_reset">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñкинуто</string>
166 <string name="close">Закрити</string> 140 <string name="close">Закрити</string>
167 <string name="learn_more">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</string> 141 <string name="learn_more">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</string>
168 142 <string name="auto">Ðвто</string>
143 <string name="string_null">Null</string>
144 <string name="string_import">Імпорт</string>
145 <string name="export">ЕкÑпорт</string>
169 <!-- GPU driver installation --> 146 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">Вибрати драйвер ГП</string> 147 <string name="select_gpu_driver">Вибрати драйвер ГП</string>
171 <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> 148 <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
172 <string name="select_gpu_driver_install">Ð’Ñтановити</string> 149 <string name="select_gpu_driver_install">Ð’Ñтановити</string>
173 <string name="select_gpu_driver_default">За замовчуваннÑм</string> 150 <string name="select_gpu_driver_default">За замовчуваннÑм</string>
174 <string name="select_gpu_driver_use_default">ВикориÑтовуєтьÑÑ Ñтандартний драйвер ГП</string> 151 <string name="select_gpu_driver_use_default">ВикориÑтовуєтьÑÑ Ñтандартний драйвер ГП</string>
152 <string name="select_gpu_driver_error">Обрано неправильний драйвер, викориÑтовуєтьÑÑ Ñтандартний ÑиÑтемний!</string>
175 <string name="system_gpu_driver">СиÑтемний драйвер ГП</string> 153 <string name="system_gpu_driver">СиÑтемний драйвер ГП</string>
176 <string name="installing_driver">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð°...</string> 154 <string name="installing_driver">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð°...</string>
177 155
@@ -182,40 +160,19 @@
182 <string name="preferences_graphics">Графіка</string> 160 <string name="preferences_graphics">Графіка</string>
183 <string name="preferences_audio">Ðудіо</string> 161 <string name="preferences_audio">Ðудіо</string>
184 <string name="preferences_theme">Тема і колір</string> 162 <string name="preferences_theme">Тема і колір</string>
163 <string name="preferences_debug">ÐалагодженнÑ</string>
185 164
186 <!-- ROM loading errors --> 165 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">Ваш ROM зашифрований</string> 166 <string name="loader_error_encrypted">Ваш ROM зашифрований</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[Будь лаÑка, дотримуйтеÑÑŒ інÑтрукцій, щоб пере-дампити ваші <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">ігрові картриджі</a> або <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">вÑтановлені ігри</a>.]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[Будь лаÑка, переконайтеÑÑ, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> вÑтановлено, щоб ігри можна було розшифрувати.]]></string> 167 <string name="loader_error_encrypted_keys_description"><![CDATA[Будь лаÑка, переконайтеÑÑ, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> вÑтановлено, щоб ігри можна було розшифрувати.]]></string>
190 <string name="loader_error_video_core">СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ— відеоÑдра.</string> 168 <string name="loader_error_video_core">СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ— відеоÑдра.</string>
191 <string name="loader_error_video_core_description">Зазвичай це Ñпричинено неÑуміÑним драйвером ГП. Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувацького драйвера ГП може вирішити цю проблему.</string> 169 <string name="loader_error_video_core_description">Зазвичай це Ñпричинено неÑуміÑним драйвером ГП. Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувацького драйвера ГП може вирішити цю проблему.</string>
192 <string name="loader_error_invalid_format">Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити ROM</string> 170 <string name="loader_error_invalid_format">Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити ROM</string>
193 <string name="loader_error_file_not_found">Файл ROM не Ñ–Ñнує</string> 171 <string name="loader_error_file_not_found">Файл ROM не Ñ–Ñнує</string>
194 172
195 <!-- Emulation Menu -->
196 <string name="emulation_exit">Вихід з емулÑції</string>
197 <string name="emulation_done">Готово</string> 173 <string name="emulation_done">Готово</string>
198 <string name="emulation_fps_counter">Лічильник FPS</string>
199 <string name="emulation_toggle_controls">ÐŸÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ</string>
200 <string name="emulation_rel_stick_center">ВідноÑний центр Ñтіка</string>
201 <string name="emulation_dpad_slide">Слайд хреÑтовиною</string>
202 <string name="emulation_haptics">Тактильний зворотний зв\'Ñзок</string>
203 <string name="emulation_show_overlay">Показати оверлей</string>
204 <string name="emulation_toggle_all">Перемкнути вÑе</string>
205 <string name="emulation_control_adjust">Ðалаштувати оверлей</string>
206 <string name="emulation_control_scale">МаÑштаб</string> 174 <string name="emulation_control_scale">МаÑштаб</string>
207 <string name="emulation_control_opacity">ÐепрозоріÑть</string> 175 <string name="emulation_control_opacity">ÐепрозоріÑть</string>
208 <string name="emulation_touch_overlay_reset">Скинути оверлей</string>
209 <string name="emulation_touch_overlay_edit">Змінити оверлей</string>
210 <string name="emulation_pause">Пауза емулÑції</string>
211 <string name="emulation_unpause">Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»Ñції</string>
212 <string name="emulation_input_overlay">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð²ÐµÑ€Ð»ÐµÑ</string>
213
214 <string name="load_settings">Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ...</string>
215
216 <!-- Software keyboard -->
217 <string name="software_keyboard">Віртуальна клавіатура</string>
218
219 <!-- Errors and warnings --> 176 <!-- Errors and warnings -->
220 <string name="abort_button">Перервати</string> 177 <string name="abort_button">Перервати</string>
221 <string name="continue_button">Продовжити</string> 178 <string name="continue_button">Продовжити</string>
@@ -226,7 +183,6 @@
226 <string name="fatal_error">Фатальна помилка</string> 183 <string name="fatal_error">Фатальна помилка</string>
227 <string name="fatal_error_message">СталаÑÑ Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð° помилка. Перевірте журнал Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾ÐºÐ»Ð°Ð´Ð½Ð¾Ñ— інформації.\nÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»Ñції може призвеÑти до збоїв Ñ– помилок.</string> 184 <string name="fatal_error_message">СталаÑÑ Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð° помилка. Перевірте журнал Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾ÐºÐ»Ð°Ð´Ð½Ð¾Ñ— інформації.\nÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»Ñції може призвеÑти до збоїв Ñ– помилок.</string>
228 <string name="performance_warning">Ð’Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡Ð½Ð¾ знизить продуктивніÑть емулÑції! Ð”Ð»Ñ Ð´Ð¾ÑÑÐ³Ð½ÐµÐ½Ð½Ñ Ð½Ð°Ð¹ÐºÑ€Ð°Ñ‰Ð¸Ñ… результатів рекомендуєтьÑÑ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ñ‚Ð¸ це Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¸Ð¼.</string> 185 <string name="performance_warning">Ð’Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡Ð½Ð¾ знизить продуктивніÑть емулÑції! Ð”Ð»Ñ Ð´Ð¾ÑÑÐ³Ð½ÐµÐ½Ð½Ñ Ð½Ð°Ð¹ÐºÑ€Ð°Ñ‰Ð¸Ñ… результатів рекомендуєтьÑÑ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ñ‚Ð¸ це Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¸Ð¼.</string>
229
230 <!-- Region Names --> 186 <!-- Region Names -->
231 <string name="region_japan">ЯпоніÑ</string> 187 <string name="region_japan">ЯпоніÑ</string>
232 <string name="region_usa">СШÐ</string> 188 <string name="region_usa">СШÐ</string>
@@ -236,8 +192,7 @@
236 <string name="region_korea">КореÑ</string> 192 <string name="region_korea">КореÑ</string>
237 <string name="region_taiwan">Тайвань</string> 193 <string name="region_taiwan">Тайвань</string>
238 194
239 <!-- Language Names --> 195 <string name="memory_gigabyte">GB</string>
240
241 <!-- Renderer APIs --> 196 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 197 <string name="renderer_vulkan">Vulkan</string>
243 <string name="renderer_none">Вимкнено</string> 198 <string name="renderer_none">Вимкнено</string>
@@ -274,22 +229,18 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 229 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 230 <string name="anti_aliasing_smaa">SMAA</string>
276 231
232 <string name="screen_layout_auto">Ðвто</string>
233
277 <!-- Aspect Ratios --> 234 <!-- Aspect Ratios -->
278 <string name="ratio_default">За замовчуваннÑм (16:9)</string> 235 <string name="ratio_default">За замовчуваннÑм (16:9)</string>
279 <string name="ratio_force_four_three">ЗмуÑити 4:3</string> 236 <string name="ratio_force_four_three">ЗмуÑити 4:3</string>
280 <string name="ratio_force_twenty_one_nine">ЗмуÑити 21:9</string> 237 <string name="ratio_force_twenty_one_nine">ЗмуÑити 21:9</string>
281 <string name="ratio_force_sixteen_ten">ЗмуÑити 16:10</string> 238 <string name="ratio_force_sixteen_ten">ЗмуÑити 16:10</string>
282 <string name="ratio_stretch">РозтÑгнути до вікна</string>
283
284 <!-- CPU Accuracy --> 239 <!-- CPU Accuracy -->
285 <string name="cpu_accuracy_accurate">Точно</string> 240 <string name="cpu_accuracy_accurate">Точно</string>
286 <string name="cpu_accuracy_unsafe">Ðебезпечно</string> 241 <string name="cpu_accuracy_unsafe">Ðебезпечно</string>
287 <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string> 242 <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string>
288 243
289 <!-- Gamepad Buttons -->
290 <string name="gamepad_d_pad">Кнопки напрÑмків</string>
291 <string name="gamepad_left_stick">Лівий міні-джойÑтик</string>
292 <string name="gamepad_right_stick">Правий міні-джойÑтик</string>
293 <string name="gamepad_home">Home</string> 244 <string name="gamepad_home">Home</string>
294 <string name="gamepad_screenshot">Знімок екрану</string> 245 <string name="gamepad_screenshot">Знімок екрану</string>
295 246
@@ -297,19 +248,16 @@
297 <string name="preparing_shaders">Підготовка шейдерів</string> 248 <string name="preparing_shaders">Підготовка шейдерів</string>
298 <string name="building_shaders">Побудова шейдерів</string> 249 <string name="building_shaders">Побудова шейдерів</string>
299 250
300 <!-- Theme options -->
301 <string name="change_app_theme">Змінити тему заÑтоÑунку</string>
302 <string name="theme_default">За замовчуваннÑм</string> 251 <string name="theme_default">За замовчуваннÑм</string>
303 <string name="theme_material_you">Material You</string> 252 <string name="theme_material_you">Material You</string>
304 253
305 <!-- Theme Modes -->
306 <string name="change_theme_mode">Змінити режим теми</string>
307 <string name="theme_mode_follow_system">СиÑтемна</string> 254 <string name="theme_mode_follow_system">СиÑтемна</string>
308 <string name="theme_mode_light">Світла</string> 255 <string name="theme_mode_light">Світла</string>
309 <string name="theme_mode_dark">Темна</string> 256 <string name="theme_mode_dark">Темна</string>
310 257
311 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">ВикориÑтовувати чорне тло</string>
313 <string name="use_black_backgrounds_description">У разі викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚ÐµÐ¼Ð½Ð¾Ñ— теми заÑтоÑовуйте чорне тло.</string> 258 <string name="use_black_backgrounds_description">У разі викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚ÐµÐ¼Ð½Ð¾Ñ— теми заÑтоÑовуйте чорне тло.</string>
314 259
315</resources> 260 <string name="mute">Вимкнути звук</string>
261 <string name="unmute">Увімкнути звук</string>
262
263 </resources>
diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml
new file mode 100644
index 000000000..f977db3a2
--- /dev/null
+++ b/src/android/app/src/main/res/values-vi/strings.xml
@@ -0,0 +1,340 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3
4 <string name="app_disclaimer">Phần má»m này sẽ chạy các game cho máy chÆ¡i game Nintendo Switch. Không có title games hoặc keys được bao gồm.&lt;br /&gt;&lt;br /&gt;Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bá»™ nhá»› thiết bị cá»§a bạn.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string>
5 <string name="emulation_notification_channel_name">Giả lập đang chạy</string>
6 <string name="emulation_notification_channel_description">Hiển thị thông báo liên tục khi giả lập đang chạy.</string>
7 <string name="emulation_notification_running">yuzu đang chạy</string>
8 <string name="notice_notification_channel_name">Thông báo và lỗi</string>
9 <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string>
10 <string name="notification_permission_not_granted">Ứng dụng không được cấp quyá»n thông báo!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Chào mừng!</string>
14 <string name="welcome_description">Tìm hiểu cách cài đặt &lt;b>yuzu&lt;/b> và bắt đầu giả lập.</string>
15 <string name="get_started">Bắt đầu</string>
16 <string name="keys">Keys</string>
17 <string name="keys_description">Chá»n tệp &lt;b>prod.keys&lt;/b> cá»§a bạn bằng nút bên dưới.</string>
18 <string name="select_keys">Chá»n Keys</string>
19 <string name="games">Game</string>
20 <string name="games_description">Chá»n thư mục &lt;b>Game&lt;/b> cá»§a bạn bằng nút bên dưới.</string>
21 <string name="done">Hoàn thành</string>
22 <string name="done_description">Tất cả đã hoàn tất.\nHãy tận hưởng các game của bạn!</string>
23 <string name="text_continue">Tiếp tục</string>
24 <string name="next">Tiếp theo</string>
25 <string name="back">Trở lại</string>
26 <string name="add_games">Thêm Game</string>
27 <string name="add_games_description">Chá»n thư mục game cá»§a bạn</string>
28 <!-- Home strings -->
29 <string name="home_games">Game</string>
30 <string name="home_search">Tìm kiếm</string>
31 <string name="home_settings">Cài đặt</string>
32 <string name="empty_gamelist">Không tìm thấy tập tin hoặc chưa có thư mục game nào được chá»n.</string>
33 <string name="search_and_filter_games">Tìm và lá»c game</string>
34 <string name="select_games_folder">Chá»n thư mục game</string>
35 <string name="select_games_folder_description">Cho phép yuzu thêm vào danh sách game</string>
36 <string name="add_games_warning">Bá» qua việc lá»±a chá»n thư mục game?</string>
37 <string name="add_games_warning_description">Game sẽ không hiển thị trong danh sách nếu má»™t thư mục không được chá»n.</string>
38 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
39 <string name="home_search_games">Tìm kiếm game</string>
40 <string name="games_dir_selected">Thư mục game đã được chá»n</string>
41 <string name="install_prod_keys">Cài đặt prod.keys</string>
42 <string name="install_prod_keys_description">Yêu cầu để giải mã các game bán lẻ</string>
43 <string name="install_prod_keys_warning">BỠqua việc thêm keys?</string>
44 <string name="install_prod_keys_warning_description">Cần có keys hợp lệ để giả lập các game bán lẻ. Chỉ có các ứng dụng homebrew có thể vận hành nếu bạn tiếp tục.</string>
45 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
46 <string name="notifications">Thông báo</string>
47 <string name="notifications_description">Cấp quyá»n thông báo bằng nút bên dưới.</string>
48 <string name="give_permission">Cấp quyá»n</string>
49 <string name="notification_warning">Bá» qua việc cấp quyá»n thông báo?</string>
50 <string name="notification_warning_description">yuzu sẽ không thể gá»­i những thông báo quan trá»ng đến bạn.</string>
51 <string name="permission_denied">Äã từ chối cấp quyá»n</string>
52 <string name="permission_denied_description">Bạn từ chối cấp quyá»n này quá nhiá»u lần và giá» bạn phải cấp quyá»n thá»§ công trong cài đặt máy.</string>
53 <string name="about">Thông tin</string>
54 <string name="about_description">Phiên bản, đóng góp và những thứ khác</string>
55 <string name="warning_help">Trợ giúp</string>
56 <string name="warning_skip">Bá» qua</string>
57 <string name="warning_cancel">Há»§y bá»</string>
58 <string name="install_amiibo_keys">Cài đặt keys Amiibo</string>
59 <string name="install_amiibo_keys_description">Cần thiết để dùng Amiibo trong game</string>
60 <string name="invalid_keys_file">Tệp keys không hợp lệ đã được chá»n</string>
61 <string name="install_keys_success">Cài đặt keys thành công</string>
62 <string name="reading_keys_failure">Lá»—i Ä‘á»c keys mã hóa</string>
63 <string name="install_prod_keys_failure_extension_description">Xác minh rằng tệp keys của bạn có đuôi .keys và thử lại.</string>
64 <string name="install_amiibo_keys_failure_extension_description">Xác minh rằng tệp keys của bạn có đuôi .bin và thử lại.</string>
65 <string name="invalid_keys_error">Keys mã hoá không hợp lệ</string>
66 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
67 <string name="install_keys_failure_description">Tệp đã chá»n sai hoặc há»ng. Vui lòng trích xuất lại keys cá»§a bạn.</string>
68 <string name="install_gpu_driver">Cài đặt driver GPU</string>
69 <string name="install_gpu_driver_description">Cài đặt driver thay thế để có thể có hiệu suất tốt và chính xác hơn</string>
70 <string name="advanced_settings">Cài đặt nâng cao</string>
71 <string name="settings_description">Cấu hình cài đặt giả lập</string>
72 <string name="search_recently_played">Äã chÆ¡i gần đây</string>
73 <string name="search_recently_added">Äã thêm gần đây</string>
74 <string name="search_retail">Bán lẻ</string>
75 <string name="search_homebrew">Homebrew</string>
76 <string name="open_user_folder">Mở thư mục yuzu</string>
77 <string name="open_user_folder_description">Quản lý tệp nội bộ của yuzu</string>
78 <string name="theme_and_color_description">Thay đổi giao diện ứng dụng</string>
79 <string name="no_file_manager">Không tìm thấy trình quản lý tập tin</string>
80 <string name="notification_no_directory_link">Không thể mở thư mục yuzu</string>
81 <string name="notification_no_directory_link_description">Vui lòng xác định thư mục ngưá»i dùng vá»›i bảng Ä‘iá»u khiển bên cá»§a trình quản lý tệp thá»§ công.</string>
82 <string name="manage_save_data">Quản lý dữ liệu save</string>
83 <string name="manage_save_data_description">Äã tìm thấy dữ liệu save. Vui lòng chá»n má»™t tuỳ chá»n bên dưới.</string>
84 <string name="import_export_saves_description">Nhập hoặc xuất tệp save</string>
85 <string name="save_file_imported_success">Nhập thành công</string>
86 <string name="save_file_invalid_zip_structure">Cấu trúc thư mục save không hợp lệ</string>
87 <string name="save_file_invalid_zip_structure_description">Tên thư mục con đầu tiên phải là ID title của game.</string>
88 <string name="import_saves">Nhập</string>
89 <string name="export_saves">Xuất</string>
90 <string name="install_firmware">Cài đặt firmware</string>
91 <string name="install_firmware_description">Firmware phải được đặt trong một tập tin nén ZIP và cần thiết để khởi chạy một số game</string>
92 <string name="firmware_installing">Äang cài đặt firmware</string>
93 <string name="firmware_installed_success">Cài đặt firmware thành công</string>
94 <string name="firmware_installed_failure">Cài đặt firmware thất bại</string>
95 <string name="share_log">Chia sẻ nhật ký gỡ lỗi</string>
96 <string name="share_log_description">Chia sẻ tập tin nhật ký cá»§a yuzu để gỡ lá»—i vấn Ä‘á»</string>
97 <string name="share_log_missing">Không tìm thấy tập tin nhật ký</string>
98 <string name="install_game_content">Cài đặt nội dung game</string>
99 <string name="install_game_content_description">Cài đặt cập nhật game hoặc DLC</string>
100 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
101 <!-- About screen strings -->
102 <string name="gaia_is_not_real">Gaia không có thật</string>
103 <string name="copied_to_clipboard">Äã sao chép vào bá»™ nhá»› tạm</string>
104 <string name="about_app_description">Một giả lập Switch mã nguồn mở</string>
105 <string name="contributors">Ngưá»i đóng góp</string>
106 <string name="contributors_description">ÄÆ°á»£c làm vá»›i \u2764 từ nhóm yuzu</string>
107 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
108 <string name="licenses_description">Các dá»± án làm cho yuzu trên Android trở thành Ä‘iá»u có thể</string>
109 <string name="build">Dá»±ng</string>
110 <string name="support_link">https://discord.gg/u77vRWY</string>
111 <string name="website_link">https://yuzu-emu.org/</string>
112 <string name="github_link">https://github.com/yuzu-emu</string>
113
114 <!-- Early access upgrade strings -->
115 <string name="early_access">Early Access</string>
116 <string name="get_early_access">Tải Early Access</string>
117 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
118 <string name="get_early_access_description">Các tính năng tiên tiến, truy cập sá»›m các bản cập nhật và nhiá»u hÆ¡n nữa</string>
119 <string name="early_access_benefits">Lợi ích của Early Access</string>
120 <string name="cutting_edge_features">Tính năng tiên tiến</string>
121 <string name="early_access_updates">Truy cập sớm các bản cập nhật</string>
122 <string name="no_manual_installation">Không có cài đặt thủ công</string>
123 <string name="prioritized_support">Ưu tiên hỗ trợ</string>
124 <string name="helping_game_preservation">Hỗ trợ bảo tồn game</string>
125 <string name="our_eternal_gratitude">Sự biết ơn vô hạn của chúng tôi</string>
126 <string name="are_you_interested">Bạn có thấy hứng thú không?</string>
127
128 <!-- General settings strings -->
129 <string name="frame_limit_enable">Giới hạn tốc độ</string>
130 <string name="frame_limit_enable_description">Giá»›i hạn tốc độ giả lập ở má»™t phần trăm cụ thể cá»§a tốc độ bình thưá»ng.</string>
131 <string name="frame_limit_slider">Giới hạn phần trăm tốc độ</string>
132 <string name="frame_limit_slider_description">Xác định phần trăm để giá»›i hạn tốc độ giả lập. 100% là tốc độ bình thưá»ng. Giá trị cao hÆ¡n hoặc thấp hÆ¡n sẽ tăng hoặc giảm giá»›i hạn tốc độ.</string>
133 <string name="cpu_accuracy">Äá»™ chính xác CPU</string>
134 <!-- System settings strings -->
135 <string name="use_docked_mode">Chế độ docked</string>
136 <string name="use_docked_mode_description">Tăng độ phân giải, giảm hiệu suất. Chế độ handheld được sử dụng khi tắt, giảm độ phân giải và tăng hiệu suất.</string>
137 <string name="emulated_region">Khu vực giả lập</string>
138 <string name="emulated_language">Ngôn ngữ giả lập</string>
139 <string name="select_rtc_date">Chá»n ngày RTC</string>
140 <string name="select_rtc_time">Chá»n giá» RTC</string>
141 <string name="use_custom_rtc">RTC tuỳ chỉnh</string>
142 <string name="use_custom_rtc_description">Cho phép bạn thiết lập má»™t đồng hồ thá»i gian thá»±c tùy chỉnh riêng biệt so vá»›i thá»i gian hệ thống hiện tại.</string>
143 <string name="set_custom_rtc">Thiết lập RTC tùy chỉnh</string>
144
145 <!-- Graphics settings strings -->
146 <string name="renderer_accuracy">Mức độ chính xác</string>
147 <string name="renderer_resolution">Äá»™ phân giải (Handheld/Docked)</string>
148 <string name="renderer_vsync">Chế độ VSync</string>
149 <string name="renderer_aspect_ratio">Tỉ lệ khung hình</string>
150 <string name="renderer_scaling_filter">Bá»™ lá»c Ä‘iá»u chỉnh cá»­a sổ</string>
151 <string name="renderer_anti_aliasing">Phương pháp khử răng cưa</string>
152 <string name="renderer_force_max_clock">Buộc chạy ở xung nhịp tối đa (chỉ cho Adreno)</string>
153 <string name="renderer_force_max_clock_description">Buộc GPU hoạt động ở xung nhịp tối đa có thể (ràng buộc nhiệt độ vẫn sẽ được áp dụng).</string>
154 <string name="renderer_asynchronous_shaders">Dùng các shader bất đồng bộ</string>
155 <string name="renderer_asynchronous_shaders_description">Biên dịch các shader bất đồng bộ, giảm tình trạng giật lag nhưng có thể gây ra các lỗi.</string>
156 <string name="renderer_reactive_flushing">Dùng xả tương ứng</string>
157 <string name="renderer_reactive_flushing_description">Cải thiện độ chính xác kết xuất trong má»™t số game nhưng đồng thá»i giảm hiệu suất.</string>
158 <string name="use_disk_shader_cache">Lưu bộ nhớ đệm shader trên ổ cứng</string>
159 <string name="use_disk_shader_cache_description">Giảm tình trạng giật lag bằng cách lưu trữ và tải các shader được tạo ra nội bộ.</string>
160
161 <!-- Debug settings strings -->
162 <string name="cpu">CPU</string>
163 <string name="renderer_api">API</string>
164 <string name="renderer_debug">Gỡ lỗi đồ hoạ</string>
165 <string name="renderer_debug_description">Äặt API đồ há»a vào chế độ gỡ lá»—i chậm.</string>
166 <string name="audio_volume">Âm lượng</string>
167 <string name="audio_volume_description">Xác định âm lượng của đầu ra âm thanh.</string>
168
169 <!-- Miscellaneous -->
170 <string name="slider_default">Mặc định</string>
171 <string name="ini_saved">Cài đặt đã lưu</string>
172 <string name="gameid_saved">Cài đặt đã lưu cho %1$s</string>
173 <string name="error_saving">Lỗi khi lưu %1$s.ini: %2$s</string>
174 <string name="loading">Äang tải...</string>
175 <string name="reset_setting_confirmation">Bạn có muốn đặt lại cài đặt này vỠgiá trị mặc định không?</string>
176 <string name="reset_to_default">Äặt lại vá» mặc định</string>
177 <string name="reset_all_settings">Bạn có muốn đặt lại tất cả các cài đặt vỠgiá trị mặc định không?</string>
178 <string name="reset_all_settings_description">Tất cả các cài đặt nâng cao sẽ được đặt lại vá» cấu hình mặc định. Äiá»u này không thể hoàn tác.</string>
179 <string name="settings_reset">Cài đặt đã được đặt lại</string>
180 <string name="close">Äóng</string>
181 <string name="learn_more">Tìm hiểu thêm</string>
182 <string name="auto">Tự động</string>
183 <string name="submit">Gá»­i</string>
184 <string name="string_null">Null</string>
185 <string name="string_import">Nhập</string>
186 <string name="export">Xuất</string>
187 <!-- GPU driver installation -->
188 <string name="select_gpu_driver">Chá»n driver GPU</string>
189 <string name="select_gpu_driver_title">Bạn có muốn thay thế driver GPU hiện tại không?</string>
190 <string name="select_gpu_driver_install">Cài đặt</string>
191 <string name="select_gpu_driver_default">Mặc định</string>
192 <string name="select_gpu_driver_use_default">Dùng driver GPU mặc định</string>
193 <string name="select_gpu_driver_error">Driver không hợp lệ đã được chá»n, dùng mặc định hệ thống!</string>
194 <string name="system_gpu_driver">Driver GPU hệ thống</string>
195 <string name="installing_driver">Äang cài đặt driver...</string>
196
197 <!-- Preferences Screen -->
198 <string name="preferences_settings">Cài đặt</string>
199 <string name="preferences_general">Chung</string>
200 <string name="preferences_system">Hệ thống</string>
201 <string name="preferences_graphics">Äồ hoạ</string>
202 <string name="preferences_audio">Âm thanh</string>
203 <string name="preferences_theme">Chủ đỠvà màu sắc</string>
204 <string name="preferences_debug">Gỡ lỗi</string>
205
206 <!-- ROM loading errors -->
207 <string name="loader_error_encrypted">ROM của bạn đã bị mã hoá</string>
208 <string name="loader_error_encrypted_keys_description"><![CDATA[Vui lòng đảm bảo tệp <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> đã được cài đặt để các game có thể được giải mã.]]></string>
209 <string name="loader_error_video_core">Äã xảy ra lá»—i khi khởi tạo lõi video</string>
210 <string name="loader_error_video_core_description">Việc này thưá»ng do driver GPU không tương thích. Cài đặt má»™t driver GPU tùy chỉnh có thể giải quyết vấn đỠnày.</string>
211 <string name="loader_error_invalid_format">Không thể nạp ROM</string>
212 <string name="loader_error_file_not_found">Tệp ROM không tồn tại</string>
213
214 <!-- Emulation Menu -->
215 <string name="emulation_exit">Thoát giả lập</string>
216 <string name="emulation_done">Hoàn thành</string>
217 <string name="emulation_fps_counter">Bộ đếm FPS</string>
218 <string name="emulation_toggle_controls">Chuyển đổi Ä‘iá»u khiển</string>
219 <string name="emulation_rel_stick_center">Trung tâm nút cần xoay tương đối</string>
220 <string name="emulation_dpad_slide">Trượt D-pad</string>
221 <string name="emulation_haptics">Chạm haptics</string>
222 <string name="emulation_show_overlay">Hiện lớp phủ</string>
223 <string name="emulation_toggle_all">Chuyển đổi tất cả</string>
224 <string name="emulation_control_adjust">Äiá»u chỉnh lá»›p phá»§</string>
225 <string name="emulation_control_scale">Tỉ lệ thu phóng</string>
226 <string name="emulation_control_opacity">Äá»™ má»</string>
227 <string name="emulation_touch_overlay_reset">Äặt lại lá»›p phá»§</string>
228 <string name="emulation_touch_overlay_edit">Chỉnh sửa lớp phủ</string>
229 <string name="emulation_pause">Tạm đừng giả lập</string>
230 <string name="emulation_unpause">Tiếp tục giả lập</string>
231 <string name="emulation_input_overlay">Tuỳ chá»n lá»›p phá»§</string>
232
233 <string name="load_settings">Äang tải cài đặt...</string>
234
235 <!-- Software keyboard -->
236 <string name="software_keyboard">Bàn phím má»m</string>
237
238 <!-- Errors and warnings -->
239 <string name="abort_button">Há»§y bá»</string>
240 <string name="continue_button">Tiếp tục</string>
241 <string name="system_archive_not_found">Không tìm thấy bản lưu trữ của hệ thống</string>
242 <string name="system_archive_not_found_message">%s bị thiếu. Vui lòng trích xuất các bản lưu trữ hệ thống của bạn.\nNếu chạy tiếp giả lập có thể bị crash và lỗi.</string>
243 <string name="system_archive_general">Một bản lưu trữ của hệ thống</string>
244 <string name="save_load_error">Lỗi Lưu/Tải</string>
245 <string name="fatal_error">Lá»—i nghiêm trá»ng</string>
246 <string name="fatal_error_message">Äã xảy ra lá»—i nghiêm trá»ng. Kiểm tra nhật ký để biết thêm chi tiết.\nNếu chạy tiếp giả lập có thể bị crash và lá»—i.</string>
247 <string name="performance_warning">Tắt cài đặt này sẽ làm giảm đáng kể hiệu suất giả lập! Äể có trải nghiệm tốt nhất, bạn nên bật cài đặt này.</string>
248 <!-- Region Names -->
249 <string name="region_japan">Nhật Bản</string>
250 <string name="region_usa">Hoa Kỳ</string>
251 <string name="region_europe">Châu Âu</string>
252 <string name="region_australia">Úc</string>
253 <string name="region_china">Trung Quốc</string>
254 <string name="region_korea">Hàn Quốc</string>
255 <string name="region_taiwan">Äài Loan</string>
256
257 <string name="memory_gigabyte">GB</string>
258 <!-- Renderer APIs -->
259 <string name="renderer_vulkan">Vulkan</string>
260 <string name="renderer_none">Không có</string>
261
262 <!-- Renderer Accuracy -->
263 <string name="renderer_accuracy_normal">Bình thưá»ng</string>
264 <string name="renderer_accuracy_high">Cao</string>
265 <string name="renderer_accuracy_extreme">Cực đại (Chậm)</string>
266
267 <!-- Resolutions -->
268 <string name="resolution_half">0.5X (360p/540p)</string>
269 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
270 <string name="resolution_one">1X (720p/1080p)</string>
271 <string name="resolution_two">2X (1440p/2160p) (Chậm)</string>
272 <string name="resolution_three">3X (2160p/3240p) (Chậm)</string>
273 <string name="resolution_four">4X (2880p/4320p) (Chậm)</string>
274
275 <!-- Renderer VSync -->
276 <string name="renderer_vsync_immediate">Immediate (Tắt)</string>
277 <string name="renderer_vsync_mailbox">Mailbox</string>
278 <string name="renderer_vsync_fifo">FIFO (Bật)</string>
279 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
280
281 <!-- Scaling Filters -->
282 <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string>
283 <string name="scaling_filter_bilinear">Bilinear</string>
284 <string name="scaling_filter_bicubic">Bicubic</string>
285 <string name="scaling_filter_gaussian">Gaussian</string>
286 <string name="scaling_filter_scale_force">ScaleForce</string>
287 <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string>
288
289 <!-- Anti-Aliasing -->
290 <string name="anti_aliasing_none">Không có</string>
291 <string name="anti_aliasing_fxaa">FXAA</string>
292 <string name="anti_aliasing_smaa">SMAA</string>
293
294 <string name="screen_layout_auto">Tự động</string>
295
296 <!-- Aspect Ratios -->
297 <string name="ratio_default">Mặc định (16:9)</string>
298 <string name="ratio_force_four_three">Dùng 4:3</string>
299 <string name="ratio_force_twenty_one_nine">Dùng 21:9</string>
300 <string name="ratio_force_sixteen_ten">Dùng 16:10</string>
301 <string name="ratio_stretch">Mở rộng đến cửa sổ</string>
302
303 <!-- CPU Accuracy -->
304 <string name="cpu_accuracy_accurate">Chính xác</string>
305 <string name="cpu_accuracy_unsafe">Không an toàn</string>
306 <string name="cpu_accuracy_paranoid">Paranoid (Chậm)</string>
307
308 <!-- Gamepad Buttons -->
309 <string name="gamepad_d_pad">D-pad</string>
310 <string name="gamepad_left_stick">Cần trái</string>
311 <string name="gamepad_right_stick">Cần phải</string>
312 <string name="gamepad_home">Home</string>
313 <string name="gamepad_screenshot">Ảnh chụp màn hình</string>
314
315 <!-- Disk shader cache -->
316 <string name="preparing_shaders">Äang chuẩn bị shader</string>
317 <string name="building_shaders">Äang đựng shader</string>
318
319 <!-- Theme options -->
320 <string name="change_app_theme">Thay đổi chủ đỠứng dụng</string>
321 <string name="theme_default">Mặc định</string>
322 <string name="theme_material_you">Material You</string>
323
324 <!-- Theme Modes -->
325 <string name="change_theme_mode">Thay đổi chá»§ Ä‘á»</string>
326 <string name="theme_mode_follow_system">Theo hệ thống</string>
327 <string name="theme_mode_light">Sáng</string>
328 <string name="theme_mode_dark">Tối</string>
329
330 <!-- Black backgrounds theme -->
331 <string name="use_black_backgrounds">Ná»n Ä‘en</string>
332 <string name="use_black_backgrounds_description">Khi sá»­ dụng chá»§ đỠtối, hãy áp dụng ná»n Ä‘en.</string>
333
334 <string name="mute">Tắt tiếng</string>
335 <string name="unmute">Bật tiếng</string>
336
337 <!-- Licenses screen strings -->
338 <string name="licenses">Giấy phép</string>
339 <string name="license_fidelityfx_fsr_description">Upscaling chất lượng cao từ AMD</string>
340 </resources>
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index c0e885751..13455564f 100644
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">此软件å¯ä»¥è¿è¡Œ Nintendo Switch 游æˆï¼Œä½†ä¸åŒ…å«ä»»ä½•游æˆå’Œå¯†é’¥æ–‡ä»¶ã€‚&lt;br /&gt;&lt;br /&gt;在开始å‰ï¼Œè¯·æ‰¾åˆ°æ”¾ç½®äºŽè®¾å¤‡å­˜å‚¨ä¸­çš„ <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> 4 <string name="app_disclaimer">此软件å¯ä»¥è¿è¡Œ Nintendo Switch 游æˆï¼Œä½†ä¸åŒ…å«ä»»ä½•游æˆå’Œå¯†é’¥æ–‡ä»¶ã€‚&lt;br /&gt;&lt;br /&gt;在开始å‰ï¼Œè¯·æ‰¾åˆ°æ”¾ç½®äºŽè®¾å¤‡å­˜å‚¨ä¸­çš„ <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string>
5 <string name="emulation_notification_channel_name">正在进行模拟</string> 5 <string name="emulation_notification_channel_name">正在进行模拟</string>
@@ -17,7 +17,7 @@
17 <string name="keys_description">使用下方的按钮æ¥é€‰æ‹©ä½ çš„ &lt;b>prod.keys&lt;/b> 文件。</string> 17 <string name="keys_description">使用下方的按钮æ¥é€‰æ‹©ä½ çš„ &lt;b>prod.keys&lt;/b> 文件。</string>
18 <string name="select_keys">选择密钥文件</string> 18 <string name="select_keys">选择密钥文件</string>
19 <string name="games">游æˆ</string> 19 <string name="games">游æˆ</string>
20 <string name="games_description">使用下方的按钮选择你的 &lt;b>游æˆ&lt;/b> 文件夹。</string> 20 <string name="games_description">使用下方的按钮选择你的&lt;b>游æˆ&lt;/b>文件夹。</string>
21 <string name="done">完æˆ</string> 21 <string name="done">完æˆ</string>
22 <string name="done_description">你完æˆäº†å…¨éƒ¨è®¾ç½®ã€‚\n玩的开心ï¼</string> 22 <string name="done_description">你完æˆäº†å…¨éƒ¨è®¾ç½®ã€‚\n玩的开心ï¼</string>
23 <string name="text_continue">ç»§ç»­</string> 23 <string name="text_continue">ç»§ç»­</string>
@@ -25,6 +25,7 @@
25 <string name="back">上一步</string> 25 <string name="back">上一步</string>
26 <string name="add_games">添加游æˆ</string> 26 <string name="add_games">添加游æˆ</string>
27 <string name="add_games_description">é€‰æ‹©ä½ çš„æ¸¸æˆæ–‡ä»¶å¤¹</string> 27 <string name="add_games_description">é€‰æ‹©ä½ çš„æ¸¸æˆæ–‡ä»¶å¤¹</string>
28 <string name="step_complete">完æˆï¼</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">游æˆ</string> 31 <string name="home_games">游æˆ</string>
@@ -38,6 +39,7 @@
38 <string name="add_games_warning_description">å¦‚æžœæœªé€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹ï¼Œæ¸¸æˆå°†ä¸ä¼šæ˜¾ç¤ºåœ¨æ¸¸æˆåˆ—表中。</string> 39 <string name="add_games_warning_description">å¦‚æžœæœªé€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹ï¼Œæ¸¸æˆå°†ä¸ä¼šæ˜¾ç¤ºåœ¨æ¸¸æˆåˆ—表中。</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">æœç´¢æ¸¸æˆ</string> 41 <string name="home_search_games">æœç´¢æ¸¸æˆ</string>
42 <string name="search_settings">æœç´¢è®¾ç½®</string>
41 <string name="games_dir_selected">å·²é€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹</string> 43 <string name="games_dir_selected">å·²é€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹</string>
42 <string name="install_prod_keys">安装 prod.keys 文件</string> 44 <string name="install_prod_keys">安装 prod.keys 文件</string>
43 <string name="install_prod_keys_description">需è¦å¯†é’¥æ–‡ä»¶æ¥è§£å¯†æ¸¸æˆ</string> 45 <string name="install_prod_keys_description">需è¦å¯†é’¥æ–‡ä»¶æ¥è§£å¯†æ¸¸æˆ</string>
@@ -61,12 +63,15 @@
61 <string name="invalid_keys_file">选择的密钥文件无效</string> 63 <string name="invalid_keys_file">选择的密钥文件无效</string>
62 <string name="install_keys_success">密钥文件已æˆåŠŸå®‰è£…</string> 64 <string name="install_keys_success">密钥文件已æˆåŠŸå®‰è£…</string>
63 <string name="reading_keys_failure">读å–加密密钥时出错</string> 65 <string name="reading_keys_failure">读å–加密密钥时出错</string>
66 <string name="install_prod_keys_failure_extension_description">è¯·ç¡®ä¿æ‚¨çš„密钥文件扩展å为 .keys å¹¶é‡è¯•。</string>
67 <string name="install_amiibo_keys_failure_extension_description">è¯·ç¡®ä¿æ‚¨çš„密钥文件扩展å为 .bin å¹¶é‡è¯•。</string>
64 <string name="invalid_keys_error">无效的加密密钥</string> 68 <string name="invalid_keys_error">无效的加密密钥</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">é€‰æ‹©çš„å¯†é’¥æ–‡ä»¶ä¸æ­£ç¡®æˆ–å·²æŸåã€‚è¯·é‡æ–°è½¬å‚¨å¯†é’¥æ–‡ä»¶ã€‚</string> 70 <string name="install_keys_failure_description">é€‰æ‹©çš„å¯†é’¥æ–‡ä»¶ä¸æ­£ç¡®æˆ–å·²æŸåã€‚è¯·é‡æ–°è½¬å‚¨å¯†é’¥æ–‡ä»¶ã€‚</string>
67 <string name="install_gpu_driver">安装 GPU 驱动</string> 71 <string name="install_gpu_driver">安装 GPU 驱动</string>
68 <string name="install_gpu_driver_description">安装替代的驱动程åºä»¥èŽ·å¾—æ›´å¥½çš„æ€§èƒ½å’Œç²¾åº¦</string> 72 <string name="install_gpu_driver_description">安装替代的驱动程åºä»¥èŽ·å¾—æ›´å¥½çš„æ€§èƒ½å’Œç²¾åº¦</string>
69 <string name="advanced_settings">高级选项</string> 73 <string name="advanced_settings">高级选项</string>
74 <string name="advanced_settings_game">高级选项: %1$s</string>
70 <string name="settings_description">更改模拟器设置</string> 75 <string name="settings_description">更改模拟器设置</string>
71 <string name="search_recently_played">最近游玩</string> 76 <string name="search_recently_played">最近游玩</string>
72 <string name="search_recently_added">最近添加</string> 77 <string name="search_recently_added">最近添加</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">ç¬¬ä¸€ä¸ªå­æ–‡ä»¶å¤¹åç§°å¿…é¡»ä¸ºå½“å‰æ¸¸æˆçš„ ID。</string> 91 <string name="save_file_invalid_zip_structure_description">ç¬¬ä¸€ä¸ªå­æ–‡ä»¶å¤¹åç§°å¿…é¡»ä¸ºå½“å‰æ¸¸æˆçš„ ID。</string>
87 <string name="import_saves">导入</string> 92 <string name="import_saves">导入</string>
88 <string name="export_saves">导出</string> 93 <string name="export_saves">导出</string>
94 <string name="install_firmware">安装固件</string>
95 <string name="install_firmware_description">固件文件必须为 zip æ ¼å¼ï¼Œå¯åЍæŸäº›æ¸¸æˆæ—¶å¿…需</string>
96 <string name="firmware_installing">正在安装固件</string>
97 <string name="firmware_installed_success">固件已æˆåŠŸå®‰è£…</string>
98 <string name="firmware_installed_failure">固件安装失败</string>
99 <string name="firmware_installed_failure_description">请确ä¿å›ºä»¶ nca 文件ä½äºŽ zip 压缩包的根目录,然åŽé‡è¯•。</string>
100 <string name="share_log">分享调试日志</string>
101 <string name="share_log_description">分享 yuzu 日志文件以便调试</string>
102 <string name="share_log_missing">未找到日志文件</string>
103 <string name="install_game_content">安装游æˆé™„加内容</string>
104 <string name="install_game_content_description">å®‰è£…æ¸¸æˆæ›´æ–°åŠ DLC</string>
105 <string name="installing_game_content">安装中...</string>
106 <string name="install_game_content_failure">å‘ NAND 安装文件时失败</string>
107 <string name="install_game_content_failure_description">请确ä¿é™„加内容的有效性,并且 prod.keys 密钥文件已安装。</string>
108 <string name="install_game_content_failure_base">为é¿å…产生冲çªï¼Œæ­¤åŠŸèƒ½ä¸èƒ½ç”¨äºŽå®‰è£…æ¸¸æˆæœ¬ä½“。</string>
109 <string name="install_game_content_failure_file_extension">åªæœ‰ NSP 或 XCI æ ¼å¼çš„附加内容å¯ä»¥å®‰è£…ã€‚è¯·ç¡®ä¿æ‚¨çš„æ¸¸æˆé™„加内容是有效的。</string>
110 <string name="install_game_content_failed_count">%1$d 安装出错</string>
111 <string name="install_game_content_success">游æˆé™„加内容已æˆåŠŸå®‰è£…</string>
112 <string name="install_game_content_success_install">%1$d 安装æˆåŠŸ</string>
113 <string name="install_game_content_success_overwrite">%1$d 覆盖安装æˆåŠŸ</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">䏿”¯æŒè‡ªå®šä¹‰é©±åЍ</string>
116 <string name="custom_driver_not_supported_description">æ­¤è®¾å¤‡ä¸æ”¯æŒè‡ªå®šä¹‰é©±åŠ¨ã€‚\n请之åŽå†è®¿é—®æ­¤é¡¹ï¼ŒæŸ¥çœ‹æ˜¯å¦å·²ä¸ºæ­¤è®¾å¤‡æ·»åŠ æ”¯æŒã€‚</string>
117 <string name="manage_yuzu_data">ç®¡ç† yuzu æ•°æ®</string>
118 <string name="manage_yuzu_data_description">导入/导出固件ã€å¯†é’¥ã€ç”¨æˆ·æ•°æ®åŠå…¶ä»–。</string>
119 <string name="share_save_file">分享存档文件</string>
120 <string name="export_save_failed">导出存档文件失败</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia ä¸çœŸå®ž</string> 123 <string name="gaia_is_not_real">Gaia ä¸çœŸå®ž</string>
@@ -94,14 +126,25 @@
94 <string name="contributors">贡献者</string> 126 <string name="contributors">贡献者</string>
95 <string name="contributors_description">使用æ¥è‡ª yuzu 团队的 \u2764 制作</string> 127 <string name="contributors_description">使用æ¥è‡ª yuzu 团队的 \u2764 制作</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">Android 版 yuzu 离ä¸å¼€è¿™äº›é¡¹ç›®çš„æ”¯æŒ</string>
97 <string name="build">构建版本</string> 130 <string name="build">构建版本</string>
131 <string name="user_data">用户数æ®</string>
132 <string name="user_data_description">导入/å¯¼å‡ºåº”ç”¨ç¨‹åºæ‰€æœ‰æ•°æ®ã€‚\n\nå¯¼å…¥ç”¨æˆ·æ•°æ®æ—¶ï¼Œå°†åˆ é™¤å½“剿‰€æœ‰çš„用户数æ®ï¼</string>
133 <string name="exporting_user_data">正在导出用户数æ®...</string>
134 <string name="importing_user_data">正在导入用户数æ®...</string>
135 <string name="import_user_data">导入用户数æ®</string>
136 <string name="invalid_yuzu_backup">无效的 yuzu 备份</string>
137 <string name="user_data_export_success">å¯¼å‡ºç”¨æˆ·æ•°æ®æˆåŠŸ</string>
138 <string name="user_data_import_success">å¯¼å…¥ç”¨æˆ·æ•°æ®æˆåŠŸ</string>
139 <string name="user_data_export_cancelled">已喿¶ˆå¯¼å‡ºæ•°æ®</string>
140 <string name="user_data_import_failed_description">请确ä¿ç”¨æˆ·æ•°æ®æ–‡ä»¶å¤¹ä½äºŽ zip 压缩包的根目录,并在 config/config.ini 路径中包å«é…置文件,然åŽé‡è¯•。</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
101 144
102 <!-- Early access upgrade strings --> 145 <!-- Early access upgrade strings -->
103 <string name="early_access">抢先体验</string> 146 <string name="early_access">抢先体验</string>
104 <string name="get_early_access">å–得抢先体验</string> 147 <string name="get_early_access">èŽ·å–æŠ¢å…ˆä½“éªŒï¼</string>
105 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> 148 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
106 <string name="get_early_access_description">æœ€æ–°çš„åŠŸèƒ½ã€æŠ¢å…ˆæ›´æ–°ã€ä»¥åŠæ›´å¤š</string> 149 <string name="get_early_access_description">æœ€æ–°çš„åŠŸèƒ½ã€æŠ¢å…ˆæ›´æ–°ã€ä»¥åŠæ›´å¤š</string>
107 <string name="early_access_benefits">抢先体验的æƒç›Š</string> 150 <string name="early_access_benefits">抢先体验的æƒç›Š</string>
@@ -109,33 +152,34 @@
109 <string name="early_access_updates">抢先更新</string> 152 <string name="early_access_updates">抢先更新</string>
110 <string name="no_manual_installation">无需手动安装</string> 153 <string name="no_manual_installation">无需手动安装</string>
111 <string name="prioritized_support">优先支æŒ</string> 154 <string name="prioritized_support">优先支æŒ</string>
112 <string name="helping_game_preservation">帮助ä¿ç•™æ¸¸æˆ</string> 155 <string name="helping_game_preservation">帮助ä¿ç•™æ¸¸çŽ©åŽ†å²</string>
113 <string name="our_eternal_gratitude">我们真诚的感激</string> 156 <string name="our_eternal_gratitude">我们真诚的感激</string>
114 <string name="are_you_interested">您对此感兴趣å—?</string> 157 <string name="are_you_interested">您对此感兴趣å—?</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">å¯ç”¨è¿è¡Œé€Ÿåº¦é™åˆ¶</string> 160 <string name="frame_limit_enable">è¿è¡Œé€Ÿåº¦é™åˆ¶</string>
118 <string name="frame_limit_enable_description">å¯ç”¨åŽï¼Œæ¨¡æ‹Ÿé€Ÿåº¦å°†é™åˆ¶åœ¨æ­£å¸¸è¿è¡Œé€Ÿåº¦çš„æŒ‡å®šç™¾åˆ†æ¯”。</string> 161 <string name="frame_limit_enable_description">å°†è¿è¡Œé€Ÿåº¦é™åˆ¶ä¸ºæ­£å¸¸é€Ÿåº¦çš„æŒ‡å®šç™¾åˆ†æ¯”。</string>
119 <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string> 162 <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string>
120 <string name="frame_limit_slider_description">指定é™åˆ¶æ¨¡æ‹Ÿé€Ÿåº¦çš„百分比。预设为 100%,此时模拟速度将被é™åˆ¶ä¸ºæ ‡å‡†é€Ÿåº¦ã€‚更高或更低的值将增加或é™ä½Žé€Ÿåº¦é™åˆ¶ä¸Šé™ã€‚</string> 163 <string name="frame_limit_slider_description">指定é™åˆ¶è¿è¡Œé€Ÿåº¦çš„百分比。100% 为正常速度。更高或更低的值将增加或é™ä½Žé€Ÿåº¦é™åˆ¶ä¸Šé™ã€‚</string>
121 <string name="cpu_accuracy">CPU 精度</string> 164 <string name="cpu_accuracy">CPU 精度</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">主机模å¼</string> 168 <string name="use_docked_mode">主机模å¼</string>
125 <string name="use_docked_mode_description">以主机模å¼è¿›è¡Œæ¨¡æ‹Ÿï¼Œç‰ºç‰²æ€§èƒ½å¹¶æé«˜ç”»é¢åˆ†è¾¨çŽ‡ã€‚</string> 169 <string name="use_docked_mode_description">æé«˜åˆ†è¾¨çŽ‡ï¼Œä½†é™ä½Žæ€§èƒ½ã€‚ç¦ç”¨æ­¤é¡¹æ—¶ä½¿ç”¨æŽŒæœºæ¨¡å¼ï¼Œé™ä½Žåˆ†è¾¨çއ并æé«˜æ€§èƒ½ã€‚</string>
126 <string name="emulated_region">模拟区域</string> 170 <string name="emulated_region">模拟区域</string>
127 <string name="emulated_language">模拟语言</string> 171 <string name="emulated_language">模拟语言</string>
128 <string name="select_rtc_date">选择日期</string> 172 <string name="select_rtc_date">选择日期</string>
129 <string name="select_rtc_time">选择时间</string> 173 <string name="select_rtc_time">选择时间</string>
130 <string name="use_custom_rtc">å¯ç”¨è‡ªå®šä¹‰ç³»ç»Ÿæ—¶é’Ÿ</string> 174 <string name="use_custom_rtc">自定义系统时间</string>
131 <string name="use_custom_rtc_description">此选项å…许您设置与目å‰ç³»ç»Ÿæ—¶é—´ç›¸ç‹¬ç«‹çš„自定义系统时钟</string> 175 <string name="use_custom_rtc_description">此选项å…许您设置与目å‰ç³»ç»Ÿæ—¶é—´ç›¸ç‹¬ç«‹çš„自定义系统时钟。</string>
132 <string name="set_custom_rtc">设置自定义系统时钟</string> 176 <string name="set_custom_rtc">设置自定义系统时间</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">精度等级</string> 179 <string name="renderer_accuracy">精度等级</string>
137 <string name="renderer_resolution">分辨率</string> 180 <string name="renderer_resolution">分辨率 (掌机模å¼/主机模å¼)</string>
138 <string name="renderer_vsync">åž‚ç›´åŒæ­¥æ¨¡å¼</string> 181 <string name="renderer_vsync">åž‚ç›´åŒæ­¥æ¨¡å¼</string>
182 <string name="renderer_screen_layout">å±å¹•æ–¹å‘</string>
139 <string name="renderer_aspect_ratio">å±å¹•纵横比</string> 183 <string name="renderer_aspect_ratio">å±å¹•纵横比</string>
140 <string name="renderer_scaling_filter">çª—å£æ»¤é•œ</string> 184 <string name="renderer_scaling_filter">çª—å£æ»¤é•œ</string>
141 <string name="renderer_anti_aliasing">抗锯齿方å¼</string> 185 <string name="renderer_anti_aliasing">抗锯齿方å¼</string>
@@ -143,12 +187,23 @@
143 <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟è¿è¡Œ (ä»è¢«æ¸©æŽ§é™åˆ¶)。</string> 187 <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟è¿è¡Œ (ä»è¢«æ¸©æŽ§é™åˆ¶)。</string>
144 <string name="renderer_asynchronous_shaders">使用异步ç€è‰²å™¨</string> 188 <string name="renderer_asynchronous_shaders">使用异步ç€è‰²å™¨</string>
145 <string name="renderer_asynchronous_shaders_description">异步编译ç€è‰²å™¨ï¼Œå‡å°‘å¡é¡¿ï¼Œä½†å¯èƒ½å¼•入故障。</string> 189 <string name="renderer_asynchronous_shaders_description">异步编译ç€è‰²å™¨ï¼Œå‡å°‘å¡é¡¿ï¼Œä½†å¯èƒ½å¼•入故障。</string>
146 <string name="renderer_debug">å¯ç”¨å›¾å½¢è°ƒè¯•</string> 190 <string name="renderer_reactive_flushing">å¯ç”¨å应性刷新</string>
147 <string name="renderer_debug_description">å¯ç”¨æ—¶ï¼Œå›¾å½¢ API 将进入较慢的调试模å¼ã€‚</string> 191 <string name="renderer_reactive_flushing_description">牺牲性能,æé«˜æŸäº›æ¸¸æˆçš„æ¸²æŸ“精度。</string>
148 <string name="use_disk_shader_cache">使用ç£ç›˜ç€è‰²å™¨ç¼“å­˜</string> 192 <string name="use_disk_shader_cache">ç£ç›˜ç€è‰²å™¨ç¼“å­˜</string>
149 <string name="use_disk_shader_cache_description">将生æˆçš„ç€è‰²å™¨ç¼“存于ç£ç›˜ä¸­å¹¶è¿›è¡Œè¯»å–以å‡å°‘å¡é¡¿ã€‚</string> 193 <string name="use_disk_shader_cache_description">将生æˆçš„ç€è‰²å™¨ç¼“存于ç£ç›˜ä¸­å¹¶è¿›è¡Œè¯»å–,以å‡å°‘å¡é¡¿ã€‚</string>
194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">CPU 调试</string>
198 <string name="cpu_debug_mode_description">å°† CPU 设置为较慢的调试模å¼ã€‚</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
201 <string name="renderer_debug">图形调试</string>
202 <string name="renderer_debug_description">将图形 API 设置为较慢的调试模å¼ã€‚</string>
203 <string name="fastmem">Fastmem</string>
150 204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">输出引擎</string>
152 <string name="audio_volume">音é‡</string> 207 <string name="audio_volume">音é‡</string>
153 <string name="audio_volume_description">指定输出的音é‡ã€‚</string> 208 <string name="audio_volume_description">指定输出的音é‡ã€‚</string>
154 209
@@ -157,7 +212,9 @@
157 <string name="ini_saved">å·²ä¿å­˜è®¾ç½®</string> 212 <string name="ini_saved">å·²ä¿å­˜è®¾ç½®</string>
158 <string name="gameid_saved">å·²ä¿å­˜ %1$s 的设置</string> 213 <string name="gameid_saved">å·²ä¿å­˜ %1$s 的设置</string>
159 <string name="error_saving">ä¿å­˜ %1$s.ini 时出错: %2$s</string> 214 <string name="error_saving">ä¿å­˜ %1$s.ini 时出错: %2$s</string>
215 <string name="unimplemented_menu">未生效èœå•</string>
160 <string name="loading">加载中…</string> 216 <string name="loading">加载中…</string>
217 <string name="shutting_down">正在关闭…</string>
161 <string name="reset_setting_confirmation">您è¦å°†æ­¤è®¾å®šé‡è®¾ä¸ºé»˜è®¤å€¼å—?</string> 218 <string name="reset_setting_confirmation">您è¦å°†æ­¤è®¾å®šé‡è®¾ä¸ºé»˜è®¤å€¼å—?</string>
162 <string name="reset_to_default">æ¢å¤é»˜è®¤</string> 219 <string name="reset_to_default">æ¢å¤é»˜è®¤</string>
163 <string name="reset_all_settings">é‡ç½®æ‰€æœ‰è®¾ç½®é¡¹ï¼Ÿ</string> 220 <string name="reset_all_settings">é‡ç½®æ‰€æœ‰è®¾ç½®é¡¹ï¼Ÿ</string>
@@ -165,6 +222,14 @@
165 <string name="settings_reset">é‡è®¾è®¾ç½®é¡¹</string> 222 <string name="settings_reset">é‡è®¾è®¾ç½®é¡¹</string>
166 <string name="close">关闭</string> 223 <string name="close">关闭</string>
167 <string name="learn_more">了解更多</string> 224 <string name="learn_more">了解更多</string>
225 <string name="auto">自动</string>
226 <string name="submit">æäº¤</string>
227 <string name="string_null">æ— </string>
228 <string name="string_import">导入</string>
229 <string name="export">导出</string>
230 <string name="export_failed">导出失败</string>
231 <string name="import_failed">导入失败</string>
232 <string name="cancelling">å–æ¶ˆä¸­</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">选择 GPU 驱动程åº</string> 235 <string name="select_gpu_driver">选择 GPU 驱动程åº</string>
@@ -172,6 +237,7 @@
172 <string name="select_gpu_driver_install">安装</string> 237 <string name="select_gpu_driver_install">安装</string>
173 <string name="select_gpu_driver_default">系统默认</string> 238 <string name="select_gpu_driver_default">系统默认</string>
174 <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程åº</string> 239 <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程åº</string>
240 <string name="select_gpu_driver_error">é€‰æ‹©çš„é©±åŠ¨ç¨‹åºæ— æ•ˆï¼Œå°†ä½¿ç”¨ç³»ç»Ÿé»˜è®¤çš„驱动程åºï¼</string>
175 <string name="system_gpu_driver">系统 GPU 驱动程åº</string> 241 <string name="system_gpu_driver">系统 GPU 驱动程åº</string>
176 <string name="installing_driver">正在安装驱动程åºâ€¦</string> 242 <string name="installing_driver">正在安装驱动程åºâ€¦</string>
177 243
@@ -182,10 +248,11 @@
182 <string name="preferences_graphics">图形</string> 248 <string name="preferences_graphics">图形</string>
183 <string name="preferences_audio">声音</string> 249 <string name="preferences_audio">声音</string>
184 <string name="preferences_theme">主题和色彩</string> 250 <string name="preferences_theme">主题和色彩</string>
251 <string name="preferences_debug">调试</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">您的 ROM 已加密</string> 254 <string name="loader_error_encrypted">您的 ROM 已加密</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[请å‚考指å—釿–°è½¬å‚¨ä½ çš„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">游æˆå¡å¸¦</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">已安装的游æˆ</a>。]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[请按照指å—釿–°è½¬å‚¨æ‚¨çš„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">游æˆå¡å¸¦</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">已安装的游æˆ</a>。]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[è¯·ç¡®ä¿ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游æˆå¯ä»¥è¢«è§£å¯†ã€‚]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[è¯·ç¡®ä¿ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游æˆå¯ä»¥è¢«è§£å¯†ã€‚]]></string>
190 <string name="loader_error_video_core">åˆå§‹åŒ–视频核心时å‘生错误</string> 257 <string name="loader_error_video_core">åˆå§‹åŒ–视频核心时å‘生错误</string>
191 <string name="loader_error_video_core_description">这通常由ä¸å…¼å®¹çš„ GPU 驱动程åºé€ æˆï¼Œå®‰è£…自定义 GPU 驱动程åºå¯èƒ½è§£å†³æ­¤é—®é¢˜ã€‚</string> 258 <string name="loader_error_video_core_description">这通常由ä¸å…¼å®¹çš„ GPU 驱动程åºé€ æˆï¼Œå®‰è£…自定义 GPU 驱动程åºå¯èƒ½è§£å†³æ­¤é—®é¢˜ã€‚</string>
@@ -226,6 +293,9 @@
226 <string name="fatal_error">致命错误</string> 293 <string name="fatal_error">致命错误</string>
227 <string name="fatal_error_message">å‘生致命错误,请查阅日志获å–详细信æ¯ã€‚\n继续模拟å¯èƒ½ä¼šé€ æˆå´©æºƒå’Œé”™è¯¯ã€‚</string> 294 <string name="fatal_error_message">å‘生致命错误,请查阅日志获å–详细信æ¯ã€‚\n继续模拟å¯èƒ½ä¼šé€ æˆå´©æºƒå’Œé”™è¯¯ã€‚</string>
228 <string name="performance_warning">关闭此项会显著é™ä½Žæ¨¡æ‹Ÿæ€§èƒ½ï¼å»ºè®®æ‚¨å°†æ­¤é¡¹ä¿æŒä¸ºå¯ç”¨çжæ€ã€‚</string> 295 <string name="performance_warning">关闭此项会显著é™ä½Žæ¨¡æ‹Ÿæ€§èƒ½ï¼å»ºè®®æ‚¨å°†æ­¤é¡¹ä¿æŒä¸ºå¯ç”¨çжæ€ã€‚</string>
296 <string name="device_memory_inadequate">设备 RAM: %1$s\n推è RAM: %2$s</string>
297 <string name="memory_formatted">%1$s%2$s</string>
298 <string name="no_game_present">当剿²¡æœ‰å¯å¯åŠ¨çš„æ¸¸æˆï¼</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">日本</string> 301 <string name="region_japan">日本</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">韩国</string> 306 <string name="region_korea">韩国</string>
237 <string name="region_taiwan">䏭国尿¹¾</string> 307 <string name="region_taiwan">䏭国尿¹¾</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Byte</string>
311 <string name="memory_kilobyte">KB</string>
312 <string name="memory_megabyte">MB</string>
313 <string name="memory_gigabyte">GB</string>
314 <string name="memory_terabyte">TB</string>
315 <string name="memory_petabyte">PB</string>
316 <string name="memory_exabyte">EB</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 319 <string name="renderer_vulkan">Vulkan</string>
@@ -274,6 +351,11 @@
274 <string name="anti_aliasing_fxaa">快速近似抗锯齿</string> 351 <string name="anti_aliasing_fxaa">快速近似抗锯齿</string>
275 <string name="anti_aliasing_smaa">å­åƒç´ å½¢æ€å­¦æŠ—锯齿</string> 352 <string name="anti_aliasing_smaa">å­åƒç´ å½¢æ€å­¦æŠ—锯齿</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">横å‘大å±</string>
356 <string name="screen_layout_portrait">纵å‘å±å¹•</string>
357 <string name="screen_layout_auto">自动</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">默认 (16:9)</string> 360 <string name="ratio_default">默认 (16:9)</string>
279 <string name="ratio_force_four_three">强制 4:3</string> 361 <string name="ratio_force_four_three">强制 4:3</string>
@@ -303,13 +385,27 @@
303 <string name="theme_material_you">Material You</string> 385 <string name="theme_material_you">Material You</string>
304 386
305 <!-- Theme Modes --> 387 <!-- Theme Modes -->
306 <string name="change_theme_mode">主题模å¼</string> 388 <string name="change_theme_mode">更改主题模å¼</string>
307 <string name="theme_mode_follow_system">è·Ÿéšç³»ç»Ÿ</string> 389 <string name="theme_mode_follow_system">è·Ÿéšç³»ç»Ÿ</string>
308 <string name="theme_mode_light">浅色</string> 390 <string name="theme_mode_light">浅色</string>
309 <string name="theme_mode_dark">深色</string> 391 <string name="theme_mode_dark">深色</string>
310 392
393 <!-- Audio output engines -->
394 <string name="cubeb">cubeb</string>
395
311 <!-- Black backgrounds theme --> 396 <!-- Black backgrounds theme -->
312 <string name="use_black_backgrounds">使用黑色背景</string> 397 <string name="use_black_backgrounds">使用黑色背景</string>
313 <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string> 398 <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string>
314 399
315</resources> 400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">画中画</string>
402 <string name="picture_in_picture_description">模拟器ä½äºŽåŽå°æ—¶æœ€å°åŒ–窗å£</string>
403 <string name="pause">æš‚åœ</string>
404 <string name="play">开始</string>
405 <string name="mute">é™éŸ³</string>
406 <string name="unmute">å–æ¶ˆé™éŸ³</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">许å¯è¯</string>
410 <string name="license_fidelityfx_fsr_description">æ¥è‡ª AMD 的高å“质画质å‡çº§</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index 4a21bf893..b8f468c68 100644
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">此軟體å¯ä»¥åŸ·è¡Œ Nintendo Switch ä¸»æ©ŸéŠæˆ²ï¼Œä½†ä¸åŒ…å«ä»»ä½•éŠæˆ²å’Œé‡‘鑰。&lt;br /&gt;&lt;br /&gt;在您開始å‰ï¼Œè«‹æ‰¾åˆ°æ”¾ç½®æ–¼æ‚¨çš„è£ç½®å„²å­˜ç©ºé–“çš„ <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> 4 <string name="app_disclaimer">此軟體å¯ä»¥åŸ·è¡Œ Nintendo Switch ä¸»æ©ŸéŠæˆ²ï¼Œä½†ä¸åŒ…å«ä»»ä½•éŠæˆ²å’Œé‡‘鑰。&lt;br /&gt;&lt;br /&gt;在您開始å‰ï¼Œè«‹æ‰¾åˆ°æ”¾ç½®æ–¼æ‚¨çš„è£ç½®å„²å­˜ç©ºé–“çš„ <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string>
5 <string name="emulation_notification_channel_name">模擬進行中</string> 5 <string name="emulation_notification_channel_name">模擬進行中</string>
@@ -25,6 +25,7 @@
25 <string name="back">上一步</string> 25 <string name="back">上一步</string>
26 <string name="add_games">æ–°å¢žéŠæˆ²</string> 26 <string name="add_games">æ–°å¢žéŠæˆ²</string>
27 <string name="add_games_description">é¸å–æ‚¨çš„éŠæˆ²è³‡æ–™å¤¾</string> 27 <string name="add_games_description">é¸å–æ‚¨çš„éŠæˆ²è³‡æ–™å¤¾</string>
28 <string name="step_complete">完æˆï¼</string>
28 29
29 <!-- Home strings --> 30 <!-- Home strings -->
30 <string name="home_games">éŠæˆ²</string> 31 <string name="home_games">éŠæˆ²</string>
@@ -33,11 +34,12 @@
33 <string name="empty_gamelist">找ä¸åˆ°æª”案,或者尚未é¸å–éŠæˆ²ç›®éŒ„。</string> 34 <string name="empty_gamelist">找ä¸åˆ°æª”案,或者尚未é¸å–éŠæˆ²ç›®éŒ„。</string>
34 <string name="search_and_filter_games">æœå°‹ä¸¦ç¯©é¸éŠæˆ²</string> 35 <string name="search_and_filter_games">æœå°‹ä¸¦ç¯©é¸éŠæˆ²</string>
35 <string name="select_games_folder">é¸å–éŠæˆ²è³‡æ–™å¤¾</string> 36 <string name="select_games_folder">é¸å–éŠæˆ²è³‡æ–™å¤¾</string>
36 <string name="select_games_folder_description">一律å…許 yuzu å¡«å…¥éŠæˆ²æ¸…å–®</string> 37 <string name="select_games_folder_description">å…許 yuzu å¡«å…¥éŠæˆ²æ¸…å–®</string>
37 <string name="add_games_warning">è·³éŽé¸å–éŠæˆ²è³‡æ–™å¤¾ï¼Ÿ</string> 38 <string name="add_games_warning">è·³éŽé¸å–éŠæˆ²è³‡æ–™å¤¾ï¼Ÿ</string>
38 <string name="add_games_warning_description">如果資料夾未é¸å–ï¼ŒéŠæˆ²å°‡ä¸æœƒé¡¯ç¤ºåœ¨éŠæˆ²æ¸…單。</string> 39 <string name="add_games_warning_description">如果資料夾未é¸å–ï¼ŒéŠæˆ²å°‡ä¸æœƒé¡¯ç¤ºåœ¨éŠæˆ²æ¸…單。</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">æœå°‹éŠæˆ²</string> 41 <string name="home_search_games">æœå°‹éŠæˆ²</string>
42 <string name="search_settings">æœç´¢è®¾ç½®</string>
41 <string name="games_dir_selected">éŠæˆ²ç›®éŒ„å·²é¸å–</string> 43 <string name="games_dir_selected">éŠæˆ²ç›®éŒ„å·²é¸å–</string>
42 <string name="install_prod_keys">å®‰è£ prod.keys</string> 44 <string name="install_prod_keys">å®‰è£ prod.keys</string>
43 <string name="install_prod_keys_description">需è¦è§£å¯†é›¶å”®éŠæˆ²</string> 45 <string name="install_prod_keys_description">需è¦è§£å¯†é›¶å”®éŠæˆ²</string>
@@ -60,13 +62,16 @@
60 <string name="install_amiibo_keys_description">需è¦åœ¨éŠæˆ²ä¸­ä½¿ç”¨ Amiibo</string> 62 <string name="install_amiibo_keys_description">需è¦åœ¨éŠæˆ²ä¸­ä½¿ç”¨ Amiibo</string>
61 <string name="invalid_keys_file">無效的金鑰檔案已é¸å–</string> 63 <string name="invalid_keys_file">無效的金鑰檔案已é¸å–</string>
62 <string name="install_keys_success">金鑰已æˆåŠŸå®‰è£</string> 64 <string name="install_keys_success">金鑰已æˆåŠŸå®‰è£</string>
63 <string name="reading_keys_failure">讀å–加密金鑰時出ç¾éŒ¯èª¤</string> 65 <string name="reading_keys_failure">讀å–加密金鑰時發生錯誤</string>
66 <string name="install_prod_keys_failure_extension_description">驗證您的金鑰檔案是å¦å…·æœ‰ .keys 副檔å並å†è©¦ä¸€æ¬¡ã€‚</string>
67 <string name="install_amiibo_keys_failure_extension_description">驗證您的金鑰檔案是å¦å…·æœ‰ .bin 副檔å並å†è©¦ä¸€æ¬¡ã€‚</string>
64 <string name="invalid_keys_error">無效的加密金鑰</string> 68 <string name="invalid_keys_error">無效的加密金鑰</string>
65 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 69 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
66 <string name="install_keys_failure_description">é¸å–çš„æª”æ¡ˆä¸æ­£ç¢ºæˆ–å·²ææ¯€ï¼Œè«‹é‡æ–°å‚¾å°æ‚¨çš„金鑰。</string> 70 <string name="install_keys_failure_description">é¸å–çš„æª”æ¡ˆä¸æ­£ç¢ºæˆ–å·²ææ¯€ï¼Œè«‹é‡æ–°å‚¾å°æ‚¨çš„金鑰。</string>
67 <string name="install_gpu_driver">å®‰è£ GPU 驅動程å¼</string> 71 <string name="install_gpu_driver">å®‰è£ GPU 驅動程å¼</string>
68 <string name="install_gpu_driver_description">å®‰è£æ›¿ä»£é©…動程å¼ä»¥å–得潛在的更佳效能或準確度</string> 72 <string name="install_gpu_driver_description">å®‰è£æ›¿ä»£é©…動程å¼ä»¥å–得潛在的更佳效能或準確度</string>
69 <string name="advanced_settings">進階設定</string> 73 <string name="advanced_settings">進階設定</string>
74 <string name="advanced_settings_game">高级选项: %1$s</string>
70 <string name="settings_description">進行模擬器設定</string> 75 <string name="settings_description">進行模擬器設定</string>
71 <string name="search_recently_played">最近éŠçŽ©</string> 76 <string name="search_recently_played">最近éŠçŽ©</string>
72 <string name="search_recently_added">最近新增</string> 77 <string name="search_recently_added">最近新增</string>
@@ -86,6 +91,33 @@
86 <string name="save_file_invalid_zip_structure_description">首個å­è³‡æ–™å¤¾åç¨±å¿…é ˆç‚ºéŠæˆ²æ¨™é¡Œ ID。</string> 91 <string name="save_file_invalid_zip_structure_description">首個å­è³‡æ–™å¤¾åç¨±å¿…é ˆç‚ºéŠæˆ²æ¨™é¡Œ ID。</string>
87 <string name="import_saves">匯入</string> 92 <string name="import_saves">匯入</string>
88 <string name="export_saves">匯出</string> 93 <string name="export_saves">匯出</string>
94 <string name="install_firmware">安è£éŸŒé«”</string>
95 <string name="install_firmware_description">韌體必須為 ZIP å°å­˜æª”ï¼Œå°‡æœƒç”¨æ–¼éƒ¨åˆ†éŠæˆ²çš„啟動</string>
96 <string name="firmware_installing">正在安è£éŸŒé«”</string>
97 <string name="firmware_installed_success">韌體已æˆåŠŸå®‰è£</string>
98 <string name="firmware_installed_failure">韌體安è£å¤±æ•—</string>
99 <string name="firmware_installed_failure_description">请确ä¿å›ºä»¶ nca 文件ä½äºŽ zip 压缩包的根目录,然åŽé‡è¯•。</string>
100 <string name="share_log">分享åµéŒ¯è¨˜éŒ„</string>
101 <string name="share_log_description">分享 yuzu 的記錄檔以便å°ç›¸é—œå•題進行åµéŒ¯</string>
102 <string name="share_log_missing">找ä¸åˆ°è¨˜éŒ„檔</string>
103 <string name="install_game_content">安è£éŠæˆ²å…§å®¹</string>
104 <string name="install_game_content_description">安è£éŠæˆ²æ›´æ–°æˆ– DLC</string>
105 <string name="installing_game_content">安装中...</string>
106 <string name="install_game_content_failure">å‘ NAND 安装文件时失败</string>
107 <string name="install_game_content_failure_description">请确ä¿é™„加内容的有效性,并且 prod.keys 密钥文件已安装。</string>
108 <string name="install_game_content_failure_base">为é¿å…产生冲çªï¼Œæ­¤åŠŸèƒ½ä¸èƒ½ç”¨äºŽå®‰è£…æ¸¸æˆæœ¬ä½“。</string>
109 <string name="install_game_content_failure_file_extension">åªæœ‰ NSP 或 XCI æ ¼å¼çš„附加内容å¯ä»¥å®‰è£…ã€‚è¯·ç¡®ä¿æ‚¨çš„æ¸¸æˆé™„加内容是有效的。</string>
110 <string name="install_game_content_failed_count">%1$d 安装出错</string>
111 <string name="install_game_content_success">游æˆé™„加内容已æˆåŠŸå®‰è£…</string>
112 <string name="install_game_content_success_install">%1$d 安装æˆåŠŸ</string>
113 <string name="install_game_content_success_overwrite">%1$d 覆盖安装æˆåŠŸ</string>
114 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
115 <string name="custom_driver_not_supported">䏿”¯æŒè‡ªå®šä¹‰é©±åЍ</string>
116 <string name="custom_driver_not_supported_description">æ­¤è®¾å¤‡ä¸æ”¯æŒè‡ªå®šä¹‰é©±åŠ¨ã€‚\n请之åŽå†è®¿é—®æ­¤é¡¹ï¼ŒæŸ¥çœ‹æ˜¯å¦å·²ä¸ºæ­¤è®¾å¤‡æ·»åŠ æ”¯æŒã€‚</string>
117 <string name="manage_yuzu_data">ç®¡ç† yuzu æ•°æ®</string>
118 <string name="manage_yuzu_data_description">导入/导出固件ã€å¯†é’¥ã€ç”¨æˆ·æ•°æ®åŠå…¶ä»–。</string>
119 <string name="share_save_file">分享存档文件</string>
120 <string name="export_save_failed">导出存档文件失败</string>
89 121
90 <!-- About screen strings --> 122 <!-- About screen strings -->
91 <string name="gaia_is_not_real">Gaia ä¸çœŸå¯¦</string> 123 <string name="gaia_is_not_real">Gaia ä¸çœŸå¯¦</string>
@@ -94,7 +126,18 @@
94 <string name="contributors">åƒèˆ‡è€…</string> 126 <string name="contributors">åƒèˆ‡è€…</string>
95 <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string> 127 <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> 128 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
129 <string name="licenses_description">這些專案使 yuzu Android 版æˆç‚ºå¯èƒ½</string>
97 <string name="build">組建</string> 130 <string name="build">組建</string>
131 <string name="user_data">用户数æ®</string>
132 <string name="user_data_description">导入/å¯¼å‡ºåº”ç”¨ç¨‹åºæ‰€æœ‰æ•°æ®ã€‚\n\nå¯¼å…¥ç”¨æˆ·æ•°æ®æ—¶ï¼Œå°†åˆ é™¤å½“剿‰€æœ‰çš„用户数æ®ï¼</string>
133 <string name="exporting_user_data">正在导出用户数æ®...</string>
134 <string name="importing_user_data">正在导入用户数æ®...</string>
135 <string name="import_user_data">导入用户数æ®</string>
136 <string name="invalid_yuzu_backup">无效的 yuzu 备份</string>
137 <string name="user_data_export_success">å¯¼å‡ºç”¨æˆ·æ•°æ®æˆåŠŸ</string>
138 <string name="user_data_import_success">å¯¼å…¥ç”¨æˆ·æ•°æ®æˆåŠŸ</string>
139 <string name="user_data_export_cancelled">已喿¶ˆå¯¼å‡ºæ•°æ®</string>
140 <string name="user_data_import_failed_description">请确ä¿ç”¨æˆ·æ•°æ®æ–‡ä»¶å¤¹ä½äºŽ zip 压缩包的根目录,并在 config/config.ini 路径中包å«é…置文件,然åŽé‡è¯•。</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string> 141 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string> 142 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string> 143 <string name="github_link">https://github.com/yuzu-emu</string>
@@ -114,28 +157,29 @@
114 <string name="are_you_interested">æ‚¨ä»æ„Ÿèˆˆè¶£å—Žï¼Ÿ</string> 157 <string name="are_you_interested">æ‚¨ä»æ„Ÿèˆˆè¶£å—Žï¼Ÿ</string>
115 158
116 <!-- General settings strings --> 159 <!-- General settings strings -->
117 <string name="frame_limit_enable">啟用é™åˆ¶é€Ÿåº¦</string> 160 <string name="frame_limit_enable">é™åˆ¶é€Ÿåº¦</string>
118 <string name="frame_limit_enable_description">若啟用,模擬速度將會é™åˆ¶åœ¨æ¨™æº–速度的指定百分比。</string> 161 <string name="frame_limit_enable_description">將模擬速度é™åˆ¶åœ¨æ¨™æº–速度的指定百分比。</string>
119 <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string> 162 <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string>
120 <string name="frame_limit_slider_description">指定é™åˆ¶æ¨¡æ“¬é€Ÿåº¦çš„百分比。é è¨­ç‚º 100%,模擬速度將被é™åˆ¶ç‚ºæ¨™æº–速度。更高或更低的值將會增加或減少速度é™åˆ¶ã€‚</string> 163 <string name="frame_limit_slider_description">指定é™åˆ¶æ¨¡æ“¬é€Ÿåº¦çš„百分比。100% 為標準速度,更高或更低的值將會增加或減少速度é™åˆ¶ã€‚</string>
121 <string name="cpu_accuracy">CPU 準確度</string> 164 <string name="cpu_accuracy">CPU 準確度</string>
165 <string name="value_with_units">%1$s%2$s</string>
122 166
123 <!-- System settings strings --> 167 <!-- System settings strings -->
124 <string name="use_docked_mode">底座模å¼</string> 168 <string name="use_docked_mode">底座模å¼</string>
125 <string name="use_docked_mode_description">ä»¥åº•åº§æ¨¡å¼æ¨¡æ“¬ï¼Œä»¥çŠ§ç‰²æ•ˆèƒ½çš„ä»£åƒ¹æé«˜è§£æžåº¦ã€‚</string> 169 <string name="use_docked_mode_description">æé«˜è§£æžåº¦ï¼Œé™ä½Žæ•ˆèƒ½ã€‚åœç”¨å¾Œå°‡æœƒä½¿ç”¨æ‰‹ææ¨¡å¼ï¼Œæœƒé™ä½Žè§£æžåº¦ä¸¦æé«˜æ•ˆèƒ½ã€‚</string>
126 <string name="emulated_region">模擬å€åŸŸ</string> 170 <string name="emulated_region">模擬å€åŸŸ</string>
127 <string name="emulated_language">模擬語言</string> 171 <string name="emulated_language">模擬語言</string>
128 <string name="select_rtc_date">é¸å– RTC 日期</string> 172 <string name="select_rtc_date">é¸å– RTC 日期</string>
129 <string name="select_rtc_time">é¸å– RTC 時間</string> 173 <string name="select_rtc_time">é¸å– RTC 時間</string>
130 <string name="use_custom_rtc">啟用自訂 RTC</string> 174 <string name="use_custom_rtc">自訂 RTC</string>
131 <string name="use_custom_rtc_description">此設定å…許您設定與您的目å‰ç³»çµ±æ™‚間相互ç¨ç«‹çš„è‡ªè¨‚å³æ™‚時é˜</string> 175 <string name="use_custom_rtc_description">å…許您設定與您的目å‰ç³»çµ±æ™‚間相互ç¨ç«‹çš„è‡ªè¨‚å³æ™‚時é˜ã€‚</string>
132 <string name="set_custom_rtc">設定自訂 RTC</string> 176 <string name="set_custom_rtc">設定自訂 RTC</string>
133 177
134 <!-- Graphics settings strings --> 178 <!-- Graphics settings strings -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">準確度層級</string> 179 <string name="renderer_accuracy">準確度層級</string>
137 <string name="renderer_resolution">è§£æžåº¦</string> 180 <string name="renderer_resolution">è§£æžåº¦ (手æ/底座)</string>
138 <string name="renderer_vsync">VSync 模å¼</string> 181 <string name="renderer_vsync">VSync 模å¼</string>
182 <string name="renderer_screen_layout">å±å¹•æ–¹å‘</string>
139 <string name="renderer_aspect_ratio">長寬比</string> 183 <string name="renderer_aspect_ratio">長寬比</string>
140 <string name="renderer_scaling_filter">è¦–çª—é©æ‡‰éŽæ¿¾å™¨</string> 184 <string name="renderer_scaling_filter">è¦–çª—é©æ‡‰éŽæ¿¾å™¨</string>
141 <string name="renderer_anti_aliasing">消除鋸齒方法</string> 185 <string name="renderer_anti_aliasing">消除鋸齒方法</string>
@@ -143,12 +187,23 @@
143 <string name="renderer_force_max_clock_description">強制 GPU 以最大å¯èƒ½æ™‚脈執行 (熱溫é™åˆ¶ä»è¢«å¥—用)。</string> 187 <string name="renderer_force_max_clock_description">強制 GPU 以最大å¯èƒ½æ™‚脈執行 (熱溫é™åˆ¶ä»è¢«å¥—用)。</string>
144 <string name="renderer_asynchronous_shaders">使用éžåŒæ­¥è‘—色器</string> 188 <string name="renderer_asynchronous_shaders">使用éžåŒæ­¥è‘—色器</string>
145 <string name="renderer_asynchronous_shaders_description">éžåŒæ­¥ç·¨è­¯è‘—色器,將會減少間斷,但å¯èƒ½æœƒå¼•入故障。</string> 189 <string name="renderer_asynchronous_shaders_description">éžåŒæ­¥ç·¨è­¯è‘—色器,將會減少間斷,但å¯èƒ½æœƒå¼•入故障。</string>
146 <string name="renderer_debug">啟用圖形åµéŒ¯</string> 190 <string name="renderer_reactive_flushing">ä½¿ç”¨é‡æ–°å•Ÿç”¨æŽ’清</string>
147 <string name="renderer_debug_description">æ ¸å–æ™‚,åœå½¢ API 將會進入慢速åµéŒ¯æ¨¡å¼ã€‚</string> 191 <string name="renderer_reactive_flushing_description">犧牲效能,以改å–éƒ¨åˆ†éŠæˆ²çš„轉譯準確度。</string>
148 <string name="use_disk_shader_cache">使用ç£ç¢Ÿè‘—色器快å–</string> 192 <string name="use_disk_shader_cache">ç£ç¢Ÿè‘—色器快å–</string>
149 <string name="use_disk_shader_cache_description">é€éŽå°‡ç”¢ç”Ÿçš„著色器儲存並載入至ç£ç¢Ÿï¼Œæ¸›å°‘中斷。</string> 193 <string name="use_disk_shader_cache_description">é€éŽå°‡ç”¢ç”Ÿçš„著色器儲存並載入至ç£ç¢Ÿï¼Œæ¸›å°‘中斷。</string>
150 194
195 <!-- Debug settings strings -->
196 <string name="cpu">CPU</string>
197 <string name="cpu_debug_mode">CPU 调试</string>
198 <string name="cpu_debug_mode_description">å°† CPU 设置为较慢的调试模å¼ã€‚</string>
199 <string name="gpu">GPU</string>
200 <string name="renderer_api">API</string>
201 <string name="renderer_debug">圖形åµéŒ¯</string>
202 <string name="renderer_debug_description">將圖形 API 設為慢速åµéŒ¯æ¨¡å¼ã€‚</string>
203 <string name="fastmem">Fastmem</string>
204
151 <!-- Audio settings strings --> 205 <!-- Audio settings strings -->
206 <string name="audio_output_engine">输出引擎</string>
152 <string name="audio_volume">音é‡</string> 207 <string name="audio_volume">音é‡</string>
153 <string name="audio_volume_description">指定音訊輸出音é‡ã€‚</string> 208 <string name="audio_volume_description">指定音訊輸出音é‡ã€‚</string>
154 209
@@ -157,7 +212,9 @@
157 <string name="ini_saved">已儲存設定</string> 212 <string name="ini_saved">已儲存設定</string>
158 <string name="gameid_saved">已儲存 %1$s 設定</string> 213 <string name="gameid_saved">已儲存 %1$s 設定</string>
159 <string name="error_saving">儲存 %1$s 時發生錯誤 ini: %2$s</string> 214 <string name="error_saving">儲存 %1$s 時發生錯誤 ini: %2$s</string>
215 <string name="unimplemented_menu">未生效èœå•</string>
160 <string name="loading">正在載入…</string> 216 <string name="loading">正在載入…</string>
217 <string name="shutting_down">正在关闭…</string>
161 <string name="reset_setting_confirmation">è¦å°‡æ­¤è¨­å®šé‡è¨­å›žé è¨­å€¼å—Žï¼Ÿ</string> 218 <string name="reset_setting_confirmation">è¦å°‡æ­¤è¨­å®šé‡è¨­å›žé è¨­å€¼å—Žï¼Ÿ</string>
162 <string name="reset_to_default">é‡è¨­ç‚ºé è¨­å€¼</string> 219 <string name="reset_to_default">é‡è¨­ç‚ºé è¨­å€¼</string>
163 <string name="reset_all_settings">é‡è¨­æ‰€æœ‰è¨­å®šï¼Ÿ</string> 220 <string name="reset_all_settings">é‡è¨­æ‰€æœ‰è¨­å®šï¼Ÿ</string>
@@ -165,6 +222,14 @@
165 <string name="settings_reset">設定已é‡è¨­</string> 222 <string name="settings_reset">設定已é‡è¨­</string>
166 <string name="close">關閉</string> 223 <string name="close">關閉</string>
167 <string name="learn_more">深入瞭解</string> 224 <string name="learn_more">深入瞭解</string>
225 <string name="auto">自動</string>
226 <string name="submit">æäº¤</string>
227 <string name="string_null">ç„¡</string>
228 <string name="string_import">匯入</string>
229 <string name="export">匯出</string>
230 <string name="export_failed">导出失败</string>
231 <string name="import_failed">导入失败</string>
232 <string name="cancelling">å–æ¶ˆä¸­</string>
168 233
169 <!-- GPU driver installation --> 234 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">é¸å– GPU 驅動程å¼</string> 235 <string name="select_gpu_driver">é¸å– GPU 驅動程å¼</string>
@@ -172,6 +237,7 @@
172 <string name="select_gpu_driver_install">安è£</string> 237 <string name="select_gpu_driver_install">安è£</string>
173 <string name="select_gpu_driver_default">é è¨­</string> 238 <string name="select_gpu_driver_default">é è¨­</string>
174 <string name="select_gpu_driver_use_default">使用é è¨­ GPU 驅動程å¼</string> 239 <string name="select_gpu_driver_use_default">使用é è¨­ GPU 驅動程å¼</string>
240 <string name="select_gpu_driver_error">é¸å–的驅動程å¼ç„¡æ•ˆï¼Œå°‡ä½¿ç”¨ç³»çµ±é è¨­é©…動程å¼ï¼</string>
175 <string name="system_gpu_driver">系統 GPU 驅動程å¼</string> 241 <string name="system_gpu_driver">系統 GPU 驅動程å¼</string>
176 <string name="installing_driver">正在安è£é©…動程å¼â€¦</string> 242 <string name="installing_driver">正在安è£é©…動程å¼â€¦</string>
177 243
@@ -182,10 +248,11 @@
182 <string name="preferences_graphics">圖形</string> 248 <string name="preferences_graphics">圖形</string>
183 <string name="preferences_audio">音訊</string> 249 <string name="preferences_audio">音訊</string>
184 <string name="preferences_theme">主題和色彩</string> 250 <string name="preferences_theme">主題和色彩</string>
251 <string name="preferences_debug">åµéŒ¯</string>
185 252
186 <!-- ROM loading errors --> 253 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">您的 ROM 已加密</string> 254 <string name="loader_error_encrypted">您的 ROM 已加密</string>
188 <string name="loader_error_encrypted_roms_description"><![CDATA[è«‹ä¾å¾ªæŒ‡å—釿–°å‚¾å°æ‚¨çš„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">éŠæˆ²å¡åŒ£</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">å®‰è£æ¨™é¡Œ</a>。]]></string> 255 <string name="loader_error_encrypted_roms_description"><![CDATA[请按照指å—釿–°è½¬å‚¨æ‚¨çš„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">游æˆå¡å¸¦</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">已安装的游æˆ</a>。]]></string>
189 <string name="loader_error_encrypted_keys_description"><![CDATA[è«‹ç¢ºä¿æ‚¨çš„ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安è£ï¼Œè®“éŠæˆ²å¯ä»¥è§£å¯†ã€‚]]></string> 256 <string name="loader_error_encrypted_keys_description"><![CDATA[è«‹ç¢ºä¿æ‚¨çš„ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安è£ï¼Œè®“éŠæˆ²å¯ä»¥è§£å¯†ã€‚]]></string>
190 <string name="loader_error_video_core">åˆå§‹åŒ–視訊核心時發生錯誤</string> 257 <string name="loader_error_video_core">åˆå§‹åŒ–視訊核心時發生錯誤</string>
191 <string name="loader_error_video_core_description">這經常由ä¸ç›¸å®¹çš„ GPU 驅動程å¼é€ æˆï¼Œå®‰è£è‡ªè¨‚ GPU 驅動程å¼å¯èƒ½æœƒè§£æ±ºæ­¤å•題。</string> 258 <string name="loader_error_video_core_description">這經常由ä¸ç›¸å®¹çš„ GPU 驅動程å¼é€ æˆï¼Œå®‰è£è‡ªè¨‚ GPU 驅動程å¼å¯èƒ½æœƒè§£æ±ºæ­¤å•題。</string>
@@ -219,13 +286,16 @@
219 <!-- Errors and warnings --> 286 <!-- Errors and warnings -->
220 <string name="abort_button">中止</string> 287 <string name="abort_button">中止</string>
221 <string name="continue_button">繼續</string> 288 <string name="continue_button">繼續</string>
222 <string name="system_archive_not_found">找ä¸åˆ°ç³»çµ±æª”案</string> 289 <string name="system_archive_not_found">找ä¸åˆ°ç³»çµ±å°å­˜</string>
223 <string name="system_archive_not_found_message">%s éºå¤±ï¼Œè«‹å‚¾å°æ‚¨çš„系統å°å­˜ã€‚\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string> 290 <string name="system_archive_not_found_message">%s éºå¤±ï¼Œè«‹å‚¾å°æ‚¨çš„系統å°å­˜ã€‚\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string>
224 <string name="system_archive_general">系統å°å­˜</string> 291 <string name="system_archive_general">系統å°å­˜</string>
225 <string name="save_load_error">儲存/載入發生錯誤</string> 292 <string name="save_load_error">儲存/載入發生錯誤</string>
226 <string name="fatal_error">åš´é‡éŒ¯èª¤</string> 293 <string name="fatal_error">åš´é‡éŒ¯èª¤</string>
227 <string name="fatal_error_message">發生嚴é‡éŒ¯èª¤ï¼Œæª¢æŸ¥è¨˜éŒ„以å–得詳細資訊。\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string> 294 <string name="fatal_error_message">發生嚴é‡éŒ¯èª¤ï¼Œæª¢æŸ¥è¨˜éŒ„以å–得詳細資訊。\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string>
228 <string name="performance_warning">關閉此設定會顯著é™ä½Žæ¨¡æ“¬æ•ˆèƒ½ï¼å¦‚éœ€æœ€ä½³é«”é©—ï¼Œå»ºè­°æ‚¨å°‡æ­¤è¨­å®šä¿æŒç‚ºå•Ÿç”¨ç‹€æ…‹ã€‚</string> 295 <string name="performance_warning">關閉此設定會顯著é™ä½Žæ¨¡æ“¬æ•ˆèƒ½ï¼å¦‚éœ€æœ€ä½³é«”é©—ï¼Œå»ºè­°æ‚¨å°‡æ­¤è¨­å®šä¿æŒç‚ºå•Ÿç”¨ç‹€æ…‹ã€‚</string>
296 <string name="device_memory_inadequate">设备 RAM: %1$s\n推è RAM: %2$s</string>
297 <string name="memory_formatted">%1$s%2$s</string>
298 <string name="no_game_present">当剿²¡æœ‰å¯å¯åŠ¨çš„æ¸¸æˆï¼</string>
229 299
230 <!-- Region Names --> 300 <!-- Region Names -->
231 <string name="region_japan">日本</string> 301 <string name="region_japan">日本</string>
@@ -236,7 +306,14 @@
236 <string name="region_korea">å—韓</string> 306 <string name="region_korea">å—韓</string>
237 <string name="region_taiwan">å°ç£</string> 307 <string name="region_taiwan">å°ç£</string>
238 308
239 <!-- Language Names --> 309 <!-- Memory Sizes -->
310 <string name="memory_byte">Byte</string>
311 <string name="memory_kilobyte">KB</string>
312 <string name="memory_megabyte">MB</string>
313 <string name="memory_gigabyte">英國</string>
314 <string name="memory_terabyte">TB</string>
315 <string name="memory_petabyte">PB</string>
316 <string name="memory_exabyte">EB</string>
240 317
241 <!-- Renderer APIs --> 318 <!-- Renderer APIs -->
242 <string name="renderer_vulkan">Vulkan</string> 319 <string name="renderer_vulkan">Vulkan</string>
@@ -274,14 +351,20 @@
274 <string name="anti_aliasing_fxaa">FXAA</string> 351 <string name="anti_aliasing_fxaa">FXAA</string>
275 <string name="anti_aliasing_smaa">SMAA</string> 352 <string name="anti_aliasing_smaa">SMAA</string>
276 353
354 <!-- Screen Layouts -->
355 <string name="screen_layout_landscape">横å‘大å±</string>
356 <string name="screen_layout_portrait">纵å‘å±å¹•</string>
357 <string name="screen_layout_auto">自動</string>
358
277 <!-- Aspect Ratios --> 359 <!-- Aspect Ratios -->
278 <string name="ratio_default">é è¨­ (16:9)</string> 360 <string name="ratio_default">é è¨­ (16:9)</string>
279 <string name="ratio_force_four_three">強制 4:3</string> 361 <string name="ratio_force_four_three">強制 4:3</string>
280 <string name="ratio_force_twenty_one_nine">強制 21:9</string> 362 <string name="ratio_force_twenty_one_nine">強制 21:9</string>
281 <string name="ratio_force_sixteen_ten">強制 16:10</string> 363 <string name="ratio_force_sixteen_ten">強制 16:10</string>
282 <string name="ratio_stretch">延伸視窗</string> 364 <string name="ratio_stretch">延展視窗</string>
283 365
284 <!-- CPU Accuracy --> 366 <!-- CPU Accuracy -->
367 <string name="cpu_accuracy_accurate">高精度</string>
285 <string name="cpu_accuracy_unsafe">低精度</string> 368 <string name="cpu_accuracy_unsafe">低精度</string>
286 <string name="cpu_accuracy_paranoid">ä¸åˆç† (æ…¢)</string> 369 <string name="cpu_accuracy_paranoid">ä¸åˆç† (æ…¢)</string>
287 370
@@ -307,8 +390,22 @@
307 <string name="theme_mode_light">淺色</string> 390 <string name="theme_mode_light">淺色</string>
308 <string name="theme_mode_dark">深色</string> 391 <string name="theme_mode_dark">深色</string>
309 392
393 <!-- Audio output engines -->
394 <string name="cubeb">cubeb</string>
395
310 <!-- Black backgrounds theme --> 396 <!-- Black backgrounds theme -->
311 <string name="use_black_backgrounds">使用黑色背景</string> 397 <string name="use_black_backgrounds">黑色背景</string>
312 <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string> 398 <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string>
313 399
314</resources> 400 <!-- Picture-In-Picture -->
401 <string name="picture_in_picture">画中画</string>
402 <string name="picture_in_picture_description">模拟器ä½äºŽåŽå°æ—¶æœ€å°åŒ–窗å£</string>
403 <string name="pause">æš‚åœ</string>
404 <string name="play">开始</string>
405 <string name="mute">éœéŸ³</string>
406 <string name="unmute">å–æ¶ˆéœéŸ³</string>
407
408 <!-- Licenses screen strings -->
409 <string name="licenses">授權</string>
410 <string name="license_fidelityfx_fsr_description">來自 AMD çš„å‡ç´šåœ–åƒå“質</string>
411 </resources>
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index dc10159c9..51bcc49a3 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -2,7 +2,6 @@
2<resources> 2<resources>
3 3
4 <string-array name="regionNames"> 4 <string-array name="regionNames">
5 <item>@string/auto</item>
6 <item>@string/region_australia</item> 5 <item>@string/region_australia</item>
7 <item>@string/region_china</item> 6 <item>@string/region_china</item>
8 <item>@string/region_europe</item> 7 <item>@string/region_europe</item>
@@ -13,7 +12,6 @@
13 </string-array> 12 </string-array>
14 13
15 <integer-array name="regionValues"> 14 <integer-array name="regionValues">
16 <item>-1</item>
17 <item>3</item> 15 <item>3</item>
18 <item>4</item> 16 <item>4</item>
19 <item>2</item> 17 <item>2</item>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index b92978140..471af8795 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -72,7 +72,7 @@
72 <string name="invalid_keys_error">Invalid encryption keys</string> 72 <string name="invalid_keys_error">Invalid encryption keys</string>
73 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> 73 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
74 <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> 74 <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
75 <string name="gpu_driver_manager">GPU Driver Manager</string> 75 <string name="gpu_driver_manager">GPU driver manager</string>
76 <string name="install_gpu_driver">Install GPU driver</string> 76 <string name="install_gpu_driver">Install GPU driver</string>
77 <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> 77 <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
78 <string name="advanced_settings">Advanced settings</string> 78 <string name="advanced_settings">Advanced settings</string>
@@ -91,6 +91,7 @@
91 <string name="manage_save_data">Manage save data</string> 91 <string name="manage_save_data">Manage save data</string>
92 <string name="manage_save_data_description">Save data found. Please select an option below.</string> 92 <string name="manage_save_data_description">Save data found. Please select an option below.</string>
93 <string name="import_export_saves_description">Import or export save files</string> 93 <string name="import_export_saves_description">Import or export save files</string>
94 <string name="save_files_exporting">Exporting save files…</string>
94 <string name="save_file_imported_success">Imported successfully</string> 95 <string name="save_file_imported_success">Imported successfully</string>
95 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> 96 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
96 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> 97 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@@ -240,6 +241,7 @@
240 <string name="shutting_down">Shutting down…</string> 241 <string name="shutting_down">Shutting down…</string>
241 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> 242 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
242 <string name="reset_to_default">Reset to default</string> 243 <string name="reset_to_default">Reset to default</string>
244 <string name="reset_to_default_description">Resets all advanced settings</string>
243 <string name="reset_all_settings">Reset all settings?</string> 245 <string name="reset_all_settings">Reset all settings?</string>
244 <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> 246 <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string>
245 <string name="settings_reset">Settings reset</string> 247 <string name="settings_reset">Settings reset</string>
@@ -255,6 +257,7 @@
255 <string name="cancelling">Cancelling</string> 257 <string name="cancelling">Cancelling</string>
256 <string name="install">Install</string> 258 <string name="install">Install</string>
257 <string name="delete">Delete</string> 259 <string name="delete">Delete</string>
260 <string name="export_success">Exported successfully</string>
258 261
259 <!-- GPU driver installation --> 262 <!-- GPU driver installation -->
260 <string name="select_gpu_driver">Select GPU driver</string> 263 <string name="select_gpu_driver">Select GPU driver</string>
@@ -271,10 +274,14 @@
271 <string name="preferences_settings">Settings</string> 274 <string name="preferences_settings">Settings</string>
272 <string name="preferences_general">General</string> 275 <string name="preferences_general">General</string>
273 <string name="preferences_system">System</string> 276 <string name="preferences_system">System</string>
277 <string name="preferences_system_description">Docked mode, region, language</string>
274 <string name="preferences_graphics">Graphics</string> 278 <string name="preferences_graphics">Graphics</string>
279 <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
275 <string name="preferences_audio">Audio</string> 280 <string name="preferences_audio">Audio</string>
281 <string name="preferences_audio_description">Output engine, volume</string>
276 <string name="preferences_theme">Theme and color</string> 282 <string name="preferences_theme">Theme and color</string>
277 <string name="preferences_debug">Debug</string> 283 <string name="preferences_debug">Debug</string>
284 <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
278 285
279 <!-- ROM loading errors --> 286 <!-- ROM loading errors -->
280 <string name="loader_error_encrypted">Your ROM is encrypted</string> 287 <string name="loader_error_encrypted">Your ROM is encrypted</string>
diff --git a/src/android/app/src/main/res/xml/locales_config.xml b/src/android/app/src/main/res/xml/locales_config.xml
deleted file mode 100644
index 51b88d9dc..000000000
--- a/src/android/app/src/main/res/xml/locales_config.xml
+++ /dev/null
@@ -1,17 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
3 <locale android:name="en" /> <!-- English (default) -->
4 <locale android:name="de" /> <!-- German -->
5 <locale android:name="es" /> <!-- Spanish -->
6 <locale android:name="fr" /> <!-- French -->
7 <locale android:name="it" /> <!-- Italian -->
8 <locale android:name="ja" /> <!-- Japanese -->
9 <locale android:name="nb" /> <!-- Norwegian Bokmal -->
10 <locale android:name="pl" /> <!-- Polish -->
11 <locale android:name="pt-rBR" /> <!-- Portuguese (Brazil) -->
12 <locale android:name="pt-RPT" /> <!-- Portuguese (Portugal) -->
13 <locale android:name="ru" /> <!-- Russian -->
14 <locale android:name="uk" /> <!-- Ukranian -->
15 <locale android:name="zh-rCN" /> <!-- Chinese (China) -->
16 <locale android:name="zh-rTW" /> <!-- Chinese (Taiwan) -->
17</locale-config>
diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
index 2c16d3769..e2b9eb566 100644
--- a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
+++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
@@ -1,107 +1,107 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "audio_core/adsp/apps/opus/opus_decode_object.h" 4#include "audio_core/adsp/apps/opus/opus_decode_object.h"
5#include "common/assert.h" 5#include "common/assert.h"
6 6
7namespace AudioCore::ADSP::OpusDecoder { 7namespace AudioCore::ADSP::OpusDecoder {
8namespace { 8namespace {
9bool IsValidChannelCount(u32 channel_count) { 9bool IsValidChannelCount(u32 channel_count) {
10 return channel_count == 1 || channel_count == 2; 10 return channel_count == 1 || channel_count == 2;
11} 11}
12} // namespace 12} // namespace
13 13
14u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { 14u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
15 if (!IsValidChannelCount(channel_count)) { 15 if (!IsValidChannelCount(channel_count)) {
16 return 0; 16 return 0;
17 } 17 }
18 return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); 18 return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
19} 19}
20 20
21OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { 21OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
22 auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer); 22 auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
23 auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2); 23 auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
24 24
25 if (new_decoder->magic == DecodeObjectMagic) { 25 if (new_decoder->magic == DecodeObjectMagic) {
26 if (!new_decoder->initialized || 26 if (!new_decoder->initialized ||
27 (new_decoder->initialized && new_decoder->self == comparison)) { 27 (new_decoder->initialized && new_decoder->self == comparison)) {
28 new_decoder->state_valid = true; 28 new_decoder->state_valid = true;
29 } 29 }
30 } else { 30 } else {
31 new_decoder->initialized = false; 31 new_decoder->initialized = false;
32 new_decoder->state_valid = true; 32 new_decoder->state_valid = true;
33 } 33 }
34 return *new_decoder; 34 return *new_decoder;
35} 35}
36 36
37s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { 37s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
38 if (!state_valid) { 38 if (!state_valid) {
39 return OPUS_INVALID_STATE; 39 return OPUS_INVALID_STATE;
40 } 40 }
41 41
42 if (initialized) { 42 if (initialized) {
43 return OPUS_OK; 43 return OPUS_OK;
44 } 44 }
45 45
46 // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include 46 // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
47 // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer 47 // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
48 // provided. 48 // provided.
49 // We could use _create and have libopus allocate it for us, but then we have to separately 49 // We could use _create and have libopus allocate it for us, but then we have to separately
50 // track which decoder is being used between this and multistream in order to call the correct 50 // track which decoder is being used between this and multistream in order to call the correct
51 // destroy from the host side. 51 // destroy from the host side.
52 // This is a bit cringe, but is safe as these objects are only ever initialized inside the given 52 // This is a bit cringe, but is safe as these objects are only ever initialized inside the given
53 // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. 53 // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
54 decoder = (LibOpusDecoder*)(this + 1); 54 decoder = (LibOpusDecoder*)(this + 1);
55 s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); 55 s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
56 if (ret == OPUS_OK) { 56 if (ret == OPUS_OK) {
57 magic = DecodeObjectMagic; 57 magic = DecodeObjectMagic;
58 initialized = true; 58 initialized = true;
59 state_valid = true; 59 state_valid = true;
60 self = this; 60 self = this;
61 final_range = 0; 61 final_range = 0;
62 } 62 }
63 return ret; 63 return ret;
64} 64}
65 65
66s32 OpusDecodeObject::Shutdown() { 66s32 OpusDecodeObject::Shutdown() {
67 if (!state_valid) { 67 if (!state_valid) {
68 return OPUS_INVALID_STATE; 68 return OPUS_INVALID_STATE;
69 } 69 }
70 70
71 if (initialized) { 71 if (initialized) {
72 magic = 0x0; 72 magic = 0x0;
73 initialized = false; 73 initialized = false;
74 state_valid = false; 74 state_valid = false;
75 self = nullptr; 75 self = nullptr;
76 final_range = 0; 76 final_range = 0;
77 decoder = nullptr; 77 decoder = nullptr;
78 } 78 }
79 return OPUS_OK; 79 return OPUS_OK;
80} 80}
81 81
82s32 OpusDecodeObject::ResetDecoder() { 82s32 OpusDecodeObject::ResetDecoder() {
83 return opus_decoder_ctl(decoder, OPUS_RESET_STATE); 83 return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
84} 84}
85 85
86s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, 86s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
87 u64 input_data, u64 input_data_size) { 87 u64 input_data, u64 input_data_size) {
88 ASSERT(initialized); 88 ASSERT(initialized);
89 out_sample_count = 0; 89 out_sample_count = 0;
90 90
91 if (!state_valid) { 91 if (!state_valid) {
92 return OPUS_INVALID_STATE; 92 return OPUS_INVALID_STATE;
93 } 93 }
94 94
95 auto ret_code_or_samples = opus_decode( 95 auto ret_code_or_samples = opus_decode(
96 decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), 96 decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
97 reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); 97 reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
98 98
99 if (ret_code_or_samples < OPUS_OK) { 99 if (ret_code_or_samples < OPUS_OK) {
100 return ret_code_or_samples; 100 return ret_code_or_samples;
101 } 101 }
102 102
103 out_sample_count = ret_code_or_samples; 103 out_sample_count = ret_code_or_samples;
104 return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); 104 return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
105} 105}
106 106
107} // namespace AudioCore::ADSP::OpusDecoder 107} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp
index 2084de128..75f0fb9ad 100644
--- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp
+++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp
@@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) {
30 return channel_count <= OpusStreamCountMax; 30 return channel_count <= OpusStreamCountMax;
31} 31}
32 32
33bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { 33bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) {
34 return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && 34 return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
35 sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; 35 stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count;
36} 36}
37} // namespace 37} // namespace
38 38
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
index f6d362e68..05cf3975d 100644
--- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
+++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
@@ -1,111 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" 4#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
5#include "common/assert.h" 5#include "common/assert.h"
6 6
7namespace AudioCore::ADSP::OpusDecoder { 7namespace AudioCore::ADSP::OpusDecoder {
8 8
9namespace { 9namespace {
10bool IsValidChannelCount(u32 channel_count) { 10bool IsValidChannelCount(u32 channel_count) {
11 return channel_count == 1 || channel_count == 2; 11 return channel_count == 1 || channel_count == 2;
12} 12}
13 13
14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { 14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
15 return total_stream_count > 0 && stereo_stream_count > 0 && 15 return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); 16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
17} 17}
18} // namespace 18} // namespace
19 19
20u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, 20u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
21 u32 stereo_stream_count) { 21 u32 stereo_stream_count) {
22 if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { 22 if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
23 return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) + 23 return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
24 opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); 24 opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
25 } 25 }
26 return 0; 26 return 0;
27} 27}
28 28
29OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { 29OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
30 auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer); 30 auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
31 auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2); 31 auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
32 32
33 if (new_decoder->magic == DecodeMultiStreamObjectMagic) { 33 if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
34 if (!new_decoder->initialized || 34 if (!new_decoder->initialized ||
35 (new_decoder->initialized && new_decoder->self == comparison)) { 35 (new_decoder->initialized && new_decoder->self == comparison)) {
36 new_decoder->state_valid = true; 36 new_decoder->state_valid = true;
37 } 37 }
38 } else { 38 } else {
39 new_decoder->initialized = false; 39 new_decoder->initialized = false;
40 new_decoder->state_valid = true; 40 new_decoder->state_valid = true;
41 } 41 }
42 return *new_decoder; 42 return *new_decoder;
43} 43}
44 44
45s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, 45s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
46 u32 channel_count, u32 stereo_stream_count, 46 u32 channel_count, u32 stereo_stream_count,
47 u8* mappings) { 47 u8* mappings) {
48 if (!state_valid) { 48 if (!state_valid) {
49 return OPUS_INVALID_STATE; 49 return OPUS_INVALID_STATE;
50 } 50 }
51 51
52 if (initialized) { 52 if (initialized) {
53 return OPUS_OK; 53 return OPUS_OK;
54 } 54 }
55 55
56 // See OpusDecodeObject::InitializeDecoder for an explanation of this 56 // See OpusDecodeObject::InitializeDecoder for an explanation of this
57 decoder = (LibOpusMSDecoder*)(this + 1); 57 decoder = (LibOpusMSDecoder*)(this + 1);
58 s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, 58 s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
59 stereo_stream_count, mappings); 59 stereo_stream_count, mappings);
60 if (ret == OPUS_OK) { 60 if (ret == OPUS_OK) {
61 magic = DecodeMultiStreamObjectMagic; 61 magic = DecodeMultiStreamObjectMagic;
62 initialized = true; 62 initialized = true;
63 state_valid = true; 63 state_valid = true;
64 self = this; 64 self = this;
65 final_range = 0; 65 final_range = 0;
66 } 66 }
67 return ret; 67 return ret;
68} 68}
69 69
70s32 OpusMultiStreamDecodeObject::Shutdown() { 70s32 OpusMultiStreamDecodeObject::Shutdown() {
71 if (!state_valid) { 71 if (!state_valid) {
72 return OPUS_INVALID_STATE; 72 return OPUS_INVALID_STATE;
73 } 73 }
74 74
75 if (initialized) { 75 if (initialized) {
76 magic = 0x0; 76 magic = 0x0;
77 initialized = false; 77 initialized = false;
78 state_valid = false; 78 state_valid = false;
79 self = nullptr; 79 self = nullptr;
80 final_range = 0; 80 final_range = 0;
81 decoder = nullptr; 81 decoder = nullptr;
82 } 82 }
83 return OPUS_OK; 83 return OPUS_OK;
84} 84}
85 85
86s32 OpusMultiStreamDecodeObject::ResetDecoder() { 86s32 OpusMultiStreamDecodeObject::ResetDecoder() {
87 return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); 87 return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
88} 88}
89 89
90s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, 90s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
91 u64 output_data_size, u64 input_data, u64 input_data_size) { 91 u64 output_data_size, u64 input_data, u64 input_data_size) {
92 ASSERT(initialized); 92 ASSERT(initialized);
93 out_sample_count = 0; 93 out_sample_count = 0;
94 94
95 if (!state_valid) { 95 if (!state_valid) {
96 return OPUS_INVALID_STATE; 96 return OPUS_INVALID_STATE;
97 } 97 }
98 98
99 auto ret_code_or_samples = opus_multistream_decode( 99 auto ret_code_or_samples = opus_multistream_decode(
100 decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), 100 decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
101 reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); 101 reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
102 102
103 if (ret_code_or_samples < OPUS_OK) { 103 if (ret_code_or_samples < OPUS_OK) {
104 return ret_code_or_samples; 104 return ret_code_or_samples;
105 } 105 }
106 106
107 out_sample_count = ret_code_or_samples; 107 out_sample_count = ret_code_or_samples;
108 return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); 108 return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
109} 109}
110 110
111} // namespace AudioCore::ADSP::OpusDecoder 111} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp
index 5b23fce14..b7fed5304 100644
--- a/src/audio_core/opus/decoder.cpp
+++ b/src/audio_core/opus/decoder.cpp
@@ -1,179 +1,179 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "audio_core/opus/decoder.h" 4#include "audio_core/opus/decoder.h"
5#include "audio_core/opus/hardware_opus.h" 5#include "audio_core/opus/hardware_opus.h"
6#include "audio_core/opus/parameters.h" 6#include "audio_core/opus/parameters.h"
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/swap.h" 8#include "common/swap.h"
9#include "core/core.h" 9#include "core/core.h"
10 10
11namespace AudioCore::OpusDecoder { 11namespace AudioCore::OpusDecoder {
12using namespace Service::Audio; 12using namespace Service::Audio;
13namespace { 13namespace {
14OpusPacketHeader ReverseHeader(OpusPacketHeader header) { 14OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
15 OpusPacketHeader out; 15 OpusPacketHeader out;
16 out.size = Common::swap32(header.size); 16 out.size = Common::swap32(header.size);
17 out.final_range = Common::swap32(header.final_range); 17 out.final_range = Common::swap32(header.final_range);
18 return out; 18 return out;
19} 19}
20} // namespace 20} // namespace
21 21
22OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) 22OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
23 : system{system_}, hardware_opus{hardware_opus_} {} 23 : system{system_}, hardware_opus{hardware_opus_} {}
24 24
25OpusDecoder::~OpusDecoder() { 25OpusDecoder::~OpusDecoder() {
26 if (decode_object_initialized) { 26 if (decode_object_initialized) {
27 hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); 27 hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
28 } 28 }
29} 29}
30 30
31Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, 31Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
32 u64 transfer_memory_size) { 32 u64 transfer_memory_size) {
33 auto frame_size{params.use_large_frame_size ? 5760 : 1920}; 33 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
34 shared_buffer_size = transfer_memory_size; 34 shared_buffer_size = transfer_memory_size;
35 shared_buffer = std::make_unique<u8[]>(shared_buffer_size); 35 shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
36 shared_memory_mapped = true; 36 shared_memory_mapped = true;
37 37
38 buffer_size = 38 buffer_size =
39 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); 39 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
40 40
41 out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; 41 out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
42 size_t in_data_size{0x600u}; 42 size_t in_data_size{0x600u};
43 in_data = {out_data.data() - in_data_size, in_data_size}; 43 in_data = {out_data.data() - in_data_size, in_data_size};
44 44
45 ON_RESULT_FAILURE { 45 ON_RESULT_FAILURE {
46 if (shared_memory_mapped) { 46 if (shared_memory_mapped) {
47 shared_memory_mapped = false; 47 shared_memory_mapped = false;
48 ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); 48 ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
49 } 49 }
50 }; 50 };
51 51
52 R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, 52 R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
53 shared_buffer.get(), shared_buffer_size)); 53 shared_buffer.get(), shared_buffer_size));
54 54
55 sample_rate = params.sample_rate; 55 sample_rate = params.sample_rate;
56 channel_count = params.channel_count; 56 channel_count = params.channel_count;
57 use_large_frame_size = params.use_large_frame_size; 57 use_large_frame_size = params.use_large_frame_size;
58 decode_object_initialized = true; 58 decode_object_initialized = true;
59 R_SUCCEED(); 59 R_SUCCEED();
60} 60}
61 61
62Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, 62Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
63 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { 63 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
64 auto frame_size{params.use_large_frame_size ? 5760 : 1920}; 64 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
65 shared_buffer_size = transfer_memory_size; 65 shared_buffer_size = transfer_memory_size;
66 shared_buffer = std::make_unique<u8[]>(shared_buffer_size); 66 shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
67 shared_memory_mapped = true; 67 shared_memory_mapped = true;
68 68
69 buffer_size = 69 buffer_size =
70 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); 70 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
71 71
72 out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; 72 out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
73 size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; 73 size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
74 in_data = {out_data.data() - in_data_size, in_data_size}; 74 in_data = {out_data.data() - in_data_size, in_data_size};
75 75
76 ON_RESULT_FAILURE { 76 ON_RESULT_FAILURE {
77 if (shared_memory_mapped) { 77 if (shared_memory_mapped) {
78 shared_memory_mapped = false; 78 shared_memory_mapped = false;
79 ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); 79 ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
80 } 80 }
81 }; 81 };
82 82
83 R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( 83 R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
84 params.sample_rate, params.channel_count, params.total_stream_count, 84 params.sample_rate, params.channel_count, params.total_stream_count,
85 params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), 85 params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
86 shared_buffer_size)); 86 shared_buffer_size));
87 87
88 sample_rate = params.sample_rate; 88 sample_rate = params.sample_rate;
89 channel_count = params.channel_count; 89 channel_count = params.channel_count;
90 total_stream_count = params.total_stream_count; 90 total_stream_count = params.total_stream_count;
91 stereo_stream_count = params.stereo_stream_count; 91 stereo_stream_count = params.stereo_stream_count;
92 use_large_frame_size = params.use_large_frame_size; 92 use_large_frame_size = params.use_large_frame_size;
93 decode_object_initialized = true; 93 decode_object_initialized = true;
94 R_SUCCEED(); 94 R_SUCCEED();
95} 95}
96 96
97Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, 97Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
98 u32* out_sample_count, std::span<const u8> input_data, 98 u32* out_sample_count, std::span<const u8> input_data,
99 std::span<u8> output_data, bool reset) { 99 std::span<u8> output_data, bool reset) {
100 u32 out_samples; 100 u32 out_samples;
101 u64 time_taken{}; 101 u64 time_taken{};
102 102
103 R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); 103 R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
104 104
105 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; 105 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
106 OpusPacketHeader header{ReverseHeader(*header_p)}; 106 OpusPacketHeader header{ReverseHeader(*header_p)};
107 107
108 R_UNLESS(in_data.size_bytes() >= header.size && 108 R_UNLESS(in_data.size_bytes() >= header.size &&
109 header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), 109 header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
110 ResultBufferTooSmall); 110 ResultBufferTooSmall);
111 111
112 if (!shared_memory_mapped) { 112 if (!shared_memory_mapped) {
113 R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); 113 R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
114 shared_memory_mapped = true; 114 shared_memory_mapped = true;
115 } 115 }
116 116
117 std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); 117 std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
118 118
119 R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), 119 R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
120 channel_count, in_data.data(), header.size, 120 channel_count, in_data.data(), header.size,
121 shared_buffer.get(), time_taken, reset)); 121 shared_buffer.get(), time_taken, reset));
122 122
123 std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); 123 std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
124 124
125 *out_data_size = header.size + sizeof(OpusPacketHeader); 125 *out_data_size = header.size + sizeof(OpusPacketHeader);
126 *out_sample_count = out_samples; 126 *out_sample_count = out_samples;
127 if (out_time_taken) { 127 if (out_time_taken) {
128 *out_time_taken = time_taken / 1000; 128 *out_time_taken = time_taken / 1000;
129 } 129 }
130 R_SUCCEED(); 130 R_SUCCEED();
131} 131}
132 132
133Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { 133Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
134 R_SUCCEED_IF(shared_memory_mapped); 134 R_SUCCEED_IF(shared_memory_mapped);
135 shared_memory_mapped = true; 135 shared_memory_mapped = true;
136 R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); 136 R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
137} 137}
138 138
139Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, 139Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
140 u32* out_sample_count, 140 u32* out_sample_count,
141 std::span<const u8> input_data, 141 std::span<const u8> input_data,
142 std::span<u8> output_data, bool reset) { 142 std::span<u8> output_data, bool reset) {
143 u32 out_samples; 143 u32 out_samples;
144 u64 time_taken{}; 144 u64 time_taken{};
145 145
146 R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); 146 R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
147 147
148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; 148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
149 OpusPacketHeader header{ReverseHeader(*header_p)}; 149 OpusPacketHeader header{ReverseHeader(*header_p)};
150 150
151 LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", 151 LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
152 header.size, input_data.size_bytes(), in_data.size_bytes()); 152 header.size, input_data.size_bytes(), in_data.size_bytes());
153 153
154 R_UNLESS(in_data.size_bytes() >= header.size && 154 R_UNLESS(in_data.size_bytes() >= header.size &&
155 header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), 155 header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
156 ResultBufferTooSmall); 156 ResultBufferTooSmall);
157 157
158 if (!shared_memory_mapped) { 158 if (!shared_memory_mapped) {
159 R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); 159 R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
160 shared_memory_mapped = true; 160 shared_memory_mapped = true;
161 } 161 }
162 162
163 std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); 163 std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
164 164
165 R_TRY(hardware_opus.DecodeInterleavedForMultiStream( 165 R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
166 out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), 166 out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
167 header.size, shared_buffer.get(), time_taken, reset)); 167 header.size, shared_buffer.get(), time_taken, reset));
168 168
169 std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); 169 std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
170 170
171 *out_data_size = header.size + sizeof(OpusPacketHeader); 171 *out_data_size = header.size + sizeof(OpusPacketHeader);
172 *out_sample_count = out_samples; 172 *out_sample_count = out_samples;
173 if (out_time_taken) { 173 if (out_time_taken) {
174 *out_time_taken = time_taken / 1000; 174 *out_time_taken = time_taken / 1000;
175 } 175 }
176 R_SUCCEED(); 176 R_SUCCEED();
177} 177}
178 178
179} // namespace AudioCore::OpusDecoder 179} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h
index d08d8a4a4..fd728958a 100644
--- a/src/audio_core/opus/decoder.h
+++ b/src/audio_core/opus/decoder.h
@@ -1,53 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <span> 6#include <span>
7 7
8#include "audio_core/opus/parameters.h" 8#include "audio_core/opus/parameters.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/k_transfer_memory.h" 10#include "core/hle/kernel/k_transfer_memory.h"
11#include "core/hle/service/audio/errors.h" 11#include "core/hle/service/audio/errors.h"
12 12
13namespace Core { 13namespace Core {
14class System; 14class System;
15} 15}
16 16
17namespace AudioCore::OpusDecoder { 17namespace AudioCore::OpusDecoder {
18class HardwareOpus; 18class HardwareOpus;
19 19
20class OpusDecoder { 20class OpusDecoder {
21public: 21public:
22 explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); 22 explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
23 ~OpusDecoder(); 23 ~OpusDecoder();
24 24
25 Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, 25 Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
26 u64 transfer_memory_size); 26 u64 transfer_memory_size);
27 Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, 27 Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
28 u64 transfer_memory_size); 28 u64 transfer_memory_size);
29 Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, 29 Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
30 std::span<const u8> input_data, std::span<u8> output_data, bool reset); 30 std::span<const u8> input_data, std::span<u8> output_data, bool reset);
31 Result SetContext([[maybe_unused]] std::span<const u8> context); 31 Result SetContext([[maybe_unused]] std::span<const u8> context);
32 Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, 32 Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
33 u32* out_sample_count, std::span<const u8> input_data, 33 u32* out_sample_count, std::span<const u8> input_data,
34 std::span<u8> output_data, bool reset); 34 std::span<u8> output_data, bool reset);
35 35
36private: 36private:
37 Core::System& system; 37 Core::System& system;
38 HardwareOpus& hardware_opus; 38 HardwareOpus& hardware_opus;
39 std::unique_ptr<u8[]> shared_buffer{}; 39 std::unique_ptr<u8[]> shared_buffer{};
40 u64 shared_buffer_size; 40 u64 shared_buffer_size;
41 std::span<u8> in_data{}; 41 std::span<u8> in_data{};
42 std::span<u8> out_data{}; 42 std::span<u8> out_data{};
43 u64 buffer_size{}; 43 u64 buffer_size{};
44 s32 sample_rate{}; 44 s32 sample_rate{};
45 s32 channel_count{}; 45 s32 channel_count{};
46 bool use_large_frame_size{false}; 46 bool use_large_frame_size{false};
47 s32 total_stream_count{}; 47 s32 total_stream_count{};
48 s32 stereo_stream_count{}; 48 s32 stereo_stream_count{};
49 bool shared_memory_mapped{false}; 49 bool shared_memory_mapped{false};
50 bool decode_object_initialized{false}; 50 bool decode_object_initialized{false};
51}; 51};
52 52
53} // namespace AudioCore::OpusDecoder 53} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp
index 4a5382973..1464880a1 100644
--- a/src/audio_core/opus/decoder_manager.cpp
+++ b/src/audio_core/opus/decoder_manager.cpp
@@ -1,102 +1,102 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "audio_core/adsp/apps/opus/opus_decoder.h" 4#include "audio_core/adsp/apps/opus/opus_decoder.h"
5#include "audio_core/opus/decoder_manager.h" 5#include "audio_core/opus/decoder_manager.h"
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "core/core.h" 7#include "core/core.h"
8 8
9namespace AudioCore::OpusDecoder { 9namespace AudioCore::OpusDecoder {
10using namespace Service::Audio; 10using namespace Service::Audio;
11 11
12namespace { 12namespace {
13bool IsValidChannelCount(u32 channel_count) { 13bool IsValidChannelCount(u32 channel_count) {
14 return channel_count == 1 || channel_count == 2; 14 return channel_count == 1 || channel_count == 2;
15} 15}
16 16
17bool IsValidMultiStreamChannelCount(u32 channel_count) { 17bool IsValidMultiStreamChannelCount(u32 channel_count) {
18 return channel_count > 0 && channel_count <= OpusStreamCountMax; 18 return channel_count > 0 && channel_count <= OpusStreamCountMax;
19} 19}
20 20
21bool IsValidSampleRate(u32 sample_rate) { 21bool IsValidSampleRate(u32 sample_rate) {
22 return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || 22 return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
23 sample_rate == 24'000 || sample_rate == 48'000; 23 sample_rate == 24'000 || sample_rate == 48'000;
24} 24}
25 25
26bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { 26bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
27 return total_stream_count > 0 && stereo_stream_count > 0 && 27 return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
28 stereo_stream_count <= total_stream_count && 28 stereo_stream_count <= total_stream_count &&
29 total_stream_count + stereo_stream_count <= channel_count; 29 total_stream_count + stereo_stream_count <= channel_count;
30} 30}
31 31
32} // namespace 32} // namespace
33 33
34OpusDecoderManager::OpusDecoderManager(Core::System& system_) 34OpusDecoderManager::OpusDecoderManager(Core::System& system_)
35 : system{system_}, hardware_opus{system} { 35 : system{system_}, hardware_opus{system} {
36 for (u32 i = 0; i < MaxChannels; i++) { 36 for (u32 i = 0; i < MaxChannels; i++) {
37 required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); 37 required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
38 } 38 }
39} 39}
40 40
41Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { 41Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
42 OpusParametersEx ex{ 42 OpusParametersEx ex{
43 .sample_rate = params.sample_rate, 43 .sample_rate = params.sample_rate,
44 .channel_count = params.channel_count, 44 .channel_count = params.channel_count,
45 .use_large_frame_size = false, 45 .use_large_frame_size = false,
46 }; 46 };
47 R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); 47 R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
48} 48}
49 49
50Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { 50Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
51 R_RETURN(GetWorkBufferSizeExEx(params, out_size)); 51 R_RETURN(GetWorkBufferSizeExEx(params, out_size));
52} 52}
53 53
54Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { 54Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
55 R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); 55 R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
56 R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); 56 R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
57 57
58 auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; 58 auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
59 auto frame_size{params.use_large_frame_size ? 5760 : 1920}; 59 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
60 work_buffer_size += 60 work_buffer_size +=
61 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); 61 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
62 out_size = work_buffer_size + 0x600; 62 out_size = work_buffer_size + 0x600;
63 R_SUCCEED(); 63 R_SUCCEED();
64} 64}
65 65
66Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, 66Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
67 u64& out_size) { 67 u64& out_size) {
68 OpusMultiStreamParametersEx ex{ 68 OpusMultiStreamParametersEx ex{
69 .sample_rate = params.sample_rate, 69 .sample_rate = params.sample_rate,
70 .channel_count = params.channel_count, 70 .channel_count = params.channel_count,
71 .total_stream_count = params.total_stream_count, 71 .total_stream_count = params.total_stream_count,
72 .stereo_stream_count = params.stereo_stream_count, 72 .stereo_stream_count = params.stereo_stream_count,
73 .use_large_frame_size = false, 73 .use_large_frame_size = false,
74 .mappings = {}, 74 .mappings = {},
75 }; 75 };
76 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); 76 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
77} 77}
78 78
79Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, 79Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
80 u64& out_size) { 80 u64& out_size) {
81 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); 81 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
82} 82}
83 83
84Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, 84Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
85 u64& out_size) { 85 u64& out_size) {
86 R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); 86 R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
87 R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); 87 R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
88 R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, 88 R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
89 params.stereo_stream_count), 89 params.stereo_stream_count),
90 ResultInvalidOpusSampleRate); 90 ResultInvalidOpusSampleRate);
91 91
92 auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( 92 auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
93 params.total_stream_count, params.stereo_stream_count)}; 93 params.total_stream_count, params.stereo_stream_count)};
94 auto frame_size{params.use_large_frame_size ? 5760 : 1920}; 94 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
95 work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); 95 work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64);
96 work_buffer_size += 96 work_buffer_size +=
97 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); 97 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
98 out_size = work_buffer_size; 98 out_size = work_buffer_size;
99 R_SUCCEED(); 99 R_SUCCEED();
100} 100}
101 101
102} // namespace AudioCore::OpusDecoder 102} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h
index 466e1967b..70ebc4bab 100644
--- a/src/audio_core/opus/decoder_manager.h
+++ b/src/audio_core/opus/decoder_manager.h
@@ -1,38 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include "audio_core/opus/hardware_opus.h" 6#include "audio_core/opus/hardware_opus.h"
7#include "audio_core/opus/parameters.h" 7#include "audio_core/opus/parameters.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/service/audio/errors.h" 9#include "core/hle/service/audio/errors.h"
10 10
11namespace Core { 11namespace Core {
12class System; 12class System;
13} 13}
14 14
15namespace AudioCore::OpusDecoder { 15namespace AudioCore::OpusDecoder {
16 16
17class OpusDecoderManager { 17class OpusDecoderManager {
18public: 18public:
19 OpusDecoderManager(Core::System& system); 19 OpusDecoderManager(Core::System& system);
20 20
21 HardwareOpus& GetHardwareOpus() { 21 HardwareOpus& GetHardwareOpus() {
22 return hardware_opus; 22 return hardware_opus;
23 } 23 }
24 24
25 Result GetWorkBufferSize(OpusParameters& params, u64& out_size); 25 Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
26 Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); 26 Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
27 Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); 27 Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
28 Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); 28 Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
29 Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); 29 Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
30 Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); 30 Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
31 31
32private: 32private:
33 Core::System& system; 33 Core::System& system;
34 HardwareOpus hardware_opus; 34 HardwareOpus hardware_opus;
35 std::array<u64, MaxChannels> required_workbuffer_sizes{}; 35 std::array<u64, MaxChannels> required_workbuffer_sizes{};
36}; 36};
37 37
38} // namespace AudioCore::OpusDecoder 38} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp
index d6544dcb0..5ff71ab2d 100644
--- a/src/audio_core/opus/hardware_opus.cpp
+++ b/src/audio_core/opus/hardware_opus.cpp
@@ -1,241 +1,241 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array> 4#include <array>
5 5
6#include "audio_core/audio_core.h" 6#include "audio_core/audio_core.h"
7#include "audio_core/opus/hardware_opus.h" 7#include "audio_core/opus/hardware_opus.h"
8#include "core/core.h" 8#include "core/core.h"
9 9
10namespace AudioCore::OpusDecoder { 10namespace AudioCore::OpusDecoder {
11namespace { 11namespace {
12using namespace Service::Audio; 12using namespace Service::Audio;
13 13
14static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { 14static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
15 s32 error{static_cast<s32>(error_code)}; 15 s32 error{static_cast<s32>(error_code)};
16 ASSERT(error <= OPUS_OK); 16 ASSERT(error <= OPUS_OK);
17 switch (error) { 17 switch (error) {
18 case OPUS_ALLOC_FAIL: 18 case OPUS_ALLOC_FAIL:
19 R_THROW(ResultLibOpusAllocFail); 19 R_THROW(ResultLibOpusAllocFail);
20 case OPUS_INVALID_STATE: 20 case OPUS_INVALID_STATE:
21 R_THROW(ResultLibOpusInvalidState); 21 R_THROW(ResultLibOpusInvalidState);
22 case OPUS_UNIMPLEMENTED: 22 case OPUS_UNIMPLEMENTED:
23 R_THROW(ResultLibOpusUnimplemented); 23 R_THROW(ResultLibOpusUnimplemented);
24 case OPUS_INVALID_PACKET: 24 case OPUS_INVALID_PACKET:
25 R_THROW(ResultLibOpusInvalidPacket); 25 R_THROW(ResultLibOpusInvalidPacket);
26 case OPUS_INTERNAL_ERROR: 26 case OPUS_INTERNAL_ERROR:
27 R_THROW(ResultLibOpusInternalError); 27 R_THROW(ResultLibOpusInternalError);
28 case OPUS_BUFFER_TOO_SMALL: 28 case OPUS_BUFFER_TOO_SMALL:
29 R_THROW(ResultBufferTooSmall); 29 R_THROW(ResultBufferTooSmall);
30 case OPUS_BAD_ARG: 30 case OPUS_BAD_ARG:
31 R_THROW(ResultLibOpusBadArg); 31 R_THROW(ResultLibOpusBadArg);
32 case OPUS_OK: 32 case OPUS_OK:
33 R_RETURN(ResultSuccess); 33 R_RETURN(ResultSuccess);
34 } 34 }
35 UNREACHABLE(); 35 UNREACHABLE();
36} 36}
37 37
38} // namespace 38} // namespace
39 39
40HardwareOpus::HardwareOpus(Core::System& system_) 40HardwareOpus::HardwareOpus(Core::System& system_)
41 : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { 41 : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
42 opus_decoder.SetSharedMemory(shared_memory); 42 opus_decoder.SetSharedMemory(shared_memory);
43} 43}
44 44
45u64 HardwareOpus::GetWorkBufferSize(u32 channel) { 45u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
46 if (!opus_decoder.IsRunning()) { 46 if (!opus_decoder.IsRunning()) {
47 return 0; 47 return 0;
48 } 48 }
49 std::scoped_lock l{mutex}; 49 std::scoped_lock l{mutex};
50 shared_memory.host_send_data[0] = channel; 50 shared_memory.host_send_data[0] = channel;
51 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); 51 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
52 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 52 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
53 if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { 53 if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
54 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 54 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
55 ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); 55 ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
56 return 0; 56 return 0;
57 } 57 }
58 return shared_memory.dsp_return_data[0]; 58 return shared_memory.dsp_return_data[0];
59} 59}
60 60
61u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { 61u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
62 std::scoped_lock l{mutex}; 62 std::scoped_lock l{mutex};
63 shared_memory.host_send_data[0] = total_stream_count; 63 shared_memory.host_send_data[0] = total_stream_count;
64 shared_memory.host_send_data[1] = stereo_stream_count; 64 shared_memory.host_send_data[1] = stereo_stream_count;
65 opus_decoder.Send(ADSP::Direction::DSP, 65 opus_decoder.Send(ADSP::Direction::DSP,
66 ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); 66 ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
67 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 67 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
68 if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { 68 if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
69 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 69 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
70 ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); 70 ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
71 return 0; 71 return 0;
72 } 72 }
73 return shared_memory.dsp_return_data[0]; 73 return shared_memory.dsp_return_data[0];
74} 74}
75 75
76Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, 76Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
77 u64 buffer_size) { 77 u64 buffer_size) {
78 std::scoped_lock l{mutex}; 78 std::scoped_lock l{mutex};
79 shared_memory.host_send_data[0] = (u64)buffer; 79 shared_memory.host_send_data[0] = (u64)buffer;
80 shared_memory.host_send_data[1] = buffer_size; 80 shared_memory.host_send_data[1] = buffer_size;
81 shared_memory.host_send_data[2] = sample_rate; 81 shared_memory.host_send_data[2] = sample_rate;
82 shared_memory.host_send_data[3] = channel_count; 82 shared_memory.host_send_data[3] = channel_count;
83 83
84 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); 84 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
85 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 85 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
86 if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { 86 if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
87 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 87 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
88 ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); 88 ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
89 R_THROW(ResultInvalidOpusDSPReturnCode); 89 R_THROW(ResultInvalidOpusDSPReturnCode);
90 } 90 }
91 91
92 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); 92 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
93} 93}
94 94
95Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, 95Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
96 u32 total_stream_count, 96 u32 total_stream_count,
97 u32 stereo_stream_count, void* mappings, 97 u32 stereo_stream_count, void* mappings,
98 void* buffer, u64 buffer_size) { 98 void* buffer, u64 buffer_size) {
99 std::scoped_lock l{mutex}; 99 std::scoped_lock l{mutex};
100 shared_memory.host_send_data[0] = (u64)buffer; 100 shared_memory.host_send_data[0] = (u64)buffer;
101 shared_memory.host_send_data[1] = buffer_size; 101 shared_memory.host_send_data[1] = buffer_size;
102 shared_memory.host_send_data[2] = sample_rate; 102 shared_memory.host_send_data[2] = sample_rate;
103 shared_memory.host_send_data[3] = channel_count; 103 shared_memory.host_send_data[3] = channel_count;
104 shared_memory.host_send_data[4] = total_stream_count; 104 shared_memory.host_send_data[4] = total_stream_count;
105 shared_memory.host_send_data[5] = stereo_stream_count; 105 shared_memory.host_send_data[5] = stereo_stream_count;
106 106
107 ASSERT(channel_count <= MaxChannels); 107 ASSERT(channel_count <= MaxChannels);
108 std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); 108 std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
109 109
110 opus_decoder.Send(ADSP::Direction::DSP, 110 opus_decoder.Send(ADSP::Direction::DSP,
111 ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); 111 ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
112 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 112 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
113 if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { 113 if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
114 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 114 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
115 ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); 115 ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
116 R_THROW(ResultInvalidOpusDSPReturnCode); 116 R_THROW(ResultInvalidOpusDSPReturnCode);
117 } 117 }
118 118
119 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); 119 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
120} 120}
121 121
122Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { 122Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
123 std::scoped_lock l{mutex}; 123 std::scoped_lock l{mutex};
124 shared_memory.host_send_data[0] = (u64)buffer; 124 shared_memory.host_send_data[0] = (u64)buffer;
125 shared_memory.host_send_data[1] = buffer_size; 125 shared_memory.host_send_data[1] = buffer_size;
126 126
127 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); 127 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
128 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 128 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
129 ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, 129 ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
130 "Expected Opus shutdown code {}, got {}", 130 "Expected Opus shutdown code {}, got {}",
131 ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); 131 ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
132 132
133 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); 133 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
134} 134}
135 135
136Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { 136Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
137 std::scoped_lock l{mutex}; 137 std::scoped_lock l{mutex};
138 shared_memory.host_send_data[0] = (u64)buffer; 138 shared_memory.host_send_data[0] = (u64)buffer;
139 shared_memory.host_send_data[1] = buffer_size; 139 shared_memory.host_send_data[1] = buffer_size;
140 140
141 opus_decoder.Send(ADSP::Direction::DSP, 141 opus_decoder.Send(ADSP::Direction::DSP,
142 ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); 142 ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
143 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 143 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
144 ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, 144 ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
145 "Expected Opus shutdown code {}, got {}", 145 "Expected Opus shutdown code {}, got {}",
146 ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); 146 ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
147 147
148 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); 148 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
149} 149}
150 150
151Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, 151Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
152 u64 output_data_size, u32 channel_count, void* input_data, 152 u64 output_data_size, u32 channel_count, void* input_data,
153 u64 input_data_size, void* buffer, u64& out_time_taken, 153 u64 input_data_size, void* buffer, u64& out_time_taken,
154 bool reset) { 154 bool reset) {
155 std::scoped_lock l{mutex}; 155 std::scoped_lock l{mutex};
156 shared_memory.host_send_data[0] = (u64)buffer; 156 shared_memory.host_send_data[0] = (u64)buffer;
157 shared_memory.host_send_data[1] = (u64)input_data; 157 shared_memory.host_send_data[1] = (u64)input_data;
158 shared_memory.host_send_data[2] = input_data_size; 158 shared_memory.host_send_data[2] = input_data_size;
159 shared_memory.host_send_data[3] = (u64)output_data; 159 shared_memory.host_send_data[3] = (u64)output_data;
160 shared_memory.host_send_data[4] = output_data_size; 160 shared_memory.host_send_data[4] = output_data_size;
161 shared_memory.host_send_data[5] = 0; 161 shared_memory.host_send_data[5] = 0;
162 shared_memory.host_send_data[6] = reset; 162 shared_memory.host_send_data[6] = reset;
163 163
164 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); 164 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
165 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 165 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
166 if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { 166 if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
167 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 167 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
168 ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); 168 ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
169 R_THROW(ResultInvalidOpusDSPReturnCode); 169 R_THROW(ResultInvalidOpusDSPReturnCode);
170 } 170 }
171 171
172 auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; 172 auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
173 if (error_code == OPUS_OK) { 173 if (error_code == OPUS_OK) {
174 out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); 174 out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
175 out_time_taken = 1000 * shared_memory.dsp_return_data[2]; 175 out_time_taken = 1000 * shared_memory.dsp_return_data[2];
176 } 176 }
177 R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); 177 R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
178} 178}
179 179
180Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, 180Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
181 u64 output_data_size, u32 channel_count, 181 u64 output_data_size, u32 channel_count,
182 void* input_data, u64 input_data_size, 182 void* input_data, u64 input_data_size,
183 void* buffer, u64& out_time_taken, 183 void* buffer, u64& out_time_taken,
184 bool reset) { 184 bool reset) {
185 std::scoped_lock l{mutex}; 185 std::scoped_lock l{mutex};
186 shared_memory.host_send_data[0] = (u64)buffer; 186 shared_memory.host_send_data[0] = (u64)buffer;
187 shared_memory.host_send_data[1] = (u64)input_data; 187 shared_memory.host_send_data[1] = (u64)input_data;
188 shared_memory.host_send_data[2] = input_data_size; 188 shared_memory.host_send_data[2] = input_data_size;
189 shared_memory.host_send_data[3] = (u64)output_data; 189 shared_memory.host_send_data[3] = (u64)output_data;
190 shared_memory.host_send_data[4] = output_data_size; 190 shared_memory.host_send_data[4] = output_data_size;
191 shared_memory.host_send_data[5] = 0; 191 shared_memory.host_send_data[5] = 0;
192 shared_memory.host_send_data[6] = reset; 192 shared_memory.host_send_data[6] = reset;
193 193
194 opus_decoder.Send(ADSP::Direction::DSP, 194 opus_decoder.Send(ADSP::Direction::DSP,
195 ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); 195 ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
196 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 196 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
197 if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { 197 if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
198 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 198 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
199 ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); 199 ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
200 R_THROW(ResultInvalidOpusDSPReturnCode); 200 R_THROW(ResultInvalidOpusDSPReturnCode);
201 } 201 }
202 202
203 auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; 203 auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
204 if (error_code == OPUS_OK) { 204 if (error_code == OPUS_OK) {
205 out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); 205 out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
206 out_time_taken = 1000 * shared_memory.dsp_return_data[2]; 206 out_time_taken = 1000 * shared_memory.dsp_return_data[2];
207 } 207 }
208 R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); 208 R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
209} 209}
210 210
211Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { 211Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
212 std::scoped_lock l{mutex}; 212 std::scoped_lock l{mutex};
213 shared_memory.host_send_data[0] = (u64)buffer; 213 shared_memory.host_send_data[0] = (u64)buffer;
214 shared_memory.host_send_data[1] = buffer_size; 214 shared_memory.host_send_data[1] = buffer_size;
215 215
216 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); 216 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
217 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 217 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
218 if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { 218 if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
219 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 219 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
220 ADSP::OpusDecoder::Message::MapMemoryOK, msg); 220 ADSP::OpusDecoder::Message::MapMemoryOK, msg);
221 R_THROW(ResultInvalidOpusDSPReturnCode); 221 R_THROW(ResultInvalidOpusDSPReturnCode);
222 } 222 }
223 R_SUCCEED(); 223 R_SUCCEED();
224} 224}
225 225
226Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { 226Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
227 std::scoped_lock l{mutex}; 227 std::scoped_lock l{mutex};
228 shared_memory.host_send_data[0] = (u64)buffer; 228 shared_memory.host_send_data[0] = (u64)buffer;
229 shared_memory.host_send_data[1] = buffer_size; 229 shared_memory.host_send_data[1] = buffer_size;
230 230
231 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); 231 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
232 auto msg = opus_decoder.Receive(ADSP::Direction::Host); 232 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
233 if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { 233 if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
234 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", 234 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
235 ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); 235 ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
236 R_THROW(ResultInvalidOpusDSPReturnCode); 236 R_THROW(ResultInvalidOpusDSPReturnCode);
237 } 237 }
238 R_SUCCEED(); 238 R_SUCCEED();
239} 239}
240 240
241} // namespace AudioCore::OpusDecoder 241} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h
index 7013a6b40..b10184baa 100644
--- a/src/audio_core/opus/hardware_opus.h
+++ b/src/audio_core/opus/hardware_opus.h
@@ -1,45 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <mutex> 6#include <mutex>
7#include <opus.h> 7#include <opus.h>
8 8
9#include "audio_core/adsp/apps/opus/opus_decoder.h" 9#include "audio_core/adsp/apps/opus/opus_decoder.h"
10#include "audio_core/adsp/apps/opus/shared_memory.h" 10#include "audio_core/adsp/apps/opus/shared_memory.h"
11#include "audio_core/adsp/mailbox.h" 11#include "audio_core/adsp/mailbox.h"
12#include "core/hle/service/audio/errors.h" 12#include "core/hle/service/audio/errors.h"
13 13
14namespace AudioCore::OpusDecoder { 14namespace AudioCore::OpusDecoder {
15class HardwareOpus { 15class HardwareOpus {
16public: 16public:
17 HardwareOpus(Core::System& system); 17 HardwareOpus(Core::System& system);
18 18
19 u64 GetWorkBufferSize(u32 channel); 19 u64 GetWorkBufferSize(u32 channel);
20 u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); 20 u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
21 21
22 Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, 22 Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
23 u64 buffer_size); 23 u64 buffer_size);
24 Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, 24 Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
25 u32 totaL_stream_count, u32 stereo_stream_count, 25 u32 totaL_stream_count, u32 stereo_stream_count,
26 void* mappings, void* buffer, u64 buffer_size); 26 void* mappings, void* buffer, u64 buffer_size);
27 Result ShutdownDecodeObject(void* buffer, u64 buffer_size); 27 Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
28 Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); 28 Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
29 Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, 29 Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
30 u32 channel_count, void* input_data, u64 input_data_size, void* buffer, 30 u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
31 u64& out_time_taken, bool reset); 31 u64& out_time_taken, bool reset);
32 Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, 32 Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
33 u64 output_data_size, u32 channel_count, 33 u64 output_data_size, u32 channel_count,
34 void* input_data, u64 input_data_size, void* buffer, 34 void* input_data, u64 input_data_size, void* buffer,
35 u64& out_time_taken, bool reset); 35 u64& out_time_taken, bool reset);
36 Result MapMemory(void* buffer, u64 buffer_size); 36 Result MapMemory(void* buffer, u64 buffer_size);
37 Result UnmapMemory(void* buffer, u64 buffer_size); 37 Result UnmapMemory(void* buffer, u64 buffer_size);
38 38
39private: 39private:
40 Core::System& system; 40 Core::System& system;
41 std::mutex mutex; 41 std::mutex mutex;
42 ADSP::OpusDecoder::OpusDecoder& opus_decoder; 42 ADSP::OpusDecoder::OpusDecoder& opus_decoder;
43 ADSP::OpusDecoder::SharedMemory shared_memory; 43 ADSP::OpusDecoder::SharedMemory shared_memory;
44}; 44};
45} // namespace AudioCore::OpusDecoder 45} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index bbb598bc5..51a23fe15 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -146,7 +146,7 @@ public:
146 return; 146 return;
147 } 147 }
148 148
149 paused = true; 149 SignalPause();
150 if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { 150 if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
151 LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); 151 LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
152 } 152 }
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index 7b89151de..96e0efce2 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -111,7 +111,7 @@ public:
111 if (device == 0 || paused) { 111 if (device == 0 || paused) {
112 return; 112 return;
113 } 113 }
114 paused = true; 114 SignalPause();
115 SDL_PauseAudioDevice(device, 1); 115 SDL_PauseAudioDevice(device, 1);
116 } 116 }
117 117
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index d66d04fae..2a09db599 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
282void SinkStream::WaitFreeSpace(std::stop_token stop_token) { 282void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
283 std::unique_lock lk{release_mutex}; 283 std::unique_lock lk{release_mutex};
284 release_cv.wait_for(lk, std::chrono::milliseconds(5), 284 release_cv.wait_for(lk, std::chrono::milliseconds(5),
285 [this]() { return queued_buffers < max_queue_size; }); 285 [this]() { return paused || queued_buffers < max_queue_size; });
286 if (queued_buffers > max_queue_size + 3) { 286 if (queued_buffers > max_queue_size + 3) {
287 Common::CondvarWait(release_cv, lk, stop_token, 287 Common::CondvarWait(release_cv, lk, stop_token,
288 [this] { return queued_buffers < max_queue_size; }); 288 [this] { return paused || queued_buffers < max_queue_size; });
289 } 289 }
290} 290}
291 291
292void SinkStream::SignalPause() {
293 {
294 std::scoped_lock lk{release_mutex};
295 paused = true;
296 }
297 release_cv.notify_one();
298}
299
292} // namespace AudioCore::Sink 300} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 6a4996ca3..f2ccd19b8 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -214,6 +214,12 @@ public:
214 void WaitFreeSpace(std::stop_token stop_token); 214 void WaitFreeSpace(std::stop_token stop_token);
215 215
216protected: 216protected:
217 /**
218 * Unblocks the ADSP if the stream is paused.
219 */
220 void SignalPause();
221
222protected:
217 /// Core system 223 /// Core system
218 Core::System& system; 224 Core::System& system;
219 /// Type of this stream 225 /// Type of this stream
diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp
index 88fdba527..f437d7187 100644
--- a/src/common/arm64/native_clock.cpp
+++ b/src/common/arm64/native_clock.cpp
@@ -1,6 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifdef ANDROID
5#include <sys/system_properties.h>
6#endif
4#include "common/arm64/native_clock.h" 7#include "common/arm64/native_clock.h"
5 8
6namespace Common::Arm64 { 9namespace Common::Arm64 {
@@ -65,7 +68,23 @@ bool NativeClock::IsNative() const {
65 68
66u64 NativeClock::GetHostCNTFRQ() { 69u64 NativeClock::GetHostCNTFRQ() {
67 u64 cntfrq_el0 = 0; 70 u64 cntfrq_el0 = 0;
68 asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); 71 std::string_view board{""};
72#ifdef ANDROID
73 char buffer[PROP_VALUE_MAX];
74 int len{__system_property_get("ro.product.board", buffer)};
75 board = std::string_view(buffer, static_cast<size_t>(len));
76#endif
77 if (board == "s5e9925") { // Exynos 2200
78 cntfrq_el0 = 25600000;
79 } else if (board == "exynos2100") { // Exynos 2100
80 cntfrq_el0 = 26000000;
81 } else if (board == "exynos9810") { // Exynos 9810
82 cntfrq_el0 = 26000000;
83 } else if (board == "s5e8825") { // Exynos 1280
84 cntfrq_el0 = 26000000;
85 } else {
86 asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
87 }
69 return cntfrq_el0; 88 return cntfrq_el0;
70} 89}
71 90
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 4b1690269..166dc3dce 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -9,12 +9,12 @@ PageTable::PageTable() = default;
9 9
10PageTable::~PageTable() noexcept = default; 10PageTable::~PageTable() noexcept = default;
11 11
12bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, 12bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
13 u64 address) const { 13 Common::ProcessAddress address) const {
14 // Setup invalid defaults. 14 // Setup invalid defaults.
15 out_entry.phys_addr = 0; 15 out_entry->phys_addr = 0;
16 out_entry.block_size = page_size; 16 out_entry->block_size = page_size;
17 out_context.next_page = 0; 17 out_context->next_page = 0;
18 18
19 // Validate that we can read the actual entry. 19 // Validate that we can read the actual entry.
20 const auto page = address / page_size; 20 const auto page = address / page_size;
@@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_
29 } 29 }
30 30
31 // Populate the results. 31 // Populate the results.
32 out_entry.phys_addr = phys_addr + address; 32 out_entry->phys_addr = phys_addr + GetInteger(address);
33 out_context.next_page = page + 1; 33 out_context->next_page = page + 1;
34 out_context.next_offset = address + page_size; 34 out_context->next_offset = GetInteger(address) + page_size;
35 35
36 return true; 36 return true;
37} 37}
38 38
39bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { 39bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
40 // Setup invalid defaults. 40 // Setup invalid defaults.
41 out_entry.phys_addr = 0; 41 out_entry->phys_addr = 0;
42 out_entry.block_size = page_size; 42 out_entry->block_size = page_size;
43 43
44 // Validate that we can read the actual entry. 44 // Validate that we can read the actual entry.
45 const auto page = context.next_page; 45 const auto page = context->next_page;
46 if (page >= backing_addr.size()) { 46 if (page >= backing_addr.size()) {
47 return false; 47 return false;
48 } 48 }
@@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c
54 } 54 }
55 55
56 // Populate the results. 56 // Populate the results.
57 out_entry.phys_addr = phys_addr + context.next_offset; 57 out_entry->phys_addr = phys_addr + context->next_offset;
58 context.next_page = page + 1; 58 context->next_page = page + 1;
59 context.next_offset += page_size; 59 context->next_offset += page_size;
60 60
61 return true; 61 return true;
62} 62}
diff --git a/src/common/page_table.h b/src/common/page_table.h
index e653d52ad..5340f7d86 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -6,6 +6,7 @@
6#include <atomic> 6#include <atomic>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/typed_address.h"
9#include "common/virtual_buffer.h" 10#include "common/virtual_buffer.h"
10 11
11namespace Common { 12namespace Common {
@@ -100,9 +101,9 @@ struct PageTable {
100 PageTable(PageTable&&) noexcept = default; 101 PageTable(PageTable&&) noexcept = default;
101 PageTable& operator=(PageTable&&) noexcept = default; 102 PageTable& operator=(PageTable&&) noexcept = default;
102 103
103 bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, 104 bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
104 u64 address) const; 105 Common::ProcessAddress address) const;
105 bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; 106 bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
106 107
107 /** 108 /**
108 * Resizes the page table to be able to accommodate enough pages within 109 * Resizes the page table to be able to accommodate enough pages within
@@ -117,6 +118,16 @@ struct PageTable {
117 return current_address_space_width_in_bits; 118 return current_address_space_width_in_bits;
118 } 119 }
119 120
121 bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
122 Common::ProcessAddress virt_addr) const {
123 if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
124 return false;
125 }
126
127 *out_phys_addr = backing_addr[virt_addr / page_size] + GetInteger(virt_addr);
128 return true;
129 }
130
120 /** 131 /**
121 * Vector of memory pointers backing each page. An entry can only be non-null if the 132 * Vector of memory pointers backing each page. An entry can only be non-null if the
122 * corresponding attribute element is of type `Memory`. 133 * corresponding attribute element is of type `Memory`.
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 98b43e49c..a10131eb2 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) {
203 case Category::Ui: 203 case Category::Ui:
204 case Category::UiGeneral: 204 case Category::UiGeneral:
205 return "UI"; 205 return "UI";
206 case Category::UiAudio:
207 return "UiAudio";
206 case Category::UiLayout: 208 case Category::UiLayout:
207 return "UiLayout"; 209 return "UILayout";
208 case Category::UiGameList: 210 case Category::UiGameList:
209 return "UiGameList"; 211 return "UIGameList";
210 case Category::Screenshots: 212 case Category::Screenshots:
211 return "Screenshots"; 213 return "Screenshots";
212 case Category::Shortcuts: 214 case Category::Shortcuts:
diff --git a/src/common/settings.h b/src/common/settings.h
index 9317075f7..b929fd957 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -153,7 +153,7 @@ struct Values {
153 true, 153 true,
154 true}; 154 true};
155 Setting<bool, false> audio_muted{ 155 Setting<bool, false> audio_muted{
156 linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; 156 linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true};
157 Setting<bool, false> dump_audio_commands{ 157 Setting<bool, false> dump_audio_commands{
158 linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; 158 linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
159 159
@@ -232,7 +232,11 @@ struct Values {
232 SwitchableSetting<bool> use_asynchronous_gpu_emulation{ 232 SwitchableSetting<bool> use_asynchronous_gpu_emulation{
233 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; 233 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
234 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, 234 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
235#ifdef ANDROID
236 AstcDecodeMode::Cpu,
237#else
235 AstcDecodeMode::Gpu, 238 AstcDecodeMode::Gpu,
239#endif
236 AstcDecodeMode::Cpu, 240 AstcDecodeMode::Cpu,
237 AstcDecodeMode::CpuAsynchronous, 241 AstcDecodeMode::CpuAsynchronous,
238 "accelerate_astc", 242 "accelerate_astc",
@@ -304,7 +308,11 @@ struct Values {
304 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; 308 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
305 309
306 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, 310 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
311#ifdef ANDROID
312 GpuAccuracy::Normal,
313#else
307 GpuAccuracy::High, 314 GpuAccuracy::High,
315#endif
308 GpuAccuracy::Normal, 316 GpuAccuracy::Normal,
309 GpuAccuracy::Extreme, 317 GpuAccuracy::Extreme,
310 "gpu_accuracy", 318 "gpu_accuracy",
@@ -313,20 +321,38 @@ struct Values {
313 true, 321 true,
314 true}; 322 true};
315 GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; 323 GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
316 SwitchableSetting<AnisotropyMode, true> max_anisotropy{ 324 SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
317 linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, 325#ifdef ANDROID
318 "max_anisotropy", Category::RendererAdvanced}; 326 AnisotropyMode::Default,
327#else
328 AnisotropyMode::Automatic,
329#endif
330 AnisotropyMode::Automatic,
331 AnisotropyMode::X16,
332 "max_anisotropy",
333 Category::RendererAdvanced};
319 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, 334 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
320 AstcRecompression::Uncompressed, 335 AstcRecompression::Uncompressed,
321 AstcRecompression::Uncompressed, 336 AstcRecompression::Uncompressed,
322 AstcRecompression::Bc3, 337 AstcRecompression::Bc3,
323 "astc_recompression", 338 "astc_recompression",
324 Category::RendererAdvanced}; 339 Category::RendererAdvanced};
325 SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", 340 SwitchableSetting<bool> async_presentation{linkage,
326 Category::RendererAdvanced}; 341#ifdef ANDROID
342 true,
343#else
344 false,
345#endif
346 "async_presentation", Category::RendererAdvanced};
327 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", 347 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
328 Category::RendererAdvanced}; 348 Category::RendererAdvanced};
329 SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", 349 SwitchableSetting<bool> use_reactive_flushing{linkage,
350#ifdef ANDROID
351 false,
352#else
353 true,
354#endif
355 "use_reactive_flushing",
330 Category::RendererAdvanced}; 356 Category::RendererAdvanced};
331 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", 357 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
332 Category::RendererAdvanced}; 358 Category::RendererAdvanced};
@@ -358,6 +384,8 @@ struct Values {
358 Category::RendererDebug}; 384 Category::RendererDebug};
359 // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control 385 // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
360 bool renderer_amdvlk_depth_bias_workaround{}; 386 bool renderer_amdvlk_depth_bias_workaround{};
387 Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
388 Category::RendererDebug};
361 389
362 // System 390 // System
363 SwitchableSetting<Language, true> language_index{linkage, 391 SwitchableSetting<Language, true> language_index{linkage,
@@ -390,7 +418,11 @@ struct Values {
390 Setting<s32> current_user{linkage, 0, "current_user", Category::System}; 418 Setting<s32> current_user{linkage, 0, "current_user", Category::System};
391 419
392 SwitchableSetting<ConsoleMode> use_docked_mode{linkage, 420 SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
421#ifdef ANDROID
422 ConsoleMode::Handheld,
423#else
393 ConsoleMode::Docked, 424 ConsoleMode::Docked,
425#endif
394 "use_docked_mode", 426 "use_docked_mode",
395 Category::System, 427 Category::System,
396 Specialization::Radio, 428 Specialization::Radio,
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 1800ab10d..7943223eb 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -32,6 +32,7 @@ enum class Category : u32 {
32 AddOns, 32 AddOns,
33 Controls, 33 Controls,
34 Ui, 34 Ui,
35 UiAudio,
35 UiGeneral, 36 UiGeneral,
36 UiLayout, 37 UiLayout,
37 UiGameList, 38 UiGameList,
diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp
index 0a6eea3cf..a6007e7b2 100644
--- a/src/common/settings_input.cpp
+++ b/src/common/settings_input.cpp
@@ -6,10 +6,11 @@
6namespace Settings { 6namespace Settings {
7namespace NativeButton { 7namespace NativeButton {
8const std::array<const char*, NumButtons> mapping = {{ 8const std::array<const char*, NumButtons> mapping = {{
9 "button_a", "button_b", "button_x", "button_y", "button_lstick", 9 "button_a", "button_b", "button_x", "button_y", "button_lstick",
10 "button_rstick", "button_l", "button_r", "button_zl", "button_zr", 10 "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
11 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", 11 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
12 "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", 12 "button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot",
13 "button_slright", "button_srright",
13}}; 14}};
14} 15}
15 16
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 46f38c703..53a95ef8f 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -29,12 +29,15 @@ enum Values : int {
29 DRight, 29 DRight,
30 DDown, 30 DDown,
31 31
32 SL, 32 SLLeft,
33 SR, 33 SRLeft,
34 34
35 Home, 35 Home,
36 Screenshot, 36 Screenshot,
37 37
38 SLRight,
39 SRRight,
40
38 NumButtons, 41 NumButtons,
39}; 42};
40 43
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e4f499135..66c10fc3f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -271,8 +271,9 @@ add_library(core STATIC
271 hle/kernel/k_page_heap.h 271 hle/kernel/k_page_heap.h
272 hle/kernel/k_page_group.cpp 272 hle/kernel/k_page_group.cpp
273 hle/kernel/k_page_group.h 273 hle/kernel/k_page_group.h
274 hle/kernel/k_page_table.cpp
275 hle/kernel/k_page_table.h 274 hle/kernel/k_page_table.h
275 hle/kernel/k_page_table_base.cpp
276 hle/kernel/k_page_table_base.h
276 hle/kernel/k_page_table_manager.h 277 hle/kernel/k_page_table_manager.h
277 hle/kernel/k_page_table_slab_heap.h 278 hle/kernel/k_page_table_slab_heap.h
278 hle/kernel/k_port.cpp 279 hle/kernel/k_port.cpp
@@ -280,6 +281,7 @@ add_library(core STATIC
280 hle/kernel/k_priority_queue.h 281 hle/kernel/k_priority_queue.h
281 hle/kernel/k_process.cpp 282 hle/kernel/k_process.cpp
282 hle/kernel/k_process.h 283 hle/kernel/k_process.h
284 hle/kernel/k_process_page_table.h
283 hle/kernel/k_readable_event.cpp 285 hle/kernel/k_readable_event.cpp
284 hle/kernel/k_readable_event.h 286 hle/kernel/k_readable_event.h
285 hle/kernel/k_resource_limit.cpp 287 hle/kernel/k_resource_limit.cpp
@@ -330,8 +332,6 @@ add_library(core STATIC
330 hle/kernel/physical_core.cpp 332 hle/kernel/physical_core.cpp
331 hle/kernel/physical_core.h 333 hle/kernel/physical_core.h
332 hle/kernel/physical_memory.h 334 hle/kernel/physical_memory.h
333 hle/kernel/process_capability.cpp
334 hle/kernel/process_capability.h
335 hle/kernel/slab_helpers.h 335 hle/kernel/slab_helpers.h
336 hle/kernel/svc.cpp 336 hle/kernel/svc.cpp
337 hle/kernel/svc.h 337 hle/kernel/svc.h
@@ -521,17 +521,28 @@ add_library(core STATIC
521 hle/service/grc/grc.h 521 hle/service/grc/grc.h
522 hle/service/hid/hid.cpp 522 hle/service/hid/hid.cpp
523 hle/service/hid/hid.h 523 hle/service/hid/hid.h
524 hle/service/hid/hid_debug_server.cpp
525 hle/service/hid/hid_debug_server.h
526 hle/service/hid/hid_firmware_settings.cpp
527 hle/service/hid/hid_firmware_settings.h
528 hle/service/hid/hid_server.cpp
529 hle/service/hid/hid_server.h
530 hle/service/hid/hid_system_server.cpp
531 hle/service/hid/hid_system_server.h
532 hle/service/hid/hid_util.h
524 hle/service/hid/hidbus.cpp 533 hle/service/hid/hidbus.cpp
525 hle/service/hid/hidbus.h 534 hle/service/hid/hidbus.h
526 hle/service/hid/irs.cpp 535 hle/service/hid/irs.cpp
527 hle/service/hid/irs.h 536 hle/service/hid/irs.h
528 hle/service/hid/irs_ring_lifo.h 537 hle/service/hid/irs_ring_lifo.h
538 hle/service/hid/resource_manager.cpp
539 hle/service/hid/resource_manager.h
529 hle/service/hid/ring_lifo.h 540 hle/service/hid/ring_lifo.h
530 hle/service/hid/xcd.cpp 541 hle/service/hid/xcd.cpp
531 hle/service/hid/xcd.h 542 hle/service/hid/xcd.h
532 hle/service/hid/errors.h 543 hle/service/hid/errors.h
533 hle/service/hid/controllers/console_sixaxis.cpp 544 hle/service/hid/controllers/console_six_axis.cpp
534 hle/service/hid/controllers/console_sixaxis.h 545 hle/service/hid/controllers/console_six_axis.h
535 hle/service/hid/controllers/controller_base.cpp 546 hle/service/hid/controllers/controller_base.cpp
536 hle/service/hid/controllers/controller_base.h 547 hle/service/hid/controllers/controller_base.h
537 hle/service/hid/controllers/debug_pad.cpp 548 hle/service/hid/controllers/debug_pad.cpp
@@ -546,6 +557,10 @@ add_library(core STATIC
546 hle/service/hid/controllers/npad.h 557 hle/service/hid/controllers/npad.h
547 hle/service/hid/controllers/palma.cpp 558 hle/service/hid/controllers/palma.cpp
548 hle/service/hid/controllers/palma.h 559 hle/service/hid/controllers/palma.h
560 hle/service/hid/controllers/seven_six_axis.cpp
561 hle/service/hid/controllers/seven_six_axis.h
562 hle/service/hid/controllers/six_axis.cpp
563 hle/service/hid/controllers/six_axis.h
549 hle/service/hid/controllers/stubbed.cpp 564 hle/service/hid/controllers/stubbed.cpp
550 hle/service/hid/controllers/stubbed.h 565 hle/service/hid/controllers/stubbed.h
551 hle/service/hid/controllers/touchscreen.cpp 566 hle/service/hid/controllers/touchscreen.cpp
@@ -715,6 +730,7 @@ add_library(core STATIC
715 hle/service/nvnflinger/producer_listener.h 730 hle/service/nvnflinger/producer_listener.h
716 hle/service/nvnflinger/status.h 731 hle/service/nvnflinger/status.h
717 hle/service/nvnflinger/ui/fence.h 732 hle/service/nvnflinger/ui/fence.h
733 hle/service/nvnflinger/ui/graphic_buffer.cpp
718 hle/service/nvnflinger/ui/graphic_buffer.h 734 hle/service/nvnflinger/ui/graphic_buffer.h
719 hle/service/nvnflinger/window.h 735 hle/service/nvnflinger/window.h
720 hle/service/olsc/olsc.cpp 736 hle/service/olsc/olsc.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 5e27dde58..558fba5bd 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -153,6 +153,14 @@ void ARM_Interface::Run() {
153 Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; 153 Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
154 HaltReason hr{}; 154 HaltReason hr{};
155 155
156 // If the thread is scheduled for termination, exit the thread.
157 if (current_thread->HasDpc()) {
158 if (current_thread->IsTerminationRequested()) {
159 current_thread->Exit();
160 UNREACHABLE();
161 }
162 }
163
156 // Notify the debugger and go to sleep if a step was performed 164 // Notify the debugger and go to sleep if a step was performed
157 // and this thread has been scheduled again. 165 // and this thread has been scheduled again.
158 if (current_thread->GetStepState() == StepState::StepPerformed) { 166 if (current_thread->GetStepState() == StepState::StepPerformed) {
@@ -174,14 +182,6 @@ void ARM_Interface::Run() {
174 } 182 }
175 system.ExitCPUProfile(); 183 system.ExitCPUProfile();
176 184
177 // If the thread is scheduled for termination, exit the thread.
178 if (current_thread->HasDpc()) {
179 if (current_thread->IsTerminationRequested()) {
180 current_thread->Exit();
181 UNREACHABLE();
182 }
183 }
184
185 // Notify the debugger and go to sleep if a breakpoint was hit, 185 // Notify the debugger and go to sleep if a breakpoint was hit,
186 // or if the thread is unable to continue for any reason. 186 // or if the thread is unable to continue for any reason.
187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { 187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index e671b270f..d6b5abc68 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
76} 76}
77 77
78void CoreTiming::ClearPendingEvents() { 78void CoreTiming::ClearPendingEvents() {
79 std::scoped_lock lock{basic_lock};
79 event_queue.clear(); 80 event_queue.clear();
80} 81}
81 82
@@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
113} 114}
114 115
115bool CoreTiming::HasPendingEvents() const { 116bool CoreTiming::HasPendingEvents() const {
117 std::scoped_lock lock{basic_lock};
116 return !(wait_set && event_queue.empty()); 118 return !(wait_set && event_queue.empty());
117} 119}
118 120
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 26a8b93a7..21548f0a9 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -161,7 +161,7 @@ private:
161 std::shared_ptr<EventType> ev_lost; 161 std::shared_ptr<EventType> ev_lost;
162 Common::Event event{}; 162 Common::Event event{};
163 Common::Event pause_event{}; 163 Common::Event pause_event{};
164 std::mutex basic_lock; 164 mutable std::mutex basic_lock;
165 std::mutex advance_lock; 165 std::mutex advance_lock;
166 std::unique_ptr<std::jthread> timer_thread; 166 std::unique_ptr<std::jthread> timer_thread;
167 std::atomic<bool> paused{}; 167 std::atomic<bool> paused{};
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 6f5f5156b..148dd3e39 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -562,6 +562,120 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
562 } 562 }
563} 563}
564 564
565static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
566 Kernel::KMemoryInfo mem_info;
567 Kernel::Svc::MemoryInfo svc_mem_info;
568 Kernel::Svc::PageInfo page_info;
569 VAddr cur_addr{base};
570
571 // Expect: r-x Code (.text)
572 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
573 svc_mem_info = mem_info.GetSvcMemoryInfo();
574 cur_addr = svc_mem_info.base_address + svc_mem_info.size;
575 if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
576 svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
577 return cur_addr - 1;
578 }
579
580 // Expect: r-- Code (.rodata)
581 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
582 svc_mem_info = mem_info.GetSvcMemoryInfo();
583 cur_addr = svc_mem_info.base_address + svc_mem_info.size;
584 if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
585 svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
586 return cur_addr - 1;
587 }
588
589 // Expect: rw- CodeData (.data)
590 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
591 svc_mem_info = mem_info.GetSvcMemoryInfo();
592 cur_addr = svc_mem_info.base_address + svc_mem_info.size;
593 return cur_addr - 1;
594}
595
596static Loader::AppLoader::Modules FindModules(Core::System& system) {
597 Loader::AppLoader::Modules modules;
598
599 auto& page_table = system.ApplicationProcess()->GetPageTable();
600 auto& memory = system.ApplicationMemory();
601 VAddr cur_addr = 0;
602
603 // Look for executable sections in Code or AliasCode regions.
604 while (true) {
605 Kernel::KMemoryInfo mem_info{};
606 Kernel::Svc::PageInfo page_info{};
607 R_ASSERT(
608 page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
609 auto svc_mem_info = mem_info.GetSvcMemoryInfo();
610
611 if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute &&
612 (svc_mem_info.state == Kernel::Svc::MemoryState::Code ||
613 svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) {
614 // Try to read the module name from its path.
615 constexpr s32 PathLengthMax = 0x200;
616 struct {
617 u32 zero;
618 s32 path_length;
619 std::array<char, PathLengthMax> path;
620 } module_path;
621
622 if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path,
623 sizeof(module_path))) {
624 if (module_path.zero == 0 && module_path.path_length > 0) {
625 // Truncate module name.
626 module_path.path[PathLengthMax - 1] = '\0';
627
628 // Ignore leading directories.
629 char* path_pointer = module_path.path.data();
630
631 for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
632 module_path.path[i] != '\0';
633 i++) {
634 if (module_path.path[i] == '/' || module_path.path[i] == '\\') {
635 path_pointer = module_path.path.data() + i + 1;
636 }
637 }
638
639 // Insert output.
640 modules.emplace(svc_mem_info.base_address, path_pointer);
641 }
642 }
643 }
644
645 // Check if we're done.
646 const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
647 if (next_address <= cur_addr) {
648 break;
649 }
650
651 cur_addr = next_address;
652 }
653
654 return modules;
655}
656
657static VAddr FindMainModuleEntrypoint(Core::System& system) {
658 Loader::AppLoader::Modules modules;
659 system.GetAppLoader().ReadNSOModules(modules);
660
661 // Do we have a module named main?
662 const auto main = std::find_if(modules.begin(), modules.end(),
663 [](const auto& key) { return key.second == "main"; });
664
665 if (main != modules.end()) {
666 return main->first;
667 }
668
669 // Do we have any loaded executable sections?
670 modules = FindModules(system);
671 if (!modules.empty()) {
672 return modules.begin()->first;
673 }
674
675 // As a last resort, use the start of the code region.
676 return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart());
677}
678
565void GDBStub::HandleQuery(std::string_view command) { 679void GDBStub::HandleQuery(std::string_view command) {
566 if (command.starts_with("TStatus")) { 680 if (command.starts_with("TStatus")) {
567 // no tracepoint support 681 // no tracepoint support
@@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) {
573 const auto target_xml{arch->GetTargetXML()}; 687 const auto target_xml{arch->GetTargetXML()};
574 SendReply(PaginateBuffer(target_xml, command.substr(30))); 688 SendReply(PaginateBuffer(target_xml, command.substr(30)));
575 } else if (command.starts_with("Offsets")) { 689 } else if (command.starts_with("Offsets")) {
576 Loader::AppLoader::Modules modules; 690 const auto main_offset = FindMainModuleEntrypoint(system);
577 system.GetAppLoader().ReadNSOModules(modules); 691 SendReply(fmt::format("TextSeg={:x}", main_offset));
578
579 const auto main = std::find_if(modules.begin(), modules.end(),
580 [](const auto& key) { return key.second == "main"; });
581 if (main != modules.end()) {
582 SendReply(fmt::format("TextSeg={:x}", main->first));
583 } else {
584 SendReply(fmt::format(
585 "TextSeg={:x}",
586 GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart())));
587 }
588 } else if (command.starts_with("Xfer:libraries:read::")) { 692 } else if (command.starts_with("Xfer:libraries:read::")) {
589 Loader::AppLoader::Modules modules; 693 auto modules = FindModules(system);
590 system.GetAppLoader().ReadNSOModules(modules);
591 694
592 std::string buffer; 695 std::string buffer;
593 buffer += R"(<?xml version="1.0"?>)"; 696 buffer += R"(<?xml version="1.0"?>)";
@@ -727,32 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory
727 } 830 }
728} 831}
729 832
730static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
731 Kernel::Svc::MemoryInfo mem_info;
732 VAddr cur_addr{base};
733
734 // Expect: r-x Code (.text)
735 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
736 cur_addr = mem_info.base_address + mem_info.size;
737 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
738 mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
739 return cur_addr - 1;
740 }
741
742 // Expect: r-- Code (.rodata)
743 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
744 cur_addr = mem_info.base_address + mem_info.size;
745 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
746 mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
747 return cur_addr - 1;
748 }
749
750 // Expect: rw- CodeData (.data)
751 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
752 cur_addr = mem_info.base_address + mem_info.size;
753 return cur_addr - 1;
754}
755
756void GDBStub::HandleRcmd(const std::vector<u8>& command) { 833void GDBStub::HandleRcmd(const std::vector<u8>& command) {
757 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; 834 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
758 std::string reply; 835 std::string reply;
@@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
767 844
768 if (command_str == "get fastmem") { 845 if (command_str == "get fastmem") {
769 if (Settings::IsFastmemEnabled()) { 846 if (Settings::IsFastmemEnabled()) {
770 const auto& impl = page_table.PageTableImpl(); 847 const auto& impl = page_table.GetImpl();
771 const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); 848 const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
772 const auto region_bits = impl.current_address_space_width_in_bits; 849 const auto region_bits = impl.current_address_space_width_in_bits;
773 const auto region_size = 1ULL << region_bits; 850 const auto region_size = 1ULL << region_bits;
@@ -779,26 +856,27 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
779 reply = "Fastmem is not enabled.\n"; 856 reply = "Fastmem is not enabled.\n";
780 } 857 }
781 } else if (command_str == "get info") { 858 } else if (command_str == "get info") {
782 Loader::AppLoader::Modules modules; 859 auto modules = FindModules(system);
783 system.GetAppLoader().ReadNSOModules(modules);
784 860
785 reply = fmt::format("Process: {:#x} ({})\n" 861 reply = fmt::format("Process: {:#x} ({})\n"
786 "Program Id: {:#018x}\n", 862 "Program Id: {:#018x}\n",
787 process->GetProcessId(), process->GetName(), process->GetProgramId()); 863 process->GetProcessId(), process->GetName(), process->GetProgramId());
788 reply += fmt::format("Layout:\n" 864 reply += fmt::format(
789 " Alias: {:#012x} - {:#012x}\n" 865 "Layout:\n"
790 " Heap: {:#012x} - {:#012x}\n" 866 " Alias: {:#012x} - {:#012x}\n"
791 " Aslr: {:#012x} - {:#012x}\n" 867 " Heap: {:#012x} - {:#012x}\n"
792 " Stack: {:#012x} - {:#012x}\n" 868 " Aslr: {:#012x} - {:#012x}\n"
793 "Modules:\n", 869 " Stack: {:#012x} - {:#012x}\n"
794 GetInteger(page_table.GetAliasRegionStart()), 870 "Modules:\n",
795 GetInteger(page_table.GetAliasRegionEnd()), 871 GetInteger(page_table.GetAliasRegionStart()),
796 GetInteger(page_table.GetHeapRegionStart()), 872 GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
797 GetInteger(page_table.GetHeapRegionEnd()), 873 GetInteger(page_table.GetHeapRegionStart()),
798 GetInteger(page_table.GetAliasCodeRegionStart()), 874 GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
799 GetInteger(page_table.GetAliasCodeRegionEnd()), 875 GetInteger(page_table.GetAliasCodeRegionStart()),
800 GetInteger(page_table.GetStackRegionStart()), 876 GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
801 GetInteger(page_table.GetStackRegionEnd())); 877 1,
878 GetInteger(page_table.GetStackRegionStart()),
879 GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
802 880
803 for (const auto& [vaddr, name] : modules) { 881 for (const auto& [vaddr, name] : modules) {
804 reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, 882 reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
@@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
811 while (true) { 889 while (true) {
812 using MemoryAttribute = Kernel::Svc::MemoryAttribute; 890 using MemoryAttribute = Kernel::Svc::MemoryAttribute;
813 891
814 auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); 892 Kernel::KMemoryInfo mem_info{};
815 893 Kernel::Svc::PageInfo page_info{};
816 if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || 894 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
817 mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { 895 cur_addr));
818 const char* state = GetMemoryStateName(mem_info.state); 896 auto svc_mem_info = mem_info.GetSvcMemoryInfo();
819 const char* perm = GetMemoryPermissionString(mem_info); 897
820 898 if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
821 const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; 899 svc_mem_info.base_address + svc_mem_info.size - 1 !=
822 const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; 900 std::numeric_limits<u64>::max()) {
823 const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; 901 const char* state = GetMemoryStateName(svc_mem_info.state);
824 const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; 902 const char* perm = GetMemoryPermissionString(svc_mem_info);
903
904 const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
905 const char i =
906 True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
907 const char d =
908 True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
909 const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
825 const char p = 910 const char p =
826 True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; 911 True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
827 912
828 reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", 913 reply += fmt::format(
829 mem_info.base_address, 914 " {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
830 mem_info.base_address + mem_info.size - 1, perm, state, l, i, 915 svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p,
831 d, u, p, mem_info.ipc_count, mem_info.device_count); 916 svc_mem_info.ipc_count, svc_mem_info.device_count);
832 } 917 }
833 918
834 const uintptr_t next_address = mem_info.base_address + mem_info.size; 919 const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
835 if (next_address <= cur_addr) { 920 if (next_address <= cur_addr) {
836 break; 921 break;
837 } 922 }
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 1c580de57..1eb1f439a 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -35,13 +35,14 @@ struct RomFSHeader {
35static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); 35static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
36 36
37struct DirectoryEntry { 37struct DirectoryEntry {
38 u32_le parent;
38 u32_le sibling; 39 u32_le sibling;
39 u32_le child_dir; 40 u32_le child_dir;
40 u32_le child_file; 41 u32_le child_file;
41 u32_le hash; 42 u32_le hash;
42 u32_le name_length; 43 u32_le name_length;
43}; 44};
44static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); 45static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
45 46
46struct FileEntry { 47struct FileEntry {
47 u32_le parent; 48 u32_le parent;
@@ -64,25 +65,22 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs
64 return {entry, string}; 65 return {entry, string};
65} 66}
66 67
67void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, 68void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset,
68 u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) { 69 u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) {
69 while (true) { 70 while (this_file_offset != ROMFS_ENTRY_EMPTY) {
70 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); 71 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
71 72
72 parent->AddFile(std::make_shared<OffsetVfsFile>( 73 parent->AddFile(std::make_shared<OffsetVfsFile>(
73 file, entry.first.size, entry.first.offset + data_offset, entry.second)); 74 file, entry.first.size, entry.first.offset + data_offset, entry.second));
74 75
75 if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
76 break;
77
78 this_file_offset = entry.first.sibling; 76 this_file_offset = entry.first.sibling;
79 } 77 }
80} 78}
81 79
82void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, 80void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset,
83 std::size_t data_offset, u32 this_dir_offset, 81 std::size_t data_offset, u32 this_dir_offset,
84 std::shared_ptr<VectorVfsDirectory> parent) { 82 std::shared_ptr<VectorVfsDirectory>& parent) {
85 while (true) { 83 while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
86 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); 84 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
87 auto current = std::make_shared<VectorVfsDirectory>( 85 auto current = std::make_shared<VectorVfsDirectory>(
88 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); 86 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
@@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
97 } 95 }
98 96
99 parent->AddDirectory(current); 97 parent->AddDirectory(current);
100 if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
101 break;
102 this_dir_offset = entry.first.sibling; 98 this_dir_offset = entry.first.sibling;
103 } 99 }
104} 100}
105} // Anonymous namespace 101} // Anonymous namespace
106 102
107VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { 103VirtualDir ExtractRomFS(VirtualFile file) {
108 RomFSHeader header{}; 104 RomFSHeader header{};
109 if (file->ReadObject(&header) != sizeof(RomFSHeader)) 105 if (file->ReadObject(&header) != sizeof(RomFSHeader))
110 return nullptr; 106 return nullptr;
@@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
113 return nullptr; 109 return nullptr;
114 110
115 const u64 file_offset = header.file_meta.offset; 111 const u64 file_offset = header.file_meta.offset;
116 const u64 dir_offset = header.directory_meta.offset + 4; 112 const u64 dir_offset = header.directory_meta.offset;
117
118 auto root =
119 std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
120 file->GetName(), file->GetContainingDirectory());
121
122 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
123 113
124 VirtualDir out = std::move(root); 114 auto root_container = std::make_shared<VectorVfsDirectory>();
125 115
126 if (type == RomFSExtractionType::SingleDiscard) 116 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container);
127 return out->GetSubdirectories().front();
128 117
129 while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { 118 if (auto root = root_container->GetSubdirectory(""); root) {
130 if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" && 119 return std::make_shared<CachedVfsDirectory>(std::move(root));
131 type == RomFSExtractionType::Truncated)
132 break;
133 out = out->GetSubdirectories().front();
134 } 120 }
135 121
136 return std::make_shared<CachedVfsDirectory>(std::move(out)); 122 return nullptr;
137} 123}
138 124
139VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { 125VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index 5d7f0c2a8..b75ff1aad 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -7,16 +7,9 @@
7 7
8namespace FileSys { 8namespace FileSys {
9 9
10enum class RomFSExtractionType {
11 Full, // Includes data directory
12 Truncated, // Traverses into data directory
13 SingleDiscard, // Traverses into the first subdirectory of root
14};
15
16// Converts a RomFS binary blob to VFS Filesystem 10// Converts a RomFS binary blob to VFS Filesystem
17// Returns nullptr on failure 11// Returns nullptr on failure
18VirtualDir ExtractRomFS(VirtualFile file, 12VirtualDir ExtractRomFS(VirtualFile file);
19 RomFSExtractionType type = RomFSExtractionType::Truncated);
20 13
21// Converts a VFS filesystem into a RomFS binary 14// Converts a VFS filesystem into a RomFS binary
22// Returns nullptr on failure 15// Returns nullptr on failure
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 79114bb6d..fae15a556 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
38using ConsoleMotionValues = ConsoleMotionInfo; 38using ConsoleMotionValues = ConsoleMotionInfo;
39using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; 39using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
40 40
41struct TouchFinger {
42 u64 last_touch{};
43 Common::Point<float> position{};
44 u32 id{};
45 TouchAttribute attribute{};
46 bool pressed{};
47};
48
49// Contains all motion related data that is used on the services 41// Contains all motion related data that is used on the services
50struct ConsoleMotion { 42struct ConsoleMotion {
51 Common::Vec3f accel{}; 43 Common::Vec3f accel{};
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 2af3f06fc..a6e681e15 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -8,6 +8,7 @@
8#include "common/thread.h" 8#include "common/thread.h"
9#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
10#include "core/hid/input_converter.h" 10#include "core/hid/input_converter.h"
11#include "core/hle/service/hid/hid_util.h"
11 12
12namespace Core::HID { 13namespace Core::HID {
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 14constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
@@ -82,7 +83,7 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde
82} 83}
83 84
84void EmulatedController::ReloadFromSettings() { 85void EmulatedController::ReloadFromSettings() {
85 const auto player_index = NpadIdTypeToIndex(npad_id_type); 86 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
86 const auto& player = Settings::values.players.GetValue()[player_index]; 87 const auto& player = Settings::values.players.GetValue()[player_index];
87 88
88 for (std::size_t index = 0; index < player.buttons.size(); ++index) { 89 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
@@ -96,18 +97,7 @@ void EmulatedController::ReloadFromSettings() {
96 } 97 }
97 98
98 controller.color_values = {}; 99 controller.color_values = {};
99 controller.colors_state.fullkey = { 100 ReloadColorsFromSettings();
100 .body = GetNpadColor(player.body_color_left),
101 .button = GetNpadColor(player.button_color_left),
102 };
103 controller.colors_state.left = {
104 .body = GetNpadColor(player.body_color_left),
105 .button = GetNpadColor(player.button_color_left),
106 };
107 controller.colors_state.right = {
108 .body = GetNpadColor(player.body_color_right),
109 .button = GetNpadColor(player.button_color_right),
110 };
111 101
112 ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); 102 ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
113 103
@@ -128,6 +118,30 @@ void EmulatedController::ReloadFromSettings() {
128 ReloadInput(); 118 ReloadInput();
129} 119}
130 120
121void EmulatedController::ReloadColorsFromSettings() {
122 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
123 const auto& player = Settings::values.players.GetValue()[player_index];
124
125 // Avoid updating colors if overridden by physical controller
126 if (controller.color_values[LeftIndex].body != 0 &&
127 controller.color_values[RightIndex].body != 0) {
128 return;
129 }
130
131 controller.colors_state.fullkey = {
132 .body = GetNpadColor(player.body_color_left),
133 .button = GetNpadColor(player.button_color_left),
134 };
135 controller.colors_state.left = {
136 .body = GetNpadColor(player.body_color_left),
137 .button = GetNpadColor(player.button_color_left),
138 };
139 controller.colors_state.right = {
140 .body = GetNpadColor(player.body_color_right),
141 .button = GetNpadColor(player.button_color_right),
142 };
143}
144
131void EmulatedController::LoadDevices() { 145void EmulatedController::LoadDevices() {
132 // TODO(german77): Use more buttons to detect the correct device 146 // TODO(german77): Use more buttons to detect the correct device
133 const auto left_joycon = button_params[Settings::NativeButton::DRight]; 147 const auto left_joycon = button_params[Settings::NativeButton::DRight];
@@ -202,7 +216,7 @@ void EmulatedController::LoadDevices() {
202} 216}
203 217
204void EmulatedController::LoadTASParams() { 218void EmulatedController::LoadTASParams() {
205 const auto player_index = NpadIdTypeToIndex(npad_id_type); 219 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
206 Common::ParamPackage common_params{}; 220 Common::ParamPackage common_params{};
207 common_params.Set("engine", "tas"); 221 common_params.Set("engine", "tas");
208 common_params.Set("port", static_cast<int>(player_index)); 222 common_params.Set("port", static_cast<int>(player_index));
@@ -230,10 +244,12 @@ void EmulatedController::LoadTASParams() {
230 tas_button_params[Settings::NativeButton::DUp].Set("button", 13); 244 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
231 tas_button_params[Settings::NativeButton::DRight].Set("button", 14); 245 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
232 tas_button_params[Settings::NativeButton::DDown].Set("button", 15); 246 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
233 tas_button_params[Settings::NativeButton::SL].Set("button", 16); 247 tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
234 tas_button_params[Settings::NativeButton::SR].Set("button", 17); 248 tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
235 tas_button_params[Settings::NativeButton::Home].Set("button", 18); 249 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
236 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); 250 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
251 tas_button_params[Settings::NativeButton::SLRight].Set("button", 20);
252 tas_button_params[Settings::NativeButton::SRRight].Set("button", 21);
237 253
238 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); 254 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
239 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); 255 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
@@ -249,7 +265,7 @@ void EmulatedController::LoadTASParams() {
249} 265}
250 266
251void EmulatedController::LoadVirtualGamepadParams() { 267void EmulatedController::LoadVirtualGamepadParams() {
252 const auto player_index = NpadIdTypeToIndex(npad_id_type); 268 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
253 Common::ParamPackage common_params{}; 269 Common::ParamPackage common_params{};
254 common_params.Set("engine", "virtual_gamepad"); 270 common_params.Set("engine", "virtual_gamepad");
255 common_params.Set("port", static_cast<int>(player_index)); 271 common_params.Set("port", static_cast<int>(player_index));
@@ -283,10 +299,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
283 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); 299 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
284 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); 300 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
285 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); 301 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
286 virtual_button_params[Settings::NativeButton::SL].Set("button", 16); 302 virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
287 virtual_button_params[Settings::NativeButton::SR].Set("button", 17); 303 virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
288 virtual_button_params[Settings::NativeButton::Home].Set("button", 18); 304 virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
289 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); 305 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
306 virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20);
307 virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21);
290 308
291 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); 309 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
292 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); 310 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
@@ -491,9 +509,11 @@ void EmulatedController::ReloadInput() {
491 }); 509 });
492 } 510 }
493 turbo_button_state = 0; 511 turbo_button_state = 0;
512 is_initalized = true;
494} 513}
495 514
496void EmulatedController::UnloadInput() { 515void EmulatedController::UnloadInput() {
516 is_initalized = false;
497 for (auto& button : button_devices) { 517 for (auto& button : button_devices) {
498 button.reset(); 518 button.reset();
499 } 519 }
@@ -598,7 +618,7 @@ bool EmulatedController::IsConfiguring() const {
598} 618}
599 619
600void EmulatedController::SaveCurrentConfig() { 620void EmulatedController::SaveCurrentConfig() {
601 const auto player_index = NpadIdTypeToIndex(npad_id_type); 621 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
602 auto& player = Settings::values.players.GetValue()[player_index]; 622 auto& player = Settings::values.players.GetValue()[player_index];
603 player.connected = is_connected; 623 player.connected = is_connected;
604 player.controller_type = MapNPadToSettingsType(npad_type); 624 player.controller_type = MapNPadToSettingsType(npad_type);
@@ -854,12 +874,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
854 controller.npad_button_state.down.Assign(current_status.value); 874 controller.npad_button_state.down.Assign(current_status.value);
855 controller.debug_pad_button_state.d_down.Assign(current_status.value); 875 controller.debug_pad_button_state.d_down.Assign(current_status.value);
856 break; 876 break;
857 case Settings::NativeButton::SL: 877 case Settings::NativeButton::SLLeft:
858 controller.npad_button_state.left_sl.Assign(current_status.value); 878 controller.npad_button_state.left_sl.Assign(current_status.value);
879 break;
880 case Settings::NativeButton::SLRight:
859 controller.npad_button_state.right_sl.Assign(current_status.value); 881 controller.npad_button_state.right_sl.Assign(current_status.value);
860 break; 882 break;
861 case Settings::NativeButton::SR: 883 case Settings::NativeButton::SRLeft:
862 controller.npad_button_state.left_sr.Assign(current_status.value); 884 controller.npad_button_state.left_sr.Assign(current_status.value);
885 break;
886 case Settings::NativeButton::SRRight:
863 controller.npad_button_state.right_sr.Assign(current_status.value); 887 controller.npad_button_state.right_sr.Assign(current_status.value);
864 break; 888 break;
865 case Settings::NativeButton::Home: 889 case Settings::NativeButton::Home:
@@ -1091,30 +1115,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
1091 1115
1092 bool is_charging = false; 1116 bool is_charging = false;
1093 bool is_powered = false; 1117 bool is_powered = false;
1094 NpadBatteryLevel battery_level = 0; 1118 NpadBatteryLevel battery_level = NpadBatteryLevel::Empty;
1095 switch (controller.battery_values[index]) { 1119 switch (controller.battery_values[index]) {
1096 case Common::Input::BatteryLevel::Charging: 1120 case Common::Input::BatteryLevel::Charging:
1097 is_charging = true; 1121 is_charging = true;
1098 is_powered = true; 1122 is_powered = true;
1099 battery_level = 6; 1123 battery_level = NpadBatteryLevel::Full;
1100 break; 1124 break;
1101 case Common::Input::BatteryLevel::Medium: 1125 case Common::Input::BatteryLevel::Medium:
1102 battery_level = 6; 1126 battery_level = NpadBatteryLevel::High;
1103 break; 1127 break;
1104 case Common::Input::BatteryLevel::Low: 1128 case Common::Input::BatteryLevel::Low:
1105 battery_level = 4; 1129 battery_level = NpadBatteryLevel::Low;
1106 break; 1130 break;
1107 case Common::Input::BatteryLevel::Critical: 1131 case Common::Input::BatteryLevel::Critical:
1108 battery_level = 2; 1132 battery_level = NpadBatteryLevel::Critical;
1109 break; 1133 break;
1110 case Common::Input::BatteryLevel::Empty: 1134 case Common::Input::BatteryLevel::Empty:
1111 battery_level = 0; 1135 battery_level = NpadBatteryLevel::Empty;
1112 break; 1136 break;
1113 case Common::Input::BatteryLevel::None: 1137 case Common::Input::BatteryLevel::None:
1114 case Common::Input::BatteryLevel::Full: 1138 case Common::Input::BatteryLevel::Full:
1115 default: 1139 default:
1116 is_powered = true; 1140 is_powered = true;
1117 battery_level = 8; 1141 battery_level = NpadBatteryLevel::Full;
1118 break; 1142 break;
1119 } 1143 }
1120 1144
@@ -1185,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1185} 1209}
1186 1210
1187bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1211bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
1212 if (!is_initalized) {
1213 return false;
1214 }
1188 if (device_index >= output_devices.size()) { 1215 if (device_index >= output_devices.size()) {
1189 return false; 1216 return false;
1190 } 1217 }
1191 if (!output_devices[device_index]) { 1218 if (!output_devices[device_index]) {
1192 return false; 1219 return false;
1193 } 1220 }
1194 const auto player_index = NpadIdTypeToIndex(npad_id_type); 1221 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1195 const auto& player = Settings::values.players.GetValue()[player_index]; 1222 const auto& player = Settings::values.players.GetValue()[player_index];
1196 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; 1223 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
1197 1224
@@ -1217,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1217} 1244}
1218 1245
1219bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1246bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1220 const auto player_index = NpadIdTypeToIndex(npad_id_type); 1247 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1221 const auto& player = Settings::values.players.GetValue()[player_index]; 1248 const auto& player = Settings::values.players.GetValue()[player_index];
1222 1249
1250 if (!is_initalized) {
1251 return false;
1252 }
1253
1223 if (!player.vibration_enabled) { 1254 if (!player.vibration_enabled) {
1224 return false; 1255 return false;
1225 } 1256 }
@@ -1239,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
1239 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { 1270 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1240 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); 1271 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1241 1272
1273 if (!is_initalized) {
1274 return Common::Input::DriverResult::InvalidHandle;
1275 }
1276
1242 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; 1277 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
1243 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1278 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1244 auto& nfc_output_device = output_devices[3]; 1279 auto& nfc_output_device = output_devices[3];
@@ -1284,6 +1319,10 @@ bool EmulatedController::SetCameraFormat(
1284 Core::IrSensor::ImageTransferProcessorFormat camera_format) { 1319 Core::IrSensor::ImageTransferProcessorFormat camera_format) {
1285 LOG_INFO(Service_HID, "Set camera format {}", camera_format); 1320 LOG_INFO(Service_HID, "Set camera format {}", camera_format);
1286 1321
1322 if (!is_initalized) {
1323 return false;
1324 }
1325
1287 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1326 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1288 auto& camera_output_device = output_devices[2]; 1327 auto& camera_output_device = output_devices[2];
1289 1328
@@ -1307,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) {
1307} 1346}
1308 1347
1309bool EmulatedController::HasNfc() const { 1348bool EmulatedController::HasNfc() const {
1349
1350 if (!is_initalized) {
1351 return false;
1352 }
1353
1310 const auto& nfc_output_device = output_devices[3]; 1354 const auto& nfc_output_device = output_devices[3];
1311 1355
1312 switch (npad_type) { 1356 switch (npad_type) {
@@ -1344,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() {
1344} 1388}
1345 1389
1346bool EmulatedController::StartNfcPolling() { 1390bool EmulatedController::StartNfcPolling() {
1391 if (!is_initalized) {
1392 return false;
1393 }
1394
1347 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1395 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1348 auto& nfc_virtual_output_device = output_devices[3]; 1396 auto& nfc_virtual_output_device = output_devices[3];
1349 1397
@@ -1355,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() {
1355} 1403}
1356 1404
1357bool EmulatedController::StopNfcPolling() { 1405bool EmulatedController::StopNfcPolling() {
1406 if (!is_initalized) {
1407 return false;
1408 }
1409
1358 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1410 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1359 auto& nfc_virtual_output_device = output_devices[3]; 1411 auto& nfc_virtual_output_device = output_devices[3];
1360 1412
@@ -1366,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() {
1366} 1418}
1367 1419
1368bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { 1420bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1421 if (!is_initalized) {
1422 return false;
1423 }
1424
1369 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1425 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1370 auto& nfc_virtual_output_device = output_devices[3]; 1426 auto& nfc_virtual_output_device = output_devices[3];
1371 1427
@@ -1378,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1378 1434
1379bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, 1435bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1380 Common::Input::MifareRequest& out_data) { 1436 Common::Input::MifareRequest& out_data) {
1437 if (!is_initalized) {
1438 return false;
1439 }
1440
1381 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1441 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1382 auto& nfc_virtual_output_device = output_devices[3]; 1442 auto& nfc_virtual_output_device = output_devices[3];
1383 1443
@@ -1390,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ
1390} 1450}
1391 1451
1392bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { 1452bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1453 if (!is_initalized) {
1454 return false;
1455 }
1456
1393 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1457 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1394 auto& nfc_virtual_output_device = output_devices[3]; 1458 auto& nfc_virtual_output_device = output_devices[3];
1395 1459
@@ -1401,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req
1401} 1465}
1402 1466
1403bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1467bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1468 if (!is_initalized) {
1469 return false;
1470 }
1471
1404 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1472 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1405 auto& nfc_virtual_output_device = output_devices[3]; 1473 auto& nfc_virtual_output_device = output_devices[3];
1406 1474
@@ -1412,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1412} 1480}
1413 1481
1414void EmulatedController::SetLedPattern() { 1482void EmulatedController::SetLedPattern() {
1483 if (!is_initalized) {
1484 return;
1485 }
1486
1415 for (auto& device : output_devices) { 1487 for (auto& device : output_devices) {
1416 if (!device) { 1488 if (!device) {
1417 continue; 1489 continue;
@@ -1627,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1627 } 1699 }
1628 if (is_connected) { 1700 if (is_connected) {
1629 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", 1701 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1630 NpadIdTypeToIndex(npad_id_type)); 1702 Service::HID::NpadIdTypeToIndex(npad_id_type));
1631 } 1703 }
1632 npad_type = npad_type_; 1704 npad_type = npad_type_;
1633} 1705}
@@ -1877,12 +1949,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const {
1877 case Settings::NativeButton::DDown: 1949 case Settings::NativeButton::DDown:
1878 button_mask.down.Assign(1); 1950 button_mask.down.Assign(1);
1879 break; 1951 break;
1880 case Settings::NativeButton::SL: 1952 case Settings::NativeButton::SLLeft:
1881 button_mask.left_sl.Assign(1); 1953 button_mask.left_sl.Assign(1);
1954 break;
1955 case Settings::NativeButton::SLRight:
1882 button_mask.right_sl.Assign(1); 1956 button_mask.right_sl.Assign(1);
1883 break; 1957 break;
1884 case Settings::NativeButton::SR: 1958 case Settings::NativeButton::SRLeft:
1885 button_mask.left_sr.Assign(1); 1959 button_mask.left_sr.Assign(1);
1960 break;
1961 case Settings::NativeButton::SRRight:
1886 button_mask.right_sr.Assign(1); 1962 button_mask.right_sr.Assign(1);
1887 break; 1963 break;
1888 default: 1964 default:
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index d4500583e..d6e20ab66 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -253,6 +253,9 @@ public:
253 /// Overrides current mapped devices with the stored configuration and reloads all input devices 253 /// Overrides current mapped devices with the stored configuration and reloads all input devices
254 void ReloadFromSettings(); 254 void ReloadFromSettings();
255 255
256 /// Updates current colors with the ones stored in the configuration
257 void ReloadColorsFromSettings();
258
256 /// Saves the current mapped configuration 259 /// Saves the current mapped configuration
257 void SaveCurrentConfig(); 260 void SaveCurrentConfig();
258 261
@@ -556,6 +559,7 @@ private:
556 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 559 NpadStyleTag supported_style_tag{NpadStyleSet::All};
557 bool is_connected{false}; 560 bool is_connected{false};
558 bool is_configuring{false}; 561 bool is_configuring{false};
562 bool is_initalized{false};
559 bool system_buttons_enabled{true}; 563 bool system_buttons_enabled{true};
560 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 564 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
561 u32 turbo_button_state{0}; 565 u32 turbo_button_state{0};
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index cf53c04d9..2cf25a870 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -6,6 +6,7 @@
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/emulated_devices.h" 7#include "core/hid/emulated_devices.h"
8#include "core/hid/hid_core.h" 8#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/hid_util.h"
9 10
10namespace Core::HID { 11namespace Core::HID {
11 12
@@ -98,11 +99,11 @@ const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
98} 99}
99 100
100EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { 101EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
101 return GetEmulatedController(IndexToNpadIdType(index)); 102 return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
102} 103}
103 104
104const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { 105const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
105 return GetEmulatedController(IndexToNpadIdType(index)); 106 return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
106} 107}
107 108
108void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { 109void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 00beb40dd..4bf285f36 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -8,6 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/point.h" 9#include "common/point.h"
10#include "common/uuid.h" 10#include "common/uuid.h"
11#include "common/vector_math.h"
11 12
12namespace Core::HID { 13namespace Core::HID {
13 14
@@ -218,6 +219,13 @@ enum class NpadIdType : u32 {
218 Invalid = 0xFFFFFFFF, 219 Invalid = 0xFFFFFFFF,
219}; 220};
220 221
222enum class NpadInterfaceType : u8 {
223 Bluetooth = 1,
224 Rail = 2,
225 Usb = 3,
226 Embedded = 4,
227};
228
221// This is nn::hid::NpadStyleIndex 229// This is nn::hid::NpadStyleIndex
222enum class NpadStyleIndex : u8 { 230enum class NpadStyleIndex : u8 {
223 None = 0, 231 None = 0,
@@ -302,6 +310,15 @@ enum class TouchScreenModeForNx : u8 {
302 Heat2, 310 Heat2,
303}; 311};
304 312
313// This is nn::hid::system::NpadBatteryLevel
314enum class NpadBatteryLevel : u32 {
315 Empty,
316 Critical,
317 Low,
318 High,
319 Full,
320};
321
305// This is nn::hid::NpadStyleTag 322// This is nn::hid::NpadStyleTag
306struct NpadStyleTag { 323struct NpadStyleTag {
307 union { 324 union {
@@ -347,6 +364,14 @@ struct TouchState {
347}; 364};
348static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 365static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
349 366
367struct TouchFinger {
368 u64 last_touch{};
369 Common::Point<float> position{};
370 u32 id{};
371 TouchAttribute attribute{};
372 bool pressed{};
373};
374
350// This is nn::hid::TouchScreenConfigurationForNx 375// This is nn::hid::TouchScreenConfigurationForNx
351struct TouchScreenConfigurationForNx { 376struct TouchScreenConfigurationForNx {
352 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; 377 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
@@ -385,16 +410,12 @@ struct NpadGcTriggerState {
385}; 410};
386static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); 411static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
387 412
388// This is nn::hid::system::NpadBatteryLevel
389using NpadBatteryLevel = u32;
390static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
391
392// This is nn::hid::system::NpadPowerInfo 413// This is nn::hid::system::NpadPowerInfo
393struct NpadPowerInfo { 414struct NpadPowerInfo {
394 bool is_powered{}; 415 bool is_powered{};
395 bool is_charging{}; 416 bool is_charging{};
396 INSERT_PADDING_BYTES(0x6); 417 INSERT_PADDING_BYTES(0x6);
397 NpadBatteryLevel battery_level{8}; 418 NpadBatteryLevel battery_level{NpadBatteryLevel::Full};
398}; 419};
399static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); 420static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
400 421
@@ -578,6 +599,29 @@ struct SixAxisSensorIcInformation {
578static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, 599static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
579 "SixAxisSensorIcInformation is an invalid size"); 600 "SixAxisSensorIcInformation is an invalid size");
580 601
602// This is nn::hid::SixAxisSensorAttribute
603struct SixAxisSensorAttribute {
604 union {
605 u32 raw{};
606 BitField<0, 1, u32> is_connected;
607 BitField<1, 1, u32> is_interpolated;
608 };
609};
610static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
611
612// This is nn::hid::SixAxisSensorState
613struct SixAxisSensorState {
614 s64 delta_time{};
615 s64 sampling_number{};
616 Common::Vec3f accel{};
617 Common::Vec3f gyro{};
618 Common::Vec3f rotation{};
619 std::array<Common::Vec3f, 3> orientation{};
620 SixAxisSensorAttribute attribute{};
621 INSERT_PADDING_BYTES(4); // Reserved
622};
623static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
624
581// This is nn::hid::VibrationDeviceHandle 625// This is nn::hid::VibrationDeviceHandle
582struct VibrationDeviceHandle { 626struct VibrationDeviceHandle {
583 NpadStyleIndex npad_type{NpadStyleIndex::None}; 627 NpadStyleIndex npad_type{NpadStyleIndex::None};
@@ -688,60 +732,4 @@ struct UniquePadId {
688}; 732};
689static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); 733static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
690 734
691/// Converts a NpadIdType to an array index.
692constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
693 switch (npad_id_type) {
694 case NpadIdType::Player1:
695 return 0;
696 case NpadIdType::Player2:
697 return 1;
698 case NpadIdType::Player3:
699 return 2;
700 case NpadIdType::Player4:
701 return 3;
702 case NpadIdType::Player5:
703 return 4;
704 case NpadIdType::Player6:
705 return 5;
706 case NpadIdType::Player7:
707 return 6;
708 case NpadIdType::Player8:
709 return 7;
710 case NpadIdType::Handheld:
711 return 8;
712 case NpadIdType::Other:
713 return 9;
714 default:
715 return 0;
716 }
717}
718
719/// Converts an array index to a NpadIdType
720constexpr NpadIdType IndexToNpadIdType(size_t index) {
721 switch (index) {
722 case 0:
723 return NpadIdType::Player1;
724 case 1:
725 return NpadIdType::Player2;
726 case 2:
727 return NpadIdType::Player3;
728 case 3:
729 return NpadIdType::Player4;
730 case 4:
731 return NpadIdType::Player5;
732 case 5:
733 return NpadIdType::Player6;
734 case 6:
735 return NpadIdType::Player7;
736 case 7:
737 return NpadIdType::Player8;
738 case 8:
739 return NpadIdType::Handheld;
740 case 9:
741 return NpadIdType::Other;
742 default:
743 return NpadIdType::Invalid;
744 }
745}
746
747} // namespace Core::HID 735} // namespace Core::HID
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 76d6b8ab0..a6bdd28f2 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -5,21 +5,22 @@
5#include "core/hid/hid_types.h" 5#include "core/hid/hid_types.h"
6#include "core/hid/input_interpreter.h" 6#include "core/hid/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h" 7#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid.h" 8#include "core/hle/service/hid/hid_server.h"
9#include "core/hle/service/hid/resource_manager.h"
9#include "core/hle/service/sm/sm.h" 10#include "core/hle/service/sm/sm.h"
10 11
11InputInterpreter::InputInterpreter(Core::System& system) 12InputInterpreter::InputInterpreter(Core::System& system)
12 : npad{system.ServiceManager() 13 : npad{system.ServiceManager()
13 .GetService<Service::HID::Hid>("hid") 14 .GetService<Service::HID::IHidServer>("hid")
14 ->GetAppletResource() 15 ->GetResourceManager()
15 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { 16 ->GetNpad()} {
16 ResetButtonStates(); 17 ResetButtonStates();
17} 18}
18 19
19InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
20 21
21void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
22 const auto button_state = npad.GetAndResetPressState(); 23 const auto button_state = npad->GetAndResetPressState();
23 24
24 previous_index = current_index; 25 previous_index = current_index;
25 current_index = (current_index + 1) % button_states.size(); 26 current_index = (current_index + 1) % button_states.size();
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h
index 8c521b381..3569aac93 100644
--- a/src/core/hid/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -16,7 +16,7 @@ enum class NpadButton : u64;
16} 16}
17 17
18namespace Service::HID { 18namespace Service::HID {
19class Controller_NPad; 19class NPad;
20} 20}
21 21
22/** 22/**
@@ -101,7 +101,7 @@ public:
101 } 101 }
102 102
103private: 103private:
104 Service::HID::Controller_NPad& npad; 104 std::shared_ptr<Service::HID::NPad> npad;
105 105
106 /// Stores 9 consecutive button states polled from HID. 106 /// Stores 9 consecutive button states polled from HID.
107 std::array<Core::HID::NpadButton, 9> button_states{}; 107 std::array<Core::HID::NpadButton, 9> button_states{};
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 59364efa1..37fa39a73 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
222 }; 222 };
223 223
224 // We succeeded. 224 // We succeeded.
225 *out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); 225 *out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
226 R_SUCCEED(); 226 R_SUCCEED();
227} 227}
228 228
@@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
238 ASSERT(Common::IsAligned(size, alignment)); 238 ASSERT(Common::IsAligned(size, alignment));
239 239
240 // Close the secure region's pages. 240 // Close the secure region's pages.
241 kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), 241 kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
242 size / PageSize); 242 size / PageSize);
243} 243}
244 244
245// Insecure Memory.
246KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) {
247 return kernel.GetSystemResourceLimit();
248}
249
250u32 KSystemControl::GetInsecureMemoryPool() {
251 return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure);
252}
253
245} // namespace Kernel::Board::Nintendo::Nx 254} // namespace Kernel::Board::Nintendo::Nx
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
index ff1feec70..60c5e58b7 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
@@ -8,7 +8,8 @@
8 8
9namespace Kernel { 9namespace Kernel {
10class KernelCore; 10class KernelCore;
11} 11class KResourceLimit;
12} // namespace Kernel
12 13
13namespace Kernel::Board::Nintendo::Nx { 14namespace Kernel::Board::Nintendo::Nx {
14 15
@@ -40,6 +41,10 @@ public:
40 u32 pool); 41 u32 pool);
41 static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, 42 static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
42 u32 pool); 43 u32 pool);
44
45 // Insecure Memory.
46 static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
47 static u32 GetInsecureMemoryPool();
43}; 48};
44 49
45} // namespace Kernel::Board::Nintendo::Nx 50} // namespace Kernel::Board::Nintendo::Nx
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
index e7da7a21d..274fee493 100644
--- a/src/core/hle/kernel/k_capabilities.cpp
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -4,14 +4,16 @@
4#include "core/hardware_properties.h" 4#include "core/hardware_properties.h"
5#include "core/hle/kernel/k_capabilities.h" 5#include "core/hle/kernel/k_capabilities.h"
6#include "core/hle/kernel/k_memory_layout.h" 6#include "core/hle/kernel/k_memory_layout.h"
7#include "core/hle/kernel/k_page_table.h" 7#include "core/hle/kernel/k_process_page_table.h"
8#include "core/hle/kernel/k_trace.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/svc_results.h" 10#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_version.h" 11#include "core/hle/kernel/svc_version.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table) { 15Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps,
16 KProcessPageTable* page_table) {
15 // We're initializing an initial process. 17 // We're initializing an initial process.
16 m_svc_access_flags.reset(); 18 m_svc_access_flags.reset();
17 m_irq_access_flags.reset(); 19 m_irq_access_flags.reset();
@@ -41,7 +43,8 @@ Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTabl
41 R_RETURN(this->SetCapabilities(kern_caps, page_table)); 43 R_RETURN(this->SetCapabilities(kern_caps, page_table));
42} 44}
43 45
44Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) { 46Result KCapabilities::InitializeForUser(std::span<const u32> user_caps,
47 KProcessPageTable* page_table) {
45 // We're initializing a user process. 48 // We're initializing a user process.
46 m_svc_access_flags.reset(); 49 m_svc_access_flags.reset();
47 m_irq_access_flags.reset(); 50 m_irq_access_flags.reset();
@@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
121 R_SUCCEED(); 124 R_SUCCEED();
122} 125}
123 126
124Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) { 127Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) {
125 const auto range_pack = MapRange{cap}; 128 const auto range_pack = MapRange{cap};
126 const auto size_pack = MapRangeSize{size_cap}; 129 const auto size_pack = MapRangeSize{size_cap};
127 130
@@ -142,16 +145,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p
142 ? KMemoryPermission::UserRead 145 ? KMemoryPermission::UserRead
143 : KMemoryPermission::UserReadWrite; 146 : KMemoryPermission::UserReadWrite;
144 if (MapRangeSize{size_cap}.normal) { 147 if (MapRangeSize{size_cap}.normal) {
145 // R_RETURN(page_table->MapStatic(phys_addr, size, perm)); 148 R_RETURN(page_table->MapStatic(phys_addr, size, perm));
146 } else { 149 } else {
147 // R_RETURN(page_table->MapIo(phys_addr, size, perm)); 150 R_RETURN(page_table->MapIo(phys_addr, size, perm));
148 } 151 }
149
150 UNIMPLEMENTED();
151 R_SUCCEED();
152} 152}
153 153
154Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { 154Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) {
155 // Get/validate address/size 155 // Get/validate address/size
156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; 156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
157 const size_t num_pages = 1; 157 const size_t num_pages = 1;
@@ -160,10 +160,7 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
160 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); 160 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
161 161
162 // Do the mapping. 162 // Do the mapping.
163 // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite)); 163 R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
164
165 UNIMPLEMENTED();
166 R_SUCCEED();
167} 164}
168 165
169template <typename F> 166template <typename F>
@@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
200 R_SUCCEED(); 197 R_SUCCEED();
201} 198}
202 199
203Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) { 200Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) {
204 // Map each region into the process's page table. 201 // Map each region into the process's page table.
205 return ProcessMapRegionCapability( 202 return ProcessMapRegionCapability(
206 cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { 203 cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
207 // R_RETURN(page_table->MapRegion(region_type, perm)); 204 R_RETURN(page_table->MapRegion(region_type, perm));
208 UNIMPLEMENTED();
209 R_SUCCEED();
210 }); 205 });
211} 206}
212 207
@@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
280} 275}
281 276
282Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, 277Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
283 KPageTable* page_table) { 278 KProcessPageTable* page_table) {
284 // Validate this is a capability we can act on. 279 // Validate this is a capability we can act on.
285 const auto type = GetCapabilityType(cap); 280 const auto type = GetCapabilityType(cap);
286 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument); 281 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
@@ -318,7 +313,7 @@ Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
318 } 313 }
319} 314}
320 315
321Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) { 316Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) {
322 u32 set_flags = 0, set_svc = 0; 317 u32 set_flags = 0, set_svc = 0;
323 318
324 for (size_t i = 0; i < caps.size(); i++) { 319 for (size_t i = 0; i < caps.size(); i++) {
@@ -335,6 +330,8 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* pag
335 330
336 // Map the range. 331 // Map the range.
337 R_TRY(this->MapRange_(cap, size_cap, page_table)); 332 R_TRY(this->MapRange_(cap, size_cap, page_table));
333 } else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
334 continue;
338 } else { 335 } else {
339 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); 336 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
340 } 337 }
diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h
index ebd4eedb1..013d952ad 100644
--- a/src/core/hle/kernel/k_capabilities.h
+++ b/src/core/hle/kernel/k_capabilities.h
@@ -15,15 +15,15 @@
15 15
16namespace Kernel { 16namespace Kernel {
17 17
18class KPageTable; 18class KProcessPageTable;
19class KernelCore; 19class KernelCore;
20 20
21class KCapabilities { 21class KCapabilities {
22public: 22public:
23 constexpr explicit KCapabilities() = default; 23 constexpr explicit KCapabilities() = default;
24 24
25 Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table); 25 Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
26 Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table); 26 Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
27 27
28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps); 28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
29 29
@@ -264,9 +264,9 @@ private:
264 264
265 Result SetCorePriorityCapability(const u32 cap); 265 Result SetCorePriorityCapability(const u32 cap);
266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc); 266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
267 Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table); 267 Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
268 Result MapIoPage_(const u32 cap, KPageTable* page_table); 268 Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
269 Result MapRegion_(const u32 cap, KPageTable* page_table); 269 Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
270 Result SetInterruptPairCapability(const u32 cap); 270 Result SetInterruptPairCapability(const u32 cap);
271 Result SetProgramTypeCapability(const u32 cap); 271 Result SetProgramTypeCapability(const u32 cap);
272 Result SetKernelVersionCapability(const u32 cap); 272 Result SetKernelVersionCapability(const u32 cap);
@@ -277,8 +277,9 @@ private:
277 static Result ProcessMapRegionCapability(const u32 cap, F f); 277 static Result ProcessMapRegionCapability(const u32 cap, F f);
278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap); 278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
279 279
280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table); 280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
281 Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table); 281 KProcessPageTable* page_table);
282 Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
282 283
283private: 284private:
284 Svc::SvcAccessFlagSet m_svc_access_flags{}; 285 Svc::SvcAccessFlagSet m_svc_access_flags{};
diff --git a/src/core/hle/kernel/k_device_address_space.cpp b/src/core/hle/kernel/k_device_address_space.cpp
index f48896715..f0703f795 100644
--- a/src/core/hle/kernel/k_device_address_space.cpp
+++ b/src/core/hle/kernel/k_device_address_space.cpp
@@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
54 R_SUCCEED(); 54 R_SUCCEED();
55} 55}
56 56
57Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_address, 57Result KDeviceAddressSpace::Map(KProcessPageTable* page_table, KProcessAddress process_address,
58 size_t size, u64 device_address, u32 option, bool is_aligned) { 58 size_t size, u64 device_address, u32 option, bool is_aligned) {
59 // Check that the address falls within the space. 59 // Check that the address falls within the space.
60 R_UNLESS((m_space_address <= device_address && 60 R_UNLESS((m_space_address <= device_address &&
@@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
113 R_SUCCEED(); 113 R_SUCCEED();
114} 114}
115 115
116Result KDeviceAddressSpace::Unmap(KPageTable* page_table, KProcessAddress process_address, 116Result KDeviceAddressSpace::Unmap(KProcessPageTable* page_table, KProcessAddress process_address,
117 size_t size, u64 device_address) { 117 size_t size, u64 device_address) {
118 // Check that the address falls within the space. 118 // Check that the address falls within the space.
119 R_UNLESS((m_space_address <= device_address && 119 R_UNLESS((m_space_address <= device_address &&
diff --git a/src/core/hle/kernel/k_device_address_space.h b/src/core/hle/kernel/k_device_address_space.h
index 18556e3cc..ff0ec8152 100644
--- a/src/core/hle/kernel/k_device_address_space.h
+++ b/src/core/hle/kernel/k_device_address_space.h
@@ -5,7 +5,7 @@
5 5
6#include <string> 6#include <string>
7 7
8#include "core/hle/kernel/k_page_table.h" 8#include "core/hle/kernel/k_process_page_table.h"
9#include "core/hle/kernel/k_typed_address.h" 9#include "core/hle/kernel/k_typed_address.h"
10#include "core/hle/kernel/slab_helpers.h" 10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
@@ -31,23 +31,23 @@ public:
31 Result Attach(Svc::DeviceName device_name); 31 Result Attach(Svc::DeviceName device_name);
32 Result Detach(Svc::DeviceName device_name); 32 Result Detach(Svc::DeviceName device_name);
33 33
34 Result MapByForce(KPageTable* page_table, KProcessAddress process_address, size_t size, 34 Result MapByForce(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
35 u64 device_address, u32 option) { 35 u64 device_address, u32 option) {
36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false)); 36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
37 } 37 }
38 38
39 Result MapAligned(KPageTable* page_table, KProcessAddress process_address, size_t size, 39 Result MapAligned(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
40 u64 device_address, u32 option) { 40 u64 device_address, u32 option) {
41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true)); 41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
42 } 42 }
43 43
44 Result Unmap(KPageTable* page_table, KProcessAddress process_address, size_t size, 44 Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
45 u64 device_address); 45 u64 device_address);
46 46
47 static void Initialize(); 47 static void Initialize();
48 48
49private: 49private:
50 Result Map(KPageTable* page_table, KProcessAddress process_address, size_t size, 50 Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
51 u64 device_address, u32 option, bool is_aligned); 51 u64 device_address, u32 option, bool is_aligned);
52 52
53private: 53private:
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index c8122644f..d7adb3169 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -394,6 +394,14 @@ private:
394 return region.GetEndAddress(); 394 return region.GetEndAddress();
395 } 395 }
396 396
397public:
398 static const KMemoryRegion* Find(const KMemoryLayout& layout, KVirtualAddress address) {
399 return Find(address, layout.GetVirtualMemoryRegionTree());
400 }
401 static const KMemoryRegion* Find(const KMemoryLayout& layout, KPhysicalAddress address) {
402 return Find(address, layout.GetPhysicalMemoryRegionTree());
403 }
404
397private: 405private:
398 u64 m_linear_phys_to_virt_diff{}; 406 u64 m_linear_phys_to_virt_diff{};
399 u64 m_linear_virt_to_phys_diff{}; 407 u64 m_linear_virt_to_phys_diff{};
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index cdc5572d8..0a973ec8c 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
456} 456}
457 457
458void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { 458void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
459 auto optimize_pa = 459 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
460 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
461 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); 460 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
462 461
463 std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); 462 std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
@@ -465,8 +464,7 @@ void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
465 464
466void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, 465void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
467 size_t num_pages) { 466 size_t num_pages) {
468 auto optimize_pa = 467 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
469 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
470 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); 468 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
471 469
472 // Get the range we're tracking. 470 // Get the range we're tracking.
@@ -485,8 +483,7 @@ void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysi
485 483
486void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, 484void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
487 size_t num_pages) { 485 size_t num_pages) {
488 auto optimize_pa = 486 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
489 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
490 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); 487 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
491 488
492 // Get the range we're tracking. 489 // Get the range we're tracking.
@@ -506,8 +503,7 @@ void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysica
506bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, 503bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
507 size_t num_pages, u8 fill_pattern) { 504 size_t num_pages, u8 fill_pattern) {
508 auto& device_memory = kernel.System().DeviceMemory(); 505 auto& device_memory = kernel.System().DeviceMemory();
509 auto optimize_pa = 506 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
510 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
511 auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); 507 auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
512 508
513 // We want to return whether any pages were newly allocated. 509 // We want to return whether any pages were newly allocated.
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
deleted file mode 100644
index 1d47bdf6b..000000000
--- a/src/core/hle/kernel/k_page_table.cpp
+++ /dev/null
@@ -1,3519 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/alignment.h"
5#include "common/assert.h"
6#include "common/literals.h"
7#include "common/scope_exit.h"
8#include "common/settings.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_address_space_info.h"
11#include "core/hle/kernel/k_memory_block.h"
12#include "core/hle/kernel/k_memory_block_manager.h"
13#include "core/hle/kernel/k_page_group.h"
14#include "core/hle/kernel/k_page_table.h"
15#include "core/hle/kernel/k_process.h"
16#include "core/hle/kernel/k_resource_limit.h"
17#include "core/hle/kernel/k_scoped_resource_reservation.h"
18#include "core/hle/kernel/k_system_control.h"
19#include "core/hle/kernel/k_system_resource.h"
20#include "core/hle/kernel/kernel.h"
21#include "core/hle/kernel/svc_results.h"
22#include "core/memory.h"
23
24namespace Kernel {
25
26namespace {
27
28class KScopedLightLockPair {
29 YUZU_NON_COPYABLE(KScopedLightLockPair);
30 YUZU_NON_MOVEABLE(KScopedLightLockPair);
31
32private:
33 KLightLock* m_lower;
34 KLightLock* m_upper;
35
36public:
37 KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) {
38 // Ensure our locks are in a consistent order.
39 if (std::addressof(lhs) <= std::addressof(rhs)) {
40 m_lower = std::addressof(lhs);
41 m_upper = std::addressof(rhs);
42 } else {
43 m_lower = std::addressof(rhs);
44 m_upper = std::addressof(lhs);
45 }
46
47 // Acquire both locks.
48 m_lower->Lock();
49 if (m_lower != m_upper) {
50 m_upper->Lock();
51 }
52 }
53
54 ~KScopedLightLockPair() {
55 // Unlock the upper lock.
56 if (m_upper != nullptr && m_upper != m_lower) {
57 m_upper->Unlock();
58 }
59
60 // Unlock the lower lock.
61 if (m_lower != nullptr) {
62 m_lower->Unlock();
63 }
64 }
65
66public:
67 // Utility.
68 void TryUnlockHalf(KLightLock& lock) {
69 // Only allow unlocking if the lock is half the pair.
70 if (m_lower != m_upper) {
71 // We want to be sure the lock is one we own.
72 if (m_lower == std::addressof(lock)) {
73 lock.Unlock();
74 m_lower = nullptr;
75 } else if (m_upper == std::addressof(lock)) {
76 lock.Unlock();
77 m_upper = nullptr;
78 }
79 }
80 }
81};
82
83using namespace Common::Literals;
84
85constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) {
86 switch (as_type) {
87 case Svc::CreateProcessFlag::AddressSpace32Bit:
88 case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
89 return 32;
90 case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
91 return 36;
92 case Svc::CreateProcessFlag::AddressSpace64Bit:
93 return 39;
94 default:
95 ASSERT(false);
96 return {};
97 }
98}
99
100} // namespace
101
102KPageTable::KPageTable(Core::System& system_)
103 : m_general_lock{system_.Kernel()},
104 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {}
105
106KPageTable::~KPageTable() = default;
107
108Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
109 bool enable_das_merge, bool from_back,
110 KMemoryManager::Pool pool, KProcessAddress code_addr,
111 size_t code_size, KSystemResource* system_resource,
112 KResourceLimit* resource_limit,
113 Core::Memory::Memory& memory) {
114
115 const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
116 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type);
117 };
118 const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) {
119 return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type);
120 };
121
122 // Set the tracking memory
123 m_memory = std::addressof(memory);
124
125 // Set our width and heap/alias sizes
126 m_address_space_width = GetAddressSpaceWidthFromType(as_type);
127 const KProcessAddress start = 0;
128 const KProcessAddress end{1ULL << m_address_space_width};
129 size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
130 size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
131
132 ASSERT(code_addr < code_addr + code_size);
133 ASSERT(code_addr + code_size - 1 <= end - 1);
134
135 // Adjust heap/alias size if we don't have an alias region
136 if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) {
137 heap_region_size += alias_region_size;
138 alias_region_size = 0;
139 }
140
141 // Set code regions and determine remaining
142 constexpr size_t RegionAlignment{2_MiB};
143 KProcessAddress process_code_start{};
144 KProcessAddress process_code_end{};
145 size_t stack_region_size{};
146 size_t kernel_map_region_size{};
147
148 if (m_address_space_width == 39) {
149 alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
150 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
151 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
152 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
153 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
154 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
155 m_alias_code_region_start = m_code_region_start;
156 m_alias_code_region_end = m_code_region_end;
157 process_code_start = Common::AlignDown(GetInteger(code_addr), RegionAlignment);
158 process_code_end = Common::AlignUp(GetInteger(code_addr) + code_size, RegionAlignment);
159 } else {
160 stack_region_size = 0;
161 kernel_map_region_size = 0;
162 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
163 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
164 m_stack_region_start = m_code_region_start;
165 m_alias_code_region_start = m_code_region_start;
166 m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
167 GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
168 m_stack_region_end = m_code_region_end;
169 m_kernel_map_region_start = m_code_region_start;
170 m_kernel_map_region_end = m_code_region_end;
171 process_code_start = m_code_region_start;
172 process_code_end = m_code_region_end;
173 }
174
175 // Set other basic fields
176 m_enable_aslr = enable_aslr;
177 m_enable_device_address_space_merge = enable_das_merge;
178 m_address_space_start = start;
179 m_address_space_end = end;
180 m_is_kernel = false;
181 m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer();
182 m_block_info_manager = system_resource->GetBlockInfoManagerPointer();
183 m_resource_limit = resource_limit;
184
185 // Determine the region we can place our undetermineds in
186 KProcessAddress alloc_start{};
187 size_t alloc_size{};
188 if ((process_code_start - m_code_region_start) >= (end - process_code_end)) {
189 alloc_start = m_code_region_start;
190 alloc_size = process_code_start - m_code_region_start;
191 } else {
192 alloc_start = process_code_end;
193 alloc_size = end - process_code_end;
194 }
195 const size_t needed_size =
196 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size);
197 R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory);
198
199 const size_t remaining_size{alloc_size - needed_size};
200
201 // Determine random placements for each region
202 size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{};
203 if (enable_aslr) {
204 alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
205 RegionAlignment;
206 heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
207 RegionAlignment;
208 stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
209 RegionAlignment;
210 kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
211 RegionAlignment;
212 }
213
214 // Setup heap and alias regions
215 m_alias_region_start = alloc_start + alias_rnd;
216 m_alias_region_end = m_alias_region_start + alias_region_size;
217 m_heap_region_start = alloc_start + heap_rnd;
218 m_heap_region_end = m_heap_region_start + heap_region_size;
219
220 if (alias_rnd <= heap_rnd) {
221 m_heap_region_start += alias_region_size;
222 m_heap_region_end += alias_region_size;
223 } else {
224 m_alias_region_start += heap_region_size;
225 m_alias_region_end += heap_region_size;
226 }
227
228 // Setup stack region
229 if (stack_region_size) {
230 m_stack_region_start = alloc_start + stack_rnd;
231 m_stack_region_end = m_stack_region_start + stack_region_size;
232
233 if (alias_rnd < stack_rnd) {
234 m_stack_region_start += alias_region_size;
235 m_stack_region_end += alias_region_size;
236 } else {
237 m_alias_region_start += stack_region_size;
238 m_alias_region_end += stack_region_size;
239 }
240
241 if (heap_rnd < stack_rnd) {
242 m_stack_region_start += heap_region_size;
243 m_stack_region_end += heap_region_size;
244 } else {
245 m_heap_region_start += stack_region_size;
246 m_heap_region_end += stack_region_size;
247 }
248 }
249
250 // Setup kernel map region
251 if (kernel_map_region_size) {
252 m_kernel_map_region_start = alloc_start + kmap_rnd;
253 m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size;
254
255 if (alias_rnd < kmap_rnd) {
256 m_kernel_map_region_start += alias_region_size;
257 m_kernel_map_region_end += alias_region_size;
258 } else {
259 m_alias_region_start += kernel_map_region_size;
260 m_alias_region_end += kernel_map_region_size;
261 }
262
263 if (heap_rnd < kmap_rnd) {
264 m_kernel_map_region_start += heap_region_size;
265 m_kernel_map_region_end += heap_region_size;
266 } else {
267 m_heap_region_start += kernel_map_region_size;
268 m_heap_region_end += kernel_map_region_size;
269 }
270
271 if (stack_region_size) {
272 if (stack_rnd < kmap_rnd) {
273 m_kernel_map_region_start += stack_region_size;
274 m_kernel_map_region_end += stack_region_size;
275 } else {
276 m_stack_region_start += kernel_map_region_size;
277 m_stack_region_end += kernel_map_region_size;
278 }
279 }
280 }
281
282 // Set heap and fill members.
283 m_current_heap_end = m_heap_region_start;
284 m_max_heap_size = 0;
285 m_mapped_physical_memory_size = 0;
286 m_mapped_unsafe_physical_memory = 0;
287 m_mapped_insecure_memory = 0;
288 m_mapped_ipc_server_memory = 0;
289
290 m_heap_fill_value = 0;
291 m_ipc_fill_value = 0;
292 m_stack_fill_value = 0;
293
294 // Set allocation option.
295 m_allocate_option =
296 KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack
297 : KMemoryManager::Direction::FromFront);
298
299 // Ensure that we regions inside our address space
300 auto IsInAddressSpace = [&](KProcessAddress addr) {
301 return m_address_space_start <= addr && addr <= m_address_space_end;
302 };
303 ASSERT(IsInAddressSpace(m_alias_region_start));
304 ASSERT(IsInAddressSpace(m_alias_region_end));
305 ASSERT(IsInAddressSpace(m_heap_region_start));
306 ASSERT(IsInAddressSpace(m_heap_region_end));
307 ASSERT(IsInAddressSpace(m_stack_region_start));
308 ASSERT(IsInAddressSpace(m_stack_region_end));
309 ASSERT(IsInAddressSpace(m_kernel_map_region_start));
310 ASSERT(IsInAddressSpace(m_kernel_map_region_end));
311
312 // Ensure that we selected regions that don't overlap
313 const KProcessAddress alias_start{m_alias_region_start};
314 const KProcessAddress alias_last{m_alias_region_end - 1};
315 const KProcessAddress heap_start{m_heap_region_start};
316 const KProcessAddress heap_last{m_heap_region_end - 1};
317 const KProcessAddress stack_start{m_stack_region_start};
318 const KProcessAddress stack_last{m_stack_region_end - 1};
319 const KProcessAddress kmap_start{m_kernel_map_region_start};
320 const KProcessAddress kmap_last{m_kernel_map_region_end - 1};
321 ASSERT(alias_last < heap_start || heap_last < alias_start);
322 ASSERT(alias_last < stack_start || stack_last < alias_start);
323 ASSERT(alias_last < kmap_start || kmap_last < alias_start);
324 ASSERT(heap_last < stack_start || stack_last < heap_start);
325 ASSERT(heap_last < kmap_start || kmap_last < heap_start);
326
327 m_current_heap_end = m_heap_region_start;
328 m_max_heap_size = 0;
329 m_mapped_physical_memory_size = 0;
330 m_memory_pool = pool;
331
332 m_page_table_impl = std::make_unique<Common::PageTable>();
333 m_page_table_impl->Resize(m_address_space_width, PageBits);
334
335 // Initialize our memory block manager.
336 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
337 m_memory_block_slab_manager));
338}
339
340void KPageTable::Finalize() {
341 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
342 if (Settings::IsFastmemEnabled()) {
343 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
344 }
345 };
346
347 // Finalize memory blocks.
348 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
349
350 // Release any insecure mapped memory.
351 if (m_mapped_insecure_memory) {
352 UNIMPLEMENTED();
353 }
354
355 // Release any ipc server memory.
356 if (m_mapped_ipc_server_memory) {
357 UNIMPLEMENTED();
358 }
359
360 // Close the backing page table, as the destructor is not called for guest objects.
361 m_page_table_impl.reset();
362}
363
364Result KPageTable::MapProcessCode(KProcessAddress addr, size_t num_pages, KMemoryState state,
365 KMemoryPermission perm) {
366 const u64 size{num_pages * PageSize};
367
368 // Validate the mapping request.
369 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
370
371 // Lock the table.
372 KScopedLightLock lk(m_general_lock);
373
374 // Verify that the destination memory is unmapped.
375 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
376 KMemoryPermission::None, KMemoryPermission::None,
377 KMemoryAttribute::None, KMemoryAttribute::None));
378
379 // Create an update allocator.
380 Result allocator_result{ResultSuccess};
381 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
382 m_memory_block_slab_manager);
383
384 // Allocate and open.
385 KPageGroup pg{m_kernel, m_block_info_manager};
386 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
387 &pg, num_pages,
388 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
389
390 R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
391
392 // Update the blocks.
393 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
394 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
395 KMemoryBlockDisableMergeAttribute::None);
396
397 R_SUCCEED();
398}
399
400Result KPageTable::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
401 size_t size) {
402 // Validate the mapping request.
403 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
404 ResultInvalidMemoryRegion);
405
406 // Lock the table.
407 KScopedLightLock lk(m_general_lock);
408
409 // Verify that the source memory is normal heap.
410 KMemoryState src_state{};
411 KMemoryPermission src_perm{};
412 size_t num_src_allocator_blocks{};
413 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
414 src_address, size, KMemoryState::All, KMemoryState::Normal,
415 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
416 KMemoryAttribute::All, KMemoryAttribute::None));
417
418 // Verify that the destination memory is unmapped.
419 size_t num_dst_allocator_blocks{};
420 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
421 KMemoryState::Free, KMemoryPermission::None,
422 KMemoryPermission::None, KMemoryAttribute::None,
423 KMemoryAttribute::None));
424
425 // Create an update allocator for the source.
426 Result src_allocator_result{ResultSuccess};
427 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
428 m_memory_block_slab_manager,
429 num_src_allocator_blocks);
430 R_TRY(src_allocator_result);
431
432 // Create an update allocator for the destination.
433 Result dst_allocator_result{ResultSuccess};
434 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
435 m_memory_block_slab_manager,
436 num_dst_allocator_blocks);
437 R_TRY(dst_allocator_result);
438
439 // Map the code memory.
440 {
441 // Determine the number of pages being operated on.
442 const size_t num_pages = size / PageSize;
443
444 // Create page groups for the memory being mapped.
445 KPageGroup pg{m_kernel, m_block_info_manager};
446 AddRegionToPages(src_address, num_pages, pg);
447
448 // We're going to perform an update, so create a helper.
449 KScopedPageTableUpdater updater(this);
450
451 // Reprotect the source as kernel-read/not mapped.
452 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
453 KMemoryPermission::NotMapped);
454 R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
455
456 // Ensure that we unprotect the source pages on failure.
457 auto unprot_guard = SCOPE_GUARD({
458 ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
459 .IsSuccess());
460 });
461
462 // Map the alias pages.
463 const KPageProperties dst_properties = {new_perm, false, false,
464 DisableMergeAttribute::DisableHead};
465 R_TRY(
466 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
467
468 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
469 // failure.
470 unprot_guard.Cancel();
471
472 // Apply the memory block updates.
473 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
474 src_state, new_perm, KMemoryAttribute::Locked,
475 KMemoryBlockDisableMergeAttribute::Locked,
476 KMemoryBlockDisableMergeAttribute::None);
477 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
478 KMemoryState::AliasCode, new_perm, KMemoryAttribute::None,
479 KMemoryBlockDisableMergeAttribute::Normal,
480 KMemoryBlockDisableMergeAttribute::None);
481 }
482
483 R_SUCCEED();
484}
485
486Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
487 size_t size,
488 ICacheInvalidationStrategy icache_invalidation_strategy) {
489 // Validate the mapping request.
490 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
491 ResultInvalidMemoryRegion);
492
493 // Lock the table.
494 KScopedLightLock lk(m_general_lock);
495
496 // Verify that the source memory is locked normal heap.
497 size_t num_src_allocator_blocks{};
498 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
499 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
500 KMemoryPermission::None, KMemoryAttribute::All,
501 KMemoryAttribute::Locked));
502
503 // Verify that the destination memory is aliasable code.
504 size_t num_dst_allocator_blocks{};
505 R_TRY(this->CheckMemoryStateContiguous(
506 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
507 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
508 KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
509
510 // Determine whether any pages being unmapped are code.
511 bool any_code_pages = false;
512 {
513 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address);
514 while (true) {
515 // Get the memory info.
516 const KMemoryInfo info = it->GetMemoryInfo();
517
518 // Check if the memory has code flag.
519 if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
520 any_code_pages = true;
521 break;
522 }
523
524 // Check if we're done.
525 if (dst_address + size - 1 <= info.GetLastAddress()) {
526 break;
527 }
528
529 // Advance.
530 ++it;
531 }
532 }
533
534 // Ensure that we maintain the instruction cache.
535 bool reprotected_pages = false;
536 SCOPE_EXIT({
537 if (reprotected_pages && any_code_pages) {
538 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
539 m_system.InvalidateCpuInstructionCacheRange(GetInteger(dst_address), size);
540 } else {
541 m_system.InvalidateCpuInstructionCaches();
542 }
543 }
544 });
545
546 // Unmap.
547 {
548 // Determine the number of pages being operated on.
549 const size_t num_pages = size / PageSize;
550
551 // Create an update allocator for the source.
552 Result src_allocator_result{ResultSuccess};
553 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
554 m_memory_block_slab_manager,
555 num_src_allocator_blocks);
556 R_TRY(src_allocator_result);
557
558 // Create an update allocator for the destination.
559 Result dst_allocator_result{ResultSuccess};
560 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
561 m_memory_block_slab_manager,
562 num_dst_allocator_blocks);
563 R_TRY(dst_allocator_result);
564
565 // Unmap the aliased copy of the pages.
566 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
567
568 // Try to set the permissions for the source pages back to what they should be.
569 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
570 OperationType::ChangePermissions));
571
572 // Apply the memory block updates.
573 m_memory_block_manager.Update(
574 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
575 KMemoryPermission::None, KMemoryAttribute::None,
576 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
577 m_memory_block_manager.Update(
578 std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal,
579 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
580 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
581
582 // Note that we reprotected pages.
583 reprotected_pages = true;
584 }
585
586 R_SUCCEED();
587}
588
589KProcessAddress KPageTable::FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
590 size_t num_pages, size_t alignment, size_t offset,
591 size_t guard_pages) {
592 KProcessAddress address = 0;
593
594 if (num_pages <= region_num_pages) {
595 if (this->IsAslrEnabled()) {
596 UNIMPLEMENTED();
597 }
598 // Find the first free area.
599 if (address == 0) {
600 address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages,
601 alignment, offset, guard_pages);
602 }
603 }
604
605 return address;
606}
607
608Result KPageTable::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) {
609 ASSERT(this->IsLockedByCurrentThread());
610
611 const size_t size = num_pages * PageSize;
612
613 // We're making a new group, not adding to an existing one.
614 R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
615
616 // Begin traversal.
617 Common::PageTable::TraversalContext context;
618 Common::PageTable::TraversalEntry next_entry;
619 R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr)),
620 ResultInvalidCurrentMemory);
621
622 // Prepare tracking variables.
623 KPhysicalAddress cur_addr = next_entry.phys_addr;
624 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
625 size_t tot_size = cur_size;
626
627 // Iterate, adding to group as we go.
628 const auto& memory_layout = m_system.Kernel().MemoryLayout();
629 while (tot_size < size) {
630 R_UNLESS(m_page_table_impl->ContinueTraversal(next_entry, context),
631 ResultInvalidCurrentMemory);
632
633 if (next_entry.phys_addr != (cur_addr + cur_size)) {
634 const size_t cur_pages = cur_size / PageSize;
635
636 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
637 R_TRY(pg.AddBlock(cur_addr, cur_pages));
638
639 cur_addr = next_entry.phys_addr;
640 cur_size = next_entry.block_size;
641 } else {
642 cur_size += next_entry.block_size;
643 }
644
645 tot_size += next_entry.block_size;
646 }
647
648 // Ensure we add the right amount for the last block.
649 if (tot_size > size) {
650 cur_size -= (tot_size - size);
651 }
652
653 // Add the last block.
654 const size_t cur_pages = cur_size / PageSize;
655 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
656 R_TRY(pg.AddBlock(cur_addr, cur_pages));
657
658 R_SUCCEED();
659}
660
661bool KPageTable::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages) {
662 ASSERT(this->IsLockedByCurrentThread());
663
664 const size_t size = num_pages * PageSize;
665 const auto& memory_layout = m_system.Kernel().MemoryLayout();
666
667 // Empty groups are necessarily invalid.
668 if (pg.empty()) {
669 return false;
670 }
671
672 // We're going to validate that the group we'd expect is the group we see.
673 auto cur_it = pg.begin();
674 KPhysicalAddress cur_block_address = cur_it->GetAddress();
675 size_t cur_block_pages = cur_it->GetNumPages();
676
677 auto UpdateCurrentIterator = [&]() {
678 if (cur_block_pages == 0) {
679 if ((++cur_it) == pg.end()) {
680 return false;
681 }
682
683 cur_block_address = cur_it->GetAddress();
684 cur_block_pages = cur_it->GetNumPages();
685 }
686 return true;
687 };
688
689 // Begin traversal.
690 Common::PageTable::TraversalContext context;
691 Common::PageTable::TraversalEntry next_entry;
692 if (!m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr))) {
693 return false;
694 }
695
696 // Prepare tracking variables.
697 KPhysicalAddress cur_addr = next_entry.phys_addr;
698 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
699 size_t tot_size = cur_size;
700
701 // Iterate, comparing expected to actual.
702 while (tot_size < size) {
703 if (!m_page_table_impl->ContinueTraversal(next_entry, context)) {
704 return false;
705 }
706
707 if (next_entry.phys_addr != (cur_addr + cur_size)) {
708 const size_t cur_pages = cur_size / PageSize;
709
710 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
711 return false;
712 }
713
714 if (!UpdateCurrentIterator()) {
715 return false;
716 }
717
718 if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
719 return false;
720 }
721
722 cur_block_address += cur_size;
723 cur_block_pages -= cur_pages;
724 cur_addr = next_entry.phys_addr;
725 cur_size = next_entry.block_size;
726 } else {
727 cur_size += next_entry.block_size;
728 }
729
730 tot_size += next_entry.block_size;
731 }
732
733 // Ensure we compare the right amount for the last block.
734 if (tot_size > size) {
735 cur_size -= (tot_size - size);
736 }
737
738 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
739 return false;
740 }
741
742 if (!UpdateCurrentIterator()) {
743 return false;
744 }
745
746 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
747}
748
749Result KPageTable::UnmapProcessMemory(KProcessAddress dst_addr, size_t size,
750 KPageTable& src_page_table, KProcessAddress src_addr) {
751 // Acquire the table locks.
752 KScopedLightLockPair lk(src_page_table.m_general_lock, m_general_lock);
753
754 const size_t num_pages{size / PageSize};
755
756 // Check that the memory is mapped in the destination process.
757 size_t num_allocator_blocks;
758 R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
759 KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
760 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
761 KMemoryAttribute::None));
762
763 // Check that the memory is mapped in the source process.
764 R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
765 KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
766 KMemoryPermission::None, KMemoryAttribute::All,
767 KMemoryAttribute::None));
768
769 // Create an update allocator.
770 Result allocator_result{ResultSuccess};
771 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
772 m_memory_block_slab_manager, num_allocator_blocks);
773 R_TRY(allocator_result);
774
775 R_TRY(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
776
777 // Apply the memory block update.
778 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages,
779 KMemoryState::Free, KMemoryPermission::None,
780 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
781 KMemoryBlockDisableMergeAttribute::Normal);
782
783 m_system.InvalidateCpuInstructionCaches();
784
785 R_SUCCEED();
786}
787
788Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
789 KProcessAddress address, size_t size,
790 KMemoryPermission test_perm, KMemoryState dst_state) {
791 // Validate pre-conditions.
792 ASSERT(this->IsLockedByCurrentThread());
793 ASSERT(test_perm == KMemoryPermission::UserReadWrite ||
794 test_perm == KMemoryPermission::UserRead);
795
796 // Check that the address is in range.
797 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
798
799 // Get the source permission.
800 const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite)
801 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
802 : KMemoryPermission::UserRead;
803
804 // Get aligned extents.
805 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize);
806 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize);
807 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize);
808 const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize);
809
810 const auto aligned_src_last = (aligned_src_end)-1;
811 const auto mapping_src_last = (mapping_src_end)-1;
812
813 // Get the test state and attribute mask.
814 KMemoryState test_state;
815 KMemoryAttribute test_attr_mask;
816 switch (dst_state) {
817 case KMemoryState::Ipc:
818 test_state = KMemoryState::FlagCanUseIpc;
819 test_attr_mask =
820 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
821 break;
822 case KMemoryState::NonSecureIpc:
823 test_state = KMemoryState::FlagCanUseNonSecureIpc;
824 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
825 break;
826 case KMemoryState::NonDeviceIpc:
827 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
828 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
829 break;
830 default:
831 R_THROW(ResultInvalidCombination);
832 }
833
834 // Ensure that on failure, we roll back appropriately.
835 size_t mapped_size = 0;
836 ON_RESULT_FAILURE {
837 if (mapped_size > 0) {
838 this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size,
839 src_perm);
840 }
841 };
842
843 size_t blocks_needed = 0;
844
845 // Iterate, mapping as needed.
846 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start);
847 while (true) {
848 const KMemoryInfo info = it->GetMemoryInfo();
849
850 // Validate the current block.
851 R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm,
852 test_attr_mask, KMemoryAttribute::None));
853
854 if (mapping_src_start < mapping_src_end && (mapping_src_start) < info.GetEndAddress() &&
855 info.GetAddress() < GetInteger(mapping_src_end)) {
856 const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start)
857 ? info.GetAddress()
858 : (mapping_src_start);
859 const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress()
860 : (mapping_src_end);
861 const size_t cur_size = cur_end - cur_start;
862
863 if (info.GetAddress() < GetInteger(mapping_src_start)) {
864 ++blocks_needed;
865 }
866 if (mapping_src_last < info.GetLastAddress()) {
867 ++blocks_needed;
868 }
869
870 // Set the permissions on the block, if we need to.
871 if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) {
872 R_TRY(Operate(cur_start, cur_size / PageSize, src_perm,
873 OperationType::ChangePermissions));
874 }
875
876 // Note that we mapped this part.
877 mapped_size += cur_size;
878 }
879
880 // If the block is at the end, we're done.
881 if (aligned_src_last <= info.GetLastAddress()) {
882 break;
883 }
884
885 // Advance.
886 ++it;
887 ASSERT(it != m_memory_block_manager.end());
888 }
889
890 if (out_blocks_needed != nullptr) {
891 ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
892 *out_blocks_needed = blocks_needed;
893 }
894
895 R_SUCCEED();
896}
897
898Result KPageTable::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
899 KProcessAddress src_addr, KMemoryPermission test_perm,
900 KMemoryState dst_state, KPageTable& src_page_table,
901 bool send) {
902 ASSERT(this->IsLockedByCurrentThread());
903 ASSERT(src_page_table.IsLockedByCurrentThread());
904
905 // Check that we can theoretically map.
906 const KProcessAddress region_start = m_alias_region_start;
907 const size_t region_size = m_alias_region_end - m_alias_region_start;
908 R_UNLESS(size < region_size, ResultOutOfAddressSpace);
909
910 // Get aligned source extents.
911 const KProcessAddress src_start = src_addr;
912 const KProcessAddress src_end = src_addr + size;
913 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize);
914 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize);
915 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize);
916 const KProcessAddress mapping_src_end =
917 Common::AlignDown(GetInteger(src_start) + size, PageSize);
918 const size_t aligned_src_size = aligned_src_end - aligned_src_start;
919 const size_t mapping_src_size =
920 (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0;
921
922 // Select a random address to map at.
923 KProcessAddress dst_addr =
924 this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize,
925 PageSize, 0, this->GetNumGuardPages());
926
927 R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace);
928
929 // Check that we can perform the operation we're about to perform.
930 ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state));
931
932 // Create an update allocator.
933 Result allocator_result;
934 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
935 m_memory_block_slab_manager);
936 R_TRY(allocator_result);
937
938 // We're going to perform an update, so create a helper.
939 KScopedPageTableUpdater updater(this);
940
941 // Reserve space for any partial pages we allocate.
942 const size_t unmapped_size = aligned_src_size - mapping_src_size;
943 KScopedResourceReservation memory_reservation(
944 m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size);
945 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
946
947 // Ensure that we manage page references correctly.
948 KPhysicalAddress start_partial_page = 0;
949 KPhysicalAddress end_partial_page = 0;
950 KProcessAddress cur_mapped_addr = dst_addr;
951
952 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
953 // free on scope exit.
954 SCOPE_EXIT({
955 if (start_partial_page != 0) {
956 m_system.Kernel().MemoryManager().Close(start_partial_page, 1);
957 }
958 if (end_partial_page != 0) {
959 m_system.Kernel().MemoryManager().Close(end_partial_page, 1);
960 }
961 });
962
963 ON_RESULT_FAILURE {
964 if (cur_mapped_addr != dst_addr) {
965 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
966 KMemoryPermission::None, OperationType::Unmap)
967 .IsSuccess());
968 }
969 };
970
971 // Allocate the start page as needed.
972 if (aligned_src_start < mapping_src_start) {
973 start_partial_page =
974 m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
975 R_UNLESS(start_partial_page != 0, ResultOutOfMemory);
976 }
977
978 // Allocate the end page as needed.
979 if (mapping_src_end < aligned_src_end &&
980 (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) {
981 end_partial_page =
982 m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
983 R_UNLESS(end_partial_page != 0, ResultOutOfMemory);
984 }
985
986 // Get the implementation.
987 auto& src_impl = src_page_table.PageTableImpl();
988
989 // Get the fill value for partial pages.
990 const auto fill_val = m_ipc_fill_value;
991
992 // Begin traversal.
993 Common::PageTable::TraversalContext context;
994 Common::PageTable::TraversalEntry next_entry;
995 bool traverse_valid =
996 src_impl.BeginTraversal(next_entry, context, GetInteger(aligned_src_start));
997 ASSERT(traverse_valid);
998
999 // Prepare tracking variables.
1000 KPhysicalAddress cur_block_addr = next_entry.phys_addr;
1001 size_t cur_block_size =
1002 next_entry.block_size - ((cur_block_addr) & (next_entry.block_size - 1));
1003 size_t tot_block_size = cur_block_size;
1004
1005 // Map the start page, if we have one.
1006 if (start_partial_page != 0) {
1007 // Ensure the page holds correct data.
1008 const KVirtualAddress start_partial_virt =
1009 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), start_partial_page);
1010 if (send) {
1011 const size_t partial_offset = src_start - aligned_src_start;
1012 size_t copy_size, clear_size;
1013 if (src_end < mapping_src_start) {
1014 copy_size = size;
1015 clear_size = mapping_src_start - src_end;
1016 } else {
1017 copy_size = mapping_src_start - src_start;
1018 clear_size = 0;
1019 }
1020
1021 std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val,
1022 partial_offset);
1023 std::memcpy(
1024 m_memory->GetPointer<void>(GetInteger(start_partial_virt) + partial_offset),
1025 m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress(
1026 m_system.Kernel().MemoryLayout(), cur_block_addr)) +
1027 partial_offset),
1028 copy_size);
1029 if (clear_size > 0) {
1030 std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt) +
1031 partial_offset + copy_size),
1032 fill_val, clear_size);
1033 }
1034 } else {
1035 std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val,
1036 PageSize);
1037 }
1038
1039 // Map the page.
1040 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
1041
1042 // Update tracking extents.
1043 cur_mapped_addr += PageSize;
1044 cur_block_addr += PageSize;
1045 cur_block_size -= PageSize;
1046
1047 // If the block's size was one page, we may need to continue traversal.
1048 if (cur_block_size == 0 && aligned_src_size > PageSize) {
1049 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1050 ASSERT(traverse_valid);
1051
1052 cur_block_addr = next_entry.phys_addr;
1053 cur_block_size = next_entry.block_size;
1054 tot_block_size += next_entry.block_size;
1055 }
1056 }
1057
1058 // Map the remaining pages.
1059 while (aligned_src_start + tot_block_size < mapping_src_end) {
1060 // Continue the traversal.
1061 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1062 ASSERT(traverse_valid);
1063
1064 // Process the block.
1065 if (next_entry.phys_addr != cur_block_addr + cur_block_size) {
1066 // Map the block we've been processing so far.
1067 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
1068 cur_block_addr));
1069
1070 // Update tracking extents.
1071 cur_mapped_addr += cur_block_size;
1072 cur_block_addr = next_entry.phys_addr;
1073 cur_block_size = next_entry.block_size;
1074 } else {
1075 cur_block_size += next_entry.block_size;
1076 }
1077 tot_block_size += next_entry.block_size;
1078 }
1079
1080 // Handle the last direct-mapped page.
1081 if (const KProcessAddress mapped_block_end =
1082 aligned_src_start + tot_block_size - cur_block_size;
1083 mapped_block_end < mapping_src_end) {
1084 const size_t last_block_size = mapping_src_end - mapped_block_end;
1085
1086 // Map the last block.
1087 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
1088 cur_block_addr));
1089
1090 // Update tracking extents.
1091 cur_mapped_addr += last_block_size;
1092 cur_block_addr += last_block_size;
1093 if (mapped_block_end + cur_block_size < aligned_src_end &&
1094 cur_block_size == last_block_size) {
1095 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1096 ASSERT(traverse_valid);
1097
1098 cur_block_addr = next_entry.phys_addr;
1099 }
1100 }
1101
1102 // Map the end page, if we have one.
1103 if (end_partial_page != 0) {
1104 // Ensure the page holds correct data.
1105 const KVirtualAddress end_partial_virt =
1106 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), end_partial_page);
1107 if (send) {
1108 const size_t copy_size = src_end - mapping_src_end;
1109 std::memcpy(m_memory->GetPointer<void>(GetInteger(end_partial_virt)),
1110 m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress(
1111 m_system.Kernel().MemoryLayout(), cur_block_addr))),
1112 copy_size);
1113 std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt) + copy_size),
1114 fill_val, PageSize - copy_size);
1115 } else {
1116 std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt)), fill_val,
1117 PageSize);
1118 }
1119
1120 // Map the page.
1121 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
1122 }
1123
1124 // Update memory blocks to reflect our changes
1125 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize,
1126 dst_state, test_perm, KMemoryAttribute::None,
1127 KMemoryBlockDisableMergeAttribute::Normal,
1128 KMemoryBlockDisableMergeAttribute::None);
1129
1130 // Set the output address.
1131 *out_addr = dst_addr + (src_start - aligned_src_start);
1132
1133 // We succeeded.
1134 memory_reservation.Commit();
1135 R_SUCCEED();
1136}
1137
1138Result KPageTable::SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
1139 KPageTable& src_page_table, KMemoryPermission test_perm,
1140 KMemoryState dst_state, bool send) {
1141 // For convenience, alias this.
1142 KPageTable& dst_page_table = *this;
1143
1144 // Acquire the table locks.
1145 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
1146
1147 // We're going to perform an update, so create a helper.
1148 KScopedPageTableUpdater updater(std::addressof(src_page_table));
1149
1150 // Perform client setup.
1151 size_t num_allocator_blocks;
1152 R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(),
1153 std::addressof(num_allocator_blocks), src_addr, size,
1154 test_perm, dst_state));
1155
1156 // Create an update allocator.
1157 Result allocator_result;
1158 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1159 src_page_table.m_memory_block_slab_manager,
1160 num_allocator_blocks);
1161 R_TRY(allocator_result);
1162
1163 // Get the mapped extents.
1164 const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize);
1165 const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize);
1166 const size_t src_map_size = src_map_end - src_map_start;
1167
1168 // Ensure that we clean up appropriately if we fail after this.
1169 const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite)
1170 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
1171 : KMemoryPermission::UserRead;
1172 ON_RESULT_FAILURE {
1173 if (src_map_end > src_map_start) {
1174 src_page_table.CleanupForIpcClientOnServerSetupFailure(
1175 updater.GetPageList(), src_map_start, src_map_size, src_perm);
1176 }
1177 };
1178
1179 // Perform server setup.
1180 R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state,
1181 src_page_table, send));
1182
1183 // If anything was mapped, ipc-lock the pages.
1184 if (src_map_start < src_map_end) {
1185 // Get the source permission.
1186 src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start,
1187 (src_map_end - src_map_start) / PageSize,
1188 &KMemoryBlock::LockForIpc, src_perm);
1189 }
1190
1191 R_SUCCEED();
1192}
1193
1194Result KPageTable::CleanupForIpcServer(KProcessAddress address, size_t size,
1195 KMemoryState dst_state) {
1196 // Validate the address.
1197 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1198
1199 // Lock the table.
1200 KScopedLightLock lk(m_general_lock);
1201
1202 // Validate the memory state.
1203 size_t num_allocator_blocks;
1204 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1205 KMemoryState::All, dst_state, KMemoryPermission::UserRead,
1206 KMemoryPermission::UserRead, KMemoryAttribute::All,
1207 KMemoryAttribute::None));
1208
1209 // Create an update allocator.
1210 Result allocator_result;
1211 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1212 m_memory_block_slab_manager, num_allocator_blocks);
1213 R_TRY(allocator_result);
1214
1215 // We're going to perform an update, so create a helper.
1216 KScopedPageTableUpdater updater(this);
1217
1218 // Get aligned extents.
1219 const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize);
1220 const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize);
1221 const size_t aligned_size = aligned_end - aligned_start;
1222 const size_t aligned_num_pages = aligned_size / PageSize;
1223
1224 // Unmap the pages.
1225 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
1226
1227 // Update memory blocks.
1228 m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages,
1229 KMemoryState::None, KMemoryPermission::None,
1230 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1231 KMemoryBlockDisableMergeAttribute::Normal);
1232
1233 // Release from the resource limit as relevant.
1234 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
1235 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
1236 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
1237 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size);
1238
1239 R_SUCCEED();
1240}
1241
1242Result KPageTable::CleanupForIpcClient(KProcessAddress address, size_t size,
1243 KMemoryState dst_state) {
1244 // Validate the address.
1245 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1246
1247 // Get aligned source extents.
1248 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
1249 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
1250 const KProcessAddress mapping_last = mapping_end - 1;
1251 const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0;
1252
1253 // If nothing was mapped, we're actually done immediately.
1254 R_SUCCEED_IF(mapping_size == 0);
1255
1256 // Get the test state and attribute mask.
1257 KMemoryState test_state;
1258 KMemoryAttribute test_attr_mask;
1259 switch (dst_state) {
1260 case KMemoryState::Ipc:
1261 test_state = KMemoryState::FlagCanUseIpc;
1262 test_attr_mask =
1263 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
1264 break;
1265 case KMemoryState::NonSecureIpc:
1266 test_state = KMemoryState::FlagCanUseNonSecureIpc;
1267 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
1268 break;
1269 case KMemoryState::NonDeviceIpc:
1270 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
1271 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
1272 break;
1273 default:
1274 R_THROW(ResultInvalidCombination);
1275 }
1276
1277 // Lock the table.
1278 // NOTE: Nintendo does this *after* creating the updater below, but this does not follow
1279 // convention elsewhere in KPageTable.
1280 KScopedLightLock lk(m_general_lock);
1281
1282 // We're going to perform an update, so create a helper.
1283 KScopedPageTableUpdater updater(this);
1284
1285 // Ensure that on failure, we roll back appropriately.
1286 size_t mapped_size = 0;
1287 ON_RESULT_FAILURE {
1288 if (mapped_size > 0) {
1289 // Determine where the mapping ends.
1290 const auto mapped_end = (mapping_start) + mapped_size;
1291 const auto mapped_last = mapped_end - 1;
1292
1293 // Get current and next iterators.
1294 KMemoryBlockManager::const_iterator start_it =
1295 m_memory_block_manager.FindIterator(mapping_start);
1296 KMemoryBlockManager::const_iterator next_it = start_it;
1297 ++next_it;
1298
1299 // Get the current block info.
1300 KMemoryInfo cur_info = start_it->GetMemoryInfo();
1301
1302 // Create tracking variables.
1303 KProcessAddress cur_address = cur_info.GetAddress();
1304 size_t cur_size = cur_info.GetSize();
1305 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
1306 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
1307 bool first =
1308 cur_info.GetIpcDisableMergeCount() == 1 &&
1309 (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) ==
1310 KMemoryBlockDisableMergeAttribute::None;
1311
1312 while (((cur_address) + cur_size - 1) < mapped_last) {
1313 // Check that we have a next block.
1314 ASSERT(next_it != m_memory_block_manager.end());
1315
1316 // Get the next info.
1317 const KMemoryInfo next_info = next_it->GetMemoryInfo();
1318
1319 // Check if we can consolidate the next block's permission set with the current one.
1320
1321 const bool next_perm_eq =
1322 next_info.GetPermission() == next_info.GetOriginalPermission();
1323 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
1324 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
1325 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
1326 // We can consolidate the reprotection for the current and next block into a
1327 // single call.
1328 cur_size += next_info.GetSize();
1329 } else {
1330 // We have to operate on the current block.
1331 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
1332 ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(),
1333 OperationType::ChangePermissions)
1334 .IsSuccess());
1335 }
1336
1337 // Advance.
1338 cur_address = next_info.GetAddress();
1339 cur_size = next_info.GetSize();
1340 first = false;
1341 }
1342
1343 // Advance.
1344 cur_info = next_info;
1345 cur_perm_eq = next_perm_eq;
1346 cur_needs_set_perm = next_needs_set_perm;
1347 ++next_it;
1348 }
1349
1350 // Process the last block.
1351 if ((first || cur_needs_set_perm) && !cur_perm_eq) {
1352 ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(),
1353 OperationType::ChangePermissions)
1354 .IsSuccess());
1355 }
1356 }
1357 };
1358
1359 // Iterate, reprotecting as needed.
1360 {
1361 // Get current and next iterators.
1362 KMemoryBlockManager::const_iterator start_it =
1363 m_memory_block_manager.FindIterator(mapping_start);
1364 KMemoryBlockManager::const_iterator next_it = start_it;
1365 ++next_it;
1366
1367 // Validate the current block.
1368 KMemoryInfo cur_info = start_it->GetMemoryInfo();
1369 ASSERT(this->CheckMemoryState(cur_info, test_state, test_state, KMemoryPermission::None,
1370 KMemoryPermission::None,
1371 test_attr_mask | KMemoryAttribute::IpcLocked,
1372 KMemoryAttribute::IpcLocked)
1373 .IsSuccess());
1374
1375 // Create tracking variables.
1376 KProcessAddress cur_address = cur_info.GetAddress();
1377 size_t cur_size = cur_info.GetSize();
1378 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
1379 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
1380 bool first =
1381 cur_info.GetIpcDisableMergeCount() == 1 &&
1382 (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) ==
1383 KMemoryBlockDisableMergeAttribute::None;
1384
1385 while ((cur_address + cur_size - 1) < mapping_last) {
1386 // Check that we have a next block.
1387 ASSERT(next_it != m_memory_block_manager.end());
1388
1389 // Get the next info.
1390 const KMemoryInfo next_info = next_it->GetMemoryInfo();
1391
1392 // Validate the next block.
1393 ASSERT(this->CheckMemoryState(next_info, test_state, test_state,
1394 KMemoryPermission::None, KMemoryPermission::None,
1395 test_attr_mask | KMemoryAttribute::IpcLocked,
1396 KMemoryAttribute::IpcLocked)
1397 .IsSuccess());
1398
1399 // Check if we can consolidate the next block's permission set with the current one.
1400 const bool next_perm_eq =
1401 next_info.GetPermission() == next_info.GetOriginalPermission();
1402 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
1403 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
1404 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
1405 // We can consolidate the reprotection for the current and next block into a single
1406 // call.
1407 cur_size += next_info.GetSize();
1408 } else {
1409 // We have to operate on the current block.
1410 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
1411 R_TRY(Operate(cur_address, cur_size / PageSize,
1412 cur_needs_set_perm ? cur_info.GetOriginalPermission()
1413 : cur_info.GetPermission(),
1414 OperationType::ChangePermissions));
1415 }
1416
1417 // Mark that we mapped the block.
1418 mapped_size += cur_size;
1419
1420 // Advance.
1421 cur_address = next_info.GetAddress();
1422 cur_size = next_info.GetSize();
1423 first = false;
1424 }
1425
1426 // Advance.
1427 cur_info = next_info;
1428 cur_perm_eq = next_perm_eq;
1429 cur_needs_set_perm = next_needs_set_perm;
1430 ++next_it;
1431 }
1432
1433 // Process the last block.
1434 const auto lock_count =
1435 cur_info.GetIpcLockCount() +
1436 (next_it != m_memory_block_manager.end()
1437 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
1438 : 0);
1439 if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) {
1440 R_TRY(Operate(cur_address, cur_size / PageSize,
1441 cur_needs_set_perm ? cur_info.GetOriginalPermission()
1442 : cur_info.GetPermission(),
1443 OperationType::ChangePermissions));
1444 }
1445 }
1446
1447 // Create an update allocator.
1448 // NOTE: Guaranteed zero blocks needed here.
1449 Result allocator_result;
1450 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1451 m_memory_block_slab_manager, 0);
1452 R_TRY(allocator_result);
1453
1454 // Unlock the pages.
1455 m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start,
1456 mapping_size / PageSize, &KMemoryBlock::UnlockForIpc,
1457 KMemoryPermission::None);
1458
1459 R_SUCCEED();
1460}
1461
1462void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLinkedList* page_list,
1463 KProcessAddress address, size_t size,
1464 KMemoryPermission prot_perm) {
1465 ASSERT(this->IsLockedByCurrentThread());
1466 ASSERT(Common::IsAligned(GetInteger(address), PageSize));
1467 ASSERT(Common::IsAligned(size, PageSize));
1468
1469 // Get the mapped extents.
1470 const KProcessAddress src_map_start = address;
1471 const KProcessAddress src_map_end = address + size;
1472 const KProcessAddress src_map_last = src_map_end - 1;
1473
1474 // This function is only invoked when there's something to do.
1475 ASSERT(src_map_end > src_map_start);
1476
1477 // Iterate over blocks, fixing permissions.
1478 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
1479 while (true) {
1480 const KMemoryInfo info = it->GetMemoryInfo();
1481
1482 const auto cur_start = info.GetAddress() >= GetInteger(src_map_start)
1483 ? info.GetAddress()
1484 : GetInteger(src_map_start);
1485 const auto cur_end =
1486 src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress();
1487
1488 // If we can, fix the protections on the block.
1489 if ((info.GetIpcLockCount() == 0 &&
1490 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) ||
1491 (info.GetIpcLockCount() != 0 &&
1492 (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) {
1493 // Check if we actually need to fix the protections on the block.
1494 if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) ||
1495 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) {
1496 ASSERT(Operate(cur_start, (cur_end - cur_start) / PageSize, info.GetPermission(),
1497 OperationType::ChangePermissions)
1498 .IsSuccess());
1499 }
1500 }
1501
1502 // If we're past the end of the region, we're done.
1503 if (src_map_last <= info.GetLastAddress()) {
1504 break;
1505 }
1506
1507 // Advance.
1508 ++it;
1509 ASSERT(it != m_memory_block_manager.end());
1510 }
1511}
1512
1513Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
1514 // Lock the physical memory lock.
1515 KScopedLightLock phys_lk(m_map_physical_memory_lock);
1516
1517 // Calculate the last address for convenience.
1518 const KProcessAddress last_address = address + size - 1;
1519
1520 // Define iteration variables.
1521 KProcessAddress cur_address;
1522 size_t mapped_size;
1523
1524 // The entire mapping process can be retried.
1525 while (true) {
1526 // Check if the memory is already mapped.
1527 {
1528 // Lock the table.
1529 KScopedLightLock lk(m_general_lock);
1530
1531 // Iterate over the memory.
1532 cur_address = address;
1533 mapped_size = 0;
1534
1535 auto it = m_memory_block_manager.FindIterator(cur_address);
1536 while (true) {
1537 // Check that the iterator is valid.
1538 ASSERT(it != m_memory_block_manager.end());
1539
1540 // Get the memory info.
1541 const KMemoryInfo info = it->GetMemoryInfo();
1542
1543 // Check if we're done.
1544 if (last_address <= info.GetLastAddress()) {
1545 if (info.GetState() != KMemoryState::Free) {
1546 mapped_size += (last_address + 1 - cur_address);
1547 }
1548 break;
1549 }
1550
1551 // Track the memory if it's mapped.
1552 if (info.GetState() != KMemoryState::Free) {
1553 mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address;
1554 }
1555
1556 // Advance.
1557 cur_address = info.GetEndAddress();
1558 ++it;
1559 }
1560
1561 // If the size mapped is the size requested, we've nothing to do.
1562 R_SUCCEED_IF(size == mapped_size);
1563 }
1564
1565 // Allocate and map the memory.
1566 {
1567 // Reserve the memory from the process resource limit.
1568 KScopedResourceReservation memory_reservation(
1569 m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size);
1570 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1571
1572 // Allocate pages for the new memory.
1573 KPageGroup pg{m_kernel, m_block_info_manager};
1574 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
1575 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
1576
1577 // If we fail in the next bit (or retry), we need to cleanup the pages.
1578 // auto pg_guard = SCOPE_GUARD {
1579 // pg.OpenFirst();
1580 // pg.Close();
1581 //};
1582
1583 // Map the memory.
1584 {
1585 // Lock the table.
1586 KScopedLightLock lk(m_general_lock);
1587
1588 size_t num_allocator_blocks = 0;
1589
1590 // Verify that nobody has mapped memory since we first checked.
1591 {
1592 // Iterate over the memory.
1593 size_t checked_mapped_size = 0;
1594 cur_address = address;
1595
1596 auto it = m_memory_block_manager.FindIterator(cur_address);
1597 while (true) {
1598 // Check that the iterator is valid.
1599 ASSERT(it != m_memory_block_manager.end());
1600
1601 // Get the memory info.
1602 const KMemoryInfo info = it->GetMemoryInfo();
1603
1604 const bool is_free = info.GetState() == KMemoryState::Free;
1605 if (is_free) {
1606 if (info.GetAddress() < GetInteger(address)) {
1607 ++num_allocator_blocks;
1608 }
1609 if (last_address < info.GetLastAddress()) {
1610 ++num_allocator_blocks;
1611 }
1612 }
1613
1614 // Check if we're done.
1615 if (last_address <= info.GetLastAddress()) {
1616 if (!is_free) {
1617 checked_mapped_size += (last_address + 1 - cur_address);
1618 }
1619 break;
1620 }
1621
1622 // Track the memory if it's mapped.
1623 if (!is_free) {
1624 checked_mapped_size +=
1625 KProcessAddress(info.GetEndAddress()) - cur_address;
1626 }
1627
1628 // Advance.
1629 cur_address = info.GetEndAddress();
1630 ++it;
1631 }
1632
1633 // If the size now isn't what it was before, somebody mapped or unmapped
1634 // concurrently. If this happened, retry.
1635 if (mapped_size != checked_mapped_size) {
1636 continue;
1637 }
1638 }
1639
1640 // Create an update allocator.
1641 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
1642 Result allocator_result;
1643 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1644 m_memory_block_slab_manager,
1645 num_allocator_blocks);
1646 R_TRY(allocator_result);
1647
1648 // We're going to perform an update, so create a helper.
1649 KScopedPageTableUpdater updater(this);
1650
1651 // Prepare to iterate over the memory.
1652 auto pg_it = pg.begin();
1653 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
1654 size_t pg_pages = pg_it->GetNumPages();
1655
1656 // Reset the current tracking address, and make sure we clean up on failure.
1657 // pg_guard.Cancel();
1658 cur_address = address;
1659 ON_RESULT_FAILURE {
1660 if (cur_address > address) {
1661 const KProcessAddress last_unmap_address = cur_address - 1;
1662
1663 // Iterate, unmapping the pages.
1664 cur_address = address;
1665
1666 auto it = m_memory_block_manager.FindIterator(cur_address);
1667 while (true) {
1668 // Check that the iterator is valid.
1669 ASSERT(it != m_memory_block_manager.end());
1670
1671 // Get the memory info.
1672 const KMemoryInfo info = it->GetMemoryInfo();
1673
1674 // If the memory state is free, we mapped it and need to unmap it.
1675 if (info.GetState() == KMemoryState::Free) {
1676 // Determine the range to unmap.
1677 const size_t cur_pages =
1678 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
1679 last_unmap_address + 1 - cur_address) /
1680 PageSize;
1681
1682 // Unmap.
1683 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
1684 OperationType::Unmap)
1685 .IsSuccess());
1686 }
1687
1688 // Check if we're done.
1689 if (last_unmap_address <= info.GetLastAddress()) {
1690 break;
1691 }
1692
1693 // Advance.
1694 cur_address = info.GetEndAddress();
1695 ++it;
1696 }
1697 }
1698
1699 // Release any remaining unmapped memory.
1700 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
1701 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
1702 for (++pg_it; pg_it != pg.end(); ++pg_it) {
1703 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
1704 pg_it->GetNumPages());
1705 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
1706 pg_it->GetNumPages());
1707 }
1708 };
1709
1710 auto it = m_memory_block_manager.FindIterator(cur_address);
1711 while (true) {
1712 // Check that the iterator is valid.
1713 ASSERT(it != m_memory_block_manager.end());
1714
1715 // Get the memory info.
1716 const KMemoryInfo info = it->GetMemoryInfo();
1717
1718 // If it's unmapped, we need to map it.
1719 if (info.GetState() == KMemoryState::Free) {
1720 // Determine the range to map.
1721 size_t map_pages =
1722 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
1723 last_address + 1 - cur_address) /
1724 PageSize;
1725
1726 // While we have pages to map, map them.
1727 {
1728 // Create a page group for the current mapping range.
1729 KPageGroup cur_pg(m_kernel, m_block_info_manager);
1730 {
1731 ON_RESULT_FAILURE_2 {
1732 cur_pg.OpenFirst();
1733 cur_pg.Close();
1734 };
1735
1736 size_t remain_pages = map_pages;
1737 while (remain_pages > 0) {
1738 // Check if we're at the end of the physical block.
1739 if (pg_pages == 0) {
1740 // Ensure there are more pages to map.
1741 ASSERT(pg_it != pg.end());
1742
1743 // Advance our physical block.
1744 ++pg_it;
1745 pg_phys_addr = pg_it->GetAddress();
1746 pg_pages = pg_it->GetNumPages();
1747 }
1748
1749 // Add whatever we can to the current block.
1750 const size_t cur_pages = std::min(pg_pages, remain_pages);
1751 R_TRY(cur_pg.AddBlock(pg_phys_addr +
1752 ((pg_pages - cur_pages) * PageSize),
1753 cur_pages));
1754
1755 // Advance.
1756 remain_pages -= cur_pages;
1757 pg_pages -= cur_pages;
1758 }
1759 }
1760
1761 // Map the pages.
1762 R_TRY(this->Operate(cur_address, map_pages, cur_pg,
1763 OperationType::MapFirstGroup));
1764 }
1765 }
1766
1767 // Check if we're done.
1768 if (last_address <= info.GetLastAddress()) {
1769 break;
1770 }
1771
1772 // Advance.
1773 cur_address = info.GetEndAddress();
1774 ++it;
1775 }
1776
1777 // We succeeded, so commit the memory reservation.
1778 memory_reservation.Commit();
1779
1780 // Increase our tracked mapped size.
1781 m_mapped_physical_memory_size += (size - mapped_size);
1782
1783 // Update the relevant memory blocks.
1784 m_memory_block_manager.UpdateIfMatch(
1785 std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
1786 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
1787 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1788 address == this->GetAliasRegionStart()
1789 ? KMemoryBlockDisableMergeAttribute::Normal
1790 : KMemoryBlockDisableMergeAttribute::None,
1791 KMemoryBlockDisableMergeAttribute::None);
1792
1793 R_SUCCEED();
1794 }
1795 }
1796 }
1797}
1798
1799Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
1800 // Lock the physical memory lock.
1801 KScopedLightLock phys_lk(m_map_physical_memory_lock);
1802
1803 // Lock the table.
1804 KScopedLightLock lk(m_general_lock);
1805
1806 // Calculate the last address for convenience.
1807 const KProcessAddress last_address = address + size - 1;
1808
1809 // Define iteration variables.
1810 KProcessAddress map_start_address = 0;
1811 KProcessAddress map_last_address = 0;
1812
1813 KProcessAddress cur_address;
1814 size_t mapped_size;
1815 size_t num_allocator_blocks = 0;
1816
1817 // Check if the memory is mapped.
1818 {
1819 // Iterate over the memory.
1820 cur_address = address;
1821 mapped_size = 0;
1822
1823 auto it = m_memory_block_manager.FindIterator(cur_address);
1824 while (true) {
1825 // Check that the iterator is valid.
1826 ASSERT(it != m_memory_block_manager.end());
1827
1828 // Get the memory info.
1829 const KMemoryInfo info = it->GetMemoryInfo();
1830
1831 // Verify the memory's state.
1832 const bool is_normal = info.GetState() == KMemoryState::Normal &&
1833 info.GetAttribute() == KMemoryAttribute::None;
1834 const bool is_free = info.GetState() == KMemoryState::Free;
1835 R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
1836
1837 if (is_normal) {
1838 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
1839
1840 if (map_start_address == 0) {
1841 map_start_address = cur_address;
1842 }
1843 map_last_address =
1844 (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address;
1845
1846 if (info.GetAddress() < GetInteger(address)) {
1847 ++num_allocator_blocks;
1848 }
1849 if (last_address < info.GetLastAddress()) {
1850 ++num_allocator_blocks;
1851 }
1852
1853 mapped_size += (map_last_address + 1 - cur_address);
1854 }
1855
1856 // Check if we're done.
1857 if (last_address <= info.GetLastAddress()) {
1858 break;
1859 }
1860
1861 // Advance.
1862 cur_address = info.GetEndAddress();
1863 ++it;
1864 }
1865
1866 // If there's nothing mapped, we've nothing to do.
1867 R_SUCCEED_IF(mapped_size == 0);
1868 }
1869
1870 // Create an update allocator.
1871 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
1872 Result allocator_result;
1873 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1874 m_memory_block_slab_manager, num_allocator_blocks);
1875 R_TRY(allocator_result);
1876
1877 // We're going to perform an update, so create a helper.
1878 KScopedPageTableUpdater updater(this);
1879
1880 // Separate the mapping.
1881 R_TRY(Operate(map_start_address, (map_last_address + 1 - map_start_address) / PageSize,
1882 KMemoryPermission::None, OperationType::Separate));
1883
1884 // Reset the current tracking address, and make sure we clean up on failure.
1885 cur_address = address;
1886
1887 // Iterate over the memory, unmapping as we go.
1888 auto it = m_memory_block_manager.FindIterator(cur_address);
1889
1890 const auto clear_merge_attr =
1891 (it->GetState() == KMemoryState::Normal &&
1892 it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
1893 ? KMemoryBlockDisableMergeAttribute::Normal
1894 : KMemoryBlockDisableMergeAttribute::None;
1895
1896 while (true) {
1897 // Check that the iterator is valid.
1898 ASSERT(it != m_memory_block_manager.end());
1899
1900 // Get the memory info.
1901 const KMemoryInfo info = it->GetMemoryInfo();
1902
1903 // If the memory state is normal, we need to unmap it.
1904 if (info.GetState() == KMemoryState::Normal) {
1905 // Determine the range to unmap.
1906 const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
1907 last_address + 1 - cur_address) /
1908 PageSize;
1909
1910 // Unmap.
1911 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
1912 .IsSuccess());
1913 }
1914
1915 // Check if we're done.
1916 if (last_address <= info.GetLastAddress()) {
1917 break;
1918 }
1919
1920 // Advance.
1921 cur_address = info.GetEndAddress();
1922 ++it;
1923 }
1924
1925 // Release the memory resource.
1926 m_mapped_physical_memory_size -= mapped_size;
1927 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size);
1928
1929 // Update memory blocks.
1930 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
1931 KMemoryState::Free, KMemoryPermission::None,
1932 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1933 clear_merge_attr);
1934
1935 // We succeeded.
1936 R_SUCCEED();
1937}
1938
1939Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1940 size_t size) {
1941 // Lock the table.
1942 KScopedLightLock lk(m_general_lock);
1943
1944 // Validate that the source address's state is valid.
1945 KMemoryState src_state;
1946 size_t num_src_allocator_blocks;
1947 R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr,
1948 std::addressof(num_src_allocator_blocks), src_address, size,
1949 KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
1950 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1951 KMemoryAttribute::All, KMemoryAttribute::None));
1952
1953 // Validate that the dst address's state is valid.
1954 size_t num_dst_allocator_blocks;
1955 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
1956 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1957 KMemoryPermission::None, KMemoryAttribute::None,
1958 KMemoryAttribute::None));
1959
1960 // Create an update allocator for the source.
1961 Result src_allocator_result;
1962 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1963 m_memory_block_slab_manager,
1964 num_src_allocator_blocks);
1965 R_TRY(src_allocator_result);
1966
1967 // Create an update allocator for the destination.
1968 Result dst_allocator_result;
1969 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1970 m_memory_block_slab_manager,
1971 num_dst_allocator_blocks);
1972 R_TRY(dst_allocator_result);
1973
1974 // Map the memory.
1975 {
1976 // Determine the number of pages being operated on.
1977 const size_t num_pages = size / PageSize;
1978
1979 // Create page groups for the memory being unmapped.
1980 KPageGroup pg{m_kernel, m_block_info_manager};
1981
1982 // Create the page group representing the source.
1983 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1984
1985 // We're going to perform an update, so create a helper.
1986 KScopedPageTableUpdater updater(this);
1987
1988 // Reprotect the source as kernel-read/not mapped.
1989 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1990 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1991 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1992 const KPageProperties src_properties = {new_src_perm, false, false,
1993 DisableMergeAttribute::DisableHeadBodyTail};
1994 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
1995 OperationType::ChangePermissions));
1996
1997 // Ensure that we unprotect the source pages on failure.
1998 ON_RESULT_FAILURE {
1999 const KPageProperties unprotect_properties = {
2000 KMemoryPermission::UserReadWrite, false, false,
2001 DisableMergeAttribute::EnableHeadBodyTail};
2002 ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
2003 OperationType::ChangePermissions) == ResultSuccess);
2004 };
2005
2006 // Map the alias pages.
2007 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
2008 DisableMergeAttribute::DisableHead};
2009 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
2010 false));
2011
2012 // Apply the memory block updates.
2013 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
2014 src_state, new_src_perm, new_src_attr,
2015 KMemoryBlockDisableMergeAttribute::Locked,
2016 KMemoryBlockDisableMergeAttribute::None);
2017 m_memory_block_manager.Update(
2018 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
2019 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2020 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
2021 }
2022
2023 R_SUCCEED();
2024}
2025
2026Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
2027 size_t size) {
2028 // Lock the table.
2029 KScopedLightLock lk(m_general_lock);
2030
2031 // Validate that the source address's state is valid.
2032 KMemoryState src_state;
2033 size_t num_src_allocator_blocks;
2034 R_TRY(this->CheckMemoryState(
2035 std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks),
2036 src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
2037 KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
2038 KMemoryAttribute::All, KMemoryAttribute::Locked));
2039
2040 // Validate that the dst address's state is valid.
2041 KMemoryPermission dst_perm;
2042 size_t num_dst_allocator_blocks;
2043 R_TRY(this->CheckMemoryState(
2044 nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks),
2045 dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
2046 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
2047
2048 // Create an update allocator for the source.
2049 Result src_allocator_result;
2050 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
2051 m_memory_block_slab_manager,
2052 num_src_allocator_blocks);
2053 R_TRY(src_allocator_result);
2054
2055 // Create an update allocator for the destination.
2056 Result dst_allocator_result;
2057 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
2058 m_memory_block_slab_manager,
2059 num_dst_allocator_blocks);
2060 R_TRY(dst_allocator_result);
2061
2062 // Unmap the memory.
2063 {
2064 // Determine the number of pages being operated on.
2065 const size_t num_pages = size / PageSize;
2066
2067 // Create page groups for the memory being unmapped.
2068 KPageGroup pg{m_kernel, m_block_info_manager};
2069
2070 // Create the page group representing the destination.
2071 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
2072
2073 // Ensure the page group is the valid for the source.
2074 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
2075
2076 // We're going to perform an update, so create a helper.
2077 KScopedPageTableUpdater updater(this);
2078
2079 // Unmap the aliased copy of the pages.
2080 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
2081 DisableMergeAttribute::None};
2082 R_TRY(
2083 this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
2084
2085 // Ensure that we re-map the aliased pages on failure.
2086 ON_RESULT_FAILURE {
2087 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
2088 };
2089
2090 // Try to set the permissions for the source pages back to what they should be.
2091 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
2092 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
2093 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
2094 OperationType::ChangePermissions));
2095
2096 // Apply the memory block updates.
2097 m_memory_block_manager.Update(
2098 std::addressof(src_allocator), src_address, num_pages, src_state,
2099 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2100 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
2101 m_memory_block_manager.Update(
2102 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
2103 KMemoryPermission::None, KMemoryAttribute::None,
2104 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
2105 }
2106
2107 R_SUCCEED();
2108}
2109
2110Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
2111 size_t num_pages, KMemoryPermission perm) {
2112 ASSERT(this->IsLockedByCurrentThread());
2113
2114 // Create a page group to hold the pages we allocate.
2115 KPageGroup pg{m_kernel, m_block_info_manager};
2116
2117 // Allocate the pages.
2118 R_TRY(
2119 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
2120
2121 // Ensure that the page group is closed when we're done working with it.
2122 SCOPE_EXIT({ pg.Close(); });
2123
2124 // Clear all pages.
2125 for (const auto& it : pg) {
2126 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2127 it.GetSize());
2128 }
2129
2130 // Map the pages.
2131 R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
2132}
2133
2134Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
2135 const KPageGroup& pg, const KPageProperties properties,
2136 bool reuse_ll) {
2137 ASSERT(this->IsLockedByCurrentThread());
2138
2139 // Note the current address, so that we can iterate.
2140 const KProcessAddress start_address = address;
2141 KProcessAddress cur_address = address;
2142
2143 // Ensure that we clean up on failure.
2144 ON_RESULT_FAILURE {
2145 ASSERT(!reuse_ll);
2146 if (cur_address != start_address) {
2147 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2148 DisableMergeAttribute::None};
2149 ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
2150 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
2151 }
2152 };
2153
2154 // Iterate, mapping all pages in the group.
2155 for (const auto& block : pg) {
2156 // Map and advance.
2157 const KPageProperties cur_properties =
2158 (cur_address == start_address)
2159 ? properties
2160 : KPageProperties{properties.perm, properties.io, properties.uncached,
2161 DisableMergeAttribute::None};
2162 this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
2163 block.GetAddress());
2164 cur_address += block.GetSize();
2165 }
2166
2167 // We succeeded!
2168 R_SUCCEED();
2169}
2170
2171void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
2172 const KPageGroup& pg) {
2173 ASSERT(this->IsLockedByCurrentThread());
2174
2175 // Note the current address, so that we can iterate.
2176 const KProcessAddress start_address = address;
2177 const KProcessAddress last_address = start_address + size - 1;
2178 const KProcessAddress end_address = last_address + 1;
2179
2180 // Iterate over the memory.
2181 auto pg_it = pg.begin();
2182 ASSERT(pg_it != pg.end());
2183
2184 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
2185 size_t pg_pages = pg_it->GetNumPages();
2186
2187 auto it = m_memory_block_manager.FindIterator(start_address);
2188 while (true) {
2189 // Check that the iterator is valid.
2190 ASSERT(it != m_memory_block_manager.end());
2191
2192 // Get the memory info.
2193 const KMemoryInfo info = it->GetMemoryInfo();
2194
2195 // Determine the range to map.
2196 KProcessAddress map_address = std::max<KProcessAddress>(info.GetAddress(), start_address);
2197 const KProcessAddress map_end_address =
2198 std::min<KProcessAddress>(info.GetEndAddress(), end_address);
2199 ASSERT(map_end_address != map_address);
2200
2201 // Determine if we should disable head merge.
2202 const bool disable_head_merge =
2203 info.GetAddress() >= GetInteger(start_address) &&
2204 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
2205 const KPageProperties map_properties = {
2206 info.GetPermission(), false, false,
2207 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
2208
2209 // While we have pages to map, map them.
2210 size_t map_pages = (map_end_address - map_address) / PageSize;
2211 while (map_pages > 0) {
2212 // Check if we're at the end of the physical block.
2213 if (pg_pages == 0) {
2214 // Ensure there are more pages to map.
2215 ASSERT(pg_it != pg.end());
2216
2217 // Advance our physical block.
2218 ++pg_it;
2219 pg_phys_addr = pg_it->GetAddress();
2220 pg_pages = pg_it->GetNumPages();
2221 }
2222
2223 // Map whatever we can.
2224 const size_t cur_pages = std::min(pg_pages, map_pages);
2225 ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
2226 pg_phys_addr) == ResultSuccess);
2227
2228 // Advance.
2229 map_address += cur_pages * PageSize;
2230 map_pages -= cur_pages;
2231
2232 pg_phys_addr += cur_pages * PageSize;
2233 pg_pages -= cur_pages;
2234 }
2235
2236 // Check if we're done.
2237 if (last_address <= info.GetLastAddress()) {
2238 break;
2239 }
2240
2241 // Advance.
2242 ++it;
2243 }
2244
2245 // Check that we re-mapped precisely the page group.
2246 ASSERT((++pg_it) == pg.end());
2247}
2248
2249Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2250 KPhysicalAddress phys_addr, bool is_pa_valid,
2251 KProcessAddress region_start, size_t region_num_pages,
2252 KMemoryState state, KMemoryPermission perm) {
2253 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2254
2255 // Ensure this is a valid map request.
2256 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2257 ResultInvalidCurrentMemory);
2258 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2259
2260 // Lock the table.
2261 KScopedLightLock lk(m_general_lock);
2262
2263 // Find a random address to map at.
2264 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2265 0, this->GetNumGuardPages());
2266 R_UNLESS(addr != 0, ResultOutOfMemory);
2267 ASSERT(Common::IsAligned(GetInteger(addr), alignment));
2268 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2269 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2270 KMemoryPermission::None, KMemoryPermission::None,
2271 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2272
2273 // Create an update allocator.
2274 Result allocator_result;
2275 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2276 m_memory_block_slab_manager);
2277 R_TRY(allocator_result);
2278
2279 // We're going to perform an update, so create a helper.
2280 KScopedPageTableUpdater updater(this);
2281
2282 // Perform mapping operation.
2283 if (is_pa_valid) {
2284 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2285 R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
2286 } else {
2287 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2288 }
2289
2290 // Update the blocks.
2291 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2292 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2293 KMemoryBlockDisableMergeAttribute::None);
2294
2295 // We successfully mapped the pages.
2296 *out_addr = addr;
2297 R_SUCCEED();
2298}
2299
2300Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2301 KMemoryPermission perm) {
2302 // Check that the map is in range.
2303 const size_t size = num_pages * PageSize;
2304 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2305
2306 // Lock the table.
2307 KScopedLightLock lk(m_general_lock);
2308
2309 // Check the memory state.
2310 size_t num_allocator_blocks;
2311 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2312 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2313 KMemoryPermission::None, KMemoryAttribute::None,
2314 KMemoryAttribute::None));
2315
2316 // Create an update allocator.
2317 Result allocator_result;
2318 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2319 m_memory_block_slab_manager, num_allocator_blocks);
2320 R_TRY(allocator_result);
2321
2322 // We're going to perform an update, so create a helper.
2323 KScopedPageTableUpdater updater(this);
2324
2325 // Map the pages.
2326 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2327
2328 // Update the blocks.
2329 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2330 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2331 KMemoryBlockDisableMergeAttribute::None);
2332
2333 R_SUCCEED();
2334}
2335
2336Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2337 // Check that the unmap is in range.
2338 const size_t size = num_pages * PageSize;
2339 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2340
2341 // Lock the table.
2342 KScopedLightLock lk(m_general_lock);
2343
2344 // Check the memory state.
2345 size_t num_allocator_blocks;
2346 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2347 KMemoryState::All, state, KMemoryPermission::None,
2348 KMemoryPermission::None, KMemoryAttribute::All,
2349 KMemoryAttribute::None));
2350
2351 // Create an update allocator.
2352 Result allocator_result;
2353 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2354 m_memory_block_slab_manager, num_allocator_blocks);
2355 R_TRY(allocator_result);
2356
2357 // We're going to perform an update, so create a helper.
2358 KScopedPageTableUpdater updater(this);
2359
2360 // Perform the unmap.
2361 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2362 DisableMergeAttribute::None};
2363 R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
2364
2365 // Update the blocks.
2366 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2367 KMemoryPermission::None, KMemoryAttribute::None,
2368 KMemoryBlockDisableMergeAttribute::None,
2369 KMemoryBlockDisableMergeAttribute::Normal);
2370
2371 R_SUCCEED();
2372}
2373
2374Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2375 KProcessAddress region_start, size_t region_num_pages,
2376 KMemoryState state, KMemoryPermission perm) {
2377 ASSERT(!this->IsLockedByCurrentThread());
2378
2379 // Ensure this is a valid map request.
2380 const size_t num_pages = pg.GetNumPages();
2381 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2382 ResultInvalidCurrentMemory);
2383 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2384
2385 // Lock the table.
2386 KScopedLightLock lk(m_general_lock);
2387
2388 // Find a random address to map at.
2389 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2390 0, this->GetNumGuardPages());
2391 R_UNLESS(addr != 0, ResultOutOfMemory);
2392 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2393 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2394 KMemoryPermission::None, KMemoryPermission::None,
2395 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2396
2397 // Create an update allocator.
2398 Result allocator_result;
2399 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2400 m_memory_block_slab_manager);
2401 R_TRY(allocator_result);
2402
2403 // We're going to perform an update, so create a helper.
2404 KScopedPageTableUpdater updater(this);
2405
2406 // Perform mapping operation.
2407 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2408 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2409
2410 // Update the blocks.
2411 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2412 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2413 KMemoryBlockDisableMergeAttribute::None);
2414
2415 // We successfully mapped the pages.
2416 *out_addr = addr;
2417 R_SUCCEED();
2418}
2419
2420Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2421 KMemoryPermission perm) {
2422 ASSERT(!this->IsLockedByCurrentThread());
2423
2424 // Ensure this is a valid map request.
2425 const size_t num_pages = pg.GetNumPages();
2426 const size_t size = num_pages * PageSize;
2427 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2428
2429 // Lock the table.
2430 KScopedLightLock lk(m_general_lock);
2431
2432 // Check if state allows us to map.
2433 size_t num_allocator_blocks;
2434 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2435 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2436 KMemoryPermission::None, KMemoryAttribute::None,
2437 KMemoryAttribute::None));
2438
2439 // Create an update allocator.
2440 Result allocator_result;
2441 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2442 m_memory_block_slab_manager, num_allocator_blocks);
2443 R_TRY(allocator_result);
2444
2445 // We're going to perform an update, so create a helper.
2446 KScopedPageTableUpdater updater(this);
2447
2448 // Perform mapping operation.
2449 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2450 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2451
2452 // Update the blocks.
2453 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2454 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2455 KMemoryBlockDisableMergeAttribute::None);
2456
2457 // We successfully mapped the pages.
2458 R_SUCCEED();
2459}
2460
2461Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2462 KMemoryState state) {
2463 ASSERT(!this->IsLockedByCurrentThread());
2464
2465 // Ensure this is a valid unmap request.
2466 const size_t num_pages = pg.GetNumPages();
2467 const size_t size = num_pages * PageSize;
2468 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2469
2470 // Lock the table.
2471 KScopedLightLock lk(m_general_lock);
2472
2473 // Check if state allows us to unmap.
2474 size_t num_allocator_blocks;
2475 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2476 KMemoryState::All, state, KMemoryPermission::None,
2477 KMemoryPermission::None, KMemoryAttribute::All,
2478 KMemoryAttribute::None));
2479
2480 // Check that the page group is valid.
2481 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2482
2483 // Create an update allocator.
2484 Result allocator_result;
2485 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2486 m_memory_block_slab_manager, num_allocator_blocks);
2487 R_TRY(allocator_result);
2488
2489 // We're going to perform an update, so create a helper.
2490 KScopedPageTableUpdater updater(this);
2491
2492 // Perform unmapping operation.
2493 const KPageProperties properties = {KMemoryPermission::None, false, false,
2494 DisableMergeAttribute::None};
2495 R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
2496
2497 // Update the blocks.
2498 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2499 KMemoryPermission::None, KMemoryAttribute::None,
2500 KMemoryBlockDisableMergeAttribute::None,
2501 KMemoryBlockDisableMergeAttribute::Normal);
2502
2503 R_SUCCEED();
2504}
2505
2506Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
2507 KMemoryState state_mask, KMemoryState state,
2508 KMemoryPermission perm_mask, KMemoryPermission perm,
2509 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
2510 // Ensure that the page group isn't null.
2511 ASSERT(out != nullptr);
2512
2513 // Make sure that the region we're mapping is valid for the table.
2514 const size_t size = num_pages * PageSize;
2515 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2516
2517 // Lock the table.
2518 KScopedLightLock lk(m_general_lock);
2519
2520 // Check if state allows us to create the group.
2521 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
2522 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2523 attr_mask, attr));
2524
2525 // Create a new page group for the region.
2526 R_TRY(this->MakePageGroup(*out, address, num_pages));
2527
2528 R_SUCCEED();
2529}
2530
2531Result KPageTable::SetProcessMemoryPermission(KProcessAddress addr, size_t size,
2532 Svc::MemoryPermission svc_perm) {
2533 const size_t num_pages = size / PageSize;
2534
2535 // Lock the table.
2536 KScopedLightLock lk(m_general_lock);
2537
2538 // Verify we can change the memory permission.
2539 KMemoryState old_state;
2540 KMemoryPermission old_perm;
2541 size_t num_allocator_blocks;
2542 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
2543 std::addressof(num_allocator_blocks), addr, size,
2544 KMemoryState::FlagCode, KMemoryState::FlagCode,
2545 KMemoryPermission::None, KMemoryPermission::None,
2546 KMemoryAttribute::All, KMemoryAttribute::None));
2547
2548 // Determine new perm/state.
2549 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
2550 KMemoryState new_state = old_state;
2551 const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite;
2552 const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
2553 const bool was_x =
2554 (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
2555 ASSERT(!(is_w && is_x));
2556
2557 if (is_w) {
2558 switch (old_state) {
2559 case KMemoryState::Code:
2560 new_state = KMemoryState::CodeData;
2561 break;
2562 case KMemoryState::AliasCode:
2563 new_state = KMemoryState::AliasCodeData;
2564 break;
2565 default:
2566 ASSERT(false);
2567 break;
2568 }
2569 }
2570
2571 // Succeed if there's nothing to do.
2572 R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
2573
2574 // Create an update allocator.
2575 Result allocator_result{ResultSuccess};
2576 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2577 m_memory_block_slab_manager, num_allocator_blocks);
2578 R_TRY(allocator_result);
2579
2580 // Perform mapping operation.
2581 const auto operation =
2582 was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions;
2583 R_TRY(Operate(addr, num_pages, new_perm, operation));
2584
2585 // Update the blocks.
2586 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm,
2587 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
2588 KMemoryBlockDisableMergeAttribute::None);
2589
2590 // Ensure cache coherency, if we're setting pages as executable.
2591 if (is_x) {
2592 m_system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size);
2593 }
2594
2595 R_SUCCEED();
2596}
2597
2598KMemoryInfo KPageTable::QueryInfoImpl(KProcessAddress addr) {
2599 KScopedLightLock lk(m_general_lock);
2600
2601 return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo();
2602}
2603
2604KMemoryInfo KPageTable::QueryInfo(KProcessAddress addr) {
2605 if (!Contains(addr, 1)) {
2606 return {
2607 .m_address = GetInteger(m_address_space_end),
2608 .m_size = 0 - GetInteger(m_address_space_end),
2609 .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible),
2610 .m_device_disable_merge_left_count = 0,
2611 .m_device_disable_merge_right_count = 0,
2612 .m_ipc_lock_count = 0,
2613 .m_device_use_count = 0,
2614 .m_ipc_disable_merge_count = 0,
2615 .m_permission = KMemoryPermission::None,
2616 .m_attribute = KMemoryAttribute::None,
2617 .m_original_permission = KMemoryPermission::None,
2618 .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None,
2619 };
2620 }
2621
2622 return QueryInfoImpl(addr);
2623}
2624
2625Result KPageTable::SetMemoryPermission(KProcessAddress addr, size_t size,
2626 Svc::MemoryPermission svc_perm) {
2627 const size_t num_pages = size / PageSize;
2628
2629 // Lock the table.
2630 KScopedLightLock lk(m_general_lock);
2631
2632 // Verify we can change the memory permission.
2633 KMemoryState old_state;
2634 KMemoryPermission old_perm;
2635 size_t num_allocator_blocks;
2636 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
2637 std::addressof(num_allocator_blocks), addr, size,
2638 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect,
2639 KMemoryPermission::None, KMemoryPermission::None,
2640 KMemoryAttribute::All, KMemoryAttribute::None));
2641
2642 // Determine new perm.
2643 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
2644 R_SUCCEED_IF(old_perm == new_perm);
2645
2646 // Create an update allocator.
2647 Result allocator_result{ResultSuccess};
2648 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2649 m_memory_block_slab_manager, num_allocator_blocks);
2650 R_TRY(allocator_result);
2651
2652 // Perform mapping operation.
2653 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2654
2655 // Update the blocks.
2656 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
2657 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
2658 KMemoryBlockDisableMergeAttribute::None);
2659
2660 R_SUCCEED();
2661}
2662
2663Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) {
2664 const size_t num_pages = size / PageSize;
2665 ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
2666 KMemoryAttribute::SetMask);
2667
2668 // Lock the table.
2669 KScopedLightLock lk(m_general_lock);
2670
2671 // Verify we can change the memory attribute.
2672 KMemoryState old_state;
2673 KMemoryPermission old_perm;
2674 KMemoryAttribute old_attr;
2675 size_t num_allocator_blocks;
2676 constexpr auto AttributeTestMask =
2677 ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
2678 const KMemoryState state_test_mask =
2679 static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
2680 ? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
2681 : 0) |
2682 ((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
2683 ? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
2684 : 0));
2685 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2686 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2687 addr, size, state_test_mask, state_test_mask,
2688 KMemoryPermission::None, KMemoryPermission::None,
2689 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
2690
2691 // Create an update allocator.
2692 Result allocator_result{ResultSuccess};
2693 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2694 m_memory_block_slab_manager, num_allocator_blocks);
2695 R_TRY(allocator_result);
2696
2697 // If we need to, perform a change attribute operation.
2698 if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
2699 // Perform operation.
2700 R_TRY(this->Operate(addr, num_pages, old_perm,
2701 OperationType::ChangePermissionsAndRefreshAndFlush, 0));
2702 }
2703
2704 // Update the blocks.
2705 m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
2706 static_cast<KMemoryAttribute>(mask),
2707 static_cast<KMemoryAttribute>(attr));
2708
2709 R_SUCCEED();
2710}
2711
2712Result KPageTable::SetMaxHeapSize(size_t size) {
2713 // Lock the table.
2714 KScopedLightLock lk(m_general_lock);
2715
2716 // Only process page tables are allowed to set heap size.
2717 ASSERT(!this->IsKernel());
2718
2719 m_max_heap_size = size;
2720
2721 R_SUCCEED();
2722}
2723
2724Result KPageTable::SetHeapSize(u64* out, size_t size) {
2725 // Lock the physical memory mutex.
2726 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
2727
2728 // Try to perform a reduction in heap, instead of an extension.
2729 KProcessAddress cur_address{};
2730 size_t allocation_size{};
2731 {
2732 // Lock the table.
2733 KScopedLightLock lk(m_general_lock);
2734
2735 // Validate that setting heap size is possible at all.
2736 R_UNLESS(!m_is_kernel, ResultOutOfMemory);
2737 R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start),
2738 ResultOutOfMemory);
2739 R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory);
2740
2741 if (size < GetHeapSize()) {
2742 // The size being requested is less than the current size, so we need to free the end of
2743 // the heap.
2744
2745 // Validate memory state.
2746 size_t num_allocator_blocks;
2747 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
2748 m_heap_region_start + size, GetHeapSize() - size,
2749 KMemoryState::All, KMemoryState::Normal,
2750 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
2751 KMemoryAttribute::All, KMemoryAttribute::None));
2752
2753 // Create an update allocator.
2754 Result allocator_result{ResultSuccess};
2755 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2756 m_memory_block_slab_manager,
2757 num_allocator_blocks);
2758 R_TRY(allocator_result);
2759
2760 // Unmap the end of the heap.
2761 const auto num_pages = (GetHeapSize() - size) / PageSize;
2762 R_TRY(Operate(m_heap_region_start + size, num_pages, KMemoryPermission::None,
2763 OperationType::Unmap));
2764
2765 // Release the memory from the resource limit.
2766 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize);
2767
2768 // Apply the memory block update.
2769 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
2770 num_pages, KMemoryState::Free, KMemoryPermission::None,
2771 KMemoryAttribute::None,
2772 KMemoryBlockDisableMergeAttribute::None,
2773 size == 0 ? KMemoryBlockDisableMergeAttribute::Normal
2774 : KMemoryBlockDisableMergeAttribute::None);
2775
2776 // Update the current heap end.
2777 m_current_heap_end = m_heap_region_start + size;
2778
2779 // Set the output.
2780 *out = GetInteger(m_heap_region_start);
2781 R_SUCCEED();
2782 } else if (size == GetHeapSize()) {
2783 // The size requested is exactly the current size.
2784 *out = GetInteger(m_heap_region_start);
2785 R_SUCCEED();
2786 } else {
2787 // We have to allocate memory. Determine how much to allocate and where while the table
2788 // is locked.
2789 cur_address = m_current_heap_end;
2790 allocation_size = size - GetHeapSize();
2791 }
2792 }
2793
2794 // Reserve memory for the heap extension.
2795 KScopedResourceReservation memory_reservation(
2796 m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size);
2797 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2798
2799 // Allocate pages for the heap extension.
2800 KPageGroup pg{m_kernel, m_block_info_manager};
2801 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2802 &pg, allocation_size / PageSize,
2803 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2804
2805 // Clear all the newly allocated pages.
2806 for (const auto& it : pg) {
2807 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2808 it.GetSize());
2809 }
2810
2811 // Map the pages.
2812 {
2813 // Lock the table.
2814 KScopedLightLock lk(m_general_lock);
2815
2816 // Ensure that the heap hasn't changed since we began executing.
2817 ASSERT(cur_address == m_current_heap_end);
2818
2819 // Check the memory state.
2820 size_t num_allocator_blocks{};
2821 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end,
2822 allocation_size, KMemoryState::All, KMemoryState::Free,
2823 KMemoryPermission::None, KMemoryPermission::None,
2824 KMemoryAttribute::None, KMemoryAttribute::None));
2825
2826 // Create an update allocator.
2827 Result allocator_result{ResultSuccess};
2828 KMemoryBlockManagerUpdateAllocator allocator(
2829 std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
2830 R_TRY(allocator_result);
2831
2832 // Map the pages.
2833 const auto num_pages = allocation_size / PageSize;
2834 R_TRY(Operate(m_current_heap_end, num_pages, pg, OperationType::MapGroup));
2835
2836 // Clear all the newly allocated pages.
2837 for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
2838 std::memset(m_memory->GetPointer(m_current_heap_end + (cur_page * PageSize)), 0,
2839 PageSize);
2840 }
2841
2842 // We succeeded, so commit our memory reservation.
2843 memory_reservation.Commit();
2844
2845 // Apply the memory block update.
2846 m_memory_block_manager.Update(
2847 std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal,
2848 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2849 m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal
2850 : KMemoryBlockDisableMergeAttribute::None,
2851 KMemoryBlockDisableMergeAttribute::None);
2852
2853 // Update the current heap end.
2854 m_current_heap_end = m_heap_region_start + size;
2855
2856 // Set the output.
2857 *out = GetInteger(m_heap_region_start);
2858 R_SUCCEED();
2859 }
2860}
2861
2862Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address,
2863 size_t size, KMemoryPermission perm,
2864 bool is_aligned, bool check_heap) {
2865 // Lightly validate the range before doing anything else.
2866 const size_t num_pages = size / PageSize;
2867 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2868
2869 // Lock the table.
2870 KScopedLightLock lk(m_general_lock);
2871
2872 // Check the memory state.
2873 const auto test_state =
2874 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) |
2875 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
2876 size_t num_allocator_blocks;
2877 KMemoryState old_state;
2878 R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr,
2879 std::addressof(num_allocator_blocks), address, size, test_state,
2880 test_state, perm, perm,
2881 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked,
2882 KMemoryAttribute::None, KMemoryAttribute::DeviceShared));
2883
2884 // Create an update allocator.
2885 Result allocator_result;
2886 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2887 m_memory_block_slab_manager, num_allocator_blocks);
2888 R_TRY(allocator_result);
2889
2890 // Update the memory blocks.
2891 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
2892 &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
2893
2894 // Set whether the locked memory was io.
2895 *out_is_io =
2896 static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
2897
2898 R_SUCCEED();
2899}
2900
2901Result KPageTable::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size,
2902 bool check_heap) {
2903 // Lightly validate the range before doing anything else.
2904 const size_t num_pages = size / PageSize;
2905 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2906
2907 // Lock the table.
2908 KScopedLightLock lk(m_general_lock);
2909
2910 // Check the memory state.
2911 const auto test_state = KMemoryState::FlagCanDeviceMap |
2912 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
2913 size_t num_allocator_blocks;
2914 R_TRY(this->CheckMemoryStateContiguous(
2915 std::addressof(num_allocator_blocks), address, size, test_state, test_state,
2916 KMemoryPermission::None, KMemoryPermission::None,
2917 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
2918
2919 // Create an update allocator.
2920 Result allocator_result;
2921 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2922 m_memory_block_slab_manager, num_allocator_blocks);
2923 R_TRY(allocator_result);
2924
2925 // Update the memory blocks.
2926 const KMemoryBlockManager::MemoryBlockLockFunction lock_func =
2927 m_enable_device_address_space_merge
2928 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare
2929 : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight;
2930 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func,
2931 KMemoryPermission::None);
2932
2933 R_SUCCEED();
2934}
2935
2936Result KPageTable::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
2937 // Lightly validate the range before doing anything else.
2938 const size_t num_pages = size / PageSize;
2939 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2940
2941 // Lock the table.
2942 KScopedLightLock lk(m_general_lock);
2943
2944 // Check the memory state.
2945 size_t num_allocator_blocks;
2946 R_TRY(this->CheckMemoryStateContiguous(
2947 std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap,
2948 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
2949 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
2950
2951 // Create an update allocator.
2952 Result allocator_result{ResultSuccess};
2953 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2954 m_memory_block_slab_manager, num_allocator_blocks);
2955 R_TRY(allocator_result);
2956
2957 // Update the memory blocks.
2958 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
2959 &KMemoryBlock::UnshareToDevice, KMemoryPermission::None);
2960
2961 R_SUCCEED();
2962}
2963
2964Result KPageTable::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address,
2965 size_t size) {
2966 R_RETURN(this->LockMemoryAndOpen(
2967 nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer,
2968 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All,
2969 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None,
2970 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
2971 KMemoryAttribute::Locked));
2972}
2973
2974Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
2975 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer,
2976 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None,
2977 KMemoryPermission::None, KMemoryAttribute::All,
2978 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
2979 KMemoryAttribute::Locked, nullptr));
2980}
2981
2982Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
2983 KMemoryPermission perm) {
2984 R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
2985 KMemoryState::FlagCanTransfer, KMemoryPermission::All,
2986 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
2987 KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
2988}
2989
2990Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size,
2991 const KPageGroup& pg) {
2992 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
2993 KMemoryState::FlagCanTransfer, KMemoryPermission::None,
2994 KMemoryPermission::None, KMemoryAttribute::All,
2995 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
2996 KMemoryAttribute::Locked, std::addressof(pg)));
2997}
2998
2999Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
3000 R_RETURN(this->LockMemoryAndOpen(
3001 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
3002 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
3003 KMemoryAttribute::None, KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
3004 KMemoryAttribute::Locked));
3005}
3006
3007Result KPageTable::UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg) {
3008 R_RETURN(this->UnlockMemory(
3009 addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
3010 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
3011 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg));
3012}
3013
3014bool KPageTable::IsRegionContiguous(KProcessAddress addr, u64 size) const {
3015 auto start_ptr = m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr));
3016 for (u64 offset{}; offset < size; offset += PageSize) {
3017 if (start_ptr != m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr) + offset)) {
3018 return false;
3019 }
3020 start_ptr += PageSize;
3021 }
3022 return true;
3023}
3024
3025void KPageTable::AddRegionToPages(KProcessAddress start, size_t num_pages,
3026 KPageGroup& page_linked_list) {
3027 KProcessAddress addr{start};
3028 while (addr < start + (num_pages * PageSize)) {
3029 const KPhysicalAddress paddr{GetPhysicalAddr(addr)};
3030 ASSERT(paddr != 0);
3031 page_linked_list.AddBlock(paddr, 1);
3032 addr += PageSize;
3033 }
3034}
3035
3036KProcessAddress KPageTable::AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages,
3037 u64 needed_num_pages, size_t align) {
3038 if (m_enable_aslr) {
3039 UNIMPLEMENTED();
3040 }
3041 return m_memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0,
3042 IsKernel() ? 1 : 4);
3043}
3044
3045Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group,
3046 OperationType operation) {
3047 ASSERT(this->IsLockedByCurrentThread());
3048
3049 ASSERT(Common::IsAligned(GetInteger(addr), PageSize));
3050 ASSERT(num_pages > 0);
3051 ASSERT(num_pages == page_group.GetNumPages());
3052
3053 switch (operation) {
3054 case OperationType::MapGroup:
3055 case OperationType::MapFirstGroup: {
3056 // We want to maintain a new reference to every page in the group.
3057 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
3058
3059 for (const auto& node : page_group) {
3060 const size_t size{node.GetNumPages() * PageSize};
3061
3062 // Map the pages.
3063 m_memory->MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
3064
3065 addr += size;
3066 }
3067
3068 // We succeeded! We want to persist the reference to the pages.
3069 spg.CancelClose();
3070
3071 break;
3072 }
3073 default:
3074 ASSERT(false);
3075 break;
3076 }
3077
3078 R_SUCCEED();
3079}
3080
3081Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
3082 OperationType operation, KPhysicalAddress map_addr) {
3083 ASSERT(this->IsLockedByCurrentThread());
3084
3085 ASSERT(num_pages > 0);
3086 ASSERT(Common::IsAligned(GetInteger(addr), PageSize));
3087 ASSERT(ContainsPages(addr, num_pages));
3088
3089 switch (operation) {
3090 case OperationType::Unmap: {
3091 // Ensure that any pages we track close on exit.
3092 KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()};
3093 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
3094
3095 this->AddRegionToPages(addr, num_pages, pages_to_close);
3096 m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
3097 break;
3098 }
3099 case OperationType::Map: {
3100 ASSERT(map_addr);
3101 ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
3102 m_memory->MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
3103
3104 // Open references to pages, if we should.
3105 if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
3106 m_kernel.MemoryManager().Open(map_addr, num_pages);
3107 }
3108 break;
3109 }
3110 case OperationType::Separate: {
3111 // HACK: Unimplemented.
3112 break;
3113 }
3114 case OperationType::ChangePermissions:
3115 case OperationType::ChangePermissionsAndRefresh:
3116 case OperationType::ChangePermissionsAndRefreshAndFlush:
3117 break;
3118 default:
3119 ASSERT(false);
3120 break;
3121 }
3122 R_SUCCEED();
3123}
3124
3125void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
3126 while (page_list->Peek()) {
3127 [[maybe_unused]] auto page = page_list->Pop();
3128
3129 // TODO(bunnei): Free pages once they are allocated in guest memory
3130 // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page));
3131 // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0);
3132 // this->GetPageTableManager().Free(page);
3133 }
3134}
3135
3136KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
3137 switch (state) {
3138 case Svc::MemoryState::Free:
3139 case Svc::MemoryState::Kernel:
3140 return m_address_space_start;
3141 case Svc::MemoryState::Normal:
3142 return m_heap_region_start;
3143 case Svc::MemoryState::Ipc:
3144 case Svc::MemoryState::NonSecureIpc:
3145 case Svc::MemoryState::NonDeviceIpc:
3146 return m_alias_region_start;
3147 case Svc::MemoryState::Stack:
3148 return m_stack_region_start;
3149 case Svc::MemoryState::Static:
3150 case Svc::MemoryState::ThreadLocal:
3151 return m_kernel_map_region_start;
3152 case Svc::MemoryState::Io:
3153 case Svc::MemoryState::Shared:
3154 case Svc::MemoryState::AliasCode:
3155 case Svc::MemoryState::AliasCodeData:
3156 case Svc::MemoryState::Transfered:
3157 case Svc::MemoryState::SharedTransfered:
3158 case Svc::MemoryState::SharedCode:
3159 case Svc::MemoryState::GeneratedCode:
3160 case Svc::MemoryState::CodeOut:
3161 case Svc::MemoryState::Coverage:
3162 case Svc::MemoryState::Insecure:
3163 return m_alias_code_region_start;
3164 case Svc::MemoryState::Code:
3165 case Svc::MemoryState::CodeData:
3166 return m_code_region_start;
3167 default:
3168 UNREACHABLE();
3169 }
3170}
3171
3172size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
3173 switch (state) {
3174 case Svc::MemoryState::Free:
3175 case Svc::MemoryState::Kernel:
3176 return m_address_space_end - m_address_space_start;
3177 case Svc::MemoryState::Normal:
3178 return m_heap_region_end - m_heap_region_start;
3179 case Svc::MemoryState::Ipc:
3180 case Svc::MemoryState::NonSecureIpc:
3181 case Svc::MemoryState::NonDeviceIpc:
3182 return m_alias_region_end - m_alias_region_start;
3183 case Svc::MemoryState::Stack:
3184 return m_stack_region_end - m_stack_region_start;
3185 case Svc::MemoryState::Static:
3186 case Svc::MemoryState::ThreadLocal:
3187 return m_kernel_map_region_end - m_kernel_map_region_start;
3188 case Svc::MemoryState::Io:
3189 case Svc::MemoryState::Shared:
3190 case Svc::MemoryState::AliasCode:
3191 case Svc::MemoryState::AliasCodeData:
3192 case Svc::MemoryState::Transfered:
3193 case Svc::MemoryState::SharedTransfered:
3194 case Svc::MemoryState::SharedCode:
3195 case Svc::MemoryState::GeneratedCode:
3196 case Svc::MemoryState::CodeOut:
3197 case Svc::MemoryState::Coverage:
3198 case Svc::MemoryState::Insecure:
3199 return m_alias_code_region_end - m_alias_code_region_start;
3200 case Svc::MemoryState::Code:
3201 case Svc::MemoryState::CodeData:
3202 return m_code_region_end - m_code_region_start;
3203 default:
3204 UNREACHABLE();
3205 }
3206}
3207
3208bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
3209 const KProcessAddress end = addr + size;
3210 const KProcessAddress last = end - 1;
3211
3212 const KProcessAddress region_start = this->GetRegionAddress(state);
3213 const size_t region_size = this->GetRegionSize(state);
3214
3215 const bool is_in_region =
3216 region_start <= addr && addr < end && last <= region_start + region_size - 1;
3217 const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr ||
3218 m_heap_region_start == m_heap_region_end);
3219 const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
3220 m_alias_region_start == m_alias_region_end);
3221 switch (state) {
3222 case Svc::MemoryState::Free:
3223 case Svc::MemoryState::Kernel:
3224 return is_in_region;
3225 case Svc::MemoryState::Io:
3226 case Svc::MemoryState::Static:
3227 case Svc::MemoryState::Code:
3228 case Svc::MemoryState::CodeData:
3229 case Svc::MemoryState::Shared:
3230 case Svc::MemoryState::AliasCode:
3231 case Svc::MemoryState::AliasCodeData:
3232 case Svc::MemoryState::Stack:
3233 case Svc::MemoryState::ThreadLocal:
3234 case Svc::MemoryState::Transfered:
3235 case Svc::MemoryState::SharedTransfered:
3236 case Svc::MemoryState::SharedCode:
3237 case Svc::MemoryState::GeneratedCode:
3238 case Svc::MemoryState::CodeOut:
3239 case Svc::MemoryState::Coverage:
3240 case Svc::MemoryState::Insecure:
3241 return is_in_region && !is_in_heap && !is_in_alias;
3242 case Svc::MemoryState::Normal:
3243 ASSERT(is_in_heap);
3244 return is_in_region && !is_in_alias;
3245 case Svc::MemoryState::Ipc:
3246 case Svc::MemoryState::NonSecureIpc:
3247 case Svc::MemoryState::NonDeviceIpc:
3248 ASSERT(is_in_alias);
3249 return is_in_region && !is_in_heap;
3250 default:
3251 return false;
3252 }
3253}
3254
3255Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
3256 KMemoryState state, KMemoryPermission perm_mask,
3257 KMemoryPermission perm, KMemoryAttribute attr_mask,
3258 KMemoryAttribute attr) const {
3259 // Validate the states match expectation.
3260 R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory);
3261 R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory);
3262 R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
3263
3264 R_SUCCEED();
3265}
3266
3267Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
3268 size_t size, KMemoryState state_mask,
3269 KMemoryState state, KMemoryPermission perm_mask,
3270 KMemoryPermission perm, KMemoryAttribute attr_mask,
3271 KMemoryAttribute attr) const {
3272 ASSERT(this->IsLockedByCurrentThread());
3273
3274 // Get information about the first block.
3275 const KProcessAddress last_addr = addr + size - 1;
3276 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
3277 KMemoryInfo info = it->GetMemoryInfo();
3278
3279 // If the start address isn't aligned, we need a block.
3280 const size_t blocks_for_start_align =
3281 (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
3282
3283 while (true) {
3284 // Validate against the provided masks.
3285 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
3286
3287 // Break once we're done.
3288 if (last_addr <= info.GetLastAddress()) {
3289 break;
3290 }
3291
3292 // Advance our iterator.
3293 it++;
3294 ASSERT(it != m_memory_block_manager.cend());
3295 info = it->GetMemoryInfo();
3296 }
3297
3298 // If the end address isn't aligned, we need a block.
3299 const size_t blocks_for_end_align =
3300 (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
3301
3302 if (out_blocks_needed != nullptr) {
3303 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
3304 }
3305
3306 R_SUCCEED();
3307}
3308
3309Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
3310 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
3311 KMemoryBlockManager::const_iterator it,
3312 KProcessAddress last_addr, KMemoryState state_mask,
3313 KMemoryState state, KMemoryPermission perm_mask,
3314 KMemoryPermission perm, KMemoryAttribute attr_mask,
3315 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
3316 ASSERT(this->IsLockedByCurrentThread());
3317
3318 // Get information about the first block.
3319 KMemoryInfo info = it->GetMemoryInfo();
3320
3321 // Validate all blocks in the range have correct state.
3322 const KMemoryState first_state = info.m_state;
3323 const KMemoryPermission first_perm = info.m_permission;
3324 const KMemoryAttribute first_attr = info.m_attribute;
3325 while (true) {
3326 // Validate the current block.
3327 R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory);
3328 R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory);
3329 R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr),
3330 ResultInvalidCurrentMemory);
3331
3332 // Validate against the provided masks.
3333 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
3334
3335 // Break once we're done.
3336 if (last_addr <= info.GetLastAddress()) {
3337 break;
3338 }
3339
3340 // Advance our iterator.
3341 it++;
3342 ASSERT(it != m_memory_block_manager.cend());
3343 info = it->GetMemoryInfo();
3344 }
3345
3346 // Write output state.
3347 if (out_state != nullptr) {
3348 *out_state = first_state;
3349 }
3350 if (out_perm != nullptr) {
3351 *out_perm = first_perm;
3352 }
3353 if (out_attr != nullptr) {
3354 *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
3355 }
3356
3357 // If the end address isn't aligned, we need a block.
3358 if (out_blocks_needed != nullptr) {
3359 const size_t blocks_for_end_align =
3360 (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
3361 ? 1
3362 : 0;
3363 *out_blocks_needed = blocks_for_end_align;
3364 }
3365
3366 R_SUCCEED();
3367}
3368
3369Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
3370 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
3371 KProcessAddress addr, size_t size, KMemoryState state_mask,
3372 KMemoryState state, KMemoryPermission perm_mask,
3373 KMemoryPermission perm, KMemoryAttribute attr_mask,
3374 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
3375 ASSERT(this->IsLockedByCurrentThread());
3376
3377 // Check memory state.
3378 const KProcessAddress last_addr = addr + size - 1;
3379 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
3380 R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
3381 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
3382
3383 // If the start address isn't aligned, we need a block.
3384 if (out_blocks_needed != nullptr &&
3385 Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
3386 ++(*out_blocks_needed);
3387 }
3388
3389 R_SUCCEED();
3390}
3391
3392Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress,
3393 KProcessAddress addr, size_t size, KMemoryState state_mask,
3394 KMemoryState state, KMemoryPermission perm_mask,
3395 KMemoryPermission perm, KMemoryAttribute attr_mask,
3396 KMemoryAttribute attr, KMemoryPermission new_perm,
3397 KMemoryAttribute lock_attr) {
3398 // Validate basic preconditions.
3399 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
3400 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
3401 KMemoryAttribute::None);
3402
3403 // Validate the lock request.
3404 const size_t num_pages = size / PageSize;
3405 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
3406
3407 // Lock the table.
3408 KScopedLightLock lk(m_general_lock);
3409
3410 // Check that the output page group is empty, if it exists.
3411 if (out_pg) {
3412 ASSERT(out_pg->GetNumPages() == 0);
3413 }
3414
3415 // Check the state.
3416 KMemoryState old_state{};
3417 KMemoryPermission old_perm{};
3418 KMemoryAttribute old_attr{};
3419 size_t num_allocator_blocks{};
3420 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
3421 std::addressof(old_attr), std::addressof(num_allocator_blocks),
3422 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
3423 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
3424 attr_mask, attr));
3425
3426 // Get the physical address, if we're supposed to.
3427 if (out_KPhysicalAddress != nullptr) {
3428 ASSERT(this->GetPhysicalAddressLocked(out_KPhysicalAddress, addr));
3429 }
3430
3431 // Make the page group, if we're supposed to.
3432 if (out_pg != nullptr) {
3433 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
3434 }
3435
3436 // Create an update allocator.
3437 Result allocator_result{ResultSuccess};
3438 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3439 m_memory_block_slab_manager, num_allocator_blocks);
3440 R_TRY(allocator_result);
3441
3442 // Decide on new perm and attr.
3443 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
3444 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
3445
3446 // Update permission, if we need to.
3447 if (new_perm != old_perm) {
3448 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
3449 }
3450
3451 // Apply the memory block updates.
3452 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
3453 new_attr, KMemoryBlockDisableMergeAttribute::Locked,
3454 KMemoryBlockDisableMergeAttribute::None);
3455
3456 // If we have an output page group, open.
3457 if (out_pg) {
3458 out_pg->Open();
3459 }
3460
3461 R_SUCCEED();
3462}
3463
3464Result KPageTable::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
3465 KMemoryState state, KMemoryPermission perm_mask,
3466 KMemoryPermission perm, KMemoryAttribute attr_mask,
3467 KMemoryAttribute attr, KMemoryPermission new_perm,
3468 KMemoryAttribute lock_attr, const KPageGroup* pg) {
3469 // Validate basic preconditions.
3470 ASSERT((attr_mask & lock_attr) == lock_attr);
3471 ASSERT((attr & lock_attr) == lock_attr);
3472
3473 // Validate the unlock request.
3474 const size_t num_pages = size / PageSize;
3475 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
3476
3477 // Lock the table.
3478 KScopedLightLock lk(m_general_lock);
3479
3480 // Check the state.
3481 KMemoryState old_state{};
3482 KMemoryPermission old_perm{};
3483 KMemoryAttribute old_attr{};
3484 size_t num_allocator_blocks{};
3485 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
3486 std::addressof(old_attr), std::addressof(num_allocator_blocks),
3487 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
3488 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
3489 attr_mask, attr));
3490
3491 // Check the page group.
3492 if (pg != nullptr) {
3493 R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
3494 }
3495
3496 // Decide on new perm and attr.
3497 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
3498 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
3499
3500 // Create an update allocator.
3501 Result allocator_result{ResultSuccess};
3502 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3503 m_memory_block_slab_manager, num_allocator_blocks);
3504 R_TRY(allocator_result);
3505
3506 // Update permission, if we need to.
3507 if (new_perm != old_perm) {
3508 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
3509 }
3510
3511 // Apply the memory block updates.
3512 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
3513 new_attr, KMemoryBlockDisableMergeAttribute::None,
3514 KMemoryBlockDisableMergeAttribute::Locked);
3515
3516 R_SUCCEED();
3517}
3518
3519} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 66f16faaf..5541bc13f 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -3,548 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include "core/hle/kernel/k_page_table_base.h"
7
8#include "common/common_funcs.h"
9#include "common/page_table.h"
10#include "core/file_sys/program_metadata.h"
11#include "core/hle/kernel/k_dynamic_resource_manager.h"
12#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_block.h"
14#include "core/hle/kernel/k_memory_block_manager.h"
15#include "core/hle/kernel/k_memory_layout.h"
16#include "core/hle/kernel/k_memory_manager.h"
17#include "core/hle/kernel/k_typed_address.h"
18#include "core/hle/result.h"
19#include "core/memory.h"
20
21namespace Core {
22class System;
23}
24 7
25namespace Kernel { 8namespace Kernel {
26 9
27enum class DisableMergeAttribute : u8 { 10class KPageTable final : public KPageTableBase {
28 None = (0U << 0),
29 DisableHead = (1U << 0),
30 DisableHeadAndBody = (1U << 1),
31 EnableHeadAndBody = (1U << 2),
32 DisableTail = (1U << 3),
33 EnableTail = (1U << 4),
34 EnableAndMergeHeadBodyTail = (1U << 5),
35 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
36 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
37};
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
48class KBlockInfoManager;
49class KMemoryBlockManager;
50class KResourceLimit;
51class KSystemResource;
52
53class KPageTable final {
54protected:
55 struct PageLinkedList;
56
57public:
58 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
59
60 YUZU_NON_COPYABLE(KPageTable);
61 YUZU_NON_MOVEABLE(KPageTable);
62
63 explicit KPageTable(Core::System& system_);
64 ~KPageTable();
65
66 Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
67 bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
68 KProcessAddress code_addr, size_t code_size,
69 KSystemResource* system_resource, KResourceLimit* resource_limit,
70 Core::Memory::Memory& memory);
71
72 void Finalize();
73
74 Result MapProcessCode(KProcessAddress addr, size_t pages_count, KMemoryState state,
75 KMemoryPermission perm);
76 Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
77 Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
78 ICacheInvalidationStrategy icache_invalidation_strategy);
79 Result UnmapProcessMemory(KProcessAddress dst_addr, size_t size, KPageTable& src_page_table,
80 KProcessAddress src_addr);
81 Result MapPhysicalMemory(KProcessAddress addr, size_t size);
82 Result UnmapPhysicalMemory(KProcessAddress addr, size_t size);
83 Result MapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
84 Result UnmapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
85 Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
86 Svc::MemoryPermission svc_perm);
87 KMemoryInfo QueryInfo(KProcessAddress addr);
88 Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
89 Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr);
90 Result SetMaxHeapSize(size_t size);
91 Result SetHeapSize(u64* out, size_t size);
92 Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
93 KMemoryPermission perm, bool is_aligned, bool check_heap);
94 Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
95
96 Result UnlockForDeviceAddressSpace(KProcessAddress addr, size_t size);
97
98 Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
99 Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
100
101 Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
102 KPageTable& src_page_table, KMemoryPermission test_perm,
103 KMemoryState dst_state, bool send);
104 Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
105 Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
106
107 Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
108 KMemoryPermission perm);
109 Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
110 Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
111 Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
112 Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
113 KMemoryState state_mask, KMemoryState state,
114 KMemoryPermission perm_mask, KMemoryPermission perm,
115 KMemoryAttribute attr_mask, KMemoryAttribute attr);
116
117 Common::PageTable& PageTableImpl() {
118 return *m_page_table_impl;
119 }
120
121 const Common::PageTable& PageTableImpl() const {
122 return *m_page_table_impl;
123 }
124
125 KBlockInfoManager* GetBlockInfoManager() {
126 return m_block_info_manager;
127 }
128
129 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
130 KPhysicalAddress phys_addr, KProcessAddress region_start,
131 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
132 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
133 region_num_pages, state, perm));
134 }
135
136 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
137 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
138 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
139 this->GetRegionAddress(state),
140 this->GetRegionSize(state) / PageSize, state, perm));
141 }
142
143 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
144 KMemoryPermission perm) {
145 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
146 this->GetRegionAddress(state),
147 this->GetRegionSize(state) / PageSize, state, perm));
148 }
149
150 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
151 KMemoryPermission perm);
152 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
153
154 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
155 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
156 KMemoryPermission perm);
157 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
158 KMemoryPermission perm);
159 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
160 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
161 const KPageGroup& pg);
162
163 KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
164 size_t GetRegionSize(Svc::MemoryState state) const;
165 bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
166
167 KProcessAddress GetRegionAddress(KMemoryState state) const {
168 return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
169 }
170 size_t GetRegionSize(KMemoryState state) const {
171 return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
172 }
173 bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
174 return this->CanContain(addr, size,
175 static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
176 }
177
178protected:
179 struct PageLinkedList {
180 private:
181 struct Node {
182 Node* m_next;
183 std::array<u8, PageSize - sizeof(Node*)> m_buffer;
184 };
185
186 public:
187 constexpr PageLinkedList() = default;
188
189 void Push(Node* n) {
190 ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
191 n->m_next = m_root;
192 m_root = n;
193 }
194
195 void Push(Core::Memory::Memory& memory, KVirtualAddress addr) {
196 this->Push(memory.GetPointer<Node>(GetInteger(addr)));
197 }
198
199 Node* Peek() const {
200 return m_root;
201 }
202
203 Node* Pop() {
204 Node* const r = m_root;
205
206 m_root = r->m_next;
207 r->m_next = nullptr;
208
209 return r;
210 }
211
212 private:
213 Node* m_root{};
214 };
215 static_assert(std::is_trivially_destructible<PageLinkedList>::value);
216
217private:
218 enum class OperationType : u32 {
219 Map = 0,
220 MapGroup = 1,
221 MapFirstGroup = 2,
222 Unmap = 3,
223 ChangePermissions = 4,
224 ChangePermissionsAndRefresh = 5,
225 ChangePermissionsAndRefreshAndFlush = 6,
226 Separate = 7,
227 };
228
229 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
230 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
231
232 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
233 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
234 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
235 bool IsRegionContiguous(KProcessAddress addr, u64 size) const;
236 void AddRegionToPages(KProcessAddress start, size_t num_pages, KPageGroup& page_linked_list);
237 KMemoryInfo QueryInfoImpl(KProcessAddress addr);
238 KProcessAddress AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages,
239 u64 needed_num_pages, size_t align);
240 Result Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group,
241 OperationType operation);
242 Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
243 OperationType operation, KPhysicalAddress map_addr = 0);
244 void FinalizeUpdate(PageLinkedList* page_list);
245
246 KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
247 size_t num_pages, size_t alignment, size_t offset,
248 size_t guard_pages);
249
250 Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
251 KMemoryState state_mask, KMemoryState state,
252 KMemoryPermission perm_mask, KMemoryPermission perm,
253 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
254 Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
255 KMemoryState state, KMemoryPermission perm_mask,
256 KMemoryPermission perm, KMemoryAttribute attr_mask,
257 KMemoryAttribute attr) const {
258 R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
259 perm, attr_mask, attr));
260 }
261
262 Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
263 KMemoryPermission perm_mask, KMemoryPermission perm,
264 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
265 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
266 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
267 KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
268 KMemoryState state_mask, KMemoryState state,
269 KMemoryPermission perm_mask, KMemoryPermission perm,
270 KMemoryAttribute attr_mask, KMemoryAttribute attr,
271 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
272 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
273 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
274 KProcessAddress addr, size_t size, KMemoryState state_mask,
275 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
276 KMemoryAttribute attr_mask, KMemoryAttribute attr,
277 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
278 Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
279 KMemoryState state_mask, KMemoryState state,
280 KMemoryPermission perm_mask, KMemoryPermission perm,
281 KMemoryAttribute attr_mask, KMemoryAttribute attr,
282 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
283 R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
284 state_mask, state, perm_mask, perm, attr_mask, attr,
285 ignore_attr));
286 }
287 Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
288 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
289 KMemoryAttribute attr_mask, KMemoryAttribute attr,
290 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
291 R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
292 attr_mask, attr, ignore_attr));
293 }
294
295 Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress,
296 KProcessAddress addr, size_t size, KMemoryState state_mask,
297 KMemoryState state, KMemoryPermission perm_mask,
298 KMemoryPermission perm, KMemoryAttribute attr_mask,
299 KMemoryAttribute attr, KMemoryPermission new_perm,
300 KMemoryAttribute lock_attr);
301 Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
302 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
303 KMemoryAttribute attr_mask, KMemoryAttribute attr,
304 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
305 const KPageGroup* pg);
306
307 Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
308 bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
309
310 bool IsLockedByCurrentThread() const {
311 return m_general_lock.IsLockedByCurrentThread();
312 }
313
314 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, KPhysicalAddress phys_addr) {
315 ASSERT(this->IsLockedByCurrentThread());
316
317 return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
318 }
319
320 bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
321 ASSERT(this->IsLockedByCurrentThread());
322
323 *out = GetPhysicalAddr(virt_addr);
324
325 return *out != 0;
326 }
327
328 Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
329 KProcessAddress address, size_t size, KMemoryPermission test_perm,
330 KMemoryState dst_state);
331 Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
332 KMemoryPermission test_perm, KMemoryState dst_state,
333 KPageTable& src_page_table, bool send);
334 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
335 size_t size, KMemoryPermission prot_perm);
336
337 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
338 size_t num_pages, KMemoryPermission perm);
339 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
340 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
341
342 mutable KLightLock m_general_lock;
343 mutable KLightLock m_map_physical_memory_lock;
344
345public:
346 constexpr KProcessAddress GetAddressSpaceStart() const {
347 return m_address_space_start;
348 }
349 constexpr KProcessAddress GetAddressSpaceEnd() const {
350 return m_address_space_end;
351 }
352 constexpr size_t GetAddressSpaceSize() const {
353 return m_address_space_end - m_address_space_start;
354 }
355 constexpr KProcessAddress GetHeapRegionStart() const {
356 return m_heap_region_start;
357 }
358 constexpr KProcessAddress GetHeapRegionEnd() const {
359 return m_heap_region_end;
360 }
361 constexpr size_t GetHeapRegionSize() const {
362 return m_heap_region_end - m_heap_region_start;
363 }
364 constexpr KProcessAddress GetAliasRegionStart() const {
365 return m_alias_region_start;
366 }
367 constexpr KProcessAddress GetAliasRegionEnd() const {
368 return m_alias_region_end;
369 }
370 constexpr size_t GetAliasRegionSize() const {
371 return m_alias_region_end - m_alias_region_start;
372 }
373 constexpr KProcessAddress GetStackRegionStart() const {
374 return m_stack_region_start;
375 }
376 constexpr KProcessAddress GetStackRegionEnd() const {
377 return m_stack_region_end;
378 }
379 constexpr size_t GetStackRegionSize() const {
380 return m_stack_region_end - m_stack_region_start;
381 }
382 constexpr KProcessAddress GetKernelMapRegionStart() const {
383 return m_kernel_map_region_start;
384 }
385 constexpr KProcessAddress GetKernelMapRegionEnd() const {
386 return m_kernel_map_region_end;
387 }
388 constexpr KProcessAddress GetCodeRegionStart() const {
389 return m_code_region_start;
390 }
391 constexpr KProcessAddress GetCodeRegionEnd() const {
392 return m_code_region_end;
393 }
394 constexpr KProcessAddress GetAliasCodeRegionStart() const {
395 return m_alias_code_region_start;
396 }
397 constexpr KProcessAddress GetAliasCodeRegionEnd() const {
398 return m_alias_code_region_end;
399 }
400 constexpr size_t GetAliasCodeRegionSize() const {
401 return m_alias_code_region_end - m_alias_code_region_start;
402 }
403 size_t GetNormalMemorySize() const {
404 KScopedLightLock lk(m_general_lock);
405 return GetHeapSize() + m_mapped_physical_memory_size;
406 }
407 constexpr size_t GetAddressSpaceWidth() const {
408 return m_address_space_width;
409 }
410 constexpr size_t GetHeapSize() const {
411 return m_current_heap_end - m_heap_region_start;
412 }
413 constexpr size_t GetNumGuardPages() const {
414 return IsKernel() ? 1 : 4;
415 }
416 KPhysicalAddress GetPhysicalAddr(KProcessAddress addr) const {
417 const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
418 ASSERT(backing_addr);
419 return backing_addr + GetInteger(addr);
420 }
421 constexpr bool Contains(KProcessAddress addr) const {
422 return m_address_space_start <= addr && addr <= m_address_space_end - 1;
423 }
424 constexpr bool Contains(KProcessAddress addr, size_t size) const {
425 return m_address_space_start <= addr && addr < addr + size &&
426 addr + size - 1 <= m_address_space_end - 1;
427 }
428 constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
429 return this->Contains(addr, size) && m_alias_region_start <= addr &&
430 addr + size - 1 <= m_alias_region_end - 1;
431 }
432 constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
433 return this->Contains(addr, size) && m_heap_region_start <= addr &&
434 addr + size - 1 <= m_heap_region_end - 1;
435 }
436
437public: 11public:
438 static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout, 12 explicit KPageTable(KernelCore& kernel) : KPageTableBase(kernel) {}
439 KPhysicalAddress addr) { 13 ~KPageTable() = default;
440 return layout.GetLinearVirtualAddress(addr);
441 }
442
443 static KPhysicalAddress GetLinearMappedPhysicalAddress(const KMemoryLayout& layout,
444 KVirtualAddress addr) {
445 return layout.GetLinearPhysicalAddress(addr);
446 }
447
448 static KVirtualAddress GetHeapVirtualAddress(const KMemoryLayout& layout,
449 KPhysicalAddress addr) {
450 return GetLinearMappedVirtualAddress(layout, addr);
451 }
452
453 static KPhysicalAddress GetHeapPhysicalAddress(const KMemoryLayout& layout,
454 KVirtualAddress addr) {
455 return GetLinearMappedPhysicalAddress(layout, addr);
456 }
457
458 static KVirtualAddress GetPageTableVirtualAddress(const KMemoryLayout& layout,
459 KPhysicalAddress addr) {
460 return GetLinearMappedVirtualAddress(layout, addr);
461 }
462
463 static KPhysicalAddress GetPageTablePhysicalAddress(const KMemoryLayout& layout,
464 KVirtualAddress addr) {
465 return GetLinearMappedPhysicalAddress(layout, addr);
466 }
467
468private:
469 constexpr bool IsKernel() const {
470 return m_is_kernel;
471 }
472 constexpr bool IsAslrEnabled() const {
473 return m_enable_aslr;
474 }
475
476 constexpr bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
477 return (m_address_space_start <= addr) &&
478 (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
479 (addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
480 }
481
482private:
483 class KScopedPageTableUpdater {
484 private:
485 KPageTable* m_pt{};
486 PageLinkedList m_ll;
487
488 public:
489 explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
490 explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
491 ~KScopedPageTableUpdater() {
492 m_pt->FinalizeUpdate(this->GetPageList());
493 }
494
495 PageLinkedList* GetPageList() {
496 return std::addressof(m_ll);
497 }
498 };
499
500private:
501 KProcessAddress m_address_space_start{};
502 KProcessAddress m_address_space_end{};
503 KProcessAddress m_heap_region_start{};
504 KProcessAddress m_heap_region_end{};
505 KProcessAddress m_current_heap_end{};
506 KProcessAddress m_alias_region_start{};
507 KProcessAddress m_alias_region_end{};
508 KProcessAddress m_stack_region_start{};
509 KProcessAddress m_stack_region_end{};
510 KProcessAddress m_kernel_map_region_start{};
511 KProcessAddress m_kernel_map_region_end{};
512 KProcessAddress m_code_region_start{};
513 KProcessAddress m_code_region_end{};
514 KProcessAddress m_alias_code_region_start{};
515 KProcessAddress m_alias_code_region_end{};
516
517 size_t m_max_heap_size{};
518 size_t m_mapped_physical_memory_size{};
519 size_t m_mapped_unsafe_physical_memory{};
520 size_t m_mapped_insecure_memory{};
521 size_t m_mapped_ipc_server_memory{};
522 size_t m_address_space_width{};
523
524 KMemoryBlockManager m_memory_block_manager;
525 u32 m_allocate_option{};
526
527 bool m_is_kernel{};
528 bool m_enable_aslr{};
529 bool m_enable_device_address_space_merge{};
530
531 KMemoryBlockSlabManager* m_memory_block_slab_manager{};
532 KBlockInfoManager* m_block_info_manager{};
533 KResourceLimit* m_resource_limit{};
534
535 u32 m_heap_fill_value{};
536 u32 m_ipc_fill_value{};
537 u32 m_stack_fill_value{};
538 const KMemoryRegion* m_cached_physical_heap_region{};
539
540 KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
541 KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
542
543 std::unique_ptr<Common::PageTable> m_page_table_impl;
544
545 Core::System& m_system;
546 KernelCore& m_kernel;
547 Core::Memory::Memory* m_memory{};
548}; 14};
549 15
550} // namespace Kernel 16} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
new file mode 100644
index 000000000..47dc8fd35
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -0,0 +1,5716 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "common/settings.h"
6#include "core/core.h"
7#include "core/hle/kernel/k_address_space_info.h"
8#include "core/hle/kernel/k_page_table_base.h"
9#include "core/hle/kernel/k_scoped_resource_reservation.h"
10#include "core/hle/kernel/k_system_resource.h"
11
12namespace Kernel {
13
14namespace {
15
16class KScopedLightLockPair {
17 YUZU_NON_COPYABLE(KScopedLightLockPair);
18 YUZU_NON_MOVEABLE(KScopedLightLockPair);
19
20private:
21 KLightLock* m_lower;
22 KLightLock* m_upper;
23
24public:
25 KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) {
26 // Ensure our locks are in a consistent order.
27 if (std::addressof(lhs) <= std::addressof(rhs)) {
28 m_lower = std::addressof(lhs);
29 m_upper = std::addressof(rhs);
30 } else {
31 m_lower = std::addressof(rhs);
32 m_upper = std::addressof(lhs);
33 }
34
35 // Acquire both locks.
36 m_lower->Lock();
37 if (m_lower != m_upper) {
38 m_upper->Lock();
39 }
40 }
41
42 ~KScopedLightLockPair() {
43 // Unlock the upper lock.
44 if (m_upper != nullptr && m_upper != m_lower) {
45 m_upper->Unlock();
46 }
47
48 // Unlock the lower lock.
49 if (m_lower != nullptr) {
50 m_lower->Unlock();
51 }
52 }
53
54public:
55 // Utility.
56 void TryUnlockHalf(KLightLock& lock) {
57 // Only allow unlocking if the lock is half the pair.
58 if (m_lower != m_upper) {
59 // We want to be sure the lock is one we own.
60 if (m_lower == std::addressof(lock)) {
61 lock.Unlock();
62 m_lower = nullptr;
63 } else if (m_upper == std::addressof(lock)) {
64 lock.Unlock();
65 m_upper = nullptr;
66 }
67 }
68 }
69};
70
71template <typename AddressType>
72void InvalidateInstructionCache(Core::System& system, AddressType addr, u64 size) {
73 system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size);
74}
75
76template <typename AddressType>
77Result InvalidateDataCache(AddressType addr, u64 size) {
78 R_SUCCEED();
79}
80
81template <typename AddressType>
82Result StoreDataCache(AddressType addr, u64 size) {
83 R_SUCCEED();
84}
85
86template <typename AddressType>
87Result FlushDataCache(AddressType addr, u64 size) {
88 R_SUCCEED();
89}
90
91} // namespace
92
93void KPageTableBase::MemoryRange::Open() {
94 // If the range contains heap pages, open them.
95 if (this->IsHeap()) {
96 m_kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize);
97 }
98}
99
100void KPageTableBase::MemoryRange::Close() {
101 // If the range contains heap pages, close them.
102 if (this->IsHeap()) {
103 m_kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize);
104 }
105}
106
107KPageTableBase::KPageTableBase(KernelCore& kernel)
108 : m_kernel(kernel), m_system(kernel.System()), m_general_lock(kernel),
109 m_map_physical_memory_lock(kernel), m_device_map_lock(kernel) {}
110KPageTableBase::~KPageTableBase() = default;
111
112Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start,
113 KVirtualAddress end, Core::Memory::Memory& memory) {
114 // Initialize our members.
115 m_address_space_width =
116 static_cast<u32>(is_64_bit ? Common::BitSize<u64>() : Common::BitSize<u32>());
117 m_address_space_start = KProcessAddress(GetInteger(start));
118 m_address_space_end = KProcessAddress(GetInteger(end));
119 m_is_kernel = true;
120 m_enable_aslr = true;
121 m_enable_device_address_space_merge = false;
122
123 m_heap_region_start = 0;
124 m_heap_region_end = 0;
125 m_current_heap_end = 0;
126 m_alias_region_start = 0;
127 m_alias_region_end = 0;
128 m_stack_region_start = 0;
129 m_stack_region_end = 0;
130 m_kernel_map_region_start = 0;
131 m_kernel_map_region_end = 0;
132 m_alias_code_region_start = 0;
133 m_alias_code_region_end = 0;
134 m_code_region_start = 0;
135 m_code_region_end = 0;
136 m_max_heap_size = 0;
137 m_mapped_physical_memory_size = 0;
138 m_mapped_unsafe_physical_memory = 0;
139 m_mapped_insecure_memory = 0;
140 m_mapped_ipc_server_memory = 0;
141
142 m_memory_block_slab_manager =
143 m_kernel.GetSystemSystemResource().GetMemoryBlockSlabManagerPointer();
144 m_block_info_manager = m_kernel.GetSystemSystemResource().GetBlockInfoManagerPointer();
145 m_resource_limit = m_kernel.GetSystemResourceLimit();
146
147 m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool::System,
148 KMemoryManager::Direction::FromFront);
149 m_heap_fill_value = MemoryFillValue_Zero;
150 m_ipc_fill_value = MemoryFillValue_Zero;
151 m_stack_fill_value = MemoryFillValue_Zero;
152
153 m_cached_physical_linear_region = nullptr;
154 m_cached_physical_heap_region = nullptr;
155
156 // Initialize our implementation.
157 m_impl = std::make_unique<Common::PageTable>();
158 m_impl->Resize(m_address_space_width, PageBits);
159
160 // Set the tracking memory.
161 m_memory = std::addressof(memory);
162
163 // Initialize our memory block manager.
164 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
165 m_memory_block_slab_manager));
166}
167
168Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
169 bool enable_das_merge, bool from_back,
170 KMemoryManager::Pool pool, KProcessAddress code_address,
171 size_t code_size, KSystemResource* system_resource,
172 KResourceLimit* resource_limit,
173 Core::Memory::Memory& memory) {
174 // Calculate region extents.
175 const size_t as_width = GetAddressSpaceWidth(as_type);
176 const KProcessAddress start = 0;
177 const KProcessAddress end = (1ULL << as_width);
178
179 // Validate the region.
180 ASSERT(start <= code_address);
181 ASSERT(code_address < code_address + code_size);
182 ASSERT(code_address + code_size - 1 <= end - 1);
183
184 // Define helpers.
185 auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) {
186 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type);
187 };
188 auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) {
189 return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type);
190 };
191
192 // Set our bit width and heap/alias sizes.
193 m_address_space_width = static_cast<u32>(GetAddressSpaceWidth(as_type));
194 size_t alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
195 size_t heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
196
197 // Adjust heap/alias size if we don't have an alias region.
198 if ((as_type & Svc::CreateProcessFlag::AddressSpaceMask) ==
199 Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) {
200 heap_region_size += alias_region_size;
201 alias_region_size = 0;
202 }
203
204 // Set code regions and determine remaining sizes.
205 KProcessAddress process_code_start;
206 KProcessAddress process_code_end;
207 size_t stack_region_size;
208 size_t kernel_map_region_size;
209 if (m_address_space_width == 39) {
210 alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
211 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
212 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
213 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
214 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
215 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
216 m_alias_code_region_start = m_code_region_start;
217 m_alias_code_region_end = m_code_region_end;
218 process_code_start = Common::AlignDown(GetInteger(code_address), RegionAlignment);
219 process_code_end = Common::AlignUp(GetInteger(code_address) + code_size, RegionAlignment);
220 } else {
221 stack_region_size = 0;
222 kernel_map_region_size = 0;
223 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
224 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
225 m_stack_region_start = m_code_region_start;
226 m_alias_code_region_start = m_code_region_start;
227 m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
228 GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
229 m_stack_region_end = m_code_region_end;
230 m_kernel_map_region_start = m_code_region_start;
231 m_kernel_map_region_end = m_code_region_end;
232 process_code_start = m_code_region_start;
233 process_code_end = m_code_region_end;
234 }
235
236 // Set other basic fields.
237 m_enable_aslr = enable_aslr;
238 m_enable_device_address_space_merge = enable_das_merge;
239 m_address_space_start = start;
240 m_address_space_end = end;
241 m_is_kernel = false;
242 m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer();
243 m_block_info_manager = system_resource->GetBlockInfoManagerPointer();
244 m_resource_limit = resource_limit;
245
246 // Determine the region we can place our undetermineds in.
247 KProcessAddress alloc_start;
248 size_t alloc_size;
249 if ((GetInteger(process_code_start) - GetInteger(m_code_region_start)) >=
250 (GetInteger(end) - GetInteger(process_code_end))) {
251 alloc_start = m_code_region_start;
252 alloc_size = GetInteger(process_code_start) - GetInteger(m_code_region_start);
253 } else {
254 alloc_start = process_code_end;
255 alloc_size = GetInteger(end) - GetInteger(process_code_end);
256 }
257 const size_t needed_size =
258 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size);
259 R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory);
260
261 const size_t remaining_size = alloc_size - needed_size;
262
263 // Determine random placements for each region.
264 size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0;
265 if (enable_aslr) {
266 alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
267 RegionAlignment;
268 heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
269 RegionAlignment;
270 stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
271 RegionAlignment;
272 kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
273 RegionAlignment;
274 }
275
276 // Setup heap and alias regions.
277 m_alias_region_start = alloc_start + alias_rnd;
278 m_alias_region_end = m_alias_region_start + alias_region_size;
279 m_heap_region_start = alloc_start + heap_rnd;
280 m_heap_region_end = m_heap_region_start + heap_region_size;
281
282 if (alias_rnd <= heap_rnd) {
283 m_heap_region_start += alias_region_size;
284 m_heap_region_end += alias_region_size;
285 } else {
286 m_alias_region_start += heap_region_size;
287 m_alias_region_end += heap_region_size;
288 }
289
290 // Setup stack region.
291 if (stack_region_size) {
292 m_stack_region_start = alloc_start + stack_rnd;
293 m_stack_region_end = m_stack_region_start + stack_region_size;
294
295 if (alias_rnd < stack_rnd) {
296 m_stack_region_start += alias_region_size;
297 m_stack_region_end += alias_region_size;
298 } else {
299 m_alias_region_start += stack_region_size;
300 m_alias_region_end += stack_region_size;
301 }
302
303 if (heap_rnd < stack_rnd) {
304 m_stack_region_start += heap_region_size;
305 m_stack_region_end += heap_region_size;
306 } else {
307 m_heap_region_start += stack_region_size;
308 m_heap_region_end += stack_region_size;
309 }
310 }
311
312 // Setup kernel map region.
313 if (kernel_map_region_size) {
314 m_kernel_map_region_start = alloc_start + kmap_rnd;
315 m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size;
316
317 if (alias_rnd < kmap_rnd) {
318 m_kernel_map_region_start += alias_region_size;
319 m_kernel_map_region_end += alias_region_size;
320 } else {
321 m_alias_region_start += kernel_map_region_size;
322 m_alias_region_end += kernel_map_region_size;
323 }
324
325 if (heap_rnd < kmap_rnd) {
326 m_kernel_map_region_start += heap_region_size;
327 m_kernel_map_region_end += heap_region_size;
328 } else {
329 m_heap_region_start += kernel_map_region_size;
330 m_heap_region_end += kernel_map_region_size;
331 }
332
333 if (stack_region_size) {
334 if (stack_rnd < kmap_rnd) {
335 m_kernel_map_region_start += stack_region_size;
336 m_kernel_map_region_end += stack_region_size;
337 } else {
338 m_stack_region_start += kernel_map_region_size;
339 m_stack_region_end += kernel_map_region_size;
340 }
341 }
342 }
343
344 // Set heap and fill members.
345 m_current_heap_end = m_heap_region_start;
346 m_max_heap_size = 0;
347 m_mapped_physical_memory_size = 0;
348 m_mapped_unsafe_physical_memory = 0;
349 m_mapped_insecure_memory = 0;
350 m_mapped_ipc_server_memory = 0;
351
352 // const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled();
353 const bool fill_memory = false;
354 m_heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero;
355 m_ipc_fill_value = fill_memory ? MemoryFillValue_Ipc : MemoryFillValue_Zero;
356 m_stack_fill_value = fill_memory ? MemoryFillValue_Stack : MemoryFillValue_Zero;
357
358 // Set allocation option.
359 m_allocate_option =
360 KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack
361 : KMemoryManager::Direction::FromFront);
362
363 // Ensure that we regions inside our address space.
364 auto IsInAddressSpace = [&](KProcessAddress addr) {
365 return m_address_space_start <= addr && addr <= m_address_space_end;
366 };
367 ASSERT(IsInAddressSpace(m_alias_region_start));
368 ASSERT(IsInAddressSpace(m_alias_region_end));
369 ASSERT(IsInAddressSpace(m_heap_region_start));
370 ASSERT(IsInAddressSpace(m_heap_region_end));
371 ASSERT(IsInAddressSpace(m_stack_region_start));
372 ASSERT(IsInAddressSpace(m_stack_region_end));
373 ASSERT(IsInAddressSpace(m_kernel_map_region_start));
374 ASSERT(IsInAddressSpace(m_kernel_map_region_end));
375
376 // Ensure that we selected regions that don't overlap.
377 const KProcessAddress alias_start = m_alias_region_start;
378 const KProcessAddress alias_last = m_alias_region_end - 1;
379 const KProcessAddress heap_start = m_heap_region_start;
380 const KProcessAddress heap_last = m_heap_region_end - 1;
381 const KProcessAddress stack_start = m_stack_region_start;
382 const KProcessAddress stack_last = m_stack_region_end - 1;
383 const KProcessAddress kmap_start = m_kernel_map_region_start;
384 const KProcessAddress kmap_last = m_kernel_map_region_end - 1;
385 ASSERT(alias_last < heap_start || heap_last < alias_start);
386 ASSERT(alias_last < stack_start || stack_last < alias_start);
387 ASSERT(alias_last < kmap_start || kmap_last < alias_start);
388 ASSERT(heap_last < stack_start || stack_last < heap_start);
389 ASSERT(heap_last < kmap_start || kmap_last < heap_start);
390
391 // Initialize our implementation.
392 m_impl = std::make_unique<Common::PageTable>();
393 m_impl->Resize(m_address_space_width, PageBits);
394
395 // Set the tracking memory.
396 m_memory = std::addressof(memory);
397
398 // Initialize our memory block manager.
399 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
400 m_memory_block_slab_manager));
401}
402
403void KPageTableBase::Finalize() {
404 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
405 if (Settings::IsFastmemEnabled()) {
406 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
407 }
408 };
409
410 // Finalize memory blocks.
411 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
412
413 // Free any unsafe mapped memory.
414 if (m_mapped_unsafe_physical_memory) {
415 UNIMPLEMENTED();
416 }
417
418 // Release any insecure mapped memory.
419 if (m_mapped_insecure_memory) {
420 if (auto* const insecure_resource_limit =
421 KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
422 insecure_resource_limit != nullptr) {
423 insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
424 m_mapped_insecure_memory);
425 }
426 }
427
428 // Release any ipc server memory.
429 if (m_mapped_ipc_server_memory) {
430 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
431 m_mapped_ipc_server_memory);
432 }
433
434 // Close the backing page table, as the destructor is not called for guest objects.
435 m_impl.reset();
436}
437
438KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
439 switch (state) {
440 case Svc::MemoryState::Free:
441 case Svc::MemoryState::Kernel:
442 return m_address_space_start;
443 case Svc::MemoryState::Normal:
444 return m_heap_region_start;
445 case Svc::MemoryState::Ipc:
446 case Svc::MemoryState::NonSecureIpc:
447 case Svc::MemoryState::NonDeviceIpc:
448 return m_alias_region_start;
449 case Svc::MemoryState::Stack:
450 return m_stack_region_start;
451 case Svc::MemoryState::Static:
452 case Svc::MemoryState::ThreadLocal:
453 return m_kernel_map_region_start;
454 case Svc::MemoryState::Io:
455 case Svc::MemoryState::Shared:
456 case Svc::MemoryState::AliasCode:
457 case Svc::MemoryState::AliasCodeData:
458 case Svc::MemoryState::Transfered:
459 case Svc::MemoryState::SharedTransfered:
460 case Svc::MemoryState::SharedCode:
461 case Svc::MemoryState::GeneratedCode:
462 case Svc::MemoryState::CodeOut:
463 case Svc::MemoryState::Coverage:
464 case Svc::MemoryState::Insecure:
465 return m_alias_code_region_start;
466 case Svc::MemoryState::Code:
467 case Svc::MemoryState::CodeData:
468 return m_code_region_start;
469 default:
470 UNREACHABLE();
471 }
472}
473
474size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const {
475 switch (state) {
476 case Svc::MemoryState::Free:
477 case Svc::MemoryState::Kernel:
478 return m_address_space_end - m_address_space_start;
479 case Svc::MemoryState::Normal:
480 return m_heap_region_end - m_heap_region_start;
481 case Svc::MemoryState::Ipc:
482 case Svc::MemoryState::NonSecureIpc:
483 case Svc::MemoryState::NonDeviceIpc:
484 return m_alias_region_end - m_alias_region_start;
485 case Svc::MemoryState::Stack:
486 return m_stack_region_end - m_stack_region_start;
487 case Svc::MemoryState::Static:
488 case Svc::MemoryState::ThreadLocal:
489 return m_kernel_map_region_end - m_kernel_map_region_start;
490 case Svc::MemoryState::Io:
491 case Svc::MemoryState::Shared:
492 case Svc::MemoryState::AliasCode:
493 case Svc::MemoryState::AliasCodeData:
494 case Svc::MemoryState::Transfered:
495 case Svc::MemoryState::SharedTransfered:
496 case Svc::MemoryState::SharedCode:
497 case Svc::MemoryState::GeneratedCode:
498 case Svc::MemoryState::CodeOut:
499 case Svc::MemoryState::Coverage:
500 case Svc::MemoryState::Insecure:
501 return m_alias_code_region_end - m_alias_code_region_start;
502 case Svc::MemoryState::Code:
503 case Svc::MemoryState::CodeData:
504 return m_code_region_end - m_code_region_start;
505 default:
506 UNREACHABLE();
507 }
508}
509
510bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
511 const KProcessAddress end = addr + size;
512 const KProcessAddress last = end - 1;
513
514 const KProcessAddress region_start = this->GetRegionAddress(state);
515 const size_t region_size = this->GetRegionSize(state);
516
517 const bool is_in_region =
518 region_start <= addr && addr < end && last <= region_start + region_size - 1;
519 const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr ||
520 m_heap_region_start == m_heap_region_end);
521 const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
522 m_alias_region_start == m_alias_region_end);
523 switch (state) {
524 case Svc::MemoryState::Free:
525 case Svc::MemoryState::Kernel:
526 return is_in_region;
527 case Svc::MemoryState::Io:
528 case Svc::MemoryState::Static:
529 case Svc::MemoryState::Code:
530 case Svc::MemoryState::CodeData:
531 case Svc::MemoryState::Shared:
532 case Svc::MemoryState::AliasCode:
533 case Svc::MemoryState::AliasCodeData:
534 case Svc::MemoryState::Stack:
535 case Svc::MemoryState::ThreadLocal:
536 case Svc::MemoryState::Transfered:
537 case Svc::MemoryState::SharedTransfered:
538 case Svc::MemoryState::SharedCode:
539 case Svc::MemoryState::GeneratedCode:
540 case Svc::MemoryState::CodeOut:
541 case Svc::MemoryState::Coverage:
542 case Svc::MemoryState::Insecure:
543 return is_in_region && !is_in_heap && !is_in_alias;
544 case Svc::MemoryState::Normal:
545 ASSERT(is_in_heap);
546 return is_in_region && !is_in_alias;
547 case Svc::MemoryState::Ipc:
548 case Svc::MemoryState::NonSecureIpc:
549 case Svc::MemoryState::NonDeviceIpc:
550 ASSERT(is_in_alias);
551 return is_in_region && !is_in_heap;
552 default:
553 return false;
554 }
555}
556
557Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
558 KMemoryState state, KMemoryPermission perm_mask,
559 KMemoryPermission perm, KMemoryAttribute attr_mask,
560 KMemoryAttribute attr) const {
561 // Validate the states match expectation.
562 R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory);
563 R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory);
564 R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
565
566 R_SUCCEED();
567}
568
569Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
570 size_t size, KMemoryState state_mask,
571 KMemoryState state, KMemoryPermission perm_mask,
572 KMemoryPermission perm,
573 KMemoryAttribute attr_mask,
574 KMemoryAttribute attr) const {
575 ASSERT(this->IsLockedByCurrentThread());
576
577 // Get information about the first block.
578 const KProcessAddress last_addr = addr + size - 1;
579 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
580 KMemoryInfo info = it->GetMemoryInfo();
581
582 // If the start address isn't aligned, we need a block.
583 const size_t blocks_for_start_align =
584 (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
585
586 while (true) {
587 // Validate against the provided masks.
588 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
589
590 // Break once we're done.
591 if (last_addr <= info.GetLastAddress()) {
592 break;
593 }
594
595 // Advance our iterator.
596 it++;
597 ASSERT(it != m_memory_block_manager.cend());
598 info = it->GetMemoryInfo();
599 }
600
601 // If the end address isn't aligned, we need a block.
602 const size_t blocks_for_end_align =
603 (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
604
605 if (out_blocks_needed != nullptr) {
606 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
607 }
608
609 R_SUCCEED();
610}
611
612Result KPageTableBase::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
613 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
614 KMemoryBlockManager::const_iterator it,
615 KProcessAddress last_addr, KMemoryState state_mask,
616 KMemoryState state, KMemoryPermission perm_mask,
617 KMemoryPermission perm, KMemoryAttribute attr_mask,
618 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
619 ASSERT(this->IsLockedByCurrentThread());
620
621 // Get information about the first block.
622 KMemoryInfo info = it->GetMemoryInfo();
623
624 // Validate all blocks in the range have correct state.
625 const KMemoryState first_state = info.m_state;
626 const KMemoryPermission first_perm = info.m_permission;
627 const KMemoryAttribute first_attr = info.m_attribute;
628 while (true) {
629 // Validate the current block.
630 R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory);
631 R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory);
632 R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr),
633 ResultInvalidCurrentMemory);
634
635 // Validate against the provided masks.
636 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
637
638 // Break once we're done.
639 if (last_addr <= info.GetLastAddress()) {
640 break;
641 }
642
643 // Advance our iterator.
644 it++;
645 ASSERT(it != m_memory_block_manager.cend());
646 info = it->GetMemoryInfo();
647 }
648
649 // Write output state.
650 if (out_state != nullptr) {
651 *out_state = first_state;
652 }
653 if (out_perm != nullptr) {
654 *out_perm = first_perm;
655 }
656 if (out_attr != nullptr) {
657 *out_attr = first_attr & ~ignore_attr;
658 }
659
660 // If the end address isn't aligned, we need a block.
661 if (out_blocks_needed != nullptr) {
662 const size_t blocks_for_end_align =
663 (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
664 ? 1
665 : 0;
666 *out_blocks_needed = blocks_for_end_align;
667 }
668
669 R_SUCCEED();
670}
671
672Result KPageTableBase::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
673 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
674 KProcessAddress addr, size_t size, KMemoryState state_mask,
675 KMemoryState state, KMemoryPermission perm_mask,
676 KMemoryPermission perm, KMemoryAttribute attr_mask,
677 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
678 ASSERT(this->IsLockedByCurrentThread());
679
680 // Check memory state.
681 const KProcessAddress last_addr = addr + size - 1;
682 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
683 R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
684 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
685
686 // If the start address isn't aligned, we need a block.
687 if (out_blocks_needed != nullptr &&
688 Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
689 ++(*out_blocks_needed);
690 }
691
692 R_SUCCEED();
693}
694
695Result KPageTableBase::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr,
696 KProcessAddress addr, size_t size, KMemoryState state_mask,
697 KMemoryState state, KMemoryPermission perm_mask,
698 KMemoryPermission perm, KMemoryAttribute attr_mask,
699 KMemoryAttribute attr, KMemoryPermission new_perm,
700 KMemoryAttribute lock_attr) {
701 // Validate basic preconditions.
702 ASSERT(False(lock_attr & attr));
703 ASSERT(False(lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)));
704
705 // Validate the lock request.
706 const size_t num_pages = size / PageSize;
707 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
708
709 // Lock the table.
710 KScopedLightLock lk(m_general_lock);
711
712 // Check that the output page group is empty, if it exists.
713 if (out_pg) {
714 ASSERT(out_pg->GetNumPages() == 0);
715 }
716
717 // Check the state.
718 KMemoryState old_state;
719 KMemoryPermission old_perm;
720 KMemoryAttribute old_attr;
721 size_t num_allocator_blocks;
722 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
723 std::addressof(old_attr), std::addressof(num_allocator_blocks),
724 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
725 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
726 attr_mask, attr));
727
728 // Get the physical address, if we're supposed to.
729 if (out_paddr != nullptr) {
730 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
731 }
732
733 // Make the page group, if we're supposed to.
734 if (out_pg != nullptr) {
735 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
736 }
737
738 // Create an update allocator.
739 Result allocator_result;
740 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
741 m_memory_block_slab_manager, num_allocator_blocks);
742 R_TRY(allocator_result);
743
744 // Decide on new perm and attr.
745 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
746 KMemoryAttribute new_attr = old_attr | static_cast<KMemoryAttribute>(lock_attr);
747
748 // Update permission, if we need to.
749 if (new_perm != old_perm) {
750 // We're going to perform an update, so create a helper.
751 KScopedPageTableUpdater updater(this);
752
753 const KPageProperties properties = {new_perm, false,
754 True(old_attr & KMemoryAttribute::Uncached),
755 DisableMergeAttribute::DisableHeadBodyTail};
756 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
757 OperationType::ChangePermissions, false));
758 }
759
760 // Apply the memory block updates.
761 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
762 new_attr, KMemoryBlockDisableMergeAttribute::Locked,
763 KMemoryBlockDisableMergeAttribute::None);
764
765 // If we have an output group, open.
766 if (out_pg) {
767 out_pg->Open();
768 }
769
770 R_SUCCEED();
771}
772
773Result KPageTableBase::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
774 KMemoryState state, KMemoryPermission perm_mask,
775 KMemoryPermission perm, KMemoryAttribute attr_mask,
776 KMemoryAttribute attr, KMemoryPermission new_perm,
777 KMemoryAttribute lock_attr, const KPageGroup* pg) {
778 // Validate basic preconditions.
779 ASSERT((attr_mask & lock_attr) == lock_attr);
780 ASSERT((attr & lock_attr) == lock_attr);
781
782 // Validate the unlock request.
783 const size_t num_pages = size / PageSize;
784 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
785
786 // Lock the table.
787 KScopedLightLock lk(m_general_lock);
788
789 // Check the state.
790 KMemoryState old_state;
791 KMemoryPermission old_perm;
792 KMemoryAttribute old_attr;
793 size_t num_allocator_blocks;
794 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
795 std::addressof(old_attr), std::addressof(num_allocator_blocks),
796 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
797 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
798 attr_mask, attr));
799
800 // Check the page group.
801 if (pg != nullptr) {
802 R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
803 }
804
805 // Decide on new perm and attr.
806 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
807 KMemoryAttribute new_attr = old_attr & ~static_cast<KMemoryAttribute>(lock_attr);
808
809 // Create an update allocator.
810 Result allocator_result;
811 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
812 m_memory_block_slab_manager, num_allocator_blocks);
813 R_TRY(allocator_result);
814
815 // Update permission, if we need to.
816 if (new_perm != old_perm) {
817 // We're going to perform an update, so create a helper.
818 KScopedPageTableUpdater updater(this);
819
820 const KPageProperties properties = {new_perm, false,
821 True(old_attr & KMemoryAttribute::Uncached),
822 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
823 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
824 OperationType::ChangePermissions, false));
825 }
826
827 // Apply the memory block updates.
828 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
829 new_attr, KMemoryBlockDisableMergeAttribute::None,
830 KMemoryBlockDisableMergeAttribute::Locked);
831
832 R_SUCCEED();
833}
834
835Result KPageTableBase::QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page,
836 KProcessAddress address) const {
837 ASSERT(this->IsLockedByCurrentThread());
838 ASSERT(out_info != nullptr);
839 ASSERT(out_page != nullptr);
840
841 const KMemoryBlock* block = m_memory_block_manager.FindBlock(address);
842 R_UNLESS(block != nullptr, ResultInvalidCurrentMemory);
843
844 *out_info = block->GetMemoryInfo();
845 out_page->flags = 0;
846 R_SUCCEED();
847}
848
849Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size,
850 Svc::MemoryState state) const {
851 ASSERT(!this->IsLockedByCurrentThread());
852 ASSERT(out != nullptr);
853
854 const KProcessAddress region_start = this->GetRegionAddress(state);
855 const size_t region_size = this->GetRegionSize(state);
856
857 // Check that the address/size are potentially valid.
858 R_UNLESS((address < address + size), ResultNotFound);
859
860 // Lock the table.
861 KScopedLightLock lk(m_general_lock);
862
863 auto& impl = this->GetImpl();
864
865 // Begin traversal.
866 TraversalContext context;
867 TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
868 bool cur_valid = false;
869 TraversalEntry next_entry;
870 bool next_valid;
871 size_t tot_size = 0;
872
873 next_valid =
874 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
875 next_entry.block_size =
876 (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
877
878 // Iterate, looking for entry.
879 while (true) {
880 if ((!next_valid && !cur_valid) ||
881 (next_valid && cur_valid &&
882 next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
883 cur_entry.block_size += next_entry.block_size;
884 } else {
885 if (cur_valid && cur_entry.phys_addr <= address &&
886 address + size <= cur_entry.phys_addr + cur_entry.block_size) {
887 // Check if this region is valid.
888 const KProcessAddress mapped_address =
889 (region_start + tot_size) + GetInteger(address - cur_entry.phys_addr);
890 if (R_SUCCEEDED(this->CheckMemoryState(
891 mapped_address, size, KMemoryState::Mask, static_cast<KMemoryState>(state),
892 KMemoryPermission::UserRead, KMemoryPermission::UserRead,
893 KMemoryAttribute::None, KMemoryAttribute::None))) {
894 // It is!
895 *out = mapped_address;
896 R_SUCCEED();
897 }
898 }
899
900 // Update tracking variables.
901 tot_size += cur_entry.block_size;
902 cur_entry = next_entry;
903 cur_valid = next_valid;
904 }
905
906 if (cur_entry.block_size + tot_size >= region_size) {
907 break;
908 }
909
910 next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
911 }
912
913 // Check the last entry.
914 R_UNLESS(cur_valid, ResultNotFound);
915 R_UNLESS(cur_entry.phys_addr <= address, ResultNotFound);
916 R_UNLESS(address + size <= cur_entry.phys_addr + cur_entry.block_size, ResultNotFound);
917
918 // Check if the last region is valid.
919 const KProcessAddress mapped_address =
920 (region_start + tot_size) + GetInteger(address - cur_entry.phys_addr);
921 R_TRY_CATCH(this->CheckMemoryState(mapped_address, size, KMemoryState::All,
922 static_cast<KMemoryState>(state),
923 KMemoryPermission::UserRead, KMemoryPermission::UserRead,
924 KMemoryAttribute::None, KMemoryAttribute::None)) {
925 R_CONVERT_ALL(ResultNotFound);
926 }
927 R_END_TRY_CATCH;
928
929 // We found the region.
930 *out = mapped_address;
931 R_SUCCEED();
932}
933
934Result KPageTableBase::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
935 size_t size) {
936 // Lock the table.
937 KScopedLightLock lk(m_general_lock);
938
939 // Validate that the source address's state is valid.
940 KMemoryState src_state;
941 size_t num_src_allocator_blocks;
942 R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr,
943 std::addressof(num_src_allocator_blocks), src_address, size,
944 KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
945 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
946 KMemoryAttribute::All, KMemoryAttribute::None));
947
948 // Validate that the dst address's state is valid.
949 size_t num_dst_allocator_blocks;
950 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
951 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
952 KMemoryPermission::None, KMemoryAttribute::None,
953 KMemoryAttribute::None));
954
955 // Create an update allocator for the source.
956 Result src_allocator_result;
957 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
958 m_memory_block_slab_manager,
959 num_src_allocator_blocks);
960 R_TRY(src_allocator_result);
961
962 // Create an update allocator for the destination.
963 Result dst_allocator_result;
964 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
965 m_memory_block_slab_manager,
966 num_dst_allocator_blocks);
967 R_TRY(dst_allocator_result);
968
969 // Map the memory.
970 {
971 // Determine the number of pages being operated on.
972 const size_t num_pages = size / PageSize;
973
974 // Create page groups for the memory being unmapped.
975 KPageGroup pg(m_kernel, m_block_info_manager);
976
977 // Create the page group representing the source.
978 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
979
980 // We're going to perform an update, so create a helper.
981 KScopedPageTableUpdater updater(this);
982
983 // Reprotect the source as kernel-read/not mapped.
984 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
985 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
986 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
987 const KPageProperties src_properties = {new_src_perm, false, false,
988 DisableMergeAttribute::DisableHeadBodyTail};
989 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
990 OperationType::ChangePermissions, false));
991
992 // Ensure that we unprotect the source pages on failure.
993 ON_RESULT_FAILURE {
994 const KPageProperties unprotect_properties = {
995 KMemoryPermission::UserReadWrite, false, false,
996 DisableMergeAttribute::EnableHeadBodyTail};
997 R_ASSERT(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false,
998 unprotect_properties, OperationType::ChangePermissions, true));
999 };
1000
1001 // Map the alias pages.
1002 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
1003 DisableMergeAttribute::DisableHead};
1004 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
1005 false));
1006
1007 // Apply the memory block updates.
1008 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1009 src_state, new_src_perm, new_src_attr,
1010 KMemoryBlockDisableMergeAttribute::Locked,
1011 KMemoryBlockDisableMergeAttribute::None);
1012 m_memory_block_manager.Update(
1013 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
1014 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1015 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
1016 }
1017
1018 R_SUCCEED();
1019}
1020
1021Result KPageTableBase::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1022 size_t size) {
1023 // Lock the table.
1024 KScopedLightLock lk(m_general_lock);
1025
1026 // Validate that the source address's state is valid.
1027 KMemoryState src_state;
1028 size_t num_src_allocator_blocks;
1029 R_TRY(this->CheckMemoryState(
1030 std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks),
1031 src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
1032 KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
1033 KMemoryAttribute::All, KMemoryAttribute::Locked));
1034
1035 // Validate that the dst address's state is valid.
1036 KMemoryPermission dst_perm;
1037 size_t num_dst_allocator_blocks;
1038 R_TRY(this->CheckMemoryState(
1039 nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks),
1040 dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
1041 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
1042
1043 // Create an update allocator for the source.
1044 Result src_allocator_result;
1045 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1046 m_memory_block_slab_manager,
1047 num_src_allocator_blocks);
1048 R_TRY(src_allocator_result);
1049
1050 // Create an update allocator for the destination.
1051 Result dst_allocator_result;
1052 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1053 m_memory_block_slab_manager,
1054 num_dst_allocator_blocks);
1055 R_TRY(dst_allocator_result);
1056
1057 // Unmap the memory.
1058 {
1059 // Determine the number of pages being operated on.
1060 const size_t num_pages = size / PageSize;
1061
1062 // Create page groups for the memory being unmapped.
1063 KPageGroup pg(m_kernel, m_block_info_manager);
1064
1065 // Create the page group representing the destination.
1066 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1067
1068 // Ensure the page group is the valid for the source.
1069 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1070
1071 // We're going to perform an update, so create a helper.
1072 KScopedPageTableUpdater updater(this);
1073
1074 // Unmap the aliased copy of the pages.
1075 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
1076 DisableMergeAttribute::None};
1077 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false,
1078 dst_unmap_properties, OperationType::Unmap, false));
1079
1080 // Ensure that we re-map the aliased pages on failure.
1081 ON_RESULT_FAILURE {
1082 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
1083 };
1084
1085 // Try to set the permissions for the source pages back to what they should be.
1086 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
1087 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
1088 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
1089 OperationType::ChangePermissions, false));
1090
1091 // Apply the memory block updates.
1092 m_memory_block_manager.Update(
1093 std::addressof(src_allocator), src_address, num_pages, src_state,
1094 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1095 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
1096 m_memory_block_manager.Update(
1097 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
1098 KMemoryPermission::None, KMemoryAttribute::None,
1099 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
1100 }
1101
1102 R_SUCCEED();
1103}
1104
1105Result KPageTableBase::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
1106 size_t size) {
1107 // Validate the mapping request.
1108 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
1109 ResultInvalidMemoryRegion);
1110
1111 // Lock the table.
1112 KScopedLightLock lk(m_general_lock);
1113
1114 // Verify that the source memory is normal heap.
1115 KMemoryState src_state;
1116 KMemoryPermission src_perm;
1117 size_t num_src_allocator_blocks;
1118 R_TRY(this->CheckMemoryState(std::addressof(src_state), std::addressof(src_perm), nullptr,
1119 std::addressof(num_src_allocator_blocks), src_address, size,
1120 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
1121 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1122 KMemoryAttribute::None));
1123
1124 // Verify that the destination memory is unmapped.
1125 size_t num_dst_allocator_blocks;
1126 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
1127 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1128 KMemoryPermission::None, KMemoryAttribute::None,
1129 KMemoryAttribute::None));
1130
1131 // Create an update allocator for the source.
1132 Result src_allocator_result;
1133 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1134 m_memory_block_slab_manager,
1135 num_src_allocator_blocks);
1136 R_TRY(src_allocator_result);
1137
1138 // Create an update allocator for the destination.
1139 Result dst_allocator_result;
1140 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1141 m_memory_block_slab_manager,
1142 num_dst_allocator_blocks);
1143 R_TRY(dst_allocator_result);
1144
1145 // Map the code memory.
1146 {
1147 // Determine the number of pages being operated on.
1148 const size_t num_pages = size / PageSize;
1149
1150 // Create page groups for the memory being unmapped.
1151 KPageGroup pg(m_kernel, m_block_info_manager);
1152
1153 // Create the page group representing the source.
1154 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1155
1156 // We're going to perform an update, so create a helper.
1157 KScopedPageTableUpdater updater(this);
1158
1159 // Reprotect the source as kernel-read/not mapped.
1160 const KMemoryPermission new_perm = static_cast<KMemoryPermission>(
1161 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1162 const KPageProperties src_properties = {new_perm, false, false,
1163 DisableMergeAttribute::DisableHeadBodyTail};
1164 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
1165 OperationType::ChangePermissions, false));
1166
1167 // Ensure that we unprotect the source pages on failure.
1168 ON_RESULT_FAILURE {
1169 const KPageProperties unprotect_properties = {
1170 src_perm, false, false, DisableMergeAttribute::EnableHeadBodyTail};
1171 R_ASSERT(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false,
1172 unprotect_properties, OperationType::ChangePermissions, true));
1173 };
1174
1175 // Map the alias pages.
1176 const KPageProperties dst_properties = {new_perm, false, false,
1177 DisableMergeAttribute::DisableHead};
1178 R_TRY(
1179 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
1180
1181 // Apply the memory block updates.
1182 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1183 src_state, new_perm, KMemoryAttribute::Locked,
1184 KMemoryBlockDisableMergeAttribute::Locked,
1185 KMemoryBlockDisableMergeAttribute::None);
1186 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
1187 KMemoryState::AliasCode, new_perm, KMemoryAttribute::None,
1188 KMemoryBlockDisableMergeAttribute::Normal,
1189 KMemoryBlockDisableMergeAttribute::None);
1190 }
1191
1192 R_SUCCEED();
1193}
1194
1195Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
1196 size_t size) {
1197 // Validate the mapping request.
1198 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
1199 ResultInvalidMemoryRegion);
1200
1201 // Lock the table.
1202 KScopedLightLock lk(m_general_lock);
1203
1204 // Verify that the source memory is locked normal heap.
1205 size_t num_src_allocator_blocks;
1206 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
1207 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
1208 KMemoryPermission::None, KMemoryAttribute::All,
1209 KMemoryAttribute::Locked));
1210
1211 // Verify that the destination memory is aliasable code.
1212 size_t num_dst_allocator_blocks;
1213 R_TRY(this->CheckMemoryStateContiguous(
1214 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
1215 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
1216 KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
1217
1218 // Determine whether any pages being unmapped are code.
1219 bool any_code_pages = false;
1220 {
1221 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address);
1222 while (true) {
1223 // Get the memory info.
1224 const KMemoryInfo info = it->GetMemoryInfo();
1225
1226 // Check if the memory has code flag.
1227 if (True(info.GetState() & KMemoryState::FlagCode)) {
1228 any_code_pages = true;
1229 break;
1230 }
1231
1232 // Check if we're done.
1233 if (dst_address + size - 1 <= info.GetLastAddress()) {
1234 break;
1235 }
1236
1237 // Advance.
1238 ++it;
1239 }
1240 }
1241
1242 // Ensure that we maintain the instruction cache.
1243 bool reprotected_pages = false;
1244 SCOPE_EXIT({
1245 if (reprotected_pages && any_code_pages) {
1246 InvalidateInstructionCache(m_system, dst_address, size);
1247 }
1248 });
1249
1250 // Unmap.
1251 {
1252 // Determine the number of pages being operated on.
1253 const size_t num_pages = size / PageSize;
1254
1255 // Create page groups for the memory being unmapped.
1256 KPageGroup pg(m_kernel, m_block_info_manager);
1257
1258 // Create the page group representing the destination.
1259 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1260
1261 // Verify that the page group contains the same pages as the source.
1262 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1263
1264 // Create an update allocator for the source.
1265 Result src_allocator_result;
1266 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1267 m_memory_block_slab_manager,
1268 num_src_allocator_blocks);
1269 R_TRY(src_allocator_result);
1270
1271 // Create an update allocator for the destination.
1272 Result dst_allocator_result;
1273 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1274 m_memory_block_slab_manager,
1275 num_dst_allocator_blocks);
1276 R_TRY(dst_allocator_result);
1277
1278 // We're going to perform an update, so create a helper.
1279 KScopedPageTableUpdater updater(this);
1280
1281 // Unmap the aliased copy of the pages.
1282 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
1283 DisableMergeAttribute::None};
1284 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false,
1285 dst_unmap_properties, OperationType::Unmap, false));
1286
1287 // Ensure that we re-map the aliased pages on failure.
1288 ON_RESULT_FAILURE {
1289 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
1290 };
1291
1292 // Try to set the permissions for the source pages back to what they should be.
1293 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
1294 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
1295 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
1296 OperationType::ChangePermissions, false));
1297
1298 // Apply the memory block updates.
1299 m_memory_block_manager.Update(
1300 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
1301 KMemoryPermission::None, KMemoryAttribute::None,
1302 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
1303 m_memory_block_manager.Update(
1304 std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal,
1305 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1306 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
1307
1308 // Note that we reprotected pages.
1309 reprotected_pages = true;
1310 }
1311
1312 R_SUCCEED();
1313}
1314
1315Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
1316 // Get the insecure memory resource limit and pool.
1317 auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
1318 const auto insecure_pool =
1319 static_cast<KMemoryManager::Pool>(KSystemControl::GetInsecureMemoryPool());
1320
1321 // Reserve the insecure memory.
1322 // NOTE: ResultOutOfMemory is returned here instead of the usual LimitReached.
1323 KScopedResourceReservation memory_reservation(insecure_resource_limit,
1324 Svc::LimitableResource::PhysicalMemoryMax, size);
1325 R_UNLESS(memory_reservation.Succeeded(), ResultOutOfMemory);
1326
1327 // Allocate pages for the insecure memory.
1328 KPageGroup pg(m_kernel, m_block_info_manager);
1329 R_TRY(m_kernel.MemoryManager().AllocateAndOpen(
1330 std::addressof(pg), size / PageSize,
1331 KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront)));
1332
1333 // Close the opened pages when we're done with them.
1334 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
1335 // automatically.
1336 SCOPE_EXIT({ pg.Close(); });
1337
1338 // Clear all the newly allocated pages.
1339 for (const auto& it : pg) {
1340 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
1341 static_cast<u32>(m_heap_fill_value), it.GetSize());
1342 }
1343
1344 // Lock the table.
1345 KScopedLightLock lk(m_general_lock);
1346
1347 // Validate that the address's state is valid.
1348 size_t num_allocator_blocks;
1349 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1350 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1351 KMemoryPermission::None, KMemoryAttribute::None,
1352 KMemoryAttribute::None));
1353
1354 // Create an update allocator.
1355 Result allocator_result;
1356 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1357 m_memory_block_slab_manager, num_allocator_blocks);
1358 R_TRY(allocator_result);
1359
1360 // We're going to perform an update, so create a helper.
1361 KScopedPageTableUpdater updater(this);
1362
1363 // Map the pages.
1364 const size_t num_pages = size / PageSize;
1365 const KPageProperties map_properties = {KMemoryPermission::UserReadWrite, false, false,
1366 DisableMergeAttribute::DisableHead};
1367 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, pg, map_properties,
1368 OperationType::MapGroup, false));
1369
1370 // Apply the memory block update.
1371 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages,
1372 KMemoryState::Insecure, KMemoryPermission::UserReadWrite,
1373 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
1374 KMemoryBlockDisableMergeAttribute::None);
1375
1376 // Update our mapped insecure size.
1377 m_mapped_insecure_memory += size;
1378
1379 // Commit the memory reservation.
1380 memory_reservation.Commit();
1381
1382 // We succeeded.
1383 R_SUCCEED();
1384}
1385
1386Result KPageTableBase::UnmapInsecureMemory(KProcessAddress address, size_t size) {
1387 // Lock the table.
1388 KScopedLightLock lk(m_general_lock);
1389
1390 // Check the memory state.
1391 size_t num_allocator_blocks;
1392 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1393 KMemoryState::All, KMemoryState::Insecure, KMemoryPermission::All,
1394 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1395 KMemoryAttribute::None));
1396
1397 // Create an update allocator.
1398 Result allocator_result;
1399 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1400 m_memory_block_slab_manager, num_allocator_blocks);
1401 R_TRY(allocator_result);
1402
1403 // We're going to perform an update, so create a helper.
1404 KScopedPageTableUpdater updater(this);
1405
1406 // Unmap the memory.
1407 const size_t num_pages = size / PageSize;
1408 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
1409 DisableMergeAttribute::None};
1410 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, unmap_properties,
1411 OperationType::Unmap, false));
1412
1413 // Apply the memory block update.
1414 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
1415 KMemoryPermission::None, KMemoryAttribute::None,
1416 KMemoryBlockDisableMergeAttribute::None,
1417 KMemoryBlockDisableMergeAttribute::Normal);
1418
1419 // Update our mapped insecure size.
1420 m_mapped_insecure_memory -= size;
1421
1422 // Release the insecure memory from the insecure limit.
1423 if (auto* const insecure_resource_limit =
1424 KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
1425 insecure_resource_limit != nullptr) {
1426 insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, size);
1427 }
1428
1429 R_SUCCEED();
1430}
1431
1432KProcessAddress KPageTableBase::FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
1433 size_t num_pages, size_t alignment, size_t offset,
1434 size_t guard_pages) const {
1435 KProcessAddress address = 0;
1436
1437 if (num_pages <= region_num_pages) {
1438 if (this->IsAslrEnabled()) {
1439 // Try to directly find a free area up to 8 times.
1440 for (size_t i = 0; i < 8; i++) {
1441 const size_t random_offset =
1442 KSystemControl::GenerateRandomRange(
1443 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
1444 alignment;
1445 const KProcessAddress candidate =
1446 Common::AlignDown(GetInteger(region_start + random_offset), alignment) + offset;
1447
1448 KMemoryInfo info;
1449 Svc::PageInfo page_info;
1450 R_ASSERT(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info),
1451 candidate));
1452
1453 if (info.m_state != KMemoryState::Free) {
1454 continue;
1455 }
1456 if (!(region_start <= candidate)) {
1457 continue;
1458 }
1459 if (!(info.GetAddress() + guard_pages * PageSize <= GetInteger(candidate))) {
1460 continue;
1461 }
1462 if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <=
1463 info.GetLastAddress())) {
1464 continue;
1465 }
1466 if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <=
1467 region_start + region_num_pages * PageSize - 1)) {
1468 continue;
1469 }
1470
1471 address = candidate;
1472 break;
1473 }
1474 // Fall back to finding the first free area with a random offset.
1475 if (address == 0) {
1476 // NOTE: Nintendo does not account for guard pages here.
1477 // This may theoretically cause an offset to be chosen that cannot be mapped.
1478 // We will account for guard pages.
1479 const size_t offset_pages = KSystemControl::GenerateRandomRange(
1480 0, region_num_pages - num_pages - guard_pages);
1481 address = m_memory_block_manager.FindFreeArea(
1482 region_start + offset_pages * PageSize, region_num_pages - offset_pages,
1483 num_pages, alignment, offset, guard_pages);
1484 }
1485 }
1486 // Find the first free area.
1487 if (address == 0) {
1488 address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages,
1489 alignment, offset, guard_pages);
1490 }
1491 }
1492
1493 return address;
1494}
1495
1496size_t KPageTableBase::GetSize(KMemoryState state) const {
1497 // Lock the table.
1498 KScopedLightLock lk(m_general_lock);
1499
1500 // Iterate, counting blocks with the desired state.
1501 size_t total_size = 0;
1502 for (KMemoryBlockManager::const_iterator it =
1503 m_memory_block_manager.FindIterator(m_address_space_start);
1504 it != m_memory_block_manager.end(); ++it) {
1505 // Get the memory info.
1506 const KMemoryInfo info = it->GetMemoryInfo();
1507 if (info.GetState() == state) {
1508 total_size += info.GetSize();
1509 }
1510 }
1511
1512 return total_size;
1513}
1514
1515size_t KPageTableBase::GetCodeSize() const {
1516 return this->GetSize(KMemoryState::Code);
1517}
1518
1519size_t KPageTableBase::GetCodeDataSize() const {
1520 return this->GetSize(KMemoryState::CodeData);
1521}
1522
1523size_t KPageTableBase::GetAliasCodeSize() const {
1524 return this->GetSize(KMemoryState::AliasCode);
1525}
1526
1527size_t KPageTableBase::GetAliasCodeDataSize() const {
1528 return this->GetSize(KMemoryState::AliasCodeData);
1529}
1530
1531Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
1532 size_t num_pages, KMemoryPermission perm) {
1533 ASSERT(this->IsLockedByCurrentThread());
1534
1535 // Create a page group to hold the pages we allocate.
1536 KPageGroup pg(m_kernel, m_block_info_manager);
1537
1538 // Allocate the pages.
1539 R_TRY(
1540 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
1541
1542 // Ensure that the page group is closed when we're done working with it.
1543 SCOPE_EXIT({ pg.Close(); });
1544
1545 // Clear all pages.
1546 for (const auto& it : pg) {
1547 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
1548 static_cast<u32>(m_heap_fill_value), it.GetSize());
1549 }
1550
1551 // Map the pages.
1552 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::None};
1553 R_RETURN(this->Operate(page_list, address, num_pages, pg, properties, OperationType::MapGroup,
1554 false));
1555}
1556
1557Result KPageTableBase::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
1558 const KPageGroup& pg, const KPageProperties properties,
1559 bool reuse_ll) {
1560 ASSERT(this->IsLockedByCurrentThread());
1561
1562 // Note the current address, so that we can iterate.
1563 const KProcessAddress start_address = address;
1564 KProcessAddress cur_address = address;
1565
1566 // Ensure that we clean up on failure.
1567 ON_RESULT_FAILURE {
1568 ASSERT(!reuse_ll);
1569 if (cur_address != start_address) {
1570 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
1571 DisableMergeAttribute::None};
1572 R_ASSERT(this->Operate(page_list, start_address,
1573 (cur_address - start_address) / PageSize, 0, false,
1574 unmap_properties, OperationType::Unmap, true));
1575 }
1576 };
1577
1578 // Iterate, mapping all pages in the group.
1579 for (const auto& block : pg) {
1580 // Map and advance.
1581 const KPageProperties cur_properties =
1582 (cur_address == start_address)
1583 ? properties
1584 : KPageProperties{properties.perm, properties.io, properties.uncached,
1585 DisableMergeAttribute::None};
1586 R_TRY(this->Operate(page_list, cur_address, block.GetNumPages(), block.GetAddress(), true,
1587 cur_properties, OperationType::Map, reuse_ll));
1588 cur_address += block.GetSize();
1589 }
1590
1591 // We succeeded!
1592 R_SUCCEED();
1593}
1594
1595void KPageTableBase::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
1596 const KPageGroup& pg) {
1597 ASSERT(this->IsLockedByCurrentThread());
1598
1599 // Note the current address, so that we can iterate.
1600 const KProcessAddress start_address = address;
1601 const KProcessAddress last_address = start_address + size - 1;
1602 const KProcessAddress end_address = last_address + 1;
1603
1604 // Iterate over the memory.
1605 auto pg_it = pg.begin();
1606 ASSERT(pg_it != pg.end());
1607
1608 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
1609 size_t pg_pages = pg_it->GetNumPages();
1610
1611 auto it = m_memory_block_manager.FindIterator(start_address);
1612 while (true) {
1613 // Check that the iterator is valid.
1614 ASSERT(it != m_memory_block_manager.end());
1615
1616 // Get the memory info.
1617 const KMemoryInfo info = it->GetMemoryInfo();
1618
1619 // Determine the range to map.
1620 KProcessAddress map_address = std::max<u64>(info.GetAddress(), GetInteger(start_address));
1621 const KProcessAddress map_end_address =
1622 std::min<u64>(info.GetEndAddress(), GetInteger(end_address));
1623 ASSERT(map_end_address != map_address);
1624
1625 // Determine if we should disable head merge.
1626 const bool disable_head_merge =
1627 info.GetAddress() >= GetInteger(start_address) &&
1628 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
1629 const KPageProperties map_properties = {
1630 info.GetPermission(), false, false,
1631 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
1632
1633 // While we have pages to map, map them.
1634 size_t map_pages = (map_end_address - map_address) / PageSize;
1635 while (map_pages > 0) {
1636 // Check if we're at the end of the physical block.
1637 if (pg_pages == 0) {
1638 // Ensure there are more pages to map.
1639 ASSERT(pg_it != pg.end());
1640
1641 // Advance our physical block.
1642 ++pg_it;
1643 pg_phys_addr = pg_it->GetAddress();
1644 pg_pages = pg_it->GetNumPages();
1645 }
1646
1647 // Map whatever we can.
1648 const size_t cur_pages = std::min(pg_pages, map_pages);
1649 R_ASSERT(this->Operate(page_list, map_address, map_pages, pg_phys_addr, true,
1650 map_properties, OperationType::Map, true));
1651
1652 // Advance.
1653 map_address += cur_pages * PageSize;
1654 map_pages -= cur_pages;
1655
1656 pg_phys_addr += cur_pages * PageSize;
1657 pg_pages -= cur_pages;
1658 }
1659
1660 // Check if we're done.
1661 if (last_address <= info.GetLastAddress()) {
1662 break;
1663 }
1664
1665 // Advance.
1666 ++it;
1667 }
1668
1669 // Check that we re-mapped precisely the page group.
1670 ASSERT((++pg_it) == pg.end());
1671}
1672
1673Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) {
1674 ASSERT(this->IsLockedByCurrentThread());
1675
1676 const size_t size = num_pages * PageSize;
1677
1678 // We're making a new group, not adding to an existing one.
1679 R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
1680
1681 auto& impl = this->GetImpl();
1682
1683 // Begin traversal.
1684 TraversalContext context;
1685 TraversalEntry next_entry;
1686 R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
1687 ResultInvalidCurrentMemory);
1688
1689 // Prepare tracking variables.
1690 KPhysicalAddress cur_addr = next_entry.phys_addr;
1691 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
1692 size_t tot_size = cur_size;
1693
1694 // Iterate, adding to group as we go.
1695 while (tot_size < size) {
1696 R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
1697 ResultInvalidCurrentMemory);
1698
1699 if (next_entry.phys_addr != (cur_addr + cur_size)) {
1700 const size_t cur_pages = cur_size / PageSize;
1701
1702 R_UNLESS(IsHeapPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
1703 R_TRY(pg.AddBlock(cur_addr, cur_pages));
1704
1705 cur_addr = next_entry.phys_addr;
1706 cur_size = next_entry.block_size;
1707 } else {
1708 cur_size += next_entry.block_size;
1709 }
1710
1711 tot_size += next_entry.block_size;
1712 }
1713
1714 // Ensure we add the right amount for the last block.
1715 if (tot_size > size) {
1716 cur_size -= (tot_size - size);
1717 }
1718
1719 // add the last block.
1720 const size_t cur_pages = cur_size / PageSize;
1721 R_UNLESS(IsHeapPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
1722 R_TRY(pg.AddBlock(cur_addr, cur_pages));
1723
1724 R_SUCCEED();
1725}
1726
1727bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr,
1728 size_t num_pages) {
1729 ASSERT(this->IsLockedByCurrentThread());
1730
1731 const size_t size = num_pages * PageSize;
1732
1733 // Empty groups are necessarily invalid.
1734 if (pg.empty()) {
1735 return false;
1736 }
1737
1738 auto& impl = this->GetImpl();
1739
1740 // We're going to validate that the group we'd expect is the group we see.
1741 auto cur_it = pg.begin();
1742 KPhysicalAddress cur_block_address = cur_it->GetAddress();
1743 size_t cur_block_pages = cur_it->GetNumPages();
1744
1745 auto UpdateCurrentIterator = [&]() {
1746 if (cur_block_pages == 0) {
1747 if ((++cur_it) == pg.end()) {
1748 return false;
1749 }
1750
1751 cur_block_address = cur_it->GetAddress();
1752 cur_block_pages = cur_it->GetNumPages();
1753 }
1754 return true;
1755 };
1756
1757 // Begin traversal.
1758 TraversalContext context;
1759 TraversalEntry next_entry;
1760 if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
1761 return false;
1762 }
1763
1764 // Prepare tracking variables.
1765 KPhysicalAddress cur_addr = next_entry.phys_addr;
1766 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
1767 size_t tot_size = cur_size;
1768
1769 // Iterate, comparing expected to actual.
1770 while (tot_size < size) {
1771 if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
1772 return false;
1773 }
1774
1775 if (next_entry.phys_addr != (cur_addr + cur_size)) {
1776 const size_t cur_pages = cur_size / PageSize;
1777
1778 if (!IsHeapPhysicalAddress(cur_addr)) {
1779 return false;
1780 }
1781
1782 if (!UpdateCurrentIterator()) {
1783 return false;
1784 }
1785
1786 if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
1787 return false;
1788 }
1789
1790 cur_block_address += cur_size;
1791 cur_block_pages -= cur_pages;
1792 cur_addr = next_entry.phys_addr;
1793 cur_size = next_entry.block_size;
1794 } else {
1795 cur_size += next_entry.block_size;
1796 }
1797
1798 tot_size += next_entry.block_size;
1799 }
1800
1801 // Ensure we compare the right amount for the last block.
1802 if (tot_size > size) {
1803 cur_size -= (tot_size - size);
1804 }
1805
1806 if (!IsHeapPhysicalAddress(cur_addr)) {
1807 return false;
1808 }
1809
1810 if (!UpdateCurrentIterator()) {
1811 return false;
1812 }
1813
1814 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
1815}
1816
1817Result KPageTableBase::GetContiguousMemoryRangeWithState(
1818 MemoryRange* out, KProcessAddress address, size_t size, KMemoryState state_mask,
1819 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
1820 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1821 ASSERT(this->IsLockedByCurrentThread());
1822
1823 auto& impl = this->GetImpl();
1824
1825 // Begin a traversal.
1826 TraversalContext context;
1827 TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
1828 R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
1829 ResultInvalidCurrentMemory);
1830
1831 // Traverse until we have enough size or we aren't contiguous any more.
1832 const KPhysicalAddress phys_address = cur_entry.phys_addr;
1833 size_t contig_size;
1834 for (contig_size =
1835 cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
1836 contig_size < size; contig_size += cur_entry.block_size) {
1837 if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
1838 break;
1839 }
1840 if (cur_entry.phys_addr != phys_address + contig_size) {
1841 break;
1842 }
1843 }
1844
1845 // Take the minimum size for our region.
1846 size = std::min(size, contig_size);
1847
1848 // Check that the memory is contiguous (modulo the reference count bit).
1849 const KMemoryState test_state_mask = state_mask | KMemoryState::FlagReferenceCounted;
1850 const bool is_heap = R_SUCCEEDED(this->CheckMemoryStateContiguous(
1851 address, size, test_state_mask, state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1852 attr_mask, attr));
1853 if (!is_heap) {
1854 R_TRY(this->CheckMemoryStateContiguous(address, size, test_state_mask, state, perm_mask,
1855 perm, attr_mask, attr));
1856 }
1857
1858 // The memory is contiguous, so set the output range.
1859 out->Set(phys_address, size, is_heap);
1860 R_SUCCEED();
1861}
1862
1863Result KPageTableBase::SetMemoryPermission(KProcessAddress addr, size_t size,
1864 Svc::MemoryPermission svc_perm) {
1865 const size_t num_pages = size / PageSize;
1866
1867 // Lock the table.
1868 KScopedLightLock lk(m_general_lock);
1869
1870 // Verify we can change the memory permission.
1871 KMemoryState old_state;
1872 KMemoryPermission old_perm;
1873 size_t num_allocator_blocks;
1874 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
1875 std::addressof(num_allocator_blocks), addr, size,
1876 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect,
1877 KMemoryPermission::None, KMemoryPermission::None,
1878 KMemoryAttribute::All, KMemoryAttribute::None));
1879
1880 // Determine new perm.
1881 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
1882 R_SUCCEED_IF(old_perm == new_perm);
1883
1884 // Create an update allocator.
1885 Result allocator_result;
1886 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1887 m_memory_block_slab_manager, num_allocator_blocks);
1888 R_TRY(allocator_result);
1889
1890 // We're going to perform an update, so create a helper.
1891 KScopedPageTableUpdater updater(this);
1892
1893 // Perform mapping operation.
1894 const KPageProperties properties = {new_perm, false, false, DisableMergeAttribute::None};
1895 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
1896 OperationType::ChangePermissions, false));
1897
1898 // Update the blocks.
1899 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
1900 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1901 KMemoryBlockDisableMergeAttribute::None);
1902
1903 R_SUCCEED();
1904}
1905
1906Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t size,
1907 Svc::MemoryPermission svc_perm) {
1908 const size_t num_pages = size / PageSize;
1909
1910 // Lock the table.
1911 KScopedLightLock lk(m_general_lock);
1912
1913 // Verify we can change the memory permission.
1914 KMemoryState old_state;
1915 KMemoryPermission old_perm;
1916 size_t num_allocator_blocks;
1917 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
1918 std::addressof(num_allocator_blocks), addr, size,
1919 KMemoryState::FlagCode, KMemoryState::FlagCode,
1920 KMemoryPermission::None, KMemoryPermission::None,
1921 KMemoryAttribute::All, KMemoryAttribute::None));
1922
1923 // Make a new page group for the region.
1924 KPageGroup pg(m_kernel, m_block_info_manager);
1925
1926 // Determine new perm/state.
1927 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
1928 KMemoryState new_state = old_state;
1929 const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite;
1930 const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
1931 const bool was_x =
1932 (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
1933 ASSERT(!(is_w && is_x));
1934
1935 if (is_w) {
1936 switch (old_state) {
1937 case KMemoryState::Code:
1938 new_state = KMemoryState::CodeData;
1939 break;
1940 case KMemoryState::AliasCode:
1941 new_state = KMemoryState::AliasCodeData;
1942 break;
1943 default:
1944 UNREACHABLE();
1945 }
1946 }
1947
1948 // Create a page group, if we're setting execute permissions.
1949 if (is_x) {
1950 R_TRY(this->MakePageGroup(pg, GetInteger(addr), num_pages));
1951 }
1952
1953 // Succeed if there's nothing to do.
1954 R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
1955
1956 // Create an update allocator.
1957 Result allocator_result;
1958 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1959 m_memory_block_slab_manager, num_allocator_blocks);
1960 R_TRY(allocator_result);
1961
1962 // We're going to perform an update, so create a helper.
1963 KScopedPageTableUpdater updater(this);
1964
1965 // Perform mapping operation.
1966 const KPageProperties properties = {new_perm, false, false, DisableMergeAttribute::None};
1967 const auto operation = was_x ? OperationType::ChangePermissionsAndRefreshAndFlush
1968 : OperationType::ChangePermissions;
1969 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, operation,
1970 false));
1971
1972 // Update the blocks.
1973 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm,
1974 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1975 KMemoryBlockDisableMergeAttribute::None);
1976
1977 // Ensure cache coherency, if we're setting pages as executable.
1978 if (is_x) {
1979 for (const auto& block : pg) {
1980 StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize());
1981 }
1982 InvalidateInstructionCache(m_system, addr, size);
1983 }
1984
1985 R_SUCCEED();
1986}
1987
1988Result KPageTableBase::SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
1989 KMemoryAttribute attr) {
1990 const size_t num_pages = size / PageSize;
1991 ASSERT((mask | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask);
1992
1993 // Lock the table.
1994 KScopedLightLock lk(m_general_lock);
1995
1996 // Verify we can change the memory attribute.
1997 KMemoryState old_state;
1998 KMemoryPermission old_perm;
1999 KMemoryAttribute old_attr;
2000 size_t num_allocator_blocks;
2001 constexpr KMemoryAttribute AttributeTestMask =
2002 ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
2003 const KMemoryState state_test_mask =
2004 (True(mask & KMemoryAttribute::Uncached) ? KMemoryState::FlagCanChangeAttribute
2005 : KMemoryState::None) |
2006 (True(mask & KMemoryAttribute::PermissionLocked) ? KMemoryState::FlagCanPermissionLock
2007 : KMemoryState::None);
2008 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2009 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2010 addr, size, state_test_mask, state_test_mask,
2011 KMemoryPermission::None, KMemoryPermission::None,
2012 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
2013
2014 // Create an update allocator.
2015 Result allocator_result;
2016 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2017 m_memory_block_slab_manager, num_allocator_blocks);
2018 R_TRY(allocator_result);
2019
2020 // We're going to perform an update, so create a helper.
2021 KScopedPageTableUpdater updater(this);
2022
2023 // If we need to, perform a change attribute operation.
2024 if (True(mask & KMemoryAttribute::Uncached)) {
2025 // Determine the new attribute.
2026 const KMemoryAttribute new_attr =
2027 static_cast<KMemoryAttribute>(((old_attr & ~mask) | (attr & mask)));
2028
2029 // Perform operation.
2030 const KPageProperties properties = {old_perm, false,
2031 True(new_attr & KMemoryAttribute::Uncached),
2032 DisableMergeAttribute::None};
2033 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
2034 OperationType::ChangePermissionsAndRefreshAndFlush, false));
2035 }
2036
2037 // Update the blocks.
2038 m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages, mask, attr);
2039
2040 R_SUCCEED();
2041}
2042
2043Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
2044 // Lock the physical memory mutex.
2045 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
2046
2047 // Try to perform a reduction in heap, instead of an extension.
2048 KProcessAddress cur_address;
2049 size_t allocation_size;
2050 {
2051 // Lock the table.
2052 KScopedLightLock lk(m_general_lock);
2053
2054 // Validate that setting heap size is possible at all.
2055 R_UNLESS(!m_is_kernel, ResultOutOfMemory);
2056 R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start),
2057 ResultOutOfMemory);
2058 R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory);
2059
2060 if (size < static_cast<size_t>(m_current_heap_end - m_heap_region_start)) {
2061 // The size being requested is less than the current size, so we need to free the end of
2062 // the heap.
2063
2064 // Validate memory state.
2065 size_t num_allocator_blocks;
2066 R_TRY(this->CheckMemoryState(
2067 std::addressof(num_allocator_blocks), m_heap_region_start + size,
2068 (m_current_heap_end - m_heap_region_start) - size, KMemoryState::All,
2069 KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
2070 KMemoryAttribute::All, KMemoryAttribute::None));
2071
2072 // Create an update allocator.
2073 Result allocator_result;
2074 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2075 m_memory_block_slab_manager,
2076 num_allocator_blocks);
2077 R_TRY(allocator_result);
2078
2079 // We're going to perform an update, so create a helper.
2080 KScopedPageTableUpdater updater(this);
2081
2082 // Unmap the end of the heap.
2083 const size_t num_pages = ((m_current_heap_end - m_heap_region_start) - size) / PageSize;
2084 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2085 DisableMergeAttribute::None};
2086 R_TRY(this->Operate(updater.GetPageList(), m_heap_region_start + size, num_pages, 0,
2087 false, unmap_properties, OperationType::Unmap, false));
2088
2089 // Release the memory from the resource limit.
2090 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
2091 num_pages * PageSize);
2092
2093 // Apply the memory block update.
2094 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
2095 num_pages, KMemoryState::Free, KMemoryPermission::None,
2096 KMemoryAttribute::None,
2097 KMemoryBlockDisableMergeAttribute::None,
2098 size == 0 ? KMemoryBlockDisableMergeAttribute::Normal
2099 : KMemoryBlockDisableMergeAttribute::None);
2100
2101 // Update the current heap end.
2102 m_current_heap_end = m_heap_region_start + size;
2103
2104 // Set the output.
2105 *out = m_heap_region_start;
2106 R_SUCCEED();
2107 } else if (size == static_cast<size_t>(m_current_heap_end - m_heap_region_start)) {
2108 // The size requested is exactly the current size.
2109 *out = m_heap_region_start;
2110 R_SUCCEED();
2111 } else {
2112 // We have to allocate memory. Determine how much to allocate and where while the table
2113 // is locked.
2114 cur_address = m_current_heap_end;
2115 allocation_size = size - (m_current_heap_end - m_heap_region_start);
2116 }
2117 }
2118
2119 // Reserve memory for the heap extension.
2120 KScopedResourceReservation memory_reservation(
2121 m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, allocation_size);
2122 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2123
2124 // Allocate pages for the heap extension.
2125 KPageGroup pg(m_kernel, m_block_info_manager);
2126 R_TRY(m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize,
2127 m_allocate_option));
2128
2129 // Close the opened pages when we're done with them.
2130 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
2131 // automatically.
2132 SCOPE_EXIT({ pg.Close(); });
2133
2134 // Clear all the newly allocated pages.
2135 for (const auto& it : pg) {
2136 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value,
2137 it.GetSize());
2138 }
2139
2140 // Map the pages.
2141 {
2142 // Lock the table.
2143 KScopedLightLock lk(m_general_lock);
2144
2145 // Ensure that the heap hasn't changed since we began executing.
2146 ASSERT(cur_address == m_current_heap_end);
2147
2148 // Check the memory state.
2149 size_t num_allocator_blocks;
2150 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end,
2151 allocation_size, KMemoryState::All, KMemoryState::Free,
2152 KMemoryPermission::None, KMemoryPermission::None,
2153 KMemoryAttribute::None, KMemoryAttribute::None));
2154
2155 // Create an update allocator.
2156 Result allocator_result;
2157 KMemoryBlockManagerUpdateAllocator allocator(
2158 std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
2159 R_TRY(allocator_result);
2160
2161 // We're going to perform an update, so create a helper.
2162 KScopedPageTableUpdater updater(this);
2163
2164 // Map the pages.
2165 const size_t num_pages = allocation_size / PageSize;
2166 const KPageProperties map_properties = {KMemoryPermission::UserReadWrite, false, false,
2167 (m_current_heap_end == m_heap_region_start)
2168 ? DisableMergeAttribute::DisableHead
2169 : DisableMergeAttribute::None};
2170 R_TRY(this->Operate(updater.GetPageList(), m_current_heap_end, num_pages, pg,
2171 map_properties, OperationType::MapGroup, false));
2172
2173 // We succeeded, so commit our memory reservation.
2174 memory_reservation.Commit();
2175
2176 // Apply the memory block update.
2177 m_memory_block_manager.Update(
2178 std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal,
2179 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2180 m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal
2181 : KMemoryBlockDisableMergeAttribute::None,
2182 KMemoryBlockDisableMergeAttribute::None);
2183
2184 // Update the current heap end.
2185 m_current_heap_end = m_heap_region_start + size;
2186
2187 // Set the output.
2188 *out = m_heap_region_start;
2189 R_SUCCEED();
2190 }
2191}
2192
2193Result KPageTableBase::SetMaxHeapSize(size_t size) {
2194 // Lock the table.
2195 KScopedLightLock lk(m_general_lock);
2196
2197 // Only process page tables are allowed to set heap size.
2198 ASSERT(!this->IsKernel());
2199
2200 m_max_heap_size = size;
2201
2202 R_SUCCEED();
2203}
2204
2205Result KPageTableBase::QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
2206 KProcessAddress addr) const {
2207 // If the address is invalid, create a fake block.
2208 if (!this->Contains(addr, 1)) {
2209 *out_info = {
2210 .m_address = GetInteger(m_address_space_end),
2211 .m_size = 0 - GetInteger(m_address_space_end),
2212 .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible),
2213 .m_device_disable_merge_left_count = 0,
2214 .m_device_disable_merge_right_count = 0,
2215 .m_ipc_lock_count = 0,
2216 .m_device_use_count = 0,
2217 .m_ipc_disable_merge_count = 0,
2218 .m_permission = KMemoryPermission::None,
2219 .m_attribute = KMemoryAttribute::None,
2220 .m_original_permission = KMemoryPermission::None,
2221 .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None,
2222 };
2223 out_page_info->flags = 0;
2224
2225 R_SUCCEED();
2226 }
2227
2228 // Otherwise, lock the table and query.
2229 KScopedLightLock lk(m_general_lock);
2230 R_RETURN(this->QueryInfoImpl(out_info, out_page_info, addr));
2231}
2232
2233Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
2234 KProcessAddress address) const {
2235 // Lock the table.
2236 KScopedLightLock lk(m_general_lock);
2237
2238 // Align the address down to page size.
2239 address = Common::AlignDown(GetInteger(address), PageSize);
2240
2241 // Verify that we can query the address.
2242 KMemoryInfo info;
2243 Svc::PageInfo page_info;
2244 R_TRY(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), address));
2245
2246 // Check the memory state.
2247 R_TRY(this->CheckMemoryState(info, KMemoryState::FlagCanQueryPhysical,
2248 KMemoryState::FlagCanQueryPhysical,
2249 KMemoryPermission::UserReadExecute, KMemoryPermission::UserRead,
2250 KMemoryAttribute::None, KMemoryAttribute::None));
2251
2252 // Prepare to traverse.
2253 KPhysicalAddress phys_addr;
2254 size_t phys_size;
2255
2256 KProcessAddress virt_addr = info.GetAddress();
2257 KProcessAddress end_addr = info.GetEndAddress();
2258
2259 // Perform traversal.
2260 {
2261 // Begin traversal.
2262 TraversalContext context;
2263 TraversalEntry next_entry;
2264 bool traverse_valid =
2265 m_impl->BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
2266 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
2267
2268 // Set tracking variables.
2269 phys_addr = next_entry.phys_addr;
2270 phys_size = next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
2271
2272 // Iterate.
2273 while (true) {
2274 // Continue the traversal.
2275 traverse_valid =
2276 m_impl->ContinueTraversal(std::addressof(next_entry), std::addressof(context));
2277 if (!traverse_valid) {
2278 break;
2279 }
2280
2281 if (next_entry.phys_addr != (phys_addr + phys_size)) {
2282 // Check if we're done.
2283 if (virt_addr <= address && address <= virt_addr + phys_size - 1) {
2284 break;
2285 }
2286
2287 // Advance.
2288 phys_addr = next_entry.phys_addr;
2289 virt_addr += next_entry.block_size;
2290 phys_size =
2291 next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
2292 } else {
2293 phys_size += next_entry.block_size;
2294 }
2295
2296 // Check if we're done.
2297 if (end_addr < virt_addr + phys_size) {
2298 break;
2299 }
2300 }
2301 ASSERT(virt_addr <= address && address <= virt_addr + phys_size - 1);
2302
2303 // Ensure we use the right size.
2304 if (end_addr < virt_addr + phys_size) {
2305 phys_size = end_addr - virt_addr;
2306 }
2307 }
2308
2309 // Set the output.
2310 out->physical_address = GetInteger(phys_addr);
2311 out->virtual_address = GetInteger(virt_addr);
2312 out->size = phys_size;
2313 R_SUCCEED();
2314}
2315
2316Result KPageTableBase::MapIoImpl(KProcessAddress* out, PageLinkedList* page_list,
2317 KPhysicalAddress phys_addr, size_t size, KMemoryState state,
2318 KMemoryPermission perm) {
2319 // Check pre-conditions.
2320 ASSERT(this->IsLockedByCurrentThread());
2321 ASSERT(Common::IsAligned(GetInteger(phys_addr), PageSize));
2322 ASSERT(Common::IsAligned(size, PageSize));
2323 ASSERT(size > 0);
2324
2325 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
2326 const size_t num_pages = size / PageSize;
2327 const KPhysicalAddress last = phys_addr + size - 1;
2328
2329 // Get region extents.
2330 const KProcessAddress region_start = m_kernel_map_region_start;
2331 const size_t region_size = m_kernel_map_region_end - m_kernel_map_region_start;
2332 const size_t region_num_pages = region_size / PageSize;
2333
2334 ASSERT(this->CanContain(region_start, region_size, state));
2335
2336 // Locate the memory region.
2337 const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr);
2338 R_UNLESS(region != nullptr, ResultInvalidAddress);
2339
2340 ASSERT(region->Contains(GetInteger(phys_addr)));
2341
2342 // Ensure that the region is mappable.
2343 const bool is_rw = perm == KMemoryPermission::UserReadWrite;
2344 while (true) {
2345 // Check that the region exists.
2346 R_UNLESS(region != nullptr, ResultInvalidAddress);
2347
2348 // Check the region attributes.
2349 R_UNLESS(!region->IsDerivedFrom(KMemoryRegionType_Dram), ResultInvalidAddress);
2350 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw,
2351 ResultInvalidAddress);
2352 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), ResultInvalidAddress);
2353
2354 // Check if we're done.
2355 if (GetInteger(last) <= region->GetLastAddress()) {
2356 break;
2357 }
2358
2359 // Advance.
2360 region = region->GetNext();
2361 };
2362
2363 // Select an address to map at.
2364 KProcessAddress addr = 0;
2365 {
2366 const size_t alignment = 4_KiB;
2367 const KPhysicalAddress aligned_phys =
2368 Common::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1;
2369 R_UNLESS(aligned_phys > phys_addr, ResultInvalidAddress);
2370
2371 const KPhysicalAddress last_aligned_paddr =
2372 Common::AlignDown(GetInteger(last) + 1, alignment) - 1;
2373 R_UNLESS((last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr),
2374 ResultInvalidAddress);
2375
2376 addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
2377 this->GetNumGuardPages());
2378 R_UNLESS(addr != 0, ResultOutOfMemory);
2379 }
2380
2381 // Check that we can map IO here.
2382 ASSERT(this->CanContain(addr, size, state));
2383 R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
2384 KMemoryPermission::None, KMemoryPermission::None,
2385 KMemoryAttribute::None, KMemoryAttribute::None));
2386
2387 // Perform mapping operation.
2388 const KPageProperties properties = {perm, state == KMemoryState::IoRegister, false,
2389 DisableMergeAttribute::DisableHead};
2390 R_TRY(this->Operate(page_list, addr, num_pages, phys_addr, true, properties, OperationType::Map,
2391 false));
2392
2393 // Set the output address.
2394 *out = addr;
2395
2396 R_SUCCEED();
2397}
2398
2399Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
2400 // Lock the table.
2401 KScopedLightLock lk(m_general_lock);
2402
2403 // Create an update allocator.
2404 Result allocator_result;
2405 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2406 m_memory_block_slab_manager);
2407 R_TRY(allocator_result);
2408
2409 // We're going to perform an update, so create a helper.
2410 KScopedPageTableUpdater updater(this);
2411
2412 // Map the io memory.
2413 KProcessAddress addr;
2414 R_TRY(this->MapIoImpl(std::addressof(addr), updater.GetPageList(), phys_addr, size,
2415 KMemoryState::IoRegister, perm));
2416
2417 // Update the blocks.
2418 m_memory_block_manager.Update(std::addressof(allocator), addr, size / PageSize,
2419 KMemoryState::IoRegister, perm, KMemoryAttribute::Locked,
2420 KMemoryBlockDisableMergeAttribute::Normal,
2421 KMemoryBlockDisableMergeAttribute::None);
2422
2423 // We successfully mapped the pages.
2424 R_SUCCEED();
2425}
2426
2427Result KPageTableBase::MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr,
2428 size_t size, Svc::MemoryMapping mapping,
2429 Svc::MemoryPermission svc_perm) {
2430 const size_t num_pages = size / PageSize;
2431
2432 // Lock the table.
2433 KScopedLightLock lk(m_general_lock);
2434
2435 // Validate the memory state.
2436 size_t num_allocator_blocks;
2437 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size,
2438 KMemoryState::All, KMemoryState::None, KMemoryPermission::None,
2439 KMemoryPermission::None, KMemoryAttribute::None,
2440 KMemoryAttribute::None));
2441
2442 // Create an update allocator.
2443 Result allocator_result;
2444 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2445 m_memory_block_slab_manager, num_allocator_blocks);
2446 R_TRY(allocator_result);
2447
2448 // We're going to perform an update, so create a helper.
2449 KScopedPageTableUpdater updater(this);
2450
2451 // Perform mapping operation.
2452 const KMemoryPermission perm = ConvertToKMemoryPermission(svc_perm);
2453 const KPageProperties properties = {perm, mapping == Svc::MemoryMapping::IoRegister,
2454 mapping == Svc::MemoryMapping::Uncached,
2455 DisableMergeAttribute::DisableHead};
2456 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, phys_addr, true, properties,
2457 OperationType::Map, false));
2458
2459 // Update the blocks.
2460 const auto state =
2461 mapping == Svc::MemoryMapping::Memory ? KMemoryState::IoMemory : KMemoryState::IoRegister;
2462 m_memory_block_manager.Update(
2463 std::addressof(allocator), dst_address, num_pages, state, perm, KMemoryAttribute::Locked,
2464 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
2465
2466 // We successfully mapped the pages.
2467 R_SUCCEED();
2468}
2469
2470Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr,
2471 size_t size, Svc::MemoryMapping mapping) {
2472 const size_t num_pages = size / PageSize;
2473
2474 // Lock the table.
2475 KScopedLightLock lk(m_general_lock);
2476
2477 // Validate the memory state.
2478 KMemoryState old_state;
2479 KMemoryPermission old_perm;
2480 KMemoryAttribute old_attr;
2481 size_t num_allocator_blocks;
2482 R_TRY(this->CheckMemoryState(
2483 std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
2484 std::addressof(num_allocator_blocks), dst_address, size, KMemoryState::All,
2485 mapping == Svc::MemoryMapping::Memory ? KMemoryState::IoMemory : KMemoryState::IoRegister,
2486 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
2487 KMemoryAttribute::Locked));
2488
2489 // Validate that the region being unmapped corresponds to the physical range described.
2490 {
2491 // Get the impl.
2492 auto& impl = this->GetImpl();
2493
2494 // Begin traversal.
2495 TraversalContext context;
2496 TraversalEntry next_entry;
2497 ASSERT(
2498 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
2499
2500 // Check that the physical region matches.
2501 R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
2502
2503 // Iterate.
2504 for (size_t checked_size =
2505 next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
2506 checked_size < size; checked_size += next_entry.block_size) {
2507 // Continue the traversal.
2508 ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
2509
2510 // Check that the physical region matches.
2511 R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
2512 }
2513 }
2514
2515 // Create an update allocator.
2516 Result allocator_result;
2517 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2518 m_memory_block_slab_manager, num_allocator_blocks);
2519 R_TRY(allocator_result);
2520
2521 // We're going to perform an update, so create a helper.
2522 KScopedPageTableUpdater updater(this);
2523
2524 // If the region being unmapped is Memory, synchronize.
2525 if (mapping == Svc::MemoryMapping::Memory) {
2526 // Change the region to be uncached.
2527 const KPageProperties properties = {old_perm, false, true, DisableMergeAttribute::None};
2528 R_ASSERT(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, properties,
2529 OperationType::ChangePermissionsAndRefresh, false));
2530
2531 // Temporarily unlock ourselves, so that other operations can occur while we flush the
2532 // region.
2533 m_general_lock.Unlock();
2534 SCOPE_EXIT({ m_general_lock.Lock(); });
2535
2536 // Flush the region.
2537 R_ASSERT(FlushDataCache(dst_address, size));
2538 }
2539
2540 // Perform the unmap.
2541 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2542 DisableMergeAttribute::None};
2543 R_ASSERT(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false,
2544 unmap_properties, OperationType::Unmap, false));
2545
2546 // Update the blocks.
2547 m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages,
2548 KMemoryState::Free, KMemoryPermission::None,
2549 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
2550 KMemoryBlockDisableMergeAttribute::Normal);
2551
2552 R_SUCCEED();
2553}
2554
2555Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
2556 ASSERT(Common::IsAligned(GetInteger(phys_addr), PageSize));
2557 ASSERT(Common::IsAligned(size, PageSize));
2558 ASSERT(size > 0);
2559 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
2560 const size_t num_pages = size / PageSize;
2561 const KPhysicalAddress last = phys_addr + size - 1;
2562
2563 // Get region extents.
2564 const KProcessAddress region_start = this->GetRegionAddress(KMemoryState::Static);
2565 const size_t region_size = this->GetRegionSize(KMemoryState::Static);
2566 const size_t region_num_pages = region_size / PageSize;
2567
2568 // Locate the memory region.
2569 const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr);
2570 R_UNLESS(region != nullptr, ResultInvalidAddress);
2571
2572 ASSERT(region->Contains(GetInteger(phys_addr)));
2573 R_UNLESS(GetInteger(last) <= region->GetLastAddress(), ResultInvalidAddress);
2574
2575 // Check the region attributes.
2576 const bool is_rw = perm == KMemoryPermission::UserReadWrite;
2577 R_UNLESS(region->IsDerivedFrom(KMemoryRegionType_Dram), ResultInvalidAddress);
2578 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), ResultInvalidAddress);
2579 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw,
2580 ResultInvalidAddress);
2581
2582 // Lock the table.
2583 KScopedLightLock lk(m_general_lock);
2584
2585 // Select an address to map at.
2586 KProcessAddress addr = 0;
2587 {
2588 const size_t alignment = 4_KiB;
2589 const KPhysicalAddress aligned_phys =
2590 Common::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1;
2591 R_UNLESS(aligned_phys > phys_addr, ResultInvalidAddress);
2592
2593 const KPhysicalAddress last_aligned_paddr =
2594 Common::AlignDown(GetInteger(last) + 1, alignment) - 1;
2595 R_UNLESS((last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr),
2596 ResultInvalidAddress);
2597
2598 addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
2599 this->GetNumGuardPages());
2600 R_UNLESS(addr != 0, ResultOutOfMemory);
2601 }
2602
2603 // Check that we can map static here.
2604 ASSERT(this->CanContain(addr, size, KMemoryState::Static));
2605 R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
2606 KMemoryPermission::None, KMemoryPermission::None,
2607 KMemoryAttribute::None, KMemoryAttribute::None));
2608
2609 // Create an update allocator.
2610 Result allocator_result;
2611 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2612 m_memory_block_slab_manager);
2613 R_TRY(allocator_result);
2614
2615 // We're going to perform an update, so create a helper.
2616 KScopedPageTableUpdater updater(this);
2617
2618 // Perform mapping operation.
2619 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2620 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties,
2621 OperationType::Map, false));
2622
2623 // Update the blocks.
2624 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, KMemoryState::Static,
2625 perm, KMemoryAttribute::None,
2626 KMemoryBlockDisableMergeAttribute::Normal,
2627 KMemoryBlockDisableMergeAttribute::None);
2628
2629 // We successfully mapped the pages.
2630 R_SUCCEED();
2631}
2632
2633Result KPageTableBase::MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
2634 // Get the memory region.
2635 const KMemoryRegion* region =
2636 m_kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type);
2637 R_UNLESS(region != nullptr, ResultOutOfRange);
2638
2639 // Check that the region is valid.
2640 ASSERT(region->GetEndAddress() != 0);
2641
2642 // Map the region.
2643 R_TRY_CATCH(this->MapStatic(region->GetAddress(), region->GetSize(), perm)){
2644 R_CONVERT(ResultInvalidAddress, ResultOutOfRange)} R_END_TRY_CATCH;
2645
2646 R_SUCCEED();
2647}
2648
2649Result KPageTableBase::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2650 KPhysicalAddress phys_addr, bool is_pa_valid,
2651 KProcessAddress region_start, size_t region_num_pages,
2652 KMemoryState state, KMemoryPermission perm) {
2653 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2654
2655 // Ensure this is a valid map request.
2656 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2657 ResultInvalidCurrentMemory);
2658 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2659
2660 // Lock the table.
2661 KScopedLightLock lk(m_general_lock);
2662
2663 // Find a random address to map at.
2664 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2665 0, this->GetNumGuardPages());
2666 R_UNLESS(addr != 0, ResultOutOfMemory);
2667 ASSERT(Common::IsAligned(GetInteger(addr), alignment));
2668 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2669 R_ASSERT(this->CheckMemoryState(
2670 addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2671 KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None));
2672
2673 // Create an update allocator.
2674 Result allocator_result;
2675 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2676 m_memory_block_slab_manager);
2677 R_TRY(allocator_result);
2678
2679 // We're going to perform an update, so create a helper.
2680 KScopedPageTableUpdater updater(this);
2681
2682 // Perform mapping operation.
2683 if (is_pa_valid) {
2684 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2685 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties,
2686 OperationType::Map, false));
2687 } else {
2688 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2689 }
2690
2691 // Update the blocks.
2692 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2693 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2694 KMemoryBlockDisableMergeAttribute::None);
2695
2696 // We successfully mapped the pages.
2697 *out_addr = addr;
2698 R_SUCCEED();
2699}
2700
2701Result KPageTableBase::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2702 KMemoryPermission perm) {
2703 // Check that the map is in range.
2704 const size_t size = num_pages * PageSize;
2705 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2706
2707 // Lock the table.
2708 KScopedLightLock lk(m_general_lock);
2709
2710 // Check the memory state.
2711 size_t num_allocator_blocks;
2712 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2713 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2714 KMemoryPermission::None, KMemoryAttribute::None,
2715 KMemoryAttribute::None));
2716
2717 // Create an update allocator.
2718 Result allocator_result;
2719 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2720 m_memory_block_slab_manager, num_allocator_blocks);
2721 R_TRY(allocator_result);
2722
2723 // We're going to perform an update, so create a helper.
2724 KScopedPageTableUpdater updater(this);
2725
2726 // Map the pages.
2727 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2728
2729 // Update the blocks.
2730 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2731 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2732 KMemoryBlockDisableMergeAttribute::None);
2733
2734 R_SUCCEED();
2735}
2736
2737Result KPageTableBase::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2738 // Check that the unmap is in range.
2739 const size_t size = num_pages * PageSize;
2740 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2741
2742 // Lock the table.
2743 KScopedLightLock lk(m_general_lock);
2744
2745 // Check the memory state.
2746 size_t num_allocator_blocks;
2747 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2748 KMemoryState::All, state, KMemoryPermission::None,
2749 KMemoryPermission::None, KMemoryAttribute::All,
2750 KMemoryAttribute::None));
2751
2752 // Create an update allocator.
2753 Result allocator_result;
2754 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2755 m_memory_block_slab_manager, num_allocator_blocks);
2756 R_TRY(allocator_result);
2757
2758 // We're going to perform an update, so create a helper.
2759 KScopedPageTableUpdater updater(this);
2760
2761 // Perform the unmap.
2762 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2763 DisableMergeAttribute::None};
2764 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, unmap_properties,
2765 OperationType::Unmap, false));
2766
2767 // Update the blocks.
2768 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2769 KMemoryPermission::None, KMemoryAttribute::None,
2770 KMemoryBlockDisableMergeAttribute::None,
2771 KMemoryBlockDisableMergeAttribute::Normal);
2772
2773 R_SUCCEED();
2774}
2775
2776Result KPageTableBase::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2777 KProcessAddress region_start, size_t region_num_pages,
2778 KMemoryState state, KMemoryPermission perm) {
2779 ASSERT(!this->IsLockedByCurrentThread());
2780
2781 // Ensure this is a valid map request.
2782 const size_t num_pages = pg.GetNumPages();
2783 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2784 ResultInvalidCurrentMemory);
2785 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2786
2787 // Lock the table.
2788 KScopedLightLock lk(m_general_lock);
2789
2790 // Find a random address to map at.
2791 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2792 0, this->GetNumGuardPages());
2793 R_UNLESS(addr != 0, ResultOutOfMemory);
2794 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2795 R_ASSERT(this->CheckMemoryState(
2796 addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2797 KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None));
2798
2799 // Create an update allocator.
2800 Result allocator_result;
2801 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2802 m_memory_block_slab_manager);
2803 R_TRY(allocator_result);
2804
2805 // We're going to perform an update, so create a helper.
2806 KScopedPageTableUpdater updater(this);
2807
2808 // Perform mapping operation.
2809 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2810 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2811
2812 // Update the blocks.
2813 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2814 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2815 KMemoryBlockDisableMergeAttribute::None);
2816
2817 // We successfully mapped the pages.
2818 *out_addr = addr;
2819 R_SUCCEED();
2820}
2821
2822Result KPageTableBase::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2823 KMemoryPermission perm) {
2824 ASSERT(!this->IsLockedByCurrentThread());
2825
2826 // Ensure this is a valid map request.
2827 const size_t num_pages = pg.GetNumPages();
2828 const size_t size = num_pages * PageSize;
2829 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2830
2831 // Lock the table.
2832 KScopedLightLock lk(m_general_lock);
2833
2834 // Check if state allows us to map.
2835 size_t num_allocator_blocks;
2836 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2837 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2838 KMemoryPermission::None, KMemoryAttribute::None,
2839 KMemoryAttribute::None));
2840
2841 // Create an update allocator.
2842 Result allocator_result;
2843 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2844 m_memory_block_slab_manager, num_allocator_blocks);
2845 R_TRY(allocator_result);
2846
2847 // We're going to perform an update, so create a helper.
2848 KScopedPageTableUpdater updater(this);
2849
2850 // Perform mapping operation.
2851 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2852 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2853
2854 // Update the blocks.
2855 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2856 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2857 KMemoryBlockDisableMergeAttribute::None);
2858
2859 // We successfully mapped the pages.
2860 R_SUCCEED();
2861}
2862
2863Result KPageTableBase::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2864 KMemoryState state) {
2865 ASSERT(!this->IsLockedByCurrentThread());
2866
2867 // Ensure this is a valid unmap request.
2868 const size_t num_pages = pg.GetNumPages();
2869 const size_t size = num_pages * PageSize;
2870 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2871
2872 // Lock the table.
2873 KScopedLightLock lk(m_general_lock);
2874
2875 // Check if state allows us to unmap.
2876 size_t num_allocator_blocks;
2877 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2878 KMemoryState::All, state, KMemoryPermission::None,
2879 KMemoryPermission::None, KMemoryAttribute::All,
2880 KMemoryAttribute::None));
2881
2882 // Check that the page group is valid.
2883 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2884
2885 // Create an update allocator.
2886 Result allocator_result;
2887 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2888 m_memory_block_slab_manager, num_allocator_blocks);
2889 R_TRY(allocator_result);
2890
2891 // We're going to perform an update, so create a helper.
2892 KScopedPageTableUpdater updater(this);
2893
2894 // Perform unmapping operation.
2895 const KPageProperties properties = {KMemoryPermission::None, false, false,
2896 DisableMergeAttribute::None};
2897 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, properties,
2898 OperationType::Unmap, false));
2899
2900 // Update the blocks.
2901 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2902 KMemoryPermission::None, KMemoryAttribute::None,
2903 KMemoryBlockDisableMergeAttribute::None,
2904 KMemoryBlockDisableMergeAttribute::Normal);
2905
2906 R_SUCCEED();
2907}
2908
2909Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address,
2910 size_t num_pages, KMemoryState state_mask,
2911 KMemoryState state, KMemoryPermission perm_mask,
2912 KMemoryPermission perm, KMemoryAttribute attr_mask,
2913 KMemoryAttribute attr) {
2914 // Ensure that the page group isn't null.
2915 ASSERT(out != nullptr);
2916
2917 // Make sure that the region we're mapping is valid for the table.
2918 const size_t size = num_pages * PageSize;
2919 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2920
2921 // Lock the table.
2922 KScopedLightLock lk(m_general_lock);
2923
2924 // Check if state allows us to create the group.
2925 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
2926 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2927 attr_mask, attr));
2928
2929 // Create a new page group for the region.
2930 R_TRY(this->MakePageGroup(*out, address, num_pages));
2931
2932 // Open a new reference to the pages in the group.
2933 out->Open();
2934
2935 R_SUCCEED();
2936}
2937
2938Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_t size) {
2939 // Check that the region is in range.
2940 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2941
2942 // Lock the table.
2943 KScopedLightLock lk(m_general_lock);
2944
2945 // Check the memory state.
2946 R_TRY(this->CheckMemoryStateContiguous(
2947 address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
2948 KMemoryPermission::UserReadWrite, KMemoryPermission::UserReadWrite,
2949 KMemoryAttribute::Uncached, KMemoryAttribute::None));
2950
2951 // Get the impl.
2952 auto& impl = this->GetImpl();
2953
2954 // Begin traversal.
2955 TraversalContext context;
2956 TraversalEntry next_entry;
2957 bool traverse_valid =
2958 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
2959 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
2960
2961 // Prepare tracking variables.
2962 KPhysicalAddress cur_addr = next_entry.phys_addr;
2963 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
2964 size_t tot_size = cur_size;
2965
2966 // Iterate.
2967 while (tot_size < size) {
2968 // Continue the traversal.
2969 traverse_valid =
2970 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
2971 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
2972
2973 if (next_entry.phys_addr != (cur_addr + cur_size)) {
2974 // Check that the pages are linearly mapped.
2975 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
2976
2977 // Invalidate the block.
2978 if (cur_size > 0) {
2979 // NOTE: Nintendo does not check the result of invalidation.
2980 InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
2981 }
2982
2983 // Advance.
2984 cur_addr = next_entry.phys_addr;
2985 cur_size = next_entry.block_size;
2986 } else {
2987 cur_size += next_entry.block_size;
2988 }
2989
2990 tot_size += next_entry.block_size;
2991 }
2992
2993 // Ensure we use the right size for the last block.
2994 if (tot_size > size) {
2995 cur_size -= (tot_size - size);
2996 }
2997
2998 // Check that the last block is linearly mapped.
2999 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3000
3001 // Invalidate the last block.
3002 if (cur_size > 0) {
3003 // NOTE: Nintendo does not check the result of invalidation.
3004 InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
3005 }
3006
3007 R_SUCCEED();
3008}
3009
3010Result KPageTableBase::InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size) {
3011 // Check pre-condition: this is being called on the current process.
3012 ASSERT(this == std::addressof(GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable()));
3013
3014 // Check that the region is in range.
3015 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3016
3017 // Lock the table.
3018 KScopedLightLock lk(m_general_lock);
3019
3020 // Check the memory state.
3021 R_TRY(this->CheckMemoryStateContiguous(
3022 address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
3023 KMemoryPermission::UserReadWrite, KMemoryPermission::UserReadWrite,
3024 KMemoryAttribute::Uncached, KMemoryAttribute::None));
3025
3026 // Invalidate the data cache.
3027 R_RETURN(InvalidateDataCache(address, size));
3028}
3029
3030Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address,
3031 size_t size) {
3032 // Lightly validate the region is in range.
3033 R_UNLESS(this->Contains(src_address, size), ResultInvalidCurrentMemory);
3034
3035 // Lock the table.
3036 KScopedLightLock lk(m_general_lock);
3037
3038 // Require that the memory either be user readable or debuggable.
3039 const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3040 src_address, size, KMemoryState::None, KMemoryState::None, KMemoryPermission::UserRead,
3041 KMemoryPermission::UserRead, KMemoryAttribute::None, KMemoryAttribute::None));
3042 if (!can_read) {
3043 const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3044 src_address, size, KMemoryState::FlagCanDebug, KMemoryState::FlagCanDebug,
3045 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None,
3046 KMemoryAttribute::None));
3047 R_UNLESS(can_debug, ResultInvalidCurrentMemory);
3048 }
3049
3050 // Get the impl.
3051 auto& impl = this->GetImpl();
3052 auto& dst_memory = GetCurrentMemory(m_system.Kernel());
3053
3054 // Begin traversal.
3055 TraversalContext context;
3056 TraversalEntry next_entry;
3057 bool traverse_valid =
3058 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
3059 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
3060
3061 // Prepare tracking variables.
3062 KPhysicalAddress cur_addr = next_entry.phys_addr;
3063 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3064 size_t tot_size = cur_size;
3065
3066 auto PerformCopy = [&]() -> Result {
3067 // Ensure the address is linear mapped.
3068 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3069
3070 // Copy as much aligned data as we can.
3071 if (cur_size >= sizeof(u32)) {
3072 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3073 const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3074 FlushDataCache(copy_src, copy_size);
3075 R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, copy_size), ResultInvalidPointer);
3076
3077 dst_address += copy_size;
3078 cur_addr += copy_size;
3079 cur_size -= copy_size;
3080 }
3081
3082 // Copy remaining data.
3083 if (cur_size > 0) {
3084 const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3085 FlushDataCache(copy_src, cur_size);
3086 R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, cur_size), ResultInvalidPointer);
3087 }
3088
3089 R_SUCCEED();
3090 };
3091
3092 // Iterate.
3093 while (tot_size < size) {
3094 // Continue the traversal.
3095 traverse_valid =
3096 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3097 ASSERT(traverse_valid);
3098
3099 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3100 // Perform copy.
3101 R_TRY(PerformCopy());
3102
3103 // Advance.
3104 dst_address += cur_size;
3105
3106 cur_addr = next_entry.phys_addr;
3107 cur_size = next_entry.block_size;
3108 } else {
3109 cur_size += next_entry.block_size;
3110 }
3111
3112 tot_size += next_entry.block_size;
3113 }
3114
3115 // Ensure we use the right size for the last block.
3116 if (tot_size > size) {
3117 cur_size -= (tot_size - size);
3118 }
3119
3120 // Perform copy for the last block.
3121 R_TRY(PerformCopy());
3122
3123 R_SUCCEED();
3124}
3125
3126Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address,
3127 size_t size) {
3128 // Lightly validate the region is in range.
3129 R_UNLESS(this->Contains(dst_address, size), ResultInvalidCurrentMemory);
3130
3131 // Lock the table.
3132 KScopedLightLock lk(m_general_lock);
3133
3134 // Require that the memory either be user writable or debuggable.
3135 const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3136 dst_address, size, KMemoryState::None, KMemoryState::None, KMemoryPermission::UserReadWrite,
3137 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, KMemoryAttribute::None));
3138 if (!can_read) {
3139 const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3140 dst_address, size, KMemoryState::FlagCanDebug, KMemoryState::FlagCanDebug,
3141 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None,
3142 KMemoryAttribute::None));
3143 R_UNLESS(can_debug, ResultInvalidCurrentMemory);
3144 }
3145
3146 // Get the impl.
3147 auto& impl = this->GetImpl();
3148 auto& src_memory = GetCurrentMemory(m_system.Kernel());
3149
3150 // Begin traversal.
3151 TraversalContext context;
3152 TraversalEntry next_entry;
3153 bool traverse_valid =
3154 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
3155 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
3156
3157 // Prepare tracking variables.
3158 KPhysicalAddress cur_addr = next_entry.phys_addr;
3159 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3160 size_t tot_size = cur_size;
3161
3162 auto PerformCopy = [&]() -> Result {
3163 // Ensure the address is linear mapped.
3164 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3165
3166 // Copy as much aligned data as we can.
3167 if (cur_size >= sizeof(u32)) {
3168 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3169 void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3170 R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, copy_size),
3171 ResultInvalidCurrentMemory);
3172
3173 StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), copy_size);
3174
3175 src_address += copy_size;
3176 cur_addr += copy_size;
3177 cur_size -= copy_size;
3178 }
3179
3180 // Copy remaining data.
3181 if (cur_size > 0) {
3182 void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3183 R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, cur_size),
3184 ResultInvalidCurrentMemory);
3185
3186 StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
3187 }
3188
3189 R_SUCCEED();
3190 };
3191
3192 // Iterate.
3193 while (tot_size < size) {
3194 // Continue the traversal.
3195 traverse_valid =
3196 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3197 ASSERT(traverse_valid);
3198
3199 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3200 // Perform copy.
3201 R_TRY(PerformCopy());
3202
3203 // Advance.
3204 src_address += cur_size;
3205
3206 cur_addr = next_entry.phys_addr;
3207 cur_size = next_entry.block_size;
3208 } else {
3209 cur_size += next_entry.block_size;
3210 }
3211
3212 tot_size += next_entry.block_size;
3213 }
3214
3215 // Ensure we use the right size for the last block.
3216 if (tot_size > size) {
3217 cur_size -= (tot_size - size);
3218 }
3219
3220 // Perform copy for the last block.
3221 R_TRY(PerformCopy());
3222
3223 // Invalidate the instruction cache, as this svc allows modifying executable pages.
3224 InvalidateInstructionCache(m_system, dst_address, size);
3225
3226 R_SUCCEED();
3227}
3228
3229Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr,
3230 size_t size, KMemoryState state) {
3231 // Check pre-conditions.
3232 ASSERT(this->IsLockedByCurrentThread());
3233
3234 // Determine the mapping extents.
3235 const KPhysicalAddress map_start = Common::AlignDown(GetInteger(phys_addr), PageSize);
3236 const KPhysicalAddress map_end = Common::AlignUp(GetInteger(phys_addr) + size, PageSize);
3237 const size_t map_size = map_end - map_start;
3238
3239 // Get the memory reference to write into.
3240 auto& dst_memory = GetCurrentMemory(m_kernel);
3241
3242 // We're going to perform an update, so create a helper.
3243 KScopedPageTableUpdater updater(this);
3244
3245 // Temporarily map the io memory.
3246 KProcessAddress io_addr;
3247 R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size,
3248 state, KMemoryPermission::UserRead));
3249
3250 // Ensure we unmap the io memory when we're done with it.
3251 const KPageProperties unmap_properties =
3252 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
3253 SCOPE_EXIT({
3254 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
3255 unmap_properties, OperationType::Unmap, true));
3256 });
3257
3258 // Read the memory.
3259 const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
3260 dst_memory.CopyBlock(dst_addr, read_addr, size);
3261
3262 R_SUCCEED();
3263}
3264
3265Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr,
3266 size_t size, KMemoryState state) {
3267 // Check pre-conditions.
3268 ASSERT(this->IsLockedByCurrentThread());
3269
3270 // Determine the mapping extents.
3271 const KPhysicalAddress map_start = Common::AlignDown(GetInteger(phys_addr), PageSize);
3272 const KPhysicalAddress map_end = Common::AlignUp(GetInteger(phys_addr) + size, PageSize);
3273 const size_t map_size = map_end - map_start;
3274
3275 // Get the memory reference to read from.
3276 auto& src_memory = GetCurrentMemory(m_kernel);
3277
3278 // We're going to perform an update, so create a helper.
3279 KScopedPageTableUpdater updater(this);
3280
3281 // Temporarily map the io memory.
3282 KProcessAddress io_addr;
3283 R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size,
3284 state, KMemoryPermission::UserReadWrite));
3285
3286 // Ensure we unmap the io memory when we're done with it.
3287 const KPageProperties unmap_properties =
3288 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
3289 SCOPE_EXIT({
3290 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
3291 unmap_properties, OperationType::Unmap, true));
3292 });
3293
3294 // Write the memory.
3295 const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
3296 R_UNLESS(src_memory.CopyBlock(write_addr, src_addr, size), ResultInvalidPointer);
3297
3298 R_SUCCEED();
3299}
3300
3301Result KPageTableBase::ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address,
3302 size_t size, KMemoryState state) {
3303 // Lightly validate the range before doing anything else.
3304 R_UNLESS(this->Contains(src_address, size), ResultInvalidCurrentMemory);
3305
3306 // We need to lock both this table, and the current process's table, so set up some aliases.
3307 KPageTableBase& src_page_table = *this;
3308 KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable();
3309
3310 // Acquire the table locks.
3311 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
3312
3313 // Check that the desired range is readable io memory.
3314 R_TRY(this->CheckMemoryStateContiguous(src_address, size, KMemoryState::All, state,
3315 KMemoryPermission::UserRead, KMemoryPermission::UserRead,
3316 KMemoryAttribute::None, KMemoryAttribute::None));
3317
3318 // Read the memory.
3319 KProcessAddress dst = dst_address;
3320 const KProcessAddress last_address = src_address + size - 1;
3321 while (src_address <= last_address) {
3322 // Get the current physical address.
3323 KPhysicalAddress phys_addr;
3324 ASSERT(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), src_address));
3325
3326 // Determine the current read size.
3327 const size_t cur_size =
3328 std::min<size_t>(last_address - src_address + 1,
3329 Common::AlignDown(GetInteger(src_address) + PageSize, PageSize) -
3330 GetInteger(src_address));
3331
3332 // Read.
3333 R_TRY(dst_page_table.ReadIoMemoryImpl(dst, phys_addr, cur_size, state));
3334
3335 // Advance.
3336 src_address += cur_size;
3337 dst += cur_size;
3338 }
3339
3340 R_SUCCEED();
3341}
3342
3343Result KPageTableBase::WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address,
3344 size_t size, KMemoryState state) {
3345 // Lightly validate the range before doing anything else.
3346 R_UNLESS(this->Contains(dst_address, size), ResultInvalidCurrentMemory);
3347
3348 // We need to lock both this table, and the current process's table, so set up some aliases.
3349 KPageTableBase& src_page_table = *this;
3350 KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable();
3351
3352 // Acquire the table locks.
3353 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
3354
3355 // Check that the desired range is writable io memory.
3356 R_TRY(this->CheckMemoryStateContiguous(
3357 dst_address, size, KMemoryState::All, state, KMemoryPermission::UserReadWrite,
3358 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, KMemoryAttribute::None));
3359
3360 // Read the memory.
3361 KProcessAddress src = src_address;
3362 const KProcessAddress last_address = dst_address + size - 1;
3363 while (dst_address <= last_address) {
3364 // Get the current physical address.
3365 KPhysicalAddress phys_addr;
3366 ASSERT(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), dst_address));
3367
3368 // Determine the current read size.
3369 const size_t cur_size =
3370 std::min<size_t>(last_address - dst_address + 1,
3371 Common::AlignDown(GetInteger(dst_address) + PageSize, PageSize) -
3372 GetInteger(dst_address));
3373
3374 // Read.
3375 R_TRY(dst_page_table.WriteIoMemoryImpl(phys_addr, src, cur_size, state));
3376
3377 // Advance.
3378 dst_address += cur_size;
3379 src += cur_size;
3380 }
3381
3382 R_SUCCEED();
3383}
3384
3385Result KPageTableBase::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address,
3386 size_t size, KMemoryPermission perm,
3387 bool is_aligned, bool check_heap) {
3388 // Lightly validate the range before doing anything else.
3389 const size_t num_pages = size / PageSize;
3390 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3391
3392 // Lock the table.
3393 KScopedLightLock lk(m_general_lock);
3394
3395 // Check the memory state.
3396 const KMemoryState test_state =
3397 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) |
3398 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
3399 size_t num_allocator_blocks;
3400 KMemoryState old_state;
3401 R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr,
3402 std::addressof(num_allocator_blocks), address, size, test_state,
3403 test_state, perm, perm,
3404 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked,
3405 KMemoryAttribute::None, KMemoryAttribute::DeviceShared));
3406
3407 // Create an update allocator.
3408 Result allocator_result;
3409 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3410 m_memory_block_slab_manager, num_allocator_blocks);
3411 R_TRY(allocator_result);
3412
3413 // Update the memory blocks.
3414 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
3415 &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
3416
3417 // Set whether the locked memory was io.
3418 *out_is_io =
3419 static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
3420
3421 R_SUCCEED();
3422}
3423
3424Result KPageTableBase::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size,
3425 bool check_heap) {
3426 // Lightly validate the range before doing anything else.
3427 const size_t num_pages = size / PageSize;
3428 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3429
3430 // Lock the table.
3431 KScopedLightLock lk(m_general_lock);
3432
3433 // Check the memory state.
3434 const KMemoryState test_state =
3435 KMemoryState::FlagCanDeviceMap |
3436 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
3437 size_t num_allocator_blocks;
3438 R_TRY(this->CheckMemoryStateContiguous(
3439 std::addressof(num_allocator_blocks), address, size, test_state, test_state,
3440 KMemoryPermission::None, KMemoryPermission::None,
3441 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3442
3443 // Create an update allocator.
3444 Result allocator_result;
3445 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3446 m_memory_block_slab_manager, num_allocator_blocks);
3447 R_TRY(allocator_result);
3448
3449 // Update the memory blocks.
3450 const KMemoryBlockManager::MemoryBlockLockFunction lock_func =
3451 m_enable_device_address_space_merge
3452 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare
3453 : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight;
3454 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func,
3455 KMemoryPermission::None);
3456
3457 R_SUCCEED();
3458}
3459
3460Result KPageTableBase::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
3461 // Lightly validate the range before doing anything else.
3462 const size_t num_pages = size / PageSize;
3463 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3464
3465 // Lock the table.
3466 KScopedLightLock lk(m_general_lock);
3467
3468 // Check the memory state.
3469 size_t num_allocator_blocks;
3470 R_TRY(this->CheckMemoryStateContiguous(
3471 std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap,
3472 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
3473 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3474
3475 // Create an update allocator.
3476 Result allocator_result;
3477 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3478 m_memory_block_slab_manager, num_allocator_blocks);
3479 R_TRY(allocator_result);
3480
3481 // Update the memory blocks.
3482 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
3483 &KMemoryBlock::UnshareToDevice, KMemoryPermission::None);
3484
3485 R_SUCCEED();
3486}
3487
3488Result KPageTableBase::UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) {
3489 // Lightly validate the range before doing anything else.
3490 const size_t num_pages = size / PageSize;
3491 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3492
3493 // Lock the table.
3494 KScopedLightLock lk(m_general_lock);
3495
3496 // Check memory state.
3497 size_t allocator_num_blocks = 0;
3498 R_TRY(this->CheckMemoryStateContiguous(
3499 std::addressof(allocator_num_blocks), address, size, KMemoryState::FlagCanDeviceMap,
3500 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
3501 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3502
3503 // Create an update allocator for the region.
3504 Result allocator_result;
3505 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3506 m_memory_block_slab_manager, allocator_num_blocks);
3507 R_TRY(allocator_result);
3508
3509 // Update the memory blocks.
3510 m_memory_block_manager.UpdateLock(
3511 std::addressof(allocator), address, num_pages,
3512 m_enable_device_address_space_merge
3513 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForUnshare
3514 : &KMemoryBlock::UpdateDeviceDisableMergeStateForUnshareRight,
3515 KMemoryPermission::None);
3516
3517 R_SUCCEED();
3518}
3519
3520Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
3521 KProcessAddress address, size_t size,
3522 KMemoryPermission perm,
3523 bool is_aligned) {
3524 // Lock the table.
3525 KScopedLightLock lk(m_general_lock);
3526
3527 // Get the range.
3528 const KMemoryState test_state =
3529 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap);
3530 R_TRY(this->GetContiguousMemoryRangeWithState(
3531 out, address, size, test_state, test_state, perm, perm,
3532 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, KMemoryAttribute::None));
3533
3534 // We got the range, so open it.
3535 out->Open();
3536
3537 R_SUCCEED();
3538}
3539
3540Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out,
3541 KProcessAddress address,
3542 size_t size) {
3543 // Lock the table.
3544 KScopedLightLock lk(m_general_lock);
3545
3546 // Get the range.
3547 R_TRY(this->GetContiguousMemoryRangeWithState(
3548 out, address, size, KMemoryState::FlagCanDeviceMap, KMemoryState::FlagCanDeviceMap,
3549 KMemoryPermission::None, KMemoryPermission::None,
3550 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3551
3552 // We got the range, so open it.
3553 out->Open();
3554
3555 R_SUCCEED();
3556}
3557
3558Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address,
3559 size_t size) {
3560 R_RETURN(this->LockMemoryAndOpen(
3561 nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer,
3562 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All,
3563 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None,
3564 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
3565 KMemoryPermission::KernelReadWrite),
3566 KMemoryAttribute::Locked));
3567}
3568
3569Result KPageTableBase::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
3570 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer,
3571 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None,
3572 KMemoryPermission::None, KMemoryAttribute::All,
3573 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
3574 KMemoryAttribute::Locked, nullptr));
3575}
3576
3577Result KPageTableBase::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
3578 KMemoryPermission perm) {
3579 R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
3580 KMemoryState::FlagCanTransfer, KMemoryPermission::All,
3581 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
3582 KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
3583}
3584
3585Result KPageTableBase::UnlockForTransferMemory(KProcessAddress address, size_t size,
3586 const KPageGroup& pg) {
3587 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
3588 KMemoryState::FlagCanTransfer, KMemoryPermission::None,
3589 KMemoryPermission::None, KMemoryAttribute::All,
3590 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
3591 KMemoryAttribute::Locked, std::addressof(pg)));
3592}
3593
3594Result KPageTableBase::LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) {
3595 R_RETURN(this->LockMemoryAndOpen(
3596 out, nullptr, address, size, KMemoryState::FlagCanCodeMemory,
3597 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
3598 KMemoryAttribute::All, KMemoryAttribute::None,
3599 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
3600 KMemoryPermission::KernelReadWrite),
3601 KMemoryAttribute::Locked));
3602}
3603
3604Result KPageTableBase::UnlockForCodeMemory(KProcessAddress address, size_t size,
3605 const KPageGroup& pg) {
3606 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanCodeMemory,
3607 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
3608 KMemoryPermission::None, KMemoryAttribute::All,
3609 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
3610 KMemoryAttribute::Locked, std::addressof(pg)));
3611}
3612
3613Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange* out,
3614 KProcessAddress address,
3615 size_t size) {
3616 // Lock the table.
3617 KScopedLightLock lk(m_general_lock);
3618
3619 // Get the range.
3620 R_TRY(this->GetContiguousMemoryRangeWithState(
3621 out, address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
3622 KMemoryPermission::UserRead, KMemoryPermission::UserRead, KMemoryAttribute::Uncached,
3623 KMemoryAttribute::None));
3624
3625 // We got the range, so open it.
3626 out->Open();
3627
3628 R_SUCCEED();
3629}
3630
3631Result KPageTableBase::CopyMemoryFromLinearToUser(
3632 KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, KMemoryState src_state_mask,
3633 KMemoryState src_state, KMemoryPermission src_test_perm, KMemoryAttribute src_attr_mask,
3634 KMemoryAttribute src_attr) {
3635 // Lightly validate the range before doing anything else.
3636 R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory);
3637
3638 // Get the destination memory reference.
3639 auto& dst_memory = GetCurrentMemory(m_kernel);
3640
3641 // Copy the memory.
3642 {
3643 // Lock the table.
3644 KScopedLightLock lk(m_general_lock);
3645
3646 // Check memory state.
3647 R_TRY(this->CheckMemoryStateContiguous(
3648 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
3649 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
3650
3651 auto& impl = this->GetImpl();
3652
3653 // Begin traversal.
3654 TraversalContext context;
3655 TraversalEntry next_entry;
3656 bool traverse_valid =
3657 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
3658 ASSERT(traverse_valid);
3659
3660 // Prepare tracking variables.
3661 KPhysicalAddress cur_addr = next_entry.phys_addr;
3662 size_t cur_size =
3663 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3664 size_t tot_size = cur_size;
3665
3666 auto PerformCopy = [&]() -> Result {
3667 // Ensure the address is linear mapped.
3668 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3669
3670 // Copy as much aligned data as we can.
3671 if (cur_size >= sizeof(u32)) {
3672 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3673 R_UNLESS(dst_memory.WriteBlock(dst_addr,
3674 GetLinearMappedVirtualPointer(m_kernel, cur_addr),
3675 copy_size),
3676 ResultInvalidCurrentMemory);
3677
3678 dst_addr += copy_size;
3679 cur_addr += copy_size;
3680 cur_size -= copy_size;
3681 }
3682
3683 // Copy remaining data.
3684 if (cur_size > 0) {
3685 R_UNLESS(dst_memory.WriteBlock(
3686 dst_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size),
3687 ResultInvalidCurrentMemory);
3688 }
3689
3690 R_SUCCEED();
3691 };
3692
3693 // Iterate.
3694 while (tot_size < size) {
3695 // Continue the traversal.
3696 traverse_valid =
3697 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3698 ASSERT(traverse_valid);
3699
3700 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3701 // Perform copy.
3702 R_TRY(PerformCopy());
3703
3704 // Advance.
3705 dst_addr += cur_size;
3706
3707 cur_addr = next_entry.phys_addr;
3708 cur_size = next_entry.block_size;
3709 } else {
3710 cur_size += next_entry.block_size;
3711 }
3712
3713 tot_size += next_entry.block_size;
3714 }
3715
3716 // Ensure we use the right size for the last block.
3717 if (tot_size > size) {
3718 cur_size -= (tot_size - size);
3719 }
3720
3721 // Perform copy for the last block.
3722 R_TRY(PerformCopy());
3723 }
3724
3725 R_SUCCEED();
3726}
3727
3728Result KPageTableBase::CopyMemoryFromLinearToKernel(
3729 void* buffer, size_t size, KProcessAddress src_addr, KMemoryState src_state_mask,
3730 KMemoryState src_state, KMemoryPermission src_test_perm, KMemoryAttribute src_attr_mask,
3731 KMemoryAttribute src_attr) {
3732 // Lightly validate the range before doing anything else.
3733 R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory);
3734
3735 // Copy the memory.
3736 {
3737 // Lock the table.
3738 KScopedLightLock lk(m_general_lock);
3739
3740 // Check memory state.
3741 R_TRY(this->CheckMemoryStateContiguous(
3742 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
3743 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
3744
3745 auto& impl = this->GetImpl();
3746
3747 // Begin traversal.
3748 TraversalContext context;
3749 TraversalEntry next_entry;
3750 bool traverse_valid =
3751 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
3752 ASSERT(traverse_valid);
3753
3754 // Prepare tracking variables.
3755 KPhysicalAddress cur_addr = next_entry.phys_addr;
3756 size_t cur_size =
3757 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3758 size_t tot_size = cur_size;
3759
3760 auto PerformCopy = [&]() -> Result {
3761 // Ensure the address is linear mapped.
3762 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3763
3764 // Copy the data.
3765 std::memcpy(buffer, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
3766
3767 R_SUCCEED();
3768 };
3769
3770 // Iterate.
3771 while (tot_size < size) {
3772 // Continue the traversal.
3773 traverse_valid =
3774 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3775 ASSERT(traverse_valid);
3776
3777 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3778 // Perform copy.
3779 R_TRY(PerformCopy());
3780
3781 // Advance.
3782 buffer = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(buffer) + cur_size);
3783
3784 cur_addr = next_entry.phys_addr;
3785 cur_size = next_entry.block_size;
3786 } else {
3787 cur_size += next_entry.block_size;
3788 }
3789
3790 tot_size += next_entry.block_size;
3791 }
3792
3793 // Ensure we use the right size for the last block.
3794 if (tot_size > size) {
3795 cur_size -= (tot_size - size);
3796 }
3797
3798 // Perform copy for the last block.
3799 R_TRY(PerformCopy());
3800 }
3801
3802 R_SUCCEED();
3803}
3804
3805Result KPageTableBase::CopyMemoryFromUserToLinear(
3806 KProcessAddress dst_addr, size_t size, KMemoryState dst_state_mask, KMemoryState dst_state,
3807 KMemoryPermission dst_test_perm, KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
3808 KProcessAddress src_addr) {
3809 // Lightly validate the range before doing anything else.
3810 R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory);
3811
3812 // Get the source memory reference.
3813 auto& src_memory = GetCurrentMemory(m_kernel);
3814
3815 // Copy the memory.
3816 {
3817 // Lock the table.
3818 KScopedLightLock lk(m_general_lock);
3819
3820 // Check memory state.
3821 R_TRY(this->CheckMemoryStateContiguous(
3822 dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm,
3823 dst_attr_mask | KMemoryAttribute::Uncached, dst_attr));
3824
3825 auto& impl = this->GetImpl();
3826
3827 // Begin traversal.
3828 TraversalContext context;
3829 TraversalEntry next_entry;
3830 bool traverse_valid =
3831 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
3832 ASSERT(traverse_valid);
3833
3834 // Prepare tracking variables.
3835 KPhysicalAddress cur_addr = next_entry.phys_addr;
3836 size_t cur_size =
3837 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3838 size_t tot_size = cur_size;
3839
3840 auto PerformCopy = [&]() -> Result {
3841 // Ensure the address is linear mapped.
3842 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3843
3844 // Copy as much aligned data as we can.
3845 if (cur_size >= sizeof(u32)) {
3846 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3847 R_UNLESS(src_memory.ReadBlock(src_addr,
3848 GetLinearMappedVirtualPointer(m_kernel, cur_addr),
3849 copy_size),
3850 ResultInvalidCurrentMemory);
3851 src_addr += copy_size;
3852 cur_addr += copy_size;
3853 cur_size -= copy_size;
3854 }
3855
3856 // Copy remaining data.
3857 if (cur_size > 0) {
3858 R_UNLESS(src_memory.ReadBlock(
3859 src_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size),
3860 ResultInvalidCurrentMemory);
3861 }
3862
3863 R_SUCCEED();
3864 };
3865
3866 // Iterate.
3867 while (tot_size < size) {
3868 // Continue the traversal.
3869 traverse_valid =
3870 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3871 ASSERT(traverse_valid);
3872
3873 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3874 // Perform copy.
3875 R_TRY(PerformCopy());
3876
3877 // Advance.
3878 src_addr += cur_size;
3879
3880 cur_addr = next_entry.phys_addr;
3881 cur_size = next_entry.block_size;
3882 } else {
3883 cur_size += next_entry.block_size;
3884 }
3885
3886 tot_size += next_entry.block_size;
3887 }
3888
3889 // Ensure we use the right size for the last block.
3890 if (tot_size > size) {
3891 cur_size -= (tot_size - size);
3892 }
3893
3894 // Perform copy for the last block.
3895 R_TRY(PerformCopy());
3896 }
3897
3898 R_SUCCEED();
3899}
3900
3901Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
3902 KMemoryState dst_state_mask,
3903 KMemoryState dst_state,
3904 KMemoryPermission dst_test_perm,
3905 KMemoryAttribute dst_attr_mask,
3906 KMemoryAttribute dst_attr, void* buffer) {
3907 // Lightly validate the range before doing anything else.
3908 R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory);
3909
3910 // Copy the memory.
3911 {
3912 // Lock the table.
3913 KScopedLightLock lk(m_general_lock);
3914
3915 // Check memory state.
3916 R_TRY(this->CheckMemoryStateContiguous(
3917 dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm,
3918 dst_attr_mask | KMemoryAttribute::Uncached, dst_attr));
3919
3920 auto& impl = this->GetImpl();
3921
3922 // Begin traversal.
3923 TraversalContext context;
3924 TraversalEntry next_entry;
3925 bool traverse_valid =
3926 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
3927 ASSERT(traverse_valid);
3928
3929 // Prepare tracking variables.
3930 KPhysicalAddress cur_addr = next_entry.phys_addr;
3931 size_t cur_size =
3932 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3933 size_t tot_size = cur_size;
3934
3935 auto PerformCopy = [&]() -> Result {
3936 // Ensure the address is linear mapped.
3937 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3938
3939 // Copy the data.
3940 std::memcpy(GetLinearMappedVirtualPointer(m_kernel, cur_addr), buffer, cur_size);
3941
3942 R_SUCCEED();
3943 };
3944
3945 // Iterate.
3946 while (tot_size < size) {
3947 // Continue the traversal.
3948 traverse_valid =
3949 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3950 ASSERT(traverse_valid);
3951
3952 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3953 // Perform copy.
3954 R_TRY(PerformCopy());
3955
3956 // Advance.
3957 buffer = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(buffer) + cur_size);
3958
3959 cur_addr = next_entry.phys_addr;
3960 cur_size = next_entry.block_size;
3961 } else {
3962 cur_size += next_entry.block_size;
3963 }
3964
3965 tot_size += next_entry.block_size;
3966 }
3967
3968 // Ensure we use the right size for the last block.
3969 if (tot_size > size) {
3970 cur_size -= (tot_size - size);
3971 }
3972
3973 // Perform copy for the last block.
3974 R_TRY(PerformCopy());
3975 }
3976
3977 R_SUCCEED();
3978}
3979
3980Result KPageTableBase::CopyMemoryFromHeapToHeap(
3981 KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
3982 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
3983 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
3984 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
3985 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
3986 // For convenience, alias this.
3987 KPageTableBase& src_page_table = *this;
3988
3989 // Lightly validate the ranges before doing anything else.
3990 R_UNLESS(src_page_table.Contains(src_addr, size), ResultInvalidCurrentMemory);
3991 R_UNLESS(dst_page_table.Contains(dst_addr, size), ResultInvalidCurrentMemory);
3992
3993 // Copy the memory.
3994 {
3995 // Acquire the table locks.
3996 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
3997
3998 // Check memory state.
3999 R_TRY(src_page_table.CheckMemoryStateContiguous(
4000 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
4001 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
4002 R_TRY(dst_page_table.CheckMemoryStateContiguous(
4003 dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm,
4004 dst_attr_mask | KMemoryAttribute::Uncached, dst_attr));
4005
4006 // Get implementations.
4007 auto& src_impl = src_page_table.GetImpl();
4008 auto& dst_impl = dst_page_table.GetImpl();
4009
4010 // Prepare for traversal.
4011 TraversalContext src_context;
4012 TraversalContext dst_context;
4013 TraversalEntry src_next_entry;
4014 TraversalEntry dst_next_entry;
4015 bool traverse_valid;
4016
4017 // Begin traversal.
4018 traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
4019 std::addressof(src_context), src_addr);
4020 ASSERT(traverse_valid);
4021 traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
4022 std::addressof(dst_context), dst_addr);
4023 ASSERT(traverse_valid);
4024
4025 // Prepare tracking variables.
4026 KPhysicalAddress cur_src_block_addr = src_next_entry.phys_addr;
4027 KPhysicalAddress cur_dst_block_addr = dst_next_entry.phys_addr;
4028 size_t cur_src_size = src_next_entry.block_size -
4029 (GetInteger(cur_src_block_addr) & (src_next_entry.block_size - 1));
4030 size_t cur_dst_size = dst_next_entry.block_size -
4031 (GetInteger(cur_dst_block_addr) & (dst_next_entry.block_size - 1));
4032
4033 // Adjust the initial block sizes.
4034 src_next_entry.block_size = cur_src_size;
4035 dst_next_entry.block_size = cur_dst_size;
4036
4037 // Before we get any crazier, succeed if there's nothing to do.
4038 R_SUCCEED_IF(size == 0);
4039
4040 // We're going to manage dual traversal via an offset against the total size.
4041 KPhysicalAddress cur_src_addr = cur_src_block_addr;
4042 KPhysicalAddress cur_dst_addr = cur_dst_block_addr;
4043 size_t cur_min_size = std::min<size_t>(cur_src_size, cur_dst_size);
4044
4045 // Iterate.
4046 size_t ofs = 0;
4047 while (ofs < size) {
4048 // Determine how much we can copy this iteration.
4049 const size_t cur_copy_size = std::min<size_t>(cur_min_size, size - ofs);
4050
4051 // If we need to advance the traversals, do so.
4052 bool updated_src = false, updated_dst = false, skip_copy = false;
4053 if (ofs + cur_copy_size != size) {
4054 if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
4055 // Continue the src traversal.
4056 traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
4057 std::addressof(src_context));
4058 ASSERT(traverse_valid);
4059
4060 // Update source.
4061 updated_src = cur_src_addr + cur_min_size != src_next_entry.phys_addr;
4062 }
4063
4064 if (cur_dst_addr + cur_min_size ==
4065 dst_next_entry.phys_addr + dst_next_entry.block_size) {
4066 // Continue the dst traversal.
4067 traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
4068 std::addressof(dst_context));
4069 ASSERT(traverse_valid);
4070
4071 // Update destination.
4072 updated_dst = cur_dst_addr + cur_min_size != dst_next_entry.phys_addr;
4073 }
4074
4075 // If we didn't update either of source/destination, skip the copy this iteration.
4076 if (!updated_src && !updated_dst) {
4077 skip_copy = true;
4078
4079 // Update the source block address.
4080 cur_src_block_addr = src_next_entry.phys_addr;
4081 }
4082 }
4083
4084 // Do the copy, unless we're skipping it.
4085 if (!skip_copy) {
4086 // We need both ends of the copy to be heap blocks.
4087 R_UNLESS(IsHeapPhysicalAddress(cur_src_addr), ResultInvalidCurrentMemory);
4088 R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory);
4089
4090 // Copy the data.
4091 std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr),
4092 GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size);
4093
4094 // Update.
4095 cur_src_block_addr = src_next_entry.phys_addr;
4096 cur_src_addr = updated_src ? cur_src_block_addr : cur_src_addr + cur_copy_size;
4097 cur_dst_block_addr = dst_next_entry.phys_addr;
4098 cur_dst_addr = updated_dst ? cur_dst_block_addr : cur_dst_addr + cur_copy_size;
4099
4100 // Advance offset.
4101 ofs += cur_copy_size;
4102 }
4103
4104 // Update min size.
4105 cur_src_size = src_next_entry.block_size;
4106 cur_dst_size = dst_next_entry.block_size;
4107 cur_min_size = std::min<size_t>(cur_src_block_addr - cur_src_addr + cur_src_size,
4108 cur_dst_block_addr - cur_dst_addr + cur_dst_size);
4109 }
4110 }
4111
4112 R_SUCCEED();
4113}
4114
4115Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
4116 KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
4117 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
4118 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
4119 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
4120 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
4121 // For convenience, alias this.
4122 KPageTableBase& src_page_table = *this;
4123
4124 // Lightly validate the ranges before doing anything else.
4125 R_UNLESS(src_page_table.Contains(src_addr, size), ResultInvalidCurrentMemory);
4126 R_UNLESS(dst_page_table.Contains(dst_addr, size), ResultInvalidCurrentMemory);
4127
4128 // Copy the memory.
4129 {
4130 // Acquire the table locks.
4131 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
4132
4133 // Check memory state for source.
4134 R_TRY(src_page_table.CheckMemoryStateContiguous(
4135 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
4136 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
4137
4138 // Destination state is intentionally unchecked.
4139
4140 // Get implementations.
4141 auto& src_impl = src_page_table.GetImpl();
4142 auto& dst_impl = dst_page_table.GetImpl();
4143
4144 // Prepare for traversal.
4145 TraversalContext src_context;
4146 TraversalContext dst_context;
4147 TraversalEntry src_next_entry;
4148 TraversalEntry dst_next_entry;
4149 bool traverse_valid;
4150
4151 // Begin traversal.
4152 traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
4153 std::addressof(src_context), src_addr);
4154 ASSERT(traverse_valid);
4155 traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
4156 std::addressof(dst_context), dst_addr);
4157 ASSERT(traverse_valid);
4158
4159 // Prepare tracking variables.
4160 KPhysicalAddress cur_src_block_addr = src_next_entry.phys_addr;
4161 KPhysicalAddress cur_dst_block_addr = dst_next_entry.phys_addr;
4162 size_t cur_src_size = src_next_entry.block_size -
4163 (GetInteger(cur_src_block_addr) & (src_next_entry.block_size - 1));
4164 size_t cur_dst_size = dst_next_entry.block_size -
4165 (GetInteger(cur_dst_block_addr) & (dst_next_entry.block_size - 1));
4166
4167 // Adjust the initial block sizes.
4168 src_next_entry.block_size = cur_src_size;
4169 dst_next_entry.block_size = cur_dst_size;
4170
4171 // Before we get any crazier, succeed if there's nothing to do.
4172 R_SUCCEED_IF(size == 0);
4173
4174 // We're going to manage dual traversal via an offset against the total size.
4175 KPhysicalAddress cur_src_addr = cur_src_block_addr;
4176 KPhysicalAddress cur_dst_addr = cur_dst_block_addr;
4177 size_t cur_min_size = std::min<size_t>(cur_src_size, cur_dst_size);
4178
4179 // Iterate.
4180 size_t ofs = 0;
4181 while (ofs < size) {
4182 // Determine how much we can copy this iteration.
4183 const size_t cur_copy_size = std::min<size_t>(cur_min_size, size - ofs);
4184
4185 // If we need to advance the traversals, do so.
4186 bool updated_src = false, updated_dst = false, skip_copy = false;
4187 if (ofs + cur_copy_size != size) {
4188 if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
4189 // Continue the src traversal.
4190 traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
4191 std::addressof(src_context));
4192 ASSERT(traverse_valid);
4193
4194 // Update source.
4195 updated_src = cur_src_addr + cur_min_size != src_next_entry.phys_addr;
4196 }
4197
4198 if (cur_dst_addr + cur_min_size ==
4199 dst_next_entry.phys_addr + dst_next_entry.block_size) {
4200 // Continue the dst traversal.
4201 traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
4202 std::addressof(dst_context));
4203 ASSERT(traverse_valid);
4204
4205 // Update destination.
4206 updated_dst = cur_dst_addr + cur_min_size != dst_next_entry.phys_addr;
4207 }
4208
4209 // If we didn't update either of source/destination, skip the copy this iteration.
4210 if (!updated_src && !updated_dst) {
4211 skip_copy = true;
4212
4213 // Update the source block address.
4214 cur_src_block_addr = src_next_entry.phys_addr;
4215 }
4216 }
4217
4218 // Do the copy, unless we're skipping it.
4219 if (!skip_copy) {
4220 // We need both ends of the copy to be heap blocks.
4221 R_UNLESS(IsHeapPhysicalAddress(cur_src_addr), ResultInvalidCurrentMemory);
4222 R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory);
4223
4224 // Copy the data.
4225 std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr),
4226 GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size);
4227
4228 // Update.
4229 cur_src_block_addr = src_next_entry.phys_addr;
4230 cur_src_addr = updated_src ? cur_src_block_addr : cur_src_addr + cur_copy_size;
4231 cur_dst_block_addr = dst_next_entry.phys_addr;
4232 cur_dst_addr = updated_dst ? cur_dst_block_addr : cur_dst_addr + cur_copy_size;
4233
4234 // Advance offset.
4235 ofs += cur_copy_size;
4236 }
4237
4238 // Update min size.
4239 cur_src_size = src_next_entry.block_size;
4240 cur_dst_size = dst_next_entry.block_size;
4241 cur_min_size = std::min<size_t>(cur_src_block_addr - cur_src_addr + cur_src_size,
4242 cur_dst_block_addr - cur_dst_addr + cur_dst_size);
4243 }
4244 }
4245
4246 R_SUCCEED();
4247}
4248
4249Result KPageTableBase::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
4250 KProcessAddress address, size_t size,
4251 KMemoryPermission test_perm, KMemoryState dst_state) {
4252 // Validate pre-conditions.
4253 ASSERT(this->IsLockedByCurrentThread());
4254 ASSERT(test_perm == KMemoryPermission::UserReadWrite ||
4255 test_perm == KMemoryPermission::UserRead);
4256
4257 // Check that the address is in range.
4258 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
4259
4260 // Get the source permission.
4261 const auto src_perm = static_cast<KMemoryPermission>(
4262 (test_perm == KMemoryPermission::UserReadWrite)
4263 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
4264 : KMemoryPermission::UserRead);
4265
4266 // Get aligned extents.
4267 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize);
4268 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize);
4269 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize);
4270 const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize);
4271
4272 const auto aligned_src_last = GetInteger(aligned_src_end) - 1;
4273 const auto mapping_src_last = GetInteger(mapping_src_end) - 1;
4274
4275 // Get the test state and attribute mask.
4276 KMemoryState test_state;
4277 KMemoryAttribute test_attr_mask;
4278 switch (dst_state) {
4279 case KMemoryState::Ipc:
4280 test_state = KMemoryState::FlagCanUseIpc;
4281 test_attr_mask =
4282 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
4283 break;
4284 case KMemoryState::NonSecureIpc:
4285 test_state = KMemoryState::FlagCanUseNonSecureIpc;
4286 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4287 break;
4288 case KMemoryState::NonDeviceIpc:
4289 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
4290 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4291 break;
4292 default:
4293 R_THROW(ResultInvalidCombination);
4294 }
4295
4296 // Ensure that on failure, we roll back appropriately.
4297 size_t mapped_size = 0;
4298 ON_RESULT_FAILURE {
4299 if (mapped_size > 0) {
4300 this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size,
4301 src_perm);
4302 }
4303 };
4304
4305 size_t blocks_needed = 0;
4306
4307 // Iterate, mapping as needed.
4308 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start);
4309 while (true) {
4310 const KMemoryInfo info = it->GetMemoryInfo();
4311
4312 // Validate the current block.
4313 R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm,
4314 test_attr_mask, KMemoryAttribute::None));
4315
4316 if (mapping_src_start < mapping_src_end &&
4317 GetInteger(mapping_src_start) < info.GetEndAddress() &&
4318 info.GetAddress() < GetInteger(mapping_src_end)) {
4319 const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start)
4320 ? info.GetAddress()
4321 : GetInteger(mapping_src_start);
4322 const auto cur_end = mapping_src_last >= info.GetLastAddress()
4323 ? info.GetEndAddress()
4324 : GetInteger(mapping_src_end);
4325 const size_t cur_size = cur_end - cur_start;
4326
4327 if (info.GetAddress() < GetInteger(mapping_src_start)) {
4328 ++blocks_needed;
4329 }
4330 if (mapping_src_last < info.GetLastAddress()) {
4331 ++blocks_needed;
4332 }
4333
4334 // Set the permissions on the block, if we need to.
4335 if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) {
4336 const DisableMergeAttribute head_body_attr =
4337 (GetInteger(mapping_src_start) >= info.GetAddress())
4338 ? DisableMergeAttribute::DisableHeadAndBody
4339 : DisableMergeAttribute::None;
4340 const DisableMergeAttribute tail_attr = (cur_end == GetInteger(mapping_src_end))
4341 ? DisableMergeAttribute::DisableTail
4342 : DisableMergeAttribute::None;
4343 const KPageProperties properties = {
4344 src_perm, false, false,
4345 static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)};
4346 R_TRY(this->Operate(page_list, cur_start, cur_size / PageSize, 0, false, properties,
4347 OperationType::ChangePermissions, false));
4348 }
4349
4350 // Note that we mapped this part.
4351 mapped_size += cur_size;
4352 }
4353
4354 // If the block is at the end, we're done.
4355 if (aligned_src_last <= info.GetLastAddress()) {
4356 break;
4357 }
4358
4359 // Advance.
4360 ++it;
4361 ASSERT(it != m_memory_block_manager.end());
4362 }
4363
4364 if (out_blocks_needed != nullptr) {
4365 ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
4366 *out_blocks_needed = blocks_needed;
4367 }
4368
4369 R_SUCCEED();
4370}
4371
4372Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
4373 KProcessAddress src_addr, KMemoryPermission test_perm,
4374 KMemoryState dst_state, KPageTableBase& src_page_table,
4375 bool send) {
4376 ASSERT(this->IsLockedByCurrentThread());
4377 ASSERT(src_page_table.IsLockedByCurrentThread());
4378
4379 // Check that we can theoretically map.
4380 const KProcessAddress region_start = m_alias_region_start;
4381 const size_t region_size = m_alias_region_end - m_alias_region_start;
4382 R_UNLESS(size < region_size, ResultOutOfAddressSpace);
4383
4384 // Get aligned source extents.
4385 const KProcessAddress src_start = src_addr;
4386 const KProcessAddress src_end = src_addr + size;
4387 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize);
4388 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize);
4389 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize);
4390 const KProcessAddress mapping_src_end =
4391 Common::AlignDown(GetInteger(src_start) + size, PageSize);
4392 const size_t aligned_src_size = aligned_src_end - aligned_src_start;
4393 const size_t mapping_src_size =
4394 (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0;
4395
4396 // Select a random address to map at.
4397 KProcessAddress dst_addr = 0;
4398 {
4399 const size_t alignment = 4_KiB;
4400 const size_t offset = GetInteger(aligned_src_start) & (alignment - 1);
4401
4402 dst_addr =
4403 this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize,
4404 alignment, offset, this->GetNumGuardPages());
4405 R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace);
4406 }
4407
4408 // Check that we can perform the operation we're about to perform.
4409 ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state));
4410
4411 // Create an update allocator.
4412 Result allocator_result;
4413 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4414 m_memory_block_slab_manager);
4415 R_TRY(allocator_result);
4416
4417 // We're going to perform an update, so create a helper.
4418 KScopedPageTableUpdater updater(this);
4419
4420 // Reserve space for any partial pages we allocate.
4421 const size_t unmapped_size = aligned_src_size - mapping_src_size;
4422 KScopedResourceReservation memory_reservation(
4423 m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, unmapped_size);
4424 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
4425
4426 // Ensure that we manage page references correctly.
4427 KPhysicalAddress start_partial_page = 0;
4428 KPhysicalAddress end_partial_page = 0;
4429 KProcessAddress cur_mapped_addr = dst_addr;
4430
4431 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
4432 // free on scope exit.
4433 SCOPE_EXIT({
4434 if (start_partial_page != 0) {
4435 m_kernel.MemoryManager().Close(start_partial_page, 1);
4436 }
4437 if (end_partial_page != 0) {
4438 m_kernel.MemoryManager().Close(end_partial_page, 1);
4439 }
4440 });
4441
4442 ON_RESULT_FAILURE {
4443 if (cur_mapped_addr != dst_addr) {
4444 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
4445 DisableMergeAttribute::None};
4446 R_ASSERT(this->Operate(updater.GetPageList(), dst_addr,
4447 (cur_mapped_addr - dst_addr) / PageSize, 0, false,
4448 unmap_properties, OperationType::Unmap, true));
4449 }
4450 };
4451
4452 // Allocate the start page as needed.
4453 if (aligned_src_start < mapping_src_start) {
4454 start_partial_page =
4455 m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
4456 R_UNLESS(start_partial_page != 0, ResultOutOfMemory);
4457 }
4458
4459 // Allocate the end page as needed.
4460 if (mapping_src_end < aligned_src_end &&
4461 (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) {
4462 end_partial_page =
4463 m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
4464 R_UNLESS(end_partial_page != 0, ResultOutOfMemory);
4465 }
4466
4467 // Get the implementation.
4468 auto& src_impl = src_page_table.GetImpl();
4469
4470 // Get the fill value for partial pages.
4471 const auto fill_val = m_ipc_fill_value;
4472
4473 // Begin traversal.
4474 TraversalContext context;
4475 TraversalEntry next_entry;
4476 bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
4477 std::addressof(context), aligned_src_start);
4478 ASSERT(traverse_valid);
4479
4480 // Prepare tracking variables.
4481 KPhysicalAddress cur_block_addr = next_entry.phys_addr;
4482 size_t cur_block_size =
4483 next_entry.block_size - (GetInteger(cur_block_addr) & (next_entry.block_size - 1));
4484 size_t tot_block_size = cur_block_size;
4485
4486 // Map the start page, if we have one.
4487 if (start_partial_page != 0) {
4488 // Ensure the page holds correct data.
4489 u8* const start_partial_virt = GetHeapVirtualPointer(m_kernel, start_partial_page);
4490 if (send) {
4491 const size_t partial_offset = src_start - aligned_src_start;
4492 size_t copy_size, clear_size;
4493 if (src_end < mapping_src_start) {
4494 copy_size = size;
4495 clear_size = mapping_src_start - src_end;
4496 } else {
4497 copy_size = mapping_src_start - src_start;
4498 clear_size = 0;
4499 }
4500
4501 std::memset(start_partial_virt, fill_val, partial_offset);
4502 std::memcpy(start_partial_virt + partial_offset,
4503 GetHeapVirtualPointer(m_kernel, cur_block_addr) + partial_offset,
4504 copy_size);
4505 if (clear_size > 0) {
4506 std::memset(start_partial_virt + partial_offset + copy_size, fill_val, clear_size);
4507 }
4508 } else {
4509 std::memset(start_partial_virt, fill_val, PageSize);
4510 }
4511
4512 // Map the page.
4513 const KPageProperties start_map_properties = {test_perm, false, false,
4514 DisableMergeAttribute::DisableHead};
4515 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, 1, start_partial_page, true,
4516 start_map_properties, OperationType::Map, false));
4517
4518 // Update tracking extents.
4519 cur_mapped_addr += PageSize;
4520 cur_block_addr += PageSize;
4521 cur_block_size -= PageSize;
4522
4523 // If the block's size was one page, we may need to continue traversal.
4524 if (cur_block_size == 0 && aligned_src_size > PageSize) {
4525 traverse_valid =
4526 src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
4527 ASSERT(traverse_valid);
4528
4529 cur_block_addr = next_entry.phys_addr;
4530 cur_block_size = next_entry.block_size;
4531 tot_block_size += next_entry.block_size;
4532 }
4533 }
4534
4535 // Map the remaining pages.
4536 while (aligned_src_start + tot_block_size < mapping_src_end) {
4537 // Continue the traversal.
4538 traverse_valid =
4539 src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
4540 ASSERT(traverse_valid);
4541
4542 // Process the block.
4543 if (next_entry.phys_addr != cur_block_addr + cur_block_size) {
4544 // Map the block we've been processing so far.
4545 const KPageProperties map_properties = {test_perm, false, false,
4546 (cur_mapped_addr == dst_addr)
4547 ? DisableMergeAttribute::DisableHead
4548 : DisableMergeAttribute::None};
4549 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, cur_block_size / PageSize,
4550 cur_block_addr, true, map_properties, OperationType::Map, false));
4551
4552 // Update tracking extents.
4553 cur_mapped_addr += cur_block_size;
4554 cur_block_addr = next_entry.phys_addr;
4555 cur_block_size = next_entry.block_size;
4556 } else {
4557 cur_block_size += next_entry.block_size;
4558 }
4559 tot_block_size += next_entry.block_size;
4560 }
4561
4562 // Handle the last direct-mapped page.
4563 if (const KProcessAddress mapped_block_end =
4564 aligned_src_start + tot_block_size - cur_block_size;
4565 mapped_block_end < mapping_src_end) {
4566 const size_t last_block_size = mapping_src_end - mapped_block_end;
4567
4568 // Map the last block.
4569 const KPageProperties map_properties = {test_perm, false, false,
4570 (cur_mapped_addr == dst_addr)
4571 ? DisableMergeAttribute::DisableHead
4572 : DisableMergeAttribute::None};
4573 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, last_block_size / PageSize,
4574 cur_block_addr, true, map_properties, OperationType::Map, false));
4575
4576 // Update tracking extents.
4577 cur_mapped_addr += last_block_size;
4578 cur_block_addr += last_block_size;
4579 if (mapped_block_end + cur_block_size < aligned_src_end &&
4580 cur_block_size == last_block_size) {
4581 traverse_valid =
4582 src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
4583 ASSERT(traverse_valid);
4584
4585 cur_block_addr = next_entry.phys_addr;
4586 }
4587 }
4588
4589 // Map the end page, if we have one.
4590 if (end_partial_page != 0) {
4591 // Ensure the page holds correct data.
4592 u8* const end_partial_virt = GetHeapVirtualPointer(m_kernel, end_partial_page);
4593 if (send) {
4594 const size_t copy_size = src_end - mapping_src_end;
4595 std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_kernel, cur_block_addr),
4596 copy_size);
4597 std::memset(end_partial_virt + copy_size, fill_val, PageSize - copy_size);
4598 } else {
4599 std::memset(end_partial_virt, fill_val, PageSize);
4600 }
4601
4602 // Map the page.
4603 const KPageProperties map_properties = {test_perm, false, false,
4604 (cur_mapped_addr == dst_addr)
4605 ? DisableMergeAttribute::DisableHead
4606 : DisableMergeAttribute::None};
4607 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, 1, end_partial_page, true,
4608 map_properties, OperationType::Map, false));
4609 }
4610
4611 // Update memory blocks to reflect our changes
4612 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize,
4613 dst_state, test_perm, KMemoryAttribute::None,
4614 KMemoryBlockDisableMergeAttribute::Normal,
4615 KMemoryBlockDisableMergeAttribute::None);
4616
4617 // Set the output address.
4618 *out_addr = dst_addr + (src_start - aligned_src_start);
4619
4620 // We succeeded.
4621 memory_reservation.Commit();
4622 R_SUCCEED();
4623}
4624
4625Result KPageTableBase::SetupForIpc(KProcessAddress* out_dst_addr, size_t size,
4626 KProcessAddress src_addr, KPageTableBase& src_page_table,
4627 KMemoryPermission test_perm, KMemoryState dst_state, bool send) {
4628 // For convenience, alias this.
4629 KPageTableBase& dst_page_table = *this;
4630
4631 // Acquire the table locks.
4632 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
4633
4634 // We're going to perform an update, so create a helper.
4635 KScopedPageTableUpdater updater(std::addressof(src_page_table));
4636
4637 // Perform client setup.
4638 size_t num_allocator_blocks;
4639 R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(),
4640 std::addressof(num_allocator_blocks), src_addr, size,
4641 test_perm, dst_state));
4642
4643 // Create an update allocator.
4644 Result allocator_result;
4645 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4646 src_page_table.m_memory_block_slab_manager,
4647 num_allocator_blocks);
4648 R_TRY(allocator_result);
4649
4650 // Get the mapped extents.
4651 const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize);
4652 const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize);
4653 const size_t src_map_size = src_map_end - src_map_start;
4654
4655 // Ensure that we clean up appropriately if we fail after this.
4656 const auto src_perm = static_cast<KMemoryPermission>(
4657 (test_perm == KMemoryPermission::UserReadWrite)
4658 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
4659 : KMemoryPermission::UserRead);
4660 ON_RESULT_FAILURE {
4661 if (src_map_end > src_map_start) {
4662 src_page_table.CleanupForIpcClientOnServerSetupFailure(
4663 updater.GetPageList(), src_map_start, src_map_size, src_perm);
4664 }
4665 };
4666
4667 // Perform server setup.
4668 R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state,
4669 src_page_table, send));
4670
4671 // If anything was mapped, ipc-lock the pages.
4672 if (src_map_start < src_map_end) {
4673 // Get the source permission.
4674 src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start,
4675 (src_map_end - src_map_start) / PageSize,
4676 &KMemoryBlock::LockForIpc, src_perm);
4677 }
4678
4679 R_SUCCEED();
4680}
4681
4682Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size,
4683 KMemoryState dst_state) {
4684 // Validate the address.
4685 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
4686
4687 // Lock the table.
4688 KScopedLightLock lk(m_general_lock);
4689
4690 // Validate the memory state.
4691 size_t num_allocator_blocks;
4692 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
4693 KMemoryState::All, dst_state, KMemoryPermission::UserRead,
4694 KMemoryPermission::UserRead, KMemoryAttribute::All,
4695 KMemoryAttribute::None));
4696
4697 // Create an update allocator.
4698 Result allocator_result;
4699 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4700 m_memory_block_slab_manager, num_allocator_blocks);
4701 R_TRY(allocator_result);
4702
4703 // We're going to perform an update, so create a helper.
4704 KScopedPageTableUpdater updater(this);
4705
4706 // Get aligned extents.
4707 const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize);
4708 const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize);
4709 const size_t aligned_size = aligned_end - aligned_start;
4710 const size_t aligned_num_pages = aligned_size / PageSize;
4711
4712 // Unmap the pages.
4713 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
4714 DisableMergeAttribute::None};
4715 R_TRY(this->Operate(updater.GetPageList(), aligned_start, aligned_num_pages, 0, false,
4716 unmap_properties, OperationType::Unmap, false));
4717
4718 // Update memory blocks.
4719 m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages,
4720 KMemoryState::None, KMemoryPermission::None,
4721 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
4722 KMemoryBlockDisableMergeAttribute::Normal);
4723
4724 // Release from the resource limit as relevant.
4725 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
4726 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
4727 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
4728 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
4729 aligned_size - mapping_size);
4730
4731 R_SUCCEED();
4732}
4733
4734Result KPageTableBase::CleanupForIpcClient(KProcessAddress address, size_t size,
4735 KMemoryState dst_state) {
4736 // Validate the address.
4737 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
4738
4739 // Get aligned source extents.
4740 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
4741 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
4742 const KProcessAddress mapping_last = mapping_end - 1;
4743 const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0;
4744
4745 // If nothing was mapped, we're actually done immediately.
4746 R_SUCCEED_IF(mapping_size == 0);
4747
4748 // Get the test state and attribute mask.
4749 KMemoryState test_state;
4750 KMemoryAttribute test_attr_mask;
4751 switch (dst_state) {
4752 case KMemoryState::Ipc:
4753 test_state = KMemoryState::FlagCanUseIpc;
4754 test_attr_mask =
4755 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
4756 break;
4757 case KMemoryState::NonSecureIpc:
4758 test_state = KMemoryState::FlagCanUseNonSecureIpc;
4759 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4760 break;
4761 case KMemoryState::NonDeviceIpc:
4762 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
4763 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4764 break;
4765 default:
4766 R_THROW(ResultInvalidCombination);
4767 }
4768
4769 // Lock the table.
4770 // NOTE: Nintendo does this *after* creating the updater below, but this does not follow
4771 // convention elsewhere in KPageTableBase.
4772 KScopedLightLock lk(m_general_lock);
4773
4774 // We're going to perform an update, so create a helper.
4775 KScopedPageTableUpdater updater(this);
4776
4777 // Ensure that on failure, we roll back appropriately.
4778 size_t mapped_size = 0;
4779 ON_RESULT_FAILURE {
4780 if (mapped_size > 0) {
4781 // Determine where the mapping ends.
4782 const auto mapped_end = GetInteger(mapping_start) + mapped_size;
4783 const auto mapped_last = mapped_end - 1;
4784
4785 // Get current and next iterators.
4786 KMemoryBlockManager::const_iterator start_it =
4787 m_memory_block_manager.FindIterator(mapping_start);
4788 KMemoryBlockManager::const_iterator next_it = start_it;
4789 ++next_it;
4790
4791 // Get the current block info.
4792 KMemoryInfo cur_info = start_it->GetMemoryInfo();
4793
4794 // Create tracking variables.
4795 KProcessAddress cur_address = cur_info.GetAddress();
4796 size_t cur_size = cur_info.GetSize();
4797 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
4798 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
4799 bool first = cur_info.GetIpcDisableMergeCount() == 1 &&
4800 False(cur_info.GetDisableMergeAttribute() &
4801 KMemoryBlockDisableMergeAttribute::Locked);
4802
4803 while ((GetInteger(cur_address) + cur_size - 1) < mapped_last) {
4804 // Check that we have a next block.
4805 ASSERT(next_it != m_memory_block_manager.end());
4806
4807 // Get the next info.
4808 const KMemoryInfo next_info = next_it->GetMemoryInfo();
4809
4810 // Check if we can consolidate the next block's permission set with the current one.
4811 const bool next_perm_eq =
4812 next_info.GetPermission() == next_info.GetOriginalPermission();
4813 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
4814 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
4815 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
4816 // We can consolidate the reprotection for the current and next block into a
4817 // single call.
4818 cur_size += next_info.GetSize();
4819 } else {
4820 // We have to operate on the current block.
4821 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
4822 const KPageProperties properties = {
4823 cur_info.GetPermission(), false, false,
4824 first ? DisableMergeAttribute::EnableAndMergeHeadBodyTail
4825 : DisableMergeAttribute::None};
4826 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
4827 cur_size / PageSize, 0, false, properties,
4828 OperationType::ChangePermissions, true));
4829 }
4830
4831 // Advance.
4832 cur_address = next_info.GetAddress();
4833 cur_size = next_info.GetSize();
4834 first = false;
4835 }
4836
4837 // Advance.
4838 cur_info = next_info;
4839 cur_perm_eq = next_perm_eq;
4840 cur_needs_set_perm = next_needs_set_perm;
4841 ++next_it;
4842 }
4843
4844 // Process the last block.
4845 if ((first || cur_needs_set_perm) && !cur_perm_eq) {
4846 const KPageProperties properties = {
4847 cur_info.GetPermission(), false, false,
4848 first ? DisableMergeAttribute::EnableAndMergeHeadBodyTail
4849 : DisableMergeAttribute::None};
4850 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0,
4851 false, properties, OperationType::ChangePermissions, true));
4852 }
4853 }
4854 };
4855
4856 // Iterate, reprotecting as needed.
4857 {
4858 // Get current and next iterators.
4859 KMemoryBlockManager::const_iterator start_it =
4860 m_memory_block_manager.FindIterator(mapping_start);
4861 KMemoryBlockManager::const_iterator next_it = start_it;
4862 ++next_it;
4863
4864 // Validate the current block.
4865 KMemoryInfo cur_info = start_it->GetMemoryInfo();
4866 R_ASSERT(this->CheckMemoryState(
4867 cur_info, test_state, test_state, KMemoryPermission::None, KMemoryPermission::None,
4868 test_attr_mask | KMemoryAttribute::IpcLocked, KMemoryAttribute::IpcLocked));
4869
4870 // Create tracking variables.
4871 KProcessAddress cur_address = cur_info.GetAddress();
4872 size_t cur_size = cur_info.GetSize();
4873 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
4874 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
4875 bool first =
4876 cur_info.GetIpcDisableMergeCount() == 1 &&
4877 False(cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked);
4878
4879 while ((cur_address + cur_size - 1) < mapping_last) {
4880 // Check that we have a next block.
4881 ASSERT(next_it != m_memory_block_manager.end());
4882
4883 // Get the next info.
4884 const KMemoryInfo next_info = next_it->GetMemoryInfo();
4885
4886 // Validate the next block.
4887 R_ASSERT(this->CheckMemoryState(
4888 next_info, test_state, test_state, KMemoryPermission::None, KMemoryPermission::None,
4889 test_attr_mask | KMemoryAttribute::IpcLocked, KMemoryAttribute::IpcLocked));
4890
4891 // Check if we can consolidate the next block's permission set with the current one.
4892 const bool next_perm_eq =
4893 next_info.GetPermission() == next_info.GetOriginalPermission();
4894 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
4895 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
4896 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
4897 // We can consolidate the reprotection for the current and next block into a single
4898 // call.
4899 cur_size += next_info.GetSize();
4900 } else {
4901 // We have to operate on the current block.
4902 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
4903 const KPageProperties properties = {
4904 cur_needs_set_perm ? cur_info.GetOriginalPermission()
4905 : cur_info.GetPermission(),
4906 false, false,
4907 first ? DisableMergeAttribute::EnableHeadAndBody
4908 : DisableMergeAttribute::None};
4909 R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0,
4910 false, properties, OperationType::ChangePermissions,
4911 false));
4912 }
4913
4914 // Mark that we mapped the block.
4915 mapped_size += cur_size;
4916
4917 // Advance.
4918 cur_address = next_info.GetAddress();
4919 cur_size = next_info.GetSize();
4920 first = false;
4921 }
4922
4923 // Advance.
4924 cur_info = next_info;
4925 cur_perm_eq = next_perm_eq;
4926 cur_needs_set_perm = next_needs_set_perm;
4927 ++next_it;
4928 }
4929
4930 // Process the last block.
4931 const auto lock_count =
4932 cur_info.GetIpcLockCount() +
4933 (next_it != m_memory_block_manager.end()
4934 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
4935 : 0);
4936 if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) {
4937 const DisableMergeAttribute head_body_attr =
4938 first ? DisableMergeAttribute::EnableHeadAndBody : DisableMergeAttribute::None;
4939 const DisableMergeAttribute tail_attr =
4940 lock_count == 1 ? DisableMergeAttribute::EnableTail : DisableMergeAttribute::None;
4941 const KPageProperties properties = {
4942 cur_needs_set_perm ? cur_info.GetOriginalPermission() : cur_info.GetPermission(),
4943 false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)};
4944 R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0, false,
4945 properties, OperationType::ChangePermissions, false));
4946 }
4947 }
4948
4949 // Create an update allocator.
4950 // NOTE: Guaranteed zero blocks needed here.
4951 Result allocator_result;
4952 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4953 m_memory_block_slab_manager, 0);
4954 R_TRY(allocator_result);
4955
4956 // Unlock the pages.
4957 m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start,
4958 mapping_size / PageSize, &KMemoryBlock::UnlockForIpc,
4959 KMemoryPermission::None);
4960
4961 R_SUCCEED();
4962}
4963
4964void KPageTableBase::CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list,
4965 KProcessAddress address, size_t size,
4966 KMemoryPermission prot_perm) {
4967 ASSERT(this->IsLockedByCurrentThread());
4968 ASSERT(Common::IsAligned(GetInteger(address), PageSize));
4969 ASSERT(Common::IsAligned(size, PageSize));
4970
4971 // Get the mapped extents.
4972 const KProcessAddress src_map_start = address;
4973 const KProcessAddress src_map_end = address + size;
4974 const KProcessAddress src_map_last = src_map_end - 1;
4975
4976 // This function is only invoked when there's something to do.
4977 ASSERT(src_map_end > src_map_start);
4978
4979 // Iterate over blocks, fixing permissions.
4980 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
4981 while (true) {
4982 const KMemoryInfo info = it->GetMemoryInfo();
4983
4984 const auto cur_start = info.GetAddress() >= GetInteger(src_map_start)
4985 ? info.GetAddress()
4986 : GetInteger(src_map_start);
4987 const auto cur_end =
4988 src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress();
4989
4990 // If we can, fix the protections on the block.
4991 if ((info.GetIpcLockCount() == 0 &&
4992 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) ||
4993 (info.GetIpcLockCount() != 0 &&
4994 (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) {
4995 // Check if we actually need to fix the protections on the block.
4996 if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) ||
4997 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) {
4998 const bool start_nc = (info.GetAddress() == GetInteger(src_map_start))
4999 ? (False(info.GetDisableMergeAttribute() &
5000 (KMemoryBlockDisableMergeAttribute::Locked |
5001 KMemoryBlockDisableMergeAttribute::IpcLeft)))
5002 : info.GetAddress() <= GetInteger(src_map_start);
5003
5004 const DisableMergeAttribute head_body_attr =
5005 start_nc ? DisableMergeAttribute::EnableHeadAndBody
5006 : DisableMergeAttribute::None;
5007 DisableMergeAttribute tail_attr;
5008 if (cur_end == src_map_end && info.GetEndAddress() == src_map_end) {
5009 auto next_it = it;
5010 ++next_it;
5011
5012 const auto lock_count =
5013 info.GetIpcLockCount() +
5014 (next_it != m_memory_block_manager.end()
5015 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
5016 : 0);
5017 tail_attr = lock_count == 0 ? DisableMergeAttribute::EnableTail
5018 : DisableMergeAttribute::None;
5019 } else {
5020 tail_attr = DisableMergeAttribute::None;
5021 }
5022
5023 const KPageProperties properties = {
5024 info.GetPermission(), false, false,
5025 static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)};
5026 R_ASSERT(this->Operate(page_list, cur_start, (cur_end - cur_start) / PageSize, 0,
5027 false, properties, OperationType::ChangePermissions, true));
5028 }
5029 }
5030
5031 // If we're past the end of the region, we're done.
5032 if (src_map_last <= info.GetLastAddress()) {
5033 break;
5034 }
5035
5036 // Advance.
5037 ++it;
5038 ASSERT(it != m_memory_block_manager.end());
5039 }
5040}
5041
5042Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5043 // Lock the physical memory lock.
5044 KScopedLightLock phys_lk(m_map_physical_memory_lock);
5045
5046 // Calculate the last address for convenience.
5047 const KProcessAddress last_address = address + size - 1;
5048
5049 // Define iteration variables.
5050 KProcessAddress cur_address;
5051 size_t mapped_size;
5052
5053 // The entire mapping process can be retried.
5054 while (true) {
5055 // Check if the memory is already mapped.
5056 {
5057 // Lock the table.
5058 KScopedLightLock lk(m_general_lock);
5059
5060 // Iterate over the memory.
5061 cur_address = address;
5062 mapped_size = 0;
5063
5064 auto it = m_memory_block_manager.FindIterator(cur_address);
5065 while (true) {
5066 // Check that the iterator is valid.
5067 ASSERT(it != m_memory_block_manager.end());
5068
5069 // Get the memory info.
5070 const KMemoryInfo info = it->GetMemoryInfo();
5071
5072 // Check if we're done.
5073 if (last_address <= info.GetLastAddress()) {
5074 if (info.GetState() != KMemoryState::Free) {
5075 mapped_size += (last_address + 1 - cur_address);
5076 }
5077 break;
5078 }
5079
5080 // Track the memory if it's mapped.
5081 if (info.GetState() != KMemoryState::Free) {
5082 mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address;
5083 }
5084
5085 // Advance.
5086 cur_address = info.GetEndAddress();
5087 ++it;
5088 }
5089
5090 // If the size mapped is the size requested, we've nothing to do.
5091 R_SUCCEED_IF(size == mapped_size);
5092 }
5093
5094 // Allocate and map the memory.
5095 {
5096 // Reserve the memory from the process resource limit.
5097 KScopedResourceReservation memory_reservation(
5098 m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, size - mapped_size);
5099 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
5100
5101 // Allocate pages for the new memory.
5102 KPageGroup pg(m_kernel, m_block_info_manager);
5103 R_TRY(m_kernel.MemoryManager().AllocateForProcess(
5104 std::addressof(pg), (size - mapped_size) / PageSize, m_allocate_option,
5105 GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
5106
5107 // If we fail in the next bit (or retry), we need to cleanup the pages.
5108 auto pg_guard = SCOPE_GUARD({
5109 pg.OpenFirst();
5110 pg.Close();
5111 });
5112
5113 // Map the memory.
5114 {
5115 // Lock the table.
5116 KScopedLightLock lk(m_general_lock);
5117
5118 size_t num_allocator_blocks = 0;
5119
5120 // Verify that nobody has mapped memory since we first checked.
5121 {
5122 // Iterate over the memory.
5123 size_t checked_mapped_size = 0;
5124 cur_address = address;
5125
5126 auto it = m_memory_block_manager.FindIterator(cur_address);
5127 while (true) {
5128 // Check that the iterator is valid.
5129 ASSERT(it != m_memory_block_manager.end());
5130
5131 // Get the memory info.
5132 const KMemoryInfo info = it->GetMemoryInfo();
5133
5134 const bool is_free = info.GetState() == KMemoryState::Free;
5135 if (is_free) {
5136 if (info.GetAddress() < GetInteger(address)) {
5137 ++num_allocator_blocks;
5138 }
5139 if (last_address < info.GetLastAddress()) {
5140 ++num_allocator_blocks;
5141 }
5142 }
5143
5144 // Check if we're done.
5145 if (last_address <= info.GetLastAddress()) {
5146 if (!is_free) {
5147 checked_mapped_size += (last_address + 1 - cur_address);
5148 }
5149 break;
5150 }
5151
5152 // Track the memory if it's mapped.
5153 if (!is_free) {
5154 checked_mapped_size +=
5155 KProcessAddress(info.GetEndAddress()) - cur_address;
5156 }
5157
5158 // Advance.
5159 cur_address = info.GetEndAddress();
5160 ++it;
5161 }
5162
5163 // If the size now isn't what it was before, somebody mapped or unmapped
5164 // concurrently. If this happened, retry.
5165 if (mapped_size != checked_mapped_size) {
5166 continue;
5167 }
5168 }
5169
5170 // Create an update allocator.
5171 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
5172 Result allocator_result;
5173 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
5174 m_memory_block_slab_manager,
5175 num_allocator_blocks);
5176 R_TRY(allocator_result);
5177
5178 // We're going to perform an update, so create a helper.
5179 KScopedPageTableUpdater updater(this);
5180
5181 // Prepare to iterate over the memory.
5182 auto pg_it = pg.begin();
5183 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
5184 size_t pg_pages = pg_it->GetNumPages();
5185
5186 // Reset the current tracking address, and make sure we clean up on failure.
5187 pg_guard.Cancel();
5188 cur_address = address;
5189 ON_RESULT_FAILURE {
5190 if (cur_address > address) {
5191 const KProcessAddress last_unmap_address = cur_address - 1;
5192
5193 // Iterate, unmapping the pages.
5194 cur_address = address;
5195
5196 auto it = m_memory_block_manager.FindIterator(cur_address);
5197 while (true) {
5198 // Check that the iterator is valid.
5199 ASSERT(it != m_memory_block_manager.end());
5200
5201 // Get the memory info.
5202 const KMemoryInfo info = it->GetMemoryInfo();
5203
5204 // If the memory state is free, we mapped it and need to unmap it.
5205 if (info.GetState() == KMemoryState::Free) {
5206 // Determine the range to unmap.
5207 const KPageProperties unmap_properties = {
5208 KMemoryPermission::None, false, false,
5209 DisableMergeAttribute::None};
5210 const size_t cur_pages =
5211 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
5212 last_unmap_address + 1 - cur_address) /
5213 PageSize;
5214
5215 // Unmap.
5216 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
5217 cur_pages, 0, false, unmap_properties,
5218 OperationType::Unmap, true));
5219 }
5220
5221 // Check if we're done.
5222 if (last_unmap_address <= info.GetLastAddress()) {
5223 break;
5224 }
5225
5226 // Advance.
5227 cur_address = info.GetEndAddress();
5228 ++it;
5229 }
5230 }
5231
5232 // Release any remaining unmapped memory.
5233 m_kernel.MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
5234 m_kernel.MemoryManager().Close(pg_phys_addr, pg_pages);
5235 for (++pg_it; pg_it != pg.end(); ++pg_it) {
5236 m_kernel.MemoryManager().OpenFirst(pg_it->GetAddress(),
5237 pg_it->GetNumPages());
5238 m_kernel.MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages());
5239 }
5240 };
5241
5242 auto it = m_memory_block_manager.FindIterator(cur_address);
5243 while (true) {
5244 // Check that the iterator is valid.
5245 ASSERT(it != m_memory_block_manager.end());
5246
5247 // Get the memory info.
5248 const KMemoryInfo info = it->GetMemoryInfo();
5249
5250 // If it's unmapped, we need to map it.
5251 if (info.GetState() == KMemoryState::Free) {
5252 // Determine the range to map.
5253 const KPageProperties map_properties = {
5254 KMemoryPermission::UserReadWrite, false, false,
5255 cur_address == this->GetAliasRegionStart()
5256 ? DisableMergeAttribute::DisableHead
5257 : DisableMergeAttribute::None};
5258 size_t map_pages =
5259 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
5260 last_address + 1 - cur_address) /
5261 PageSize;
5262
5263 // While we have pages to map, map them.
5264 {
5265 // Create a page group for the current mapping range.
5266 KPageGroup cur_pg(m_kernel, m_block_info_manager);
5267 {
5268 ON_RESULT_FAILURE_2 {
5269 cur_pg.OpenFirst();
5270 cur_pg.Close();
5271 };
5272
5273 size_t remain_pages = map_pages;
5274 while (remain_pages > 0) {
5275 // Check if we're at the end of the physical block.
5276 if (pg_pages == 0) {
5277 // Ensure there are more pages to map.
5278 ASSERT(pg_it != pg.end());
5279
5280 // Advance our physical block.
5281 ++pg_it;
5282 pg_phys_addr = pg_it->GetAddress();
5283 pg_pages = pg_it->GetNumPages();
5284 }
5285
5286 // Add whatever we can to the current block.
5287 const size_t cur_pages = std::min(pg_pages, remain_pages);
5288 R_TRY(cur_pg.AddBlock(pg_phys_addr +
5289 ((pg_pages - cur_pages) * PageSize),
5290 cur_pages));
5291
5292 // Advance.
5293 remain_pages -= cur_pages;
5294 pg_pages -= cur_pages;
5295 }
5296 }
5297
5298 // Map the papges.
5299 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
5300 cur_pg, map_properties,
5301 OperationType::MapFirstGroup, false));
5302 }
5303 }
5304
5305 // Check if we're done.
5306 if (last_address <= info.GetLastAddress()) {
5307 break;
5308 }
5309
5310 // Advance.
5311 cur_address = info.GetEndAddress();
5312 ++it;
5313 }
5314
5315 // We succeeded, so commit the memory reservation.
5316 memory_reservation.Commit();
5317
5318 // Increase our tracked mapped size.
5319 m_mapped_physical_memory_size += (size - mapped_size);
5320
5321 // Update the relevant memory blocks.
5322 m_memory_block_manager.UpdateIfMatch(
5323 std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
5324 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
5325 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
5326 address == this->GetAliasRegionStart()
5327 ? KMemoryBlockDisableMergeAttribute::Normal
5328 : KMemoryBlockDisableMergeAttribute::None,
5329 KMemoryBlockDisableMergeAttribute::None);
5330
5331 R_SUCCEED();
5332 }
5333 }
5334 }
5335}
5336
5337Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
5338 // Lock the physical memory lock.
5339 KScopedLightLock phys_lk(m_map_physical_memory_lock);
5340
5341 // Lock the table.
5342 KScopedLightLock lk(m_general_lock);
5343
5344 // Calculate the last address for convenience.
5345 const KProcessAddress last_address = address + size - 1;
5346
5347 // Define iteration variables.
5348 KProcessAddress map_start_address = 0;
5349 KProcessAddress map_last_address = 0;
5350
5351 KProcessAddress cur_address;
5352 size_t mapped_size;
5353 size_t num_allocator_blocks = 0;
5354
5355 // Check if the memory is mapped.
5356 {
5357 // Iterate over the memory.
5358 cur_address = address;
5359 mapped_size = 0;
5360
5361 auto it = m_memory_block_manager.FindIterator(cur_address);
5362 while (true) {
5363 // Check that the iterator is valid.
5364 ASSERT(it != m_memory_block_manager.end());
5365
5366 // Get the memory info.
5367 const KMemoryInfo info = it->GetMemoryInfo();
5368
5369 // Verify the memory's state.
5370 const bool is_normal = info.GetState() == KMemoryState::Normal &&
5371 info.GetAttribute() == KMemoryAttribute::None;
5372 const bool is_free = info.GetState() == KMemoryState::Free;
5373 R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
5374
5375 if (is_normal) {
5376 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
5377
5378 if (map_start_address == 0) {
5379 map_start_address = cur_address;
5380 }
5381 map_last_address =
5382 (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address;
5383
5384 if (info.GetAddress() < GetInteger(address)) {
5385 ++num_allocator_blocks;
5386 }
5387 if (last_address < info.GetLastAddress()) {
5388 ++num_allocator_blocks;
5389 }
5390
5391 mapped_size += (map_last_address + 1 - cur_address);
5392 }
5393
5394 // Check if we're done.
5395 if (last_address <= info.GetLastAddress()) {
5396 break;
5397 }
5398
5399 // Advance.
5400 cur_address = info.GetEndAddress();
5401 ++it;
5402 }
5403
5404 // If there's nothing mapped, we've nothing to do.
5405 R_SUCCEED_IF(mapped_size == 0);
5406 }
5407
5408 // Create an update allocator.
5409 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
5410 Result allocator_result;
5411 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
5412 m_memory_block_slab_manager, num_allocator_blocks);
5413 R_TRY(allocator_result);
5414
5415 // We're going to perform an update, so create a helper.
5416 KScopedPageTableUpdater updater(this);
5417
5418 // Separate the mapping.
5419 const KPageProperties sep_properties = {KMemoryPermission::None, false, false,
5420 DisableMergeAttribute::None};
5421 R_TRY(this->Operate(updater.GetPageList(), map_start_address,
5422 (map_last_address + 1 - map_start_address) / PageSize, 0, false,
5423 sep_properties, OperationType::Separate, false));
5424
5425 // Reset the current tracking address, and make sure we clean up on failure.
5426 cur_address = address;
5427
5428 // Iterate over the memory, unmapping as we go.
5429 auto it = m_memory_block_manager.FindIterator(cur_address);
5430
5431 const auto clear_merge_attr =
5432 (it->GetState() == KMemoryState::Normal &&
5433 it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
5434 ? KMemoryBlockDisableMergeAttribute::Normal
5435 : KMemoryBlockDisableMergeAttribute::None;
5436
5437 while (true) {
5438 // Check that the iterator is valid.
5439 ASSERT(it != m_memory_block_manager.end());
5440
5441 // Get the memory info.
5442 const KMemoryInfo info = it->GetMemoryInfo();
5443
5444 // If the memory state is normal, we need to unmap it.
5445 if (info.GetState() == KMemoryState::Normal) {
5446 // Determine the range to unmap.
5447 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
5448 DisableMergeAttribute::None};
5449 const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
5450 last_address + 1 - cur_address) /
5451 PageSize;
5452
5453 // Unmap.
5454 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
5455 unmap_properties, OperationType::Unmap, false));
5456 }
5457
5458 // Check if we're done.
5459 if (last_address <= info.GetLastAddress()) {
5460 break;
5461 }
5462
5463 // Advance.
5464 cur_address = info.GetEndAddress();
5465 ++it;
5466 }
5467
5468 // Release the memory resource.
5469 m_mapped_physical_memory_size -= mapped_size;
5470 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, mapped_size);
5471
5472 // Update memory blocks.
5473 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
5474 KMemoryState::Free, KMemoryPermission::None,
5475 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
5476 clear_merge_attr);
5477
5478 // We succeeded.
5479 R_SUCCEED();
5480}
5481
5482Result KPageTableBase::MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
5483 UNIMPLEMENTED();
5484 R_THROW(ResultNotImplemented);
5485}
5486
5487Result KPageTableBase::UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
5488 UNIMPLEMENTED();
5489 R_THROW(ResultNotImplemented);
5490}
5491
5492Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t size,
5493 KPageTableBase& src_page_table,
5494 KProcessAddress src_address) {
5495 // We need to lock both this table, and the current process's table, so set up an alias.
5496 KPageTableBase& dst_page_table = *this;
5497
5498 // Acquire the table locks.
5499 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
5500
5501 // Check that the memory is mapped in the destination process.
5502 size_t num_allocator_blocks;
5503 R_TRY(dst_page_table.CheckMemoryState(
5504 std::addressof(num_allocator_blocks), dst_address, size, KMemoryState::All,
5505 KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
5506 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None));
5507
5508 // Check that the memory is mapped in the source process.
5509 R_TRY(src_page_table.CheckMemoryState(src_address, size, KMemoryState::FlagCanMapProcess,
5510 KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
5511 KMemoryPermission::None, KMemoryAttribute::All,
5512 KMemoryAttribute::None));
5513
5514 // Validate that the memory ranges are compatible.
5515 {
5516 // Define a helper type.
5517 struct ContiguousRangeInfo {
5518 public:
5519 KPageTableBase& m_pt;
5520 TraversalContext m_context;
5521 TraversalEntry m_entry;
5522 KPhysicalAddress m_phys_addr;
5523 size_t m_cur_size;
5524 size_t m_remaining_size;
5525
5526 public:
5527 ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
5528 : m_pt(pt), m_remaining_size(size) {
5529 // Begin a traversal.
5530 ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
5531 std::addressof(m_context), address));
5532
5533 // Setup tracking fields.
5534 m_phys_addr = m_entry.phys_addr;
5535 m_cur_size = std::min<size_t>(
5536 m_remaining_size,
5537 m_entry.block_size - (GetInteger(m_phys_addr) & (m_entry.block_size - 1)));
5538
5539 // Consume the whole contiguous block.
5540 this->DetermineContiguousBlockExtents();
5541 }
5542
5543 void ContinueTraversal() {
5544 // Update our remaining size.
5545 m_remaining_size = m_remaining_size - m_cur_size;
5546
5547 // Update our tracking fields.
5548 if (m_remaining_size > 0) {
5549 m_phys_addr = m_entry.phys_addr;
5550 m_cur_size = std::min<size_t>(m_remaining_size, m_entry.block_size);
5551
5552 // Consume the whole contiguous block.
5553 this->DetermineContiguousBlockExtents();
5554 }
5555 }
5556
5557 private:
5558 void DetermineContiguousBlockExtents() {
5559 // Continue traversing until we're not contiguous, or we have enough.
5560 while (m_cur_size < m_remaining_size) {
5561 ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
5562 std::addressof(m_context)));
5563
5564 // If we're not contiguous, we're done.
5565 if (m_entry.phys_addr != m_phys_addr + m_cur_size) {
5566 break;
5567 }
5568
5569 // Update our current size.
5570 m_cur_size = std::min(m_remaining_size, m_cur_size + m_entry.block_size);
5571 }
5572 }
5573 };
5574
5575 // Create ranges for both tables.
5576 ContiguousRangeInfo src_range(src_page_table, src_address, size);
5577 ContiguousRangeInfo dst_range(dst_page_table, dst_address, size);
5578
5579 // Validate the ranges.
5580 while (src_range.m_remaining_size > 0 && dst_range.m_remaining_size > 0) {
5581 R_UNLESS(src_range.m_phys_addr == dst_range.m_phys_addr, ResultInvalidMemoryRegion);
5582 R_UNLESS(src_range.m_cur_size == dst_range.m_cur_size, ResultInvalidMemoryRegion);
5583
5584 src_range.ContinueTraversal();
5585 dst_range.ContinueTraversal();
5586 }
5587 }
5588
5589 // We no longer need to hold our lock on the source page table.
5590 lk.TryUnlockHalf(src_page_table.m_general_lock);
5591
5592 // Create an update allocator.
5593 Result allocator_result;
5594 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
5595 m_memory_block_slab_manager, num_allocator_blocks);
5596 R_TRY(allocator_result);
5597
5598 // We're going to perform an update, so create a helper.
5599 KScopedPageTableUpdater updater(this);
5600
5601 // Unmap the memory.
5602 const size_t num_pages = size / PageSize;
5603 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
5604 DisableMergeAttribute::None};
5605 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, unmap_properties,
5606 OperationType::Unmap, false));
5607
5608 // Apply the memory block update.
5609 m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages,
5610 KMemoryState::Free, KMemoryPermission::None,
5611 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
5612 KMemoryBlockDisableMergeAttribute::Normal);
5613
5614 R_SUCCEED();
5615}
5616
5617Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_addr,
5618 size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid,
5619 const KPageProperties properties, OperationType operation,
5620 bool reuse_ll) {
5621 ASSERT(this->IsLockedByCurrentThread());
5622 ASSERT(num_pages > 0);
5623 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5624 ASSERT(this->ContainsPages(virt_addr, num_pages));
5625
5626 // As we don't allocate page entries in guest memory, we don't need to allocate them from
5627 // or free them to the page list, and so it goes unused (along with page properties).
5628
5629 switch (operation) {
5630 case OperationType::Unmap: {
5631 // Ensure that any pages we track are closed on exit.
5632 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
5633 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
5634
5635 // Make a page group representing the region to unmap.
5636 this->MakePageGroup(pages_to_close, virt_addr, num_pages);
5637
5638 // Unmap.
5639 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize);
5640
5641 R_SUCCEED();
5642 }
5643 case OperationType::Map: {
5644 ASSERT(virt_addr != 0);
5645 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5646 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr);
5647
5648 // Open references to pages, if we should.
5649 if (this->IsHeapPhysicalAddress(phys_addr)) {
5650 m_kernel.MemoryManager().Open(phys_addr, num_pages);
5651 }
5652
5653 R_SUCCEED();
5654 }
5655 case OperationType::Separate: {
5656 // TODO: Unimplemented.
5657 R_SUCCEED();
5658 }
5659 case OperationType::ChangePermissions:
5660 case OperationType::ChangePermissionsAndRefresh:
5661 case OperationType::ChangePermissionsAndRefreshAndFlush:
5662 R_SUCCEED();
5663 default:
5664 UNREACHABLE();
5665 }
5666}
5667
5668Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_addr,
5669 size_t num_pages, const KPageGroup& page_group,
5670 const KPageProperties properties, OperationType operation,
5671 bool reuse_ll) {
5672 ASSERT(this->IsLockedByCurrentThread());
5673 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5674 ASSERT(num_pages > 0);
5675 ASSERT(num_pages == page_group.GetNumPages());
5676
5677 // As we don't allocate page entries in guest memory, we don't need to allocate them from
5678 // the page list, and so it goes unused (along with page properties).
5679
5680 switch (operation) {
5681 case OperationType::MapGroup:
5682 case OperationType::MapFirstGroup: {
5683 // We want to maintain a new reference to every page in the group.
5684 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
5685
5686 for (const auto& node : page_group) {
5687 const size_t size{node.GetNumPages() * PageSize};
5688
5689 // Map the pages.
5690 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress());
5691
5692 virt_addr += size;
5693 }
5694
5695 // We succeeded! We want to persist the reference to the pages.
5696 spg.CancelClose();
5697
5698 R_SUCCEED();
5699 }
5700 default:
5701 UNREACHABLE();
5702 }
5703}
5704
5705void KPageTableBase::FinalizeUpdate(PageLinkedList* page_list) {
5706 while (page_list->Peek()) {
5707 [[maybe_unused]] auto page = page_list->Pop();
5708
5709 // TODO: Free page entries once they are allocated in guest memory.
5710 // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page));
5711 // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0);
5712 // this->GetPageTableManager().Free(page);
5713 }
5714}
5715
5716} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
new file mode 100644
index 000000000..ee2c41e67
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -0,0 +1,759 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_funcs.h"
9#include "common/page_table.h"
10#include "core/core.h"
11#include "core/hle/kernel/k_dynamic_resource_manager.h"
12#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_block.h"
14#include "core/hle/kernel/k_memory_block_manager.h"
15#include "core/hle/kernel/k_memory_layout.h"
16#include "core/hle/kernel/k_memory_manager.h"
17#include "core/hle/kernel/k_typed_address.h"
18#include "core/hle/kernel/kernel.h"
19#include "core/hle/result.h"
20#include "core/memory.h"
21
22namespace Kernel {
23
24enum class DisableMergeAttribute : u8 {
25 None = (0U << 0),
26
27 DisableHead = (1U << 0),
28 DisableHeadAndBody = (1U << 1),
29 EnableHeadAndBody = (1U << 2),
30 DisableTail = (1U << 3),
31 EnableTail = (1U << 4),
32 EnableAndMergeHeadBodyTail = (1U << 5),
33
34 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
35 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
36};
37DECLARE_ENUM_FLAG_OPERATORS(DisableMergeAttribute);
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
48class KResourceLimit;
49class KSystemResource;
50
51class KPageTableBase {
52 YUZU_NON_COPYABLE(KPageTableBase);
53 YUZU_NON_MOVEABLE(KPageTableBase);
54
55public:
56 using TraversalEntry = Common::PageTable::TraversalEntry;
57 using TraversalContext = Common::PageTable::TraversalContext;
58
59 class MemoryRange {
60 private:
61 KernelCore& m_kernel;
62 KPhysicalAddress m_address;
63 size_t m_size;
64 bool m_heap;
65
66 public:
67 explicit MemoryRange(KernelCore& kernel)
68 : m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {}
69
70 void Set(KPhysicalAddress address, size_t size, bool heap) {
71 m_address = address;
72 m_size = size;
73 m_heap = heap;
74 }
75
76 KPhysicalAddress GetAddress() const {
77 return m_address;
78 }
79 size_t GetSize() const {
80 return m_size;
81 }
82 bool IsHeap() const {
83 return m_heap;
84 }
85
86 void Open();
87 void Close();
88 };
89
90protected:
91 enum MemoryFillValue : u8 {
92 MemoryFillValue_Zero = 0,
93 MemoryFillValue_Stack = 'X',
94 MemoryFillValue_Ipc = 'Y',
95 MemoryFillValue_Heap = 'Z',
96 };
97
98 enum class OperationType {
99 Map = 0,
100 MapGroup = 1,
101 MapFirstGroup = 2,
102 Unmap = 3,
103 ChangePermissions = 4,
104 ChangePermissionsAndRefresh = 5,
105 ChangePermissionsAndRefreshAndFlush = 6,
106 Separate = 7,
107 };
108
109 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
110 static constexpr size_t RegionAlignment = 2_MiB;
111 static_assert(RegionAlignment == KernelAslrAlignment);
112
113 struct PageLinkedList {
114 private:
115 struct Node {
116 Node* m_next;
117 std::array<u8, PageSize - sizeof(Node*)> m_buffer;
118 };
119 static_assert(std::is_trivial_v<Node>);
120
121 private:
122 Node* m_root{};
123
124 public:
125 constexpr PageLinkedList() : m_root(nullptr) {}
126
127 void Push(Node* n) {
128 ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
129 n->m_next = m_root;
130 m_root = n;
131 }
132
133 Node* Peek() const {
134 return m_root;
135 }
136
137 Node* Pop() {
138 Node* const r = m_root;
139
140 m_root = r->m_next;
141 r->m_next = nullptr;
142
143 return r;
144 }
145 };
146 static_assert(std::is_trivially_destructible_v<PageLinkedList>);
147
148 static constexpr auto DefaultMemoryIgnoreAttr =
149 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
150
151 static constexpr size_t GetAddressSpaceWidth(Svc::CreateProcessFlag as_type) {
152 switch (static_cast<Svc::CreateProcessFlag>(as_type &
153 Svc::CreateProcessFlag::AddressSpaceMask)) {
154 case Svc::CreateProcessFlag::AddressSpace64Bit:
155 return 39;
156 case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
157 return 36;
158 case Svc::CreateProcessFlag::AddressSpace32Bit:
159 case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
160 return 32;
161 default:
162 UNREACHABLE();
163 }
164 }
165
166private:
167 class KScopedPageTableUpdater {
168 private:
169 KPageTableBase* m_pt;
170 PageLinkedList m_ll;
171
172 public:
173 explicit KScopedPageTableUpdater(KPageTableBase* pt) : m_pt(pt), m_ll() {}
174 explicit KScopedPageTableUpdater(KPageTableBase& pt)
175 : KScopedPageTableUpdater(std::addressof(pt)) {}
176 ~KScopedPageTableUpdater() {
177 m_pt->FinalizeUpdate(this->GetPageList());
178 }
179
180 PageLinkedList* GetPageList() {
181 return std::addressof(m_ll);
182 }
183 };
184
185private:
186 KernelCore& m_kernel;
187 Core::System& m_system;
188 KProcessAddress m_address_space_start{};
189 KProcessAddress m_address_space_end{};
190 KProcessAddress m_heap_region_start{};
191 KProcessAddress m_heap_region_end{};
192 KProcessAddress m_current_heap_end{};
193 KProcessAddress m_alias_region_start{};
194 KProcessAddress m_alias_region_end{};
195 KProcessAddress m_stack_region_start{};
196 KProcessAddress m_stack_region_end{};
197 KProcessAddress m_kernel_map_region_start{};
198 KProcessAddress m_kernel_map_region_end{};
199 KProcessAddress m_alias_code_region_start{};
200 KProcessAddress m_alias_code_region_end{};
201 KProcessAddress m_code_region_start{};
202 KProcessAddress m_code_region_end{};
203 size_t m_max_heap_size{};
204 size_t m_mapped_physical_memory_size{};
205 size_t m_mapped_unsafe_physical_memory{};
206 size_t m_mapped_insecure_memory{};
207 size_t m_mapped_ipc_server_memory{};
208 mutable KLightLock m_general_lock;
209 mutable KLightLock m_map_physical_memory_lock;
210 KLightLock m_device_map_lock;
211 std::unique_ptr<Common::PageTable> m_impl{};
212 Core::Memory::Memory* m_memory{};
213 KMemoryBlockManager m_memory_block_manager{};
214 u32 m_allocate_option{};
215 u32 m_address_space_width{};
216 bool m_is_kernel{};
217 bool m_enable_aslr{};
218 bool m_enable_device_address_space_merge{};
219 KMemoryBlockSlabManager* m_memory_block_slab_manager{};
220 KBlockInfoManager* m_block_info_manager{};
221 KResourceLimit* m_resource_limit{};
222 const KMemoryRegion* m_cached_physical_linear_region{};
223 const KMemoryRegion* m_cached_physical_heap_region{};
224 MemoryFillValue m_heap_fill_value{};
225 MemoryFillValue m_ipc_fill_value{};
226 MemoryFillValue m_stack_fill_value{};
227
228public:
229 explicit KPageTableBase(KernelCore& kernel);
230 ~KPageTableBase();
231
232 Result InitializeForKernel(bool is_64_bit, KVirtualAddress start, KVirtualAddress end,
233 Core::Memory::Memory& memory);
234 Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
235 bool enable_device_address_space_merge, bool from_back,
236 KMemoryManager::Pool pool, KProcessAddress code_address,
237 size_t code_size, KSystemResource* system_resource,
238 KResourceLimit* resource_limit, Core::Memory::Memory& memory);
239
240 void Finalize();
241
242 bool IsKernel() const {
243 return m_is_kernel;
244 }
245 bool IsAslrEnabled() const {
246 return m_enable_aslr;
247 }
248
249 bool Contains(KProcessAddress addr) const {
250 return m_address_space_start <= addr && addr <= m_address_space_end - 1;
251 }
252
253 bool Contains(KProcessAddress addr, size_t size) const {
254 return m_address_space_start <= addr && addr < addr + size &&
255 addr + size - 1 <= m_address_space_end - 1;
256 }
257
258 bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
259 return this->Contains(addr, size) && m_alias_region_start <= addr &&
260 addr + size - 1 <= m_alias_region_end - 1;
261 }
262
263 bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
264 return this->Contains(addr, size) && m_heap_region_start <= addr &&
265 addr + size - 1 <= m_heap_region_end - 1;
266 }
267
268 bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
269 // Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the
270 // alias code region.
271 return this->CanContain(addr, size, Svc::MemoryState::AliasCode);
272 }
273
274 KScopedLightLock AcquireDeviceMapLock() {
275 return KScopedLightLock(m_device_map_lock);
276 }
277
278 KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
279 size_t GetRegionSize(Svc::MemoryState state) const;
280 bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
281
282 KProcessAddress GetRegionAddress(KMemoryState state) const {
283 return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
284 }
285 size_t GetRegionSize(KMemoryState state) const {
286 return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
287 }
288 bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
289 return this->CanContain(addr, size,
290 static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
291 }
292
293public:
294 Core::Memory::Memory& GetMemory() {
295 return *m_memory;
296 }
297
298 Core::Memory::Memory& GetMemory() const {
299 return *m_memory;
300 }
301
302 Common::PageTable& GetImpl() {
303 return *m_impl;
304 }
305
306 Common::PageTable& GetImpl() const {
307 return *m_impl;
308 }
309
310 size_t GetNumGuardPages() const {
311 return this->IsKernel() ? 1 : 4;
312 }
313
314protected:
315 // NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions
316 // in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived
317 // class, and this avoids unnecessary virtual function calls.
318 Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
319 KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties,
320 OperationType operation, bool reuse_ll);
321 Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
322 const KPageGroup& page_group, const KPageProperties properties,
323 OperationType operation, bool reuse_ll);
324 void FinalizeUpdate(PageLinkedList* page_list);
325
326 bool IsLockedByCurrentThread() const {
327 return m_general_lock.IsLockedByCurrentThread();
328 }
329
330 bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) {
331 ASSERT(this->IsLockedByCurrentThread());
332
333 return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
334 m_cached_physical_linear_region, phys_addr);
335 }
336
337 bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
338 ASSERT(this->IsLockedByCurrentThread());
339
340 return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
341 m_cached_physical_linear_region, phys_addr, size);
342 }
343
344 bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
345 ASSERT(this->IsLockedByCurrentThread());
346
347 return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
348 phys_addr);
349 }
350
351 bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
352 ASSERT(this->IsLockedByCurrentThread());
353
354 return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
355 phys_addr, size);
356 }
357
358 bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) {
359 ASSERT(!this->IsLockedByCurrentThread());
360
361 return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
362 phys_addr);
363 }
364
365 bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
366 return (m_address_space_start <= addr) &&
367 (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
368 (addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
369 }
370
371private:
372 KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
373 size_t num_pages, size_t alignment, size_t offset,
374 size_t guard_pages) const;
375
376 Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
377 KMemoryState state_mask, KMemoryState state,
378 KMemoryPermission perm_mask, KMemoryPermission perm,
379 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
380 Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
381 KMemoryState state, KMemoryPermission perm_mask,
382 KMemoryPermission perm, KMemoryAttribute attr_mask,
383 KMemoryAttribute attr) const {
384 R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
385 perm, attr_mask, attr));
386 }
387
388 Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
389 KMemoryPermission perm_mask, KMemoryPermission perm,
390 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
391 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
392 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
393 KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
394 KMemoryState state_mask, KMemoryState state,
395 KMemoryPermission perm_mask, KMemoryPermission perm,
396 KMemoryAttribute attr_mask, KMemoryAttribute attr,
397 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
398 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
399 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
400 KProcessAddress addr, size_t size, KMemoryState state_mask,
401 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
402 KMemoryAttribute attr_mask, KMemoryAttribute attr,
403 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
404 Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
405 KMemoryState state_mask, KMemoryState state,
406 KMemoryPermission perm_mask, KMemoryPermission perm,
407 KMemoryAttribute attr_mask, KMemoryAttribute attr,
408 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
409 R_RETURN(this->CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
410 state_mask, state, perm_mask, perm, attr_mask, attr,
411 ignore_attr));
412 }
413 Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
414 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
415 KMemoryAttribute attr_mask, KMemoryAttribute attr,
416 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
417 R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
418 attr_mask, attr, ignore_attr));
419 }
420
421 Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, KProcessAddress addr,
422 size_t size, KMemoryState state_mask, KMemoryState state,
423 KMemoryPermission perm_mask, KMemoryPermission perm,
424 KMemoryAttribute attr_mask, KMemoryAttribute attr,
425 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
426 Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
427 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
428 KMemoryAttribute attr_mask, KMemoryAttribute attr,
429 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
430 const KPageGroup* pg);
431
432 Result QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page,
433 KProcessAddress address) const;
434
435 Result QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size,
436 Svc::MemoryState state) const;
437
438 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
439 size_t num_pages, KMemoryPermission perm);
440 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
441 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
442
443 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
444 const KPageGroup& pg);
445
446 Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
447 bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
448
449 Result GetContiguousMemoryRangeWithState(MemoryRange* out, KProcessAddress address, size_t size,
450 KMemoryState state_mask, KMemoryState state,
451 KMemoryPermission perm_mask, KMemoryPermission perm,
452 KMemoryAttribute attr_mask, KMemoryAttribute attr);
453
454 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
455 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
456 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
457
458 Result MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, KPhysicalAddress phys_addr,
459 size_t size, KMemoryState state, KMemoryPermission perm);
460 Result ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, size_t size,
461 KMemoryState state);
462 Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, size_t size,
463 KMemoryState state);
464
465 Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
466 KProcessAddress address, size_t size, KMemoryPermission test_perm,
467 KMemoryState dst_state);
468 Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
469 KMemoryPermission test_perm, KMemoryState dst_state,
470 KPageTableBase& src_page_table, bool send);
471 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
472 size_t size, KMemoryPermission prot_perm);
473
474 size_t GetSize(KMemoryState state) const;
475
476 bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
477 // Validate pre-conditions.
478 ASSERT(this->IsLockedByCurrentThread());
479
480 return this->GetImpl().GetPhysicalAddress(out, virt_addr);
481 }
482
483public:
484 bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress virt_addr) const {
485 // Validate pre-conditions.
486 ASSERT(!this->IsLockedByCurrentThread());
487
488 // Acquire exclusive access to the table while doing address translation.
489 KScopedLightLock lk(m_general_lock);
490
491 return this->GetPhysicalAddressLocked(out, virt_addr);
492 }
493
494 KBlockInfoManager* GetBlockInfoManager() const {
495 return m_block_info_manager;
496 }
497
498 Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
499 Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
500 Svc::MemoryPermission perm);
501 Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
502 KMemoryAttribute attr);
503 Result SetHeapSize(KProcessAddress* out, size_t size);
504 Result SetMaxHeapSize(size_t size);
505 Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
506 KProcessAddress addr) const;
507 Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) const;
508 Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
509 R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Static));
510 }
511 Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
512 R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Io));
513 }
514 Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
515 Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
516 Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
517 Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
518 Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
519 Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
520 Svc::MemoryMapping mapping, Svc::MemoryPermission perm);
521 Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
522 Svc::MemoryMapping mapping);
523 Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
524 Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);
525 Result MapInsecureMemory(KProcessAddress address, size_t size);
526 Result UnmapInsecureMemory(KProcessAddress address, size_t size);
527
528 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
529 KPhysicalAddress phys_addr, KProcessAddress region_start,
530 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
531 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
532 region_num_pages, state, perm));
533 }
534
535 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
536 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
537 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
538 this->GetRegionAddress(state),
539 this->GetRegionSize(state) / PageSize, state, perm));
540 }
541
542 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
543 KMemoryPermission perm) {
544 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
545 this->GetRegionAddress(state),
546 this->GetRegionSize(state) / PageSize, state, perm));
547 }
548
549 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
550 KMemoryPermission perm);
551 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
552
553 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
554 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
555 KMemoryPermission perm);
556 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
557 KMemoryPermission perm);
558 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
559
560 Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
561 KMemoryState state_mask, KMemoryState state,
562 KMemoryPermission perm_mask, KMemoryPermission perm,
563 KMemoryAttribute attr_mask, KMemoryAttribute attr);
564
565 Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
566 Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size);
567
568 Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
569 Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
570 KMemoryState state);
571
572 Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
573 Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
574 KMemoryState state);
575
576 Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
577 KMemoryPermission perm, bool is_aligned, bool check_heap);
578 Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
579
580 Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
581 Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size);
582
583 Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
584 KProcessAddress address, size_t size,
585 KMemoryPermission perm, bool is_aligned);
586 Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, KProcessAddress address,
587 size_t size);
588
589 Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
590 Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
591
592 Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
593 KMemoryPermission perm);
594 Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
595 Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size);
596 Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
597
598 Result OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KProcessAddress address,
599 size_t size);
600
601 Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
602 KProcessAddress src_addr, KMemoryState src_state_mask,
603 KMemoryState src_state, KMemoryPermission src_test_perm,
604 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
605 Result CopyMemoryFromLinearToKernel(void* buffer, size_t size, KProcessAddress src_addr,
606 KMemoryState src_state_mask, KMemoryState src_state,
607 KMemoryPermission src_test_perm,
608 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
609 Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
610 KMemoryState dst_state_mask, KMemoryState dst_state,
611 KMemoryPermission dst_test_perm,
612 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
613 KProcessAddress src_addr);
614 Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
615 KMemoryState dst_state_mask, KMemoryState dst_state,
616 KMemoryPermission dst_test_perm,
617 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
618 void* buffer);
619 Result CopyMemoryFromHeapToHeap(KPageTableBase& dst_page_table, KProcessAddress dst_addr,
620 size_t size, KMemoryState dst_state_mask,
621 KMemoryState dst_state, KMemoryPermission dst_test_perm,
622 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
623 KProcessAddress src_addr, KMemoryState src_state_mask,
624 KMemoryState src_state, KMemoryPermission src_test_perm,
625 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
626 Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
627 KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
628 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
629 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
630 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
631 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
632
633 Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
634 KPageTableBase& src_page_table, KMemoryPermission test_perm,
635 KMemoryState dst_state, bool send);
636 Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
637 Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
638
639 Result MapPhysicalMemory(KProcessAddress address, size_t size);
640 Result UnmapPhysicalMemory(KProcessAddress address, size_t size);
641
642 Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
643 Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
644
645 Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase& src_pt,
646 KProcessAddress src_address);
647
648public:
649 KProcessAddress GetAddressSpaceStart() const {
650 return m_address_space_start;
651 }
652 KProcessAddress GetHeapRegionStart() const {
653 return m_heap_region_start;
654 }
655 KProcessAddress GetAliasRegionStart() const {
656 return m_alias_region_start;
657 }
658 KProcessAddress GetStackRegionStart() const {
659 return m_stack_region_start;
660 }
661 KProcessAddress GetKernelMapRegionStart() const {
662 return m_kernel_map_region_start;
663 }
664 KProcessAddress GetCodeRegionStart() const {
665 return m_code_region_start;
666 }
667 KProcessAddress GetAliasCodeRegionStart() const {
668 return m_alias_code_region_start;
669 }
670
671 size_t GetAddressSpaceSize() const {
672 return m_address_space_end - m_address_space_start;
673 }
674 size_t GetHeapRegionSize() const {
675 return m_heap_region_end - m_heap_region_start;
676 }
677 size_t GetAliasRegionSize() const {
678 return m_alias_region_end - m_alias_region_start;
679 }
680 size_t GetStackRegionSize() const {
681 return m_stack_region_end - m_stack_region_start;
682 }
683 size_t GetKernelMapRegionSize() const {
684 return m_kernel_map_region_end - m_kernel_map_region_start;
685 }
686 size_t GetCodeRegionSize() const {
687 return m_code_region_end - m_code_region_start;
688 }
689 size_t GetAliasCodeRegionSize() const {
690 return m_alias_code_region_end - m_alias_code_region_start;
691 }
692
693 size_t GetNormalMemorySize() const {
694 // Lock the table.
695 KScopedLightLock lk(m_general_lock);
696
697 return (m_current_heap_end - m_heap_region_start) + m_mapped_physical_memory_size;
698 }
699
700 size_t GetCodeSize() const;
701 size_t GetCodeDataSize() const;
702 size_t GetAliasCodeSize() const;
703 size_t GetAliasCodeDataSize() const;
704
705 u32 GetAllocateOption() const {
706 return m_allocate_option;
707 }
708
709 u32 GetAddressSpaceWidth() const {
710 return m_address_space_width;
711 }
712
713public:
714 // Linear mapped
715 static u8* GetLinearMappedVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
716 return kernel.System().DeviceMemory().GetPointer<u8>(addr);
717 }
718
719 static KPhysicalAddress GetLinearMappedPhysicalAddress(KernelCore& kernel,
720 KVirtualAddress addr) {
721 return kernel.MemoryLayout().GetLinearPhysicalAddress(addr);
722 }
723
724 static KVirtualAddress GetLinearMappedVirtualAddress(KernelCore& kernel,
725 KPhysicalAddress addr) {
726 return kernel.MemoryLayout().GetLinearVirtualAddress(addr);
727 }
728
729 // Heap
730 static u8* GetHeapVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
731 return kernel.System().DeviceMemory().GetPointer<u8>(addr);
732 }
733
734 static KPhysicalAddress GetHeapPhysicalAddress(KernelCore& kernel, KVirtualAddress addr) {
735 return GetLinearMappedPhysicalAddress(kernel, addr);
736 }
737
738 static KVirtualAddress GetHeapVirtualAddress(KernelCore& kernel, KPhysicalAddress addr) {
739 return GetLinearMappedVirtualAddress(kernel, addr);
740 }
741
742 // Member heap
743 u8* GetHeapVirtualPointer(KPhysicalAddress addr) {
744 return GetHeapVirtualPointer(m_kernel, addr);
745 }
746
747 KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
748 return GetHeapPhysicalAddress(m_kernel, addr);
749 }
750
751 KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
752 return GetHeapVirtualAddress(m_kernel, addr);
753 }
754
755 // TODO: GetPageTableVirtualAddress
756 // TODO: GetPageTablePhysicalAddress
757};
758
759} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 1f4b0755d..3cfb414e5 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -298,9 +298,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
298 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); 298 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
299 const bool enable_das_merge = 299 const bool enable_das_merge =
300 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 300 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
301 R_TRY(m_page_table.InitializeForProcess( 301 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
302 as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, 302 params.code_address, params.code_num_pages * PageSize,
303 params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); 303 m_system_resource, res_limit, this->GetMemory()));
304 } 304 }
305 ON_RESULT_FAILURE_2 { 305 ON_RESULT_FAILURE_2 {
306 m_page_table.Finalize(); 306 m_page_table.Finalize();
@@ -391,9 +391,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
391 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); 391 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
392 const bool enable_das_merge = 392 const bool enable_das_merge =
393 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 393 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
394 R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, 394 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
395 !enable_aslr, pool, params.code_address, code_size, 395 params.code_address, code_size, m_system_resource, res_limit,
396 m_system_resource, res_limit, this->GetMemory())); 396 this->GetMemory()));
397 } 397 }
398 ON_RESULT_FAILURE_2 { 398 ON_RESULT_FAILURE_2 {
399 m_page_table.Finalize(); 399 m_page_table.Finalize();
@@ -1122,9 +1122,9 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_
1122void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} 1122void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
1123 1123
1124KProcess::KProcess(KernelCore& kernel) 1124KProcess::KProcess(KernelCore& kernel)
1125 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, 1125 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
1126 m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, 1126 m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
1127 m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} 1127 m_handle_table{kernel} {}
1128KProcess::~KProcess() = default; 1128KProcess::~KProcess() = default;
1129 1129
1130Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, 1130Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index f9f755afa..8339465fd 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -5,13 +5,14 @@
5 5
6#include <map> 6#include <map>
7 7
8#include "core/file_sys/program_metadata.h"
8#include "core/hle/kernel/code_set.h" 9#include "core/hle/kernel/code_set.h"
9#include "core/hle/kernel/k_address_arbiter.h" 10#include "core/hle/kernel/k_address_arbiter.h"
10#include "core/hle/kernel/k_capabilities.h" 11#include "core/hle/kernel/k_capabilities.h"
11#include "core/hle/kernel/k_condition_variable.h" 12#include "core/hle/kernel/k_condition_variable.h"
12#include "core/hle/kernel/k_handle_table.h" 13#include "core/hle/kernel/k_handle_table.h"
13#include "core/hle/kernel/k_page_table.h"
14#include "core/hle/kernel/k_page_table_manager.h" 14#include "core/hle/kernel/k_page_table_manager.h"
15#include "core/hle/kernel/k_process_page_table.h"
15#include "core/hle/kernel/k_system_resource.h" 16#include "core/hle/kernel/k_system_resource.h"
16#include "core/hle/kernel/k_thread.h" 17#include "core/hle/kernel/k_thread.h"
17#include "core/hle/kernel/k_thread_local_page.h" 18#include "core/hle/kernel/k_thread_local_page.h"
@@ -65,7 +66,7 @@ private:
65 using TLPIterator = TLPTree::iterator; 66 using TLPIterator = TLPTree::iterator;
66 67
67private: 68private:
68 KPageTable m_page_table; 69 KProcessPageTable m_page_table;
69 std::atomic<size_t> m_used_kernel_memory_size{}; 70 std::atomic<size_t> m_used_kernel_memory_size{};
70 TLPTree m_fully_used_tlp_tree{}; 71 TLPTree m_fully_used_tlp_tree{};
71 TLPTree m_partially_used_tlp_tree{}; 72 TLPTree m_partially_used_tlp_tree{};
@@ -254,9 +255,8 @@ public:
254 return m_is_hbl; 255 return m_is_hbl;
255 } 256 }
256 257
257 Kernel::KMemoryManager::Direction GetAllocateOption() const { 258 u32 GetAllocateOption() const {
258 // TODO: property of the KPageTableBase 259 return m_page_table.GetAllocateOption();
259 return KMemoryManager::Direction::FromFront;
260 } 260 }
261 261
262 ThreadList& GetThreadList() { 262 ThreadList& GetThreadList() {
@@ -295,10 +295,10 @@ public:
295 return m_list_lock; 295 return m_list_lock;
296 } 296 }
297 297
298 KPageTable& GetPageTable() { 298 KProcessPageTable& GetPageTable() {
299 return m_page_table; 299 return m_page_table;
300 } 300 }
301 const KPageTable& GetPageTable() const { 301 const KProcessPageTable& GetPageTable() const {
302 return m_page_table; 302 return m_page_table;
303 } 303 }
304 304
diff --git a/src/core/hle/kernel/k_process_page_table.h b/src/core/hle/kernel/k_process_page_table.h
new file mode 100644
index 000000000..b7ae5abd0
--- /dev/null
+++ b/src/core/hle/kernel/k_process_page_table.h
@@ -0,0 +1,480 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_page_table.h"
7#include "core/hle/kernel/k_scoped_lock.h"
8#include "core/hle/kernel/svc_types.h"
9
10namespace Core {
11class ARM_Interface;
12}
13
14namespace Kernel {
15
16class KProcessPageTable {
17private:
18 KPageTable m_page_table;
19
20public:
21 KProcessPageTable(KernelCore& kernel) : m_page_table(kernel) {}
22
23 Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
24 bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
25 size_t code_size, KSystemResource* system_resource,
26 KResourceLimit* resource_limit, Core::Memory::Memory& memory) {
27 R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge,
28 from_back, pool, code_address, code_size,
29 system_resource, resource_limit, memory));
30 }
31
32 void Finalize() {
33 m_page_table.Finalize();
34 }
35
36 Core::Memory::Memory& GetMemory() {
37 return m_page_table.GetMemory();
38 }
39
40 Core::Memory::Memory& GetMemory() const {
41 return m_page_table.GetMemory();
42 }
43
44 Common::PageTable& GetImpl() {
45 return m_page_table.GetImpl();
46 }
47
48 Common::PageTable& GetImpl() const {
49 return m_page_table.GetImpl();
50 }
51
52 size_t GetNumGuardPages() const {
53 return m_page_table.GetNumGuardPages();
54 }
55
56 KScopedLightLock AcquireDeviceMapLock() {
57 return m_page_table.AcquireDeviceMapLock();
58 }
59
60 Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm) {
61 R_RETURN(m_page_table.SetMemoryPermission(addr, size, perm));
62 }
63
64 Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
65 Svc::MemoryPermission perm) {
66 R_RETURN(m_page_table.SetProcessMemoryPermission(addr, size, perm));
67 }
68
69 Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
70 KMemoryAttribute attr) {
71 R_RETURN(m_page_table.SetMemoryAttribute(addr, size, mask, attr));
72 }
73
74 Result SetHeapSize(KProcessAddress* out, size_t size) {
75 R_RETURN(m_page_table.SetHeapSize(out, size));
76 }
77
78 Result SetMaxHeapSize(size_t size) {
79 R_RETURN(m_page_table.SetMaxHeapSize(size));
80 }
81
82 Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
83 KProcessAddress addr) const {
84 R_RETURN(m_page_table.QueryInfo(out_info, out_page_info, addr));
85 }
86
87 Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) {
88 R_RETURN(m_page_table.QueryPhysicalAddress(out, address));
89 }
90
91 Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
92 R_RETURN(m_page_table.QueryStaticMapping(out, address, size));
93 }
94
95 Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
96 R_RETURN(m_page_table.QueryIoMapping(out, address, size));
97 }
98
99 Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
100 R_RETURN(m_page_table.MapMemory(dst_address, src_address, size));
101 }
102
103 Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
104 R_RETURN(m_page_table.UnmapMemory(dst_address, src_address, size));
105 }
106
107 Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
108 R_RETURN(m_page_table.MapCodeMemory(dst_address, src_address, size));
109 }
110
111 Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
112 R_RETURN(m_page_table.UnmapCodeMemory(dst_address, src_address, size));
113 }
114
115 Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
116 R_RETURN(m_page_table.MapIo(phys_addr, size, perm));
117 }
118
119 Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
120 Svc::MemoryMapping mapping, Svc::MemoryPermission perm) {
121 R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm));
122 }
123
124 Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
125 Svc::MemoryMapping mapping) {
126 R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping));
127 }
128
129 Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
130 R_RETURN(m_page_table.MapStatic(phys_addr, size, perm));
131 }
132
133 Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
134 R_RETURN(m_page_table.MapRegion(region_type, perm));
135 }
136
137 Result MapInsecureMemory(KProcessAddress address, size_t size) {
138 R_RETURN(m_page_table.MapInsecureMemory(address, size));
139 }
140
141 Result UnmapInsecureMemory(KProcessAddress address, size_t size) {
142 R_RETURN(m_page_table.UnmapInsecureMemory(address, size));
143 }
144
145 Result MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
146 KMemoryPermission perm) {
147 R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm));
148 }
149
150 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state) {
151 R_RETURN(m_page_table.UnmapPageGroup(address, pg, state));
152 }
153
154 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
155 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
156 R_RETURN(m_page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm));
157 }
158
159 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
160 KMemoryPermission perm) {
161 R_RETURN(m_page_table.MapPages(out_addr, num_pages, state, perm));
162 }
163
164 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
165 KMemoryPermission perm) {
166 R_RETURN(m_page_table.MapPages(address, num_pages, state, perm));
167 }
168
169 Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
170 R_RETURN(m_page_table.UnmapPages(addr, num_pages, state));
171 }
172
173 Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
174 KMemoryState state_mask, KMemoryState state,
175 KMemoryPermission perm_mask, KMemoryPermission perm,
176 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
177 R_RETURN(m_page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state,
178 perm_mask, perm, attr_mask, attr));
179 }
180
181 Result InvalidateProcessDataCache(KProcessAddress address, size_t size) {
182 R_RETURN(m_page_table.InvalidateProcessDataCache(address, size));
183 }
184
185 Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
186 R_RETURN(m_page_table.ReadDebugMemory(dst_address, src_address, size));
187 }
188
189 Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
190 KMemoryState state) {
191 R_RETURN(m_page_table.ReadDebugIoMemory(dst_address, src_address, size, state));
192 }
193
194 Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
195 R_RETURN(m_page_table.WriteDebugMemory(dst_address, src_address, size));
196 }
197
198 Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
199 KMemoryState state) {
200 R_RETURN(m_page_table.WriteDebugIoMemory(dst_address, src_address, size, state));
201 }
202
203 Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
204 KMemoryPermission perm, bool is_aligned, bool check_heap) {
205 R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm,
206 is_aligned, check_heap));
207 }
208
209 Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) {
210 R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap));
211 }
212
213 Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
214 R_RETURN(m_page_table.UnlockForDeviceAddressSpace(address, size));
215 }
216
217 Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) {
218 R_RETURN(m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size));
219 }
220
221 Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
222 KProcessAddress address, size_t size,
223 KMemoryPermission perm, bool is_aligned) {
224 R_RETURN(m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm,
225 is_aligned));
226 }
227
228 Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
229 KProcessAddress address, size_t size) {
230 R_RETURN(m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size));
231 }
232
233 Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size) {
234 R_RETURN(m_page_table.LockForIpcUserBuffer(out, address, size));
235 }
236
237 Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
238 R_RETURN(m_page_table.UnlockForIpcUserBuffer(address, size));
239 }
240
241 Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
242 KMemoryPermission perm) {
243 R_RETURN(m_page_table.LockForTransferMemory(out, address, size, perm));
244 }
245
246 Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
247 R_RETURN(m_page_table.UnlockForTransferMemory(address, size, pg));
248 }
249
250 Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) {
251 R_RETURN(m_page_table.LockForCodeMemory(out, address, size));
252 }
253
254 Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
255 R_RETURN(m_page_table.UnlockForCodeMemory(address, size, pg));
256 }
257
258 Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange* out,
259 KProcessAddress address, size_t size) {
260 R_RETURN(m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size));
261 }
262
263 Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
264 KProcessAddress src_addr, KMemoryState src_state_mask,
265 KMemoryState src_state, KMemoryPermission src_test_perm,
266 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
267 R_RETURN(m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask,
268 src_state, src_test_perm, src_attr_mask,
269 src_attr));
270 }
271
272 Result CopyMemoryFromLinearToKernel(void* dst_addr, size_t size, KProcessAddress src_addr,
273 KMemoryState src_state_mask, KMemoryState src_state,
274 KMemoryPermission src_test_perm,
275 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
276 R_RETURN(m_page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask,
277 src_state, src_test_perm, src_attr_mask,
278 src_attr));
279 }
280
281 Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
282 KMemoryState dst_state_mask, KMemoryState dst_state,
283 KMemoryPermission dst_test_perm,
284 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
285 KProcessAddress src_addr) {
286 R_RETURN(m_page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state,
287 dst_test_perm, dst_attr_mask, dst_attr,
288 src_addr));
289 }
290
291 Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
292 KMemoryState dst_state_mask, KMemoryState dst_state,
293 KMemoryPermission dst_test_perm,
294 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
295 void* src_addr) {
296 R_RETURN(m_page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask,
297 dst_state, dst_test_perm, dst_attr_mask,
298 dst_attr, src_addr));
299 }
300
301 Result CopyMemoryFromHeapToHeap(KProcessPageTable& dst_page_table, KProcessAddress dst_addr,
302 size_t size, KMemoryState dst_state_mask,
303 KMemoryState dst_state, KMemoryPermission dst_test_perm,
304 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
305 KProcessAddress src_addr, KMemoryState src_state_mask,
306 KMemoryState src_state, KMemoryPermission src_test_perm,
307 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
308 R_RETURN(m_page_table.CopyMemoryFromHeapToHeap(
309 dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
310 dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
311 src_attr_mask, src_attr));
312 }
313
314 Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
315 KProcessPageTable& dst_page_table, KProcessAddress dst_addr, size_t size,
316 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
317 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
318 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
319 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
320 R_RETURN(m_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
321 dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
322 dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
323 src_attr_mask, src_attr));
324 }
325
326 Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
327 KProcessPageTable& src_page_table, KMemoryPermission test_perm,
328 KMemoryState dst_state, bool send) {
329 R_RETURN(m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table,
330 test_perm, dst_state, send));
331 }
332
333 Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) {
334 R_RETURN(m_page_table.CleanupForIpcServer(address, size, dst_state));
335 }
336
337 Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) {
338 R_RETURN(m_page_table.CleanupForIpcClient(address, size, dst_state));
339 }
340
341 Result MapPhysicalMemory(KProcessAddress address, size_t size) {
342 R_RETURN(m_page_table.MapPhysicalMemory(address, size));
343 }
344
345 Result UnmapPhysicalMemory(KProcessAddress address, size_t size) {
346 R_RETURN(m_page_table.UnmapPhysicalMemory(address, size));
347 }
348
349 Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
350 R_RETURN(m_page_table.MapPhysicalMemoryUnsafe(address, size));
351 }
352
353 Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
354 R_RETURN(m_page_table.UnmapPhysicalMemoryUnsafe(address, size));
355 }
356
357 Result UnmapProcessMemory(KProcessAddress dst_address, size_t size,
358 KProcessPageTable& src_page_table, KProcessAddress src_address) {
359 R_RETURN(m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table,
360 src_address));
361 }
362
363 bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress address) {
364 return m_page_table.GetPhysicalAddress(out, address);
365 }
366
367 bool Contains(KProcessAddress addr, size_t size) const {
368 return m_page_table.Contains(addr, size);
369 }
370
371 bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
372 return m_page_table.IsInAliasRegion(addr, size);
373 }
374 bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
375 return m_page_table.IsInHeapRegion(addr, size);
376 }
377 bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
378 return m_page_table.IsInUnsafeAliasRegion(addr, size);
379 }
380
381 bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
382 return m_page_table.CanContain(addr, size, state);
383 }
384
385 KProcessAddress GetAddressSpaceStart() const {
386 return m_page_table.GetAddressSpaceStart();
387 }
388 KProcessAddress GetHeapRegionStart() const {
389 return m_page_table.GetHeapRegionStart();
390 }
391 KProcessAddress GetAliasRegionStart() const {
392 return m_page_table.GetAliasRegionStart();
393 }
394 KProcessAddress GetStackRegionStart() const {
395 return m_page_table.GetStackRegionStart();
396 }
397 KProcessAddress GetKernelMapRegionStart() const {
398 return m_page_table.GetKernelMapRegionStart();
399 }
400 KProcessAddress GetCodeRegionStart() const {
401 return m_page_table.GetCodeRegionStart();
402 }
403 KProcessAddress GetAliasCodeRegionStart() const {
404 return m_page_table.GetAliasCodeRegionStart();
405 }
406
407 size_t GetAddressSpaceSize() const {
408 return m_page_table.GetAddressSpaceSize();
409 }
410 size_t GetHeapRegionSize() const {
411 return m_page_table.GetHeapRegionSize();
412 }
413 size_t GetAliasRegionSize() const {
414 return m_page_table.GetAliasRegionSize();
415 }
416 size_t GetStackRegionSize() const {
417 return m_page_table.GetStackRegionSize();
418 }
419 size_t GetKernelMapRegionSize() const {
420 return m_page_table.GetKernelMapRegionSize();
421 }
422 size_t GetCodeRegionSize() const {
423 return m_page_table.GetCodeRegionSize();
424 }
425 size_t GetAliasCodeRegionSize() const {
426 return m_page_table.GetAliasCodeRegionSize();
427 }
428
429 size_t GetNormalMemorySize() const {
430 return m_page_table.GetNormalMemorySize();
431 }
432
433 size_t GetCodeSize() const {
434 return m_page_table.GetCodeSize();
435 }
436 size_t GetCodeDataSize() const {
437 return m_page_table.GetCodeDataSize();
438 }
439
440 size_t GetAliasCodeSize() const {
441 return m_page_table.GetAliasCodeSize();
442 }
443 size_t GetAliasCodeDataSize() const {
444 return m_page_table.GetAliasCodeDataSize();
445 }
446
447 u32 GetAllocateOption() const {
448 return m_page_table.GetAllocateOption();
449 }
450
451 u32 GetAddressSpaceWidth() const {
452 return m_page_table.GetAddressSpaceWidth();
453 }
454
455 KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) {
456 return m_page_table.GetHeapPhysicalAddress(address);
457 }
458
459 u8* GetHeapVirtualPointer(KPhysicalAddress address) {
460 return m_page_table.GetHeapVirtualPointer(address);
461 }
462
463 KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) {
464 return m_page_table.GetHeapVirtualAddress(address);
465 }
466
467 KBlockInfoManager* GetBlockInfoManager() {
468 return m_page_table.GetBlockInfoManager();
469 }
470
471 KPageTable& GetBasePageTable() {
472 return m_page_table;
473 }
474
475 const KPageTable& GetBasePageTable() const {
476 return m_page_table;
477 }
478};
479
480} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index c64ceb530..3ea653163 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -383,7 +383,7 @@ Result KServerSession::SendReply(bool is_hle) {
383 if (event != nullptr) { 383 if (event != nullptr) {
384 // // Get the client process/page table. 384 // // Get the client process/page table.
385 // KProcess *client_process = client_thread->GetOwnerProcess(); 385 // KProcess *client_process = client_thread->GetOwnerProcess();
386 // KPageTable *client_page_table = std::addressof(client_process->PageTable()); 386 // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable());
387 387
388 // // If we need to, reply with an async error. 388 // // If we need to, reply with an async error.
389 // if (R_FAILED(client_result)) { 389 // if (R_FAILED(client_result)) {
diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp
index 07e92aa80..b51941faf 100644
--- a/src/core/hle/kernel/k_system_resource.cpp
+++ b/src/core/hle/kernel/k_system_resource.cpp
@@ -40,7 +40,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l
40 40
41 // Get resource pointer. 41 // Get resource pointer.
42 KPhysicalAddress resource_paddr = 42 KPhysicalAddress resource_paddr =
43 KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); 43 KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address);
44 auto* resource = 44 auto* resource =
45 m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); 45 m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr);
46 46
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
index 2c45b4232..a632d1634 100644
--- a/src/core/hle/kernel/k_thread_local_page.cpp
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -37,8 +37,8 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
37 37
38Result KThreadLocalPage::Finalize() { 38Result KThreadLocalPage::Finalize() {
39 // Get the physical address of the page. 39 // Get the physical address of the page.
40 const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr); 40 KPhysicalAddress phys_addr{};
41 ASSERT(phys_addr); 41 ASSERT(m_owner->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), m_virt_addr));
42 42
43 // Unmap the page. 43 // Unmap the page.
44 R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); 44 R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
deleted file mode 100644
index 773319ad8..000000000
--- a/src/core/hle/kernel/process_capability.cpp
+++ /dev/null
@@ -1,389 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <bit>
5
6#include "common/bit_util.h"
7#include "common/logging/log.h"
8#include "core/hle/kernel/k_handle_table.h"
9#include "core/hle/kernel/k_page_table.h"
10#include "core/hle/kernel/process_capability.h"
11#include "core/hle/kernel/svc_results.h"
12
13namespace Kernel {
14namespace {
15
16// clang-format off
17
18// Shift offsets for kernel capability types.
19enum : u32 {
20 CapabilityOffset_PriorityAndCoreNum = 3,
21 CapabilityOffset_Syscall = 4,
22 CapabilityOffset_MapPhysical = 6,
23 CapabilityOffset_MapIO = 7,
24 CapabilityOffset_MapRegion = 10,
25 CapabilityOffset_Interrupt = 11,
26 CapabilityOffset_ProgramType = 13,
27 CapabilityOffset_KernelVersion = 14,
28 CapabilityOffset_HandleTableSize = 15,
29 CapabilityOffset_Debug = 16,
30};
31
32// Combined mask of all parameters that may be initialized only once.
33constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
34 (1U << CapabilityOffset_ProgramType) |
35 (1U << CapabilityOffset_KernelVersion) |
36 (1U << CapabilityOffset_HandleTableSize) |
37 (1U << CapabilityOffset_Debug);
38
39// Packed kernel version indicating 10.4.0
40constexpr u32 PackedKernelVersion = 0x520000;
41
42// Indicates possible types of capabilities that can be specified.
43enum class CapabilityType : u32 {
44 Unset = 0U,
45 PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
46 Syscall = (1U << CapabilityOffset_Syscall) - 1,
47 MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
48 MapIO = (1U << CapabilityOffset_MapIO) - 1,
49 MapRegion = (1U << CapabilityOffset_MapRegion) - 1,
50 Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
51 ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
52 KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
53 HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
54 Debug = (1U << CapabilityOffset_Debug) - 1,
55 Ignorable = 0xFFFFFFFFU,
56};
57
58// clang-format on
59
60constexpr CapabilityType GetCapabilityType(u32 value) {
61 return static_cast<CapabilityType>((~value & (value + 1)) - 1);
62}
63
64u32 GetFlagBitOffset(CapabilityType type) {
65 const auto value = static_cast<u32>(type);
66 return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value)));
67}
68
69} // Anonymous namespace
70
71Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
72 std::size_t num_capabilities,
73 KPageTable& page_table) {
74 Clear();
75
76 // Allow all cores and priorities.
77 core_mask = 0xF;
78 priority_mask = 0xFFFFFFFFFFFFFFFF;
79 kernel_version = PackedKernelVersion;
80
81 return ParseCapabilities(capabilities, num_capabilities, page_table);
82}
83
84Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
85 std::size_t num_capabilities,
86 KPageTable& page_table) {
87 Clear();
88
89 return ParseCapabilities(capabilities, num_capabilities, page_table);
90}
91
92void ProcessCapabilities::InitializeForMetadatalessProcess() {
93 // Allow all cores and priorities
94 core_mask = 0xF;
95 priority_mask = 0xFFFFFFFFFFFFFFFF;
96 kernel_version = PackedKernelVersion;
97
98 // Allow all system calls and interrupts.
99 svc_capabilities.set();
100 interrupt_capabilities.set();
101
102 // Allow using the maximum possible amount of handles
103 handle_table_size = static_cast<s32>(KHandleTable::MaxTableSize);
104
105 // Allow all debugging capabilities.
106 is_debuggable = true;
107 can_force_debug = true;
108}
109
110Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
111 KPageTable& page_table) {
112 u32 set_flags = 0;
113 u32 set_svc_bits = 0;
114
115 for (std::size_t i = 0; i < num_capabilities; ++i) {
116 const u32 descriptor = capabilities[i];
117 const auto type = GetCapabilityType(descriptor);
118
119 if (type == CapabilityType::MapPhysical) {
120 i++;
121
122 // The MapPhysical type uses two descriptor flags for its parameters.
123 // If there's only one, then there's a problem.
124 if (i >= num_capabilities) {
125 LOG_ERROR(Kernel, "Invalid combination! i={}", i);
126 return ResultInvalidCombination;
127 }
128
129 const auto size_flags = capabilities[i];
130 if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
131 LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags);
132 return ResultInvalidCombination;
133 }
134
135 const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);
136 if (result.IsError()) {
137 LOG_ERROR(Kernel, "Failed to map physical flags! descriptor={}, size_flags={}",
138 descriptor, size_flags);
139 return result;
140 }
141 } else {
142 const auto result =
143 ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table);
144 if (result.IsError()) {
145 LOG_ERROR(
146 Kernel,
147 "Failed to parse capability flag! set_flags={}, set_svc_bits={}, descriptor={}",
148 set_flags, set_svc_bits, descriptor);
149 return result;
150 }
151 }
152 }
153
154 return ResultSuccess;
155}
156
157Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
158 KPageTable& page_table) {
159 const auto type = GetCapabilityType(flag);
160
161 if (type == CapabilityType::Unset) {
162 return ResultInvalidArgument;
163 }
164
165 // Bail early on ignorable entries, as one would expect,
166 // ignorable descriptors can be ignored.
167 if (type == CapabilityType::Ignorable) {
168 return ResultSuccess;
169 }
170
171 // Ensure that the give flag hasn't already been initialized before.
172 // If it has been, then bail.
173 const u32 flag_length = GetFlagBitOffset(type);
174 const u32 set_flag = 1U << flag_length;
175 if ((set_flag & set_flags & InitializeOnceMask) != 0) {
176 LOG_ERROR(Kernel,
177 "Attempted to initialize flags that may only be initialized once. set_flags={}",
178 set_flags);
179 return ResultInvalidCombination;
180 }
181 set_flags |= set_flag;
182
183 switch (type) {
184 case CapabilityType::PriorityAndCoreNum:
185 return HandlePriorityCoreNumFlags(flag);
186 case CapabilityType::Syscall:
187 return HandleSyscallFlags(set_svc_bits, flag);
188 case CapabilityType::MapIO:
189 return HandleMapIOFlags(flag, page_table);
190 case CapabilityType::MapRegion:
191 return HandleMapRegionFlags(flag, page_table);
192 case CapabilityType::Interrupt:
193 return HandleInterruptFlags(flag);
194 case CapabilityType::ProgramType:
195 return HandleProgramTypeFlags(flag);
196 case CapabilityType::KernelVersion:
197 return HandleKernelVersionFlags(flag);
198 case CapabilityType::HandleTableSize:
199 return HandleHandleTableFlags(flag);
200 case CapabilityType::Debug:
201 return HandleDebugFlags(flag);
202 default:
203 break;
204 }
205
206 LOG_ERROR(Kernel, "Invalid capability type! type={}", type);
207 return ResultInvalidArgument;
208}
209
210void ProcessCapabilities::Clear() {
211 svc_capabilities.reset();
212 interrupt_capabilities.reset();
213
214 core_mask = 0;
215 priority_mask = 0;
216
217 handle_table_size = 0;
218 kernel_version = 0;
219
220 program_type = ProgramType::SysModule;
221
222 is_debuggable = false;
223 can_force_debug = false;
224}
225
226Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
227 if (priority_mask != 0 || core_mask != 0) {
228 LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
229 priority_mask, core_mask);
230 return ResultInvalidArgument;
231 }
232
233 const u32 core_num_min = (flags >> 16) & 0xFF;
234 const u32 core_num_max = (flags >> 24) & 0xFF;
235 if (core_num_min > core_num_max) {
236 LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}",
237 core_num_min, core_num_max);
238 return ResultInvalidCombination;
239 }
240
241 const u32 priority_min = (flags >> 10) & 0x3F;
242 const u32 priority_max = (flags >> 4) & 0x3F;
243 if (priority_min > priority_max) {
244 LOG_ERROR(Kernel,
245 "Priority min is greater than priority max! priority_min={}, priority_max={}",
246 core_num_min, priority_max);
247 return ResultInvalidCombination;
248 }
249
250 // The switch only has 4 usable cores.
251 if (core_num_max >= 4) {
252 LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max);
253 return ResultInvalidCoreId;
254 }
255
256 const auto make_mask = [](u64 min, u64 max) {
257 const u64 range = max - min + 1;
258 const u64 mask = (1ULL << range) - 1;
259
260 return mask << min;
261 };
262
263 core_mask = make_mask(core_num_min, core_num_max);
264 priority_mask = make_mask(priority_min, priority_max);
265 return ResultSuccess;
266}
267
268Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
269 const u32 index = flags >> 29;
270 const u32 svc_bit = 1U << index;
271
272 // If we've already set this svc before, bail.
273 if ((set_svc_bits & svc_bit) != 0) {
274 return ResultInvalidCombination;
275 }
276 set_svc_bits |= svc_bit;
277
278 const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
279 for (u32 i = 0; i < 24; ++i) {
280 const u32 svc_number = index * 24 + i;
281
282 if ((svc_mask & (1U << i)) == 0) {
283 continue;
284 }
285
286 svc_capabilities[svc_number] = true;
287 }
288
289 return ResultSuccess;
290}
291
292Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
293 KPageTable& page_table) {
294 // TODO(Lioncache): Implement once the memory manager can handle this.
295 return ResultSuccess;
296}
297
298Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
299 // TODO(Lioncache): Implement once the memory manager can handle this.
300 return ResultSuccess;
301}
302
303Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
304 // TODO(Lioncache): Implement once the memory manager can handle this.
305 return ResultSuccess;
306}
307
308Result ProcessCapabilities::HandleInterruptFlags(u32 flags) {
309 constexpr u32 interrupt_ignore_value = 0x3FF;
310 const u32 interrupt0 = (flags >> 12) & 0x3FF;
311 const u32 interrupt1 = (flags >> 22) & 0x3FF;
312
313 for (u32 interrupt : {interrupt0, interrupt1}) {
314 if (interrupt == interrupt_ignore_value) {
315 continue;
316 }
317
318 // NOTE:
319 // This should be checking a generic interrupt controller value
320 // as part of the calculation, however, given we don't currently
321 // emulate that, it's sufficient to mark every interrupt as defined.
322
323 if (interrupt >= interrupt_capabilities.size()) {
324 LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}",
325 interrupt);
326 return ResultOutOfRange;
327 }
328
329 interrupt_capabilities[interrupt] = true;
330 }
331
332 return ResultSuccess;
333}
334
335Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
336 const u32 reserved = flags >> 17;
337 if (reserved != 0) {
338 LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
339 return ResultReservedUsed;
340 }
341
342 program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
343 return ResultSuccess;
344}
345
346Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
347 // Yes, the internal member variable is checked in the actual kernel here.
348 // This might look odd for options that are only allowed to be initialized
349 // just once, however the kernel has a separate initialization function for
350 // kernel processes and userland processes. The kernel variant sets this
351 // member variable ahead of time.
352
353 const u32 major_version = kernel_version >> 19;
354
355 if (major_version != 0 || flags < 0x80000) {
356 LOG_ERROR(Kernel,
357 "Kernel version is non zero or flags are too small! major_version={}, flags={}",
358 major_version, flags);
359 return ResultInvalidArgument;
360 }
361
362 kernel_version = flags;
363 return ResultSuccess;
364}
365
366Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
367 const u32 reserved = flags >> 26;
368 if (reserved != 0) {
369 LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
370 return ResultReservedUsed;
371 }
372
373 handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
374 return ResultSuccess;
375}
376
377Result ProcessCapabilities::HandleDebugFlags(u32 flags) {
378 const u32 reserved = flags >> 19;
379 if (reserved != 0) {
380 LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
381 return ResultReservedUsed;
382 }
383
384 is_debuggable = (flags & 0x20000) != 0;
385 can_force_debug = (flags & 0x40000) != 0;
386 return ResultSuccess;
387}
388
389} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
deleted file mode 100644
index ff05dc5ff..000000000
--- a/src/core/hle/kernel/process_capability.h
+++ /dev/null
@@ -1,266 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <bitset>
7
8#include "common/common_types.h"
9
10union Result;
11
12namespace Kernel {
13
14class KPageTable;
15
16/// The possible types of programs that may be indicated
17/// by the program type capability descriptor.
18enum class ProgramType {
19 SysModule,
20 Application,
21 Applet,
22};
23
24/// Handles kernel capability descriptors that are provided by
25/// application metadata. These descriptors provide information
26/// that alters certain parameters for kernel process instance
27/// that will run said application (or applet).
28///
29/// Capabilities are a sequence of flag descriptors, that indicate various
30/// configurations and constraints for a particular process.
31///
32/// Flag types are indicated by a sequence of set low bits. E.g. the
33/// types are indicated with the low bits as follows (where x indicates "don't care"):
34///
35/// - Priority and core mask : 0bxxxxxxxxxxxx0111
36/// - Allowed service call mask: 0bxxxxxxxxxxx01111
37/// - Map physical memory : 0bxxxxxxxxx0111111
38/// - Map IO memory : 0bxxxxxxxx01111111
39/// - Interrupts : 0bxxxx011111111111
40/// - Application type : 0bxx01111111111111
41/// - Kernel version : 0bx011111111111111
42/// - Handle table size : 0b0111111111111111
43/// - Debugger flags : 0b1111111111111111
44///
45/// These are essentially a bit offset subtracted by 1 to create a mask.
46/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
47/// subtracted by one (7 -> 0b0111)
48///
49/// An example of a bit layout (using the map physical layout):
50/// <example>
51/// The MapPhysical type indicates a sequence entry pair of:
52///
53/// [initial, memory_flags], where:
54///
55/// initial:
56/// bits:
57/// 7-24: Starting page to map memory at.
58/// 25 : Indicates if the memory should be mapped as read only.
59///
60/// memory_flags:
61/// bits:
62/// 7-20 : Number of pages to map
63/// 21-25: Seems to be reserved (still checked against though)
64/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
65/// </example>
66///
67class ProcessCapabilities {
68public:
69 using InterruptCapabilities = std::bitset<1024>;
70 using SyscallCapabilities = std::bitset<192>;
71
72 ProcessCapabilities() = default;
73 ProcessCapabilities(const ProcessCapabilities&) = delete;
74 ProcessCapabilities(ProcessCapabilities&&) = default;
75
76 ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
77 ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
78
79 /// Initializes this process capabilities instance for a kernel process.
80 ///
81 /// @param capabilities The capabilities to parse
82 /// @param num_capabilities The number of capabilities to parse.
83 /// @param page_table The memory manager to use for handling any mapping-related
84 /// operations (such as mapping IO memory, etc).
85 ///
86 /// @returns ResultSuccess if this capabilities instance was able to be initialized,
87 /// otherwise, an error code upon failure.
88 ///
89 Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
90 KPageTable& page_table);
91
92 /// Initializes this process capabilities instance for a userland process.
93 ///
94 /// @param capabilities The capabilities to parse.
95 /// @param num_capabilities The total number of capabilities to parse.
96 /// @param page_table The memory manager to use for handling any mapping-related
97 /// operations (such as mapping IO memory, etc).
98 ///
99 /// @returns ResultSuccess if this capabilities instance was able to be initialized,
100 /// otherwise, an error code upon failure.
101 ///
102 Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
103 KPageTable& page_table);
104
105 /// Initializes this process capabilities instance for a process that does not
106 /// have any metadata to parse.
107 ///
108 /// This is necessary, as we allow running raw executables, and the internal
109 /// kernel process capabilities also determine what CPU cores the process is
110 /// allowed to run on, and what priorities are allowed for threads. It also
111 /// determines the max handle table size, what the program type is, whether or
112 /// not the process can be debugged, or whether it's possible for a process to
113 /// forcibly debug another process.
114 ///
115 /// Given the above, this essentially enables all capabilities across the board
116 /// for the process. It allows the process to:
117 ///
118 /// - Run on any core
119 /// - Use any thread priority
120 /// - Use the maximum amount of handles a process is allowed to.
121 /// - Be debuggable
122 /// - Forcibly debug other processes.
123 ///
124 /// Note that this is not a behavior that the kernel allows a process to do via
125 /// a single function like this. This is yuzu-specific behavior to handle
126 /// executables with no capability descriptors whatsoever to derive behavior from.
127 /// It being yuzu-specific is why this is also not the default behavior and not
128 /// done by default in the constructor.
129 ///
130 void InitializeForMetadatalessProcess();
131
132 /// Gets the allowable core mask
133 u64 GetCoreMask() const {
134 return core_mask;
135 }
136
137 /// Gets the allowable priority mask
138 u64 GetPriorityMask() const {
139 return priority_mask;
140 }
141
142 /// Gets the SVC access permission bits
143 const SyscallCapabilities& GetServiceCapabilities() const {
144 return svc_capabilities;
145 }
146
147 /// Gets the valid interrupt bits.
148 const InterruptCapabilities& GetInterruptCapabilities() const {
149 return interrupt_capabilities;
150 }
151
152 /// Gets the program type for this process.
153 ProgramType GetProgramType() const {
154 return program_type;
155 }
156
157 /// Gets the number of total allowable handles for the process' handle table.
158 s32 GetHandleTableSize() const {
159 return handle_table_size;
160 }
161
162 /// Gets the kernel version value.
163 u32 GetKernelVersion() const {
164 return kernel_version;
165 }
166
167 /// Whether or not this process can be debugged.
168 bool IsDebuggable() const {
169 return is_debuggable;
170 }
171
172 /// Whether or not this process can forcibly debug another
173 /// process, even if that process is not considered debuggable.
174 bool CanForceDebug() const {
175 return can_force_debug;
176 }
177
178private:
179 /// Attempts to parse a given sequence of capability descriptors.
180 ///
181 /// @param capabilities The sequence of capability descriptors to parse.
182 /// @param num_capabilities The number of descriptors within the given sequence.
183 /// @param page_table The memory manager that will perform any memory
184 /// mapping if necessary.
185 ///
186 /// @return ResultSuccess if no errors occur, otherwise an error code.
187 ///
188 Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
189 KPageTable& page_table);
190
191 /// Attempts to parse a capability descriptor that is only represented by a
192 /// single flag set.
193 ///
194 /// @param set_flags Running set of flags that are used to catch
195 /// flags being initialized more than once when they shouldn't be.
196 /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
197 /// @param flag The flag to attempt to parse.
198 /// @param page_table The memory manager that will perform any memory
199 /// mapping if necessary.
200 ///
201 /// @return ResultSuccess if no errors occurred, otherwise an error code.
202 ///
203 Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
204 KPageTable& page_table);
205
206 /// Clears the internal state of this process capability instance. Necessary,
207 /// to have a sane starting point due to us allowing running executables without
208 /// configuration metadata. We assume a process is not going to have metadata,
209 /// and if it turns out that the process does, in fact, have metadata, then
210 /// we attempt to parse it. Thus, we need this to reset data members back to
211 /// a good state.
212 ///
213 /// DO NOT ever make this a public member function. This isn't an invariant
214 /// anything external should depend upon (and if anything comes to rely on it,
215 /// you should immediately be questioning the design of that thing, not this
216 /// class. If the kernel itself can run without depending on behavior like that,
217 /// then so can yuzu).
218 ///
219 void Clear();
220
221 /// Handles flags related to the priority and core number capability flags.
222 Result HandlePriorityCoreNumFlags(u32 flags);
223
224 /// Handles flags related to determining the allowable SVC mask.
225 Result HandleSyscallFlags(u32& set_svc_bits, u32 flags);
226
227 /// Handles flags related to mapping physical memory pages.
228 Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
229
230 /// Handles flags related to mapping IO pages.
231 Result HandleMapIOFlags(u32 flags, KPageTable& page_table);
232
233 /// Handles flags related to mapping physical memory regions.
234 Result HandleMapRegionFlags(u32 flags, KPageTable& page_table);
235
236 /// Handles flags related to the interrupt capability flags.
237 Result HandleInterruptFlags(u32 flags);
238
239 /// Handles flags related to the program type.
240 Result HandleProgramTypeFlags(u32 flags);
241
242 /// Handles flags related to the handle table size.
243 Result HandleHandleTableFlags(u32 flags);
244
245 /// Handles flags related to the kernel version capability flags.
246 Result HandleKernelVersionFlags(u32 flags);
247
248 /// Handles flags related to debug-specific capabilities.
249 Result HandleDebugFlags(u32 flags);
250
251 SyscallCapabilities svc_capabilities;
252 InterruptCapabilities interrupt_capabilities;
253
254 u64 core_mask = 0;
255 u64 priority_mask = 0;
256
257 s32 handle_table_size = 0;
258 u32 kernel_version = 0;
259
260 ProgramType program_type = ProgramType::SysModule;
261
262 bool is_debuggable = false;
263 bool can_force_debug = false;
264};
265
266} // namespace Kernel
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
index 97f1210de..4ca62860d 100644
--- a/src/core/hle/kernel/svc/svc_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -29,7 +29,8 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
29// Helper function that performs the common sanity checks for svcMapMemory 29// Helper function that performs the common sanity checks for svcMapMemory
30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing 30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
31// in the same order. 31// in the same order.
32Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) { 32Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr,
33 u64 size) {
33 if (!Common::Is4KBAligned(dst_addr)) { 34 if (!Common::Is4KBAligned(dst_addr)) {
34 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); 35 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
35 R_THROW(ResultInvalidAddress); 36 R_THROW(ResultInvalidAddress);
@@ -123,7 +124,8 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
123 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 124 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
124 125
125 // Set the memory attribute. 126 // Set the memory attribute.
126 R_RETURN(page_table.SetMemoryAttribute(address, size, mask, attr)); 127 R_RETURN(page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
128 static_cast<KMemoryAttribute>(attr)));
127} 129}
128 130
129/// Maps a memory range into a different range. 131/// Maps a memory range into a different range.
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
index 99330d02a..793e9f8d0 100644
--- a/src/core/hle/kernel/svc/svc_physical_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -16,7 +16,14 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); 16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
17 17
18 // Set the heap size. 18 // Set the heap size.
19 R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size)); 19 KProcessAddress address{};
20 R_TRY(GetCurrentProcess(system.Kernel())
21 .GetPageTable()
22 .SetHeapSize(std::addressof(address), size));
23
24 // We succeeded.
25 *out_address = GetInteger(address);
26 R_SUCCEED();
20} 27}
21 28
22/// Maps memory at a desired address 29/// Maps memory at a desired address
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp
index 07cd48175..e1427947b 100644
--- a/src/core/hle/kernel/svc/svc_process_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -247,8 +247,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
247 R_THROW(ResultInvalidCurrentMemory); 247 R_THROW(ResultInvalidCurrentMemory);
248 } 248 }
249 249
250 R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size, 250 R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size));
251 KPageTable::ICacheInvalidationStrategy::InvalidateAll));
252} 251}
253 252
254Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, 253Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address,
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp
index 51af06e97..816dcb8d0 100644
--- a/src/core/hle/kernel/svc/svc_query_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -31,12 +31,12 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn
31 } 31 }
32 32
33 auto& current_memory{GetCurrentMemory(system.Kernel())}; 33 auto& current_memory{GetCurrentMemory(system.Kernel())};
34 const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()};
35 34
36 current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info)); 35 KMemoryInfo mem_info;
36 R_TRY(process->GetPageTable().QueryInfo(std::addressof(mem_info), out_page_info, address));
37 37
38 //! This is supposed to be part of the QueryInfo call. 38 const auto svc_mem_info = mem_info.GetSvcMemoryInfo();
39 *out_page_info = {}; 39 current_memory.WriteBlock(out_memory_info, std::addressof(svc_mem_info), sizeof(svc_mem_info));
40 40
41 R_SUCCEED(); 41 R_SUCCEED();
42} 42}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index dd0b27f47..749f51f69 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -407,3 +407,34 @@ constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
407 407
408/// Evaluates a boolean expression, and succeeds if that expression is true. 408/// Evaluates a boolean expression, and succeeds if that expression is true.
409#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) 409#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
410
411#define R_TRY_CATCH(res_expr) \
412 { \
413 const auto R_CURRENT_RESULT = (res_expr); \
414 if (R_FAILED(R_CURRENT_RESULT)) { \
415 if (false)
416
417#define R_END_TRY_CATCH \
418 else if (R_FAILED(R_CURRENT_RESULT)) { \
419 R_THROW(R_CURRENT_RESULT); \
420 } \
421 } \
422 }
423
424#define R_CATCH_ALL() \
425 } \
426 else if (R_FAILED(R_CURRENT_RESULT)) { \
427 if (true)
428
429#define R_CATCH(res_expr) \
430 } \
431 else if ((res_expr) == (R_CURRENT_RESULT)) { \
432 if (true)
433
434#define R_CONVERT(catch_type, convert_type) \
435 R_CATCH(catch_type) { R_THROW(static_cast<Result>(convert_type)); }
436
437#define R_CONVERT_ALL(convert_type) \
438 R_CATCH_ALL() { R_THROW(static_cast<Result>(convert_type)); }
439
440#define R_ASSERT(res_expr) ASSERT(R_SUCCEEDED(res_expr))
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 1b1c8190e..f21553644 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -3,11 +3,13 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include <array> 5#include <array>
6
6#include "common/common_types.h" 7#include "common/common_types.h"
7#include "common/fs/file.h" 8#include "common/fs/file.h"
8#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/polyfill_ranges.h" 11#include "common/polyfill_ranges.h"
12#include "common/stb.h"
11#include "common/string_util.h" 13#include "common/string_util.h"
12#include "common/swap.h" 14#include "common/swap.h"
13#include "core/constants.h" 15#include "core/constants.h"
@@ -38,9 +40,36 @@ static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
38 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); 40 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
39} 41}
40 42
41static constexpr u32 SanitizeJPEGSize(std::size_t size) { 43static void JPGToMemory(void* context, void* data, int len) {
44 std::vector<u8>* jpg_image = static_cast<std::vector<u8>*>(context);
45 unsigned char* jpg = static_cast<unsigned char*>(data);
46 jpg_image->insert(jpg_image->end(), jpg, jpg + len);
47}
48
49static void SanitizeJPEGImageSize(std::vector<u8>& image) {
42 constexpr std::size_t max_jpeg_image_size = 0x20000; 50 constexpr std::size_t max_jpeg_image_size = 0x20000;
43 return static_cast<u32>(std::min(size, max_jpeg_image_size)); 51 constexpr int profile_dimensions = 256;
52 int original_width, original_height, color_channels;
53
54 const auto plain_image =
55 stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width,
56 &original_height, &color_channels, STBI_rgb);
57
58 // Resize image to match 256*256
59 if (original_width != profile_dimensions || original_height != profile_dimensions) {
60 // Use vector instead of array to avoid overflowing the stack
61 std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb);
62 stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(),
63 profile_dimensions, profile_dimensions, 0, STBI_rgb, 0,
64 STBIR_FILTER_BOX);
65 image.clear();
66 if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions,
67 STBI_rgb, out_image.data(), 0)) {
68 LOG_ERROR(Service_ACC, "Failed to resize the user provided image.");
69 }
70 }
71
72 image.resize(std::min(image.size(), max_jpeg_image_size));
44} 73}
45 74
46class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { 75class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> {
@@ -339,19 +368,20 @@ protected:
339 LOG_WARNING(Service_ACC, 368 LOG_WARNING(Service_ACC,
340 "Failed to load user provided image! Falling back to built-in backup..."); 369 "Failed to load user provided image! Falling back to built-in backup...");
341 ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); 370 ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
342 rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); 371 rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
343 return; 372 return;
344 } 373 }
345 374
346 const u32 size = SanitizeJPEGSize(image.GetSize()); 375 std::vector<u8> buffer(image.GetSize());
347 std::vector<u8> buffer(size);
348 376
349 if (image.Read(buffer) != buffer.size()) { 377 if (image.Read(buffer) != buffer.size()) {
350 LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); 378 LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image.");
351 } 379 }
352 380
381 SanitizeJPEGImageSize(buffer);
382
353 ctx.WriteBuffer(buffer); 383 ctx.WriteBuffer(buffer);
354 rb.Push<u32>(size); 384 rb.Push(static_cast<u32>(buffer.size()));
355 } 385 }
356 386
357 void GetImageSize(HLERequestContext& ctx) { 387 void GetImageSize(HLERequestContext& ctx) {
@@ -365,10 +395,18 @@ protected:
365 if (!image.IsOpen()) { 395 if (!image.IsOpen()) {
366 LOG_WARNING(Service_ACC, 396 LOG_WARNING(Service_ACC,
367 "Failed to load user provided image! Falling back to built-in backup..."); 397 "Failed to load user provided image! Falling back to built-in backup...");
368 rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); 398 rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
369 } else { 399 return;
370 rb.Push(SanitizeJPEGSize(image.GetSize()));
371 } 400 }
401
402 std::vector<u8> buffer(image.GetSize());
403
404 if (image.Read(buffer) != buffer.size()) {
405 LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image.");
406 }
407
408 SanitizeJPEGImageSize(buffer);
409 rb.Push(static_cast<u32>(buffer.size()));
372 } 410 }
373 411
374 void Store(HLERequestContext& ctx) { 412 void Store(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cc643ea09..a266d7c21 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -13,6 +13,7 @@
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/savedata_factory.h" 15#include "core/file_sys/savedata_factory.h"
16#include "core/hid/hid_types.h"
16#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_transfer_memory.h" 18#include "core/hle/kernel/k_transfer_memory.h"
18#include "core/hle/result.h" 19#include "core/hle/result.h"
@@ -21,6 +22,7 @@
21#include "core/hle/service/am/applet_ae.h" 22#include "core/hle/service/am/applet_ae.h"
22#include "core/hle/service/am/applet_oe.h" 23#include "core/hle/service/am/applet_oe.h"
23#include "core/hle/service/am/applets/applet_cabinet.h" 24#include "core/hle/service/am/applets/applet_cabinet.h"
25#include "core/hle/service/am/applets/applet_controller.h"
24#include "core/hle/service/am/applets/applet_mii_edit_types.h" 26#include "core/hle/service/am/applets/applet_mii_edit_types.h"
25#include "core/hle/service/am/applets/applet_profile_select.h" 27#include "core/hle/service/am/applets/applet_profile_select.h"
26#include "core/hle/service/am/applets/applet_software_keyboard_types.h" 28#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
@@ -35,6 +37,7 @@
35#include "core/hle/service/caps/caps_su.h" 37#include "core/hle/service/caps/caps_su.h"
36#include "core/hle/service/caps/caps_types.h" 38#include "core/hle/service/caps/caps_types.h"
37#include "core/hle/service/filesystem/filesystem.h" 39#include "core/hle/service/filesystem/filesystem.h"
40#include "core/hle/service/hid/controllers/npad.h"
38#include "core/hle/service/ipc_helpers.h" 41#include "core/hle/service/ipc_helpers.h"
39#include "core/hle/service/ns/ns.h" 42#include "core/hle/service/ns/ns.h"
40#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 43#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_)
73 static const FunctionInfo functions[] = { 76 static const FunctionInfo functions[] = {
74 {0, nullptr, "CreateWindow"}, 77 {0, nullptr, "CreateWindow"},
75 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 78 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
76 {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, 79 {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
77 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 80 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
78 {11, nullptr, "ReleaseForegroundRights"}, 81 {11, nullptr, "ReleaseForegroundRights"},
79 {12, nullptr, "RejectToChangeIntoBackground"}, 82 {12, nullptr, "RejectToChangeIntoBackground"},
@@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
97 rb.Push<u64>(process_id); 100 rb.Push<u64>(process_id);
98} 101}
99 102
103void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
104 const u64 process_id = 0;
105
106 LOG_WARNING(Service_AM, "(STUBBED) called");
107
108 IPC::ResponseBuilder rb{ctx, 4};
109 rb.Push(ResultSuccess);
110 rb.Push<u64>(process_id);
111}
112
100void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { 113void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
101 LOG_WARNING(Service_AM, "(STUBBED) called"); 114 LOG_WARNING(Service_AM, "(STUBBED) called");
102 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
@@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1565 {6, nullptr, "GetPopInteractiveInDataEvent"}, 1578 {6, nullptr, "GetPopInteractiveInDataEvent"},
1566 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, 1579 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
1567 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, 1580 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
1568 {12, nullptr, "GetMainAppletIdentityInfo"}, 1581 {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
1569 {13, nullptr, "CanUseApplicationCore"}, 1582 {13, nullptr, "CanUseApplicationCore"},
1570 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, 1583 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
1571 {15, nullptr, "GetMainAppletApplicationControlProperty"}, 1584 {15, nullptr, "GetMainAppletApplicationControlProperty"},
@@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1609 case Applets::AppletId::SoftwareKeyboard: 1622 case Applets::AppletId::SoftwareKeyboard:
1610 PushInShowSoftwareKeyboard(); 1623 PushInShowSoftwareKeyboard();
1611 break; 1624 break;
1625 case Applets::AppletId::Controller:
1626 PushInShowController();
1627 break;
1612 default: 1628 default:
1613 break; 1629 break;
1614 } 1630 }
@@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
1666 rb.PushRaw(applet_info); 1682 rb.PushRaw(applet_info);
1667} 1683}
1668 1684
1669void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { 1685void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
1670 struct AppletIdentityInfo { 1686 struct AppletIdentityInfo {
1671 Applets::AppletId applet_id; 1687 Applets::AppletId applet_id;
1672 INSERT_PADDING_BYTES(0x4); 1688 INSERT_PADDING_BYTES(0x4);
1673 u64 application_id; 1689 u64 application_id;
1674 }; 1690 };
1691 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1692
1693 LOG_WARNING(Service_AM, "(STUBBED) called");
1694
1695 const AppletIdentityInfo applet_info{
1696 .applet_id = Applets::AppletId::QLaunch,
1697 .application_id = 0x0100000000001000ull,
1698 };
1699
1700 IPC::ResponseBuilder rb{ctx, 6};
1701 rb.Push(ResultSuccess);
1702 rb.PushRaw(applet_info);
1703}
1675 1704
1705void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
1706 struct AppletIdentityInfo {
1707 Applets::AppletId applet_id;
1708 INSERT_PADDING_BYTES(0x4);
1709 u64 application_id;
1710 };
1711 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1676 LOG_WARNING(Service_AM, "(STUBBED) called"); 1712 LOG_WARNING(Service_AM, "(STUBBED) called");
1677 1713
1678 const AppletIdentityInfo applet_info{ 1714 const AppletIdentityInfo applet_info{
@@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() {
1737 queue_data.emplace_back(std::move(settings_data)); 1773 queue_data.emplace_back(std::move(settings_data));
1738} 1774}
1739 1775
1776void ILibraryAppletSelfAccessor::PushInShowController() {
1777 const Applets::CommonArguments common_args = {
1778 .arguments_version = Applets::CommonArgumentVersion::Version3,
1779 .size = Applets::CommonArgumentSize::Version3,
1780 .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
1781 .theme_color = Applets::ThemeColor::BasicBlack,
1782 .play_startup_sound = true,
1783 .system_tick = system.CoreTiming().GetClockTicks(),
1784 };
1785
1786 Applets::ControllerSupportArgNew user_args = {
1787 .header = {.player_count_min = 1,
1788 .player_count_max = 4,
1789 .enable_take_over_connection = true,
1790 .enable_left_justify = false,
1791 .enable_permit_joy_dual = true,
1792 .enable_single_mode = false,
1793 .enable_identification_color = false},
1794 .identification_colors = {},
1795 .enable_explain_text = false,
1796 .explain_text = {},
1797 };
1798
1799 Applets::ControllerSupportArgPrivate private_args = {
1800 .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
1801 .arg_size = sizeof(Applets::ControllerSupportArgNew),
1802 .is_home_menu = true,
1803 .flag_1 = true,
1804 .mode = Applets::ControllerSupportMode::ShowControllerSupport,
1805 .caller = Applets::ControllerSupportCaller::
1806 Application, // switchbrew: Always zero except with
1807 // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
1808 // which sets this to the input param
1809 .style_set = Core::HID::NpadStyleSet::None,
1810 .joy_hold_type = 0,
1811 };
1812 std::vector<u8> common_args_data(sizeof(common_args));
1813 std::vector<u8> private_args_data(sizeof(private_args));
1814 std::vector<u8> user_args_data(sizeof(user_args));
1815
1816 std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
1817 std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
1818 std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
1819
1820 queue_data.emplace_back(std::move(common_args_data));
1821 queue_data.emplace_back(std::move(private_args_data));
1822 queue_data.emplace_back(std::move(user_args_data));
1823}
1824
1740void ILibraryAppletSelfAccessor::PushInShowCabinetData() { 1825void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
1741 const Applets::CommonArguments arguments{ 1826 const Applets::CommonArguments arguments{
1742 .arguments_version = Applets::CommonArgumentVersion::Version3, 1827 .arguments_version = Applets::CommonArgumentVersion::Version3,
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 8f8cb8a9e..905a71b9f 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -87,6 +87,7 @@ public:
87 87
88private: 88private:
89 void GetAppletResourceUserId(HLERequestContext& ctx); 89 void GetAppletResourceUserId(HLERequestContext& ctx);
90 void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
90 void AcquireForegroundRights(HLERequestContext& ctx); 91 void AcquireForegroundRights(HLERequestContext& ctx);
91}; 92};
92 93
@@ -345,6 +346,7 @@ private:
345 void PopInData(HLERequestContext& ctx); 346 void PopInData(HLERequestContext& ctx);
346 void PushOutData(HLERequestContext& ctx); 347 void PushOutData(HLERequestContext& ctx);
347 void GetLibraryAppletInfo(HLERequestContext& ctx); 348 void GetLibraryAppletInfo(HLERequestContext& ctx);
349 void GetMainAppletIdentityInfo(HLERequestContext& ctx);
348 void ExitProcessAndReturn(HLERequestContext& ctx); 350 void ExitProcessAndReturn(HLERequestContext& ctx);
349 void GetCallerAppletIdentityInfo(HLERequestContext& ctx); 351 void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
350 void GetDesirableKeyboardLayout(HLERequestContext& ctx); 352 void GetDesirableKeyboardLayout(HLERequestContext& ctx);
@@ -355,6 +357,7 @@ private:
355 void PushInShowCabinetData(); 357 void PushInShowCabinetData();
356 void PushInShowMiiEditData(); 358 void PushInShowMiiEditData();
357 void PushInShowSoftwareKeyboard(); 359 void PushInShowSoftwareKeyboard();
360 void PushInShowController();
358 361
359 std::deque<std::vector<u8>> queue_data; 362 std::deque<std::vector<u8>> queue_data;
360}; 363};
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index b379dadeb..9d1960cb7 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
122 Service::NFP::RegisterInfoPrivate register_info{}; 122 Service::NFP::RegisterInfoPrivate register_info{};
123 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), 123 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
124 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); 124 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
125 125 register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
126 register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
126 nfp_device->SetRegisterInfoPrivate(register_info); 127 nfp_device->SetRegisterInfoPrivate(register_info);
127 break; 128 break;
128 } 129 }
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index f6c64f633..9f839f3d7 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 {
56struct ControllerSupportArgPrivate { 56struct ControllerSupportArgPrivate {
57 u32 arg_private_size{}; 57 u32 arg_private_size{};
58 u32 arg_size{}; 58 u32 arg_size{};
59 bool flag_0{}; 59 bool is_home_menu{};
60 bool flag_1{}; 60 bool flag_1{};
61 ControllerSupportMode mode{}; 61 ControllerSupportMode mode{};
62 ControllerSupportCaller caller{}; 62 ControllerSupportCaller caller{};
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 1c9a1dc29..b0ea2b381 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() {
330 LOG_DEBUG(Service_AM, "Extracting RomFS to {}", 330 LOG_DEBUG(Service_AM, "Extracting RomFS to {}",
331 Common::FS::PathToUTF8String(offline_cache_dir)); 331 Common::FS::PathToUTF8String(offline_cache_dir));
332 332
333 const auto extracted_romfs_dir = 333 const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
334 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
335 334
336 const auto temp_dir = system.GetFilesystem()->CreateDirectory( 335 const auto temp_dir = system.GetFilesystem()->CreateDirectory(
337 Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); 336 Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite);
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index f02bbc450..0bf2598b7 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -69,6 +69,30 @@ enum class AppletId : u32 {
69 MyPage = 0x1A, 69 MyPage = 0x1A,
70}; 70};
71 71
72enum class AppletProgramId : u64 {
73 QLaunch = 0x0100000000001000ull,
74 Auth = 0x0100000000001001ull,
75 Cabinet = 0x0100000000001002ull,
76 Controller = 0x0100000000001003ull,
77 DataErase = 0x0100000000001004ull,
78 Error = 0x0100000000001005ull,
79 NetConnect = 0x0100000000001006ull,
80 ProfileSelect = 0x0100000000001007ull,
81 SoftwareKeyboard = 0x0100000000001008ull,
82 MiiEdit = 0x0100000000001009ull,
83 Web = 0x010000000000100Aull,
84 Shop = 0x010000000000100Bull,
85 OverlayDisplay = 0x010000000000100Cull,
86 PhotoViewer = 0x010000000000100Dull,
87 Settings = 0x010000000000100Eull,
88 OfflineWeb = 0x010000000000100Full,
89 LoginShare = 0x0100000000001010ull,
90 WebAuth = 0x0100000000001011ull,
91 Starter = 0x0100000000001012ull,
92 MyPage = 0x0100000000001013ull,
93 MaxProgramId = 0x0100000000001FFFull,
94};
95
72enum class LibraryAppletMode : u32 { 96enum class LibraryAppletMode : u32 {
73 AllForeground = 0, 97 AllForeground = 0,
74 Background = 1, 98 Background = 1,
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 8069f75b7..c65e32489 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -127,7 +127,7 @@ public:
127 127
128private: 128private:
129 void GetCore(HLERequestContext& ctx) { 129 void GetCore(HLERequestContext& ctx) {
130 LOG_DEBUG(Service_BTM, "called"); 130 LOG_WARNING(Service_BTM, "called");
131 131
132 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 132 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
133 rb.Push(ResultSuccess); 133 rb.Push(ResultSuccess);
@@ -263,13 +263,13 @@ public:
263 explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { 263 explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
264 // clang-format off 264 // clang-format off
265 static const FunctionInfo functions[] = { 265 static const FunctionInfo functions[] = {
266 {0, nullptr, "StartGamepadPairing"}, 266 {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
267 {1, nullptr, "CancelGamepadPairing"}, 267 {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
268 {2, nullptr, "ClearGamepadPairingDatabase"}, 268 {2, nullptr, "ClearGamepadPairingDatabase"},
269 {3, nullptr, "GetPairedGamepadCount"}, 269 {3, nullptr, "GetPairedGamepadCount"},
270 {4, nullptr, "EnableRadio"}, 270 {4, nullptr, "EnableRadio"},
271 {5, nullptr, "DisableRadio"}, 271 {5, nullptr, "DisableRadio"},
272 {6, nullptr, "GetRadioOnOff"}, 272 {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
273 {7, nullptr, "AcquireRadioEvent"}, 273 {7, nullptr, "AcquireRadioEvent"},
274 {8, nullptr, "AcquireGamepadPairingEvent"}, 274 {8, nullptr, "AcquireGamepadPairingEvent"},
275 {9, nullptr, "IsGamepadPairingStarted"}, 275 {9, nullptr, "IsGamepadPairingStarted"},
@@ -280,18 +280,58 @@ public:
280 {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, 280 {14, nullptr, "AcquireAudioDeviceConnectionEvent"},
281 {15, nullptr, "ConnectAudioDevice"}, 281 {15, nullptr, "ConnectAudioDevice"},
282 {16, nullptr, "IsConnectingAudioDevice"}, 282 {16, nullptr, "IsConnectingAudioDevice"},
283 {17, nullptr, "GetConnectedAudioDevices"}, 283 {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
284 {18, nullptr, "DisconnectAudioDevice"}, 284 {18, nullptr, "DisconnectAudioDevice"},
285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, 285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
286 {20, nullptr, "GetPairedAudioDevices"}, 286 {20, nullptr, "GetPairedAudioDevices"},
287 {21, nullptr, "RemoveAudioDevicePairing"}, 287 {21, nullptr, "RemoveAudioDevicePairing"},
288 {22, nullptr, "RequestAudioDeviceConnectionRejection"}, 288 {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
289 {23, nullptr, "CancelAudioDeviceConnectionRejection"} 289 {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
290 }; 290 };
291 // clang-format on 291 // clang-format on
292 292
293 RegisterHandlers(functions); 293 RegisterHandlers(functions);
294 } 294 }
295
296private:
297 void IsRadioEnabled(HLERequestContext& ctx) {
298 LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
299
300 IPC::ResponseBuilder rb{ctx, 3};
301 rb.Push(ResultSuccess);
302 rb.Push(true);
303 }
304
305 void StartGamepadPairing(HLERequestContext& ctx) {
306 LOG_WARNING(Service_BTM, "(STUBBED) called");
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultSuccess);
309 }
310
311 void CancelGamepadPairing(HLERequestContext& ctx) {
312 LOG_WARNING(Service_BTM, "(STUBBED) called");
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(ResultSuccess);
315 }
316
317 void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
318 LOG_WARNING(Service_BTM, "(STUBBED) called");
319 IPC::ResponseBuilder rb{ctx, 2};
320 rb.Push(ResultSuccess);
321 }
322
323 void GetConnectedAudioDevices(HLERequestContext& ctx) {
324 LOG_WARNING(Service_BTM, "(STUBBED) called");
325 IPC::ResponseBuilder rb{ctx, 3};
326 rb.Push(ResultSuccess);
327 rb.Push<u32>(0);
328 }
329
330 void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
331 LOG_WARNING(Service_BTM, "(STUBBED) called");
332 IPC::ResponseBuilder rb{ctx, 2};
333 rb.Push(ResultSuccess);
334 }
295}; 335};
296 336
297class BTM_SYS final : public ServiceFramework<BTM_SYS> { 337class BTM_SYS final : public ServiceFramework<BTM_SYS> {
@@ -308,7 +348,7 @@ public:
308 348
309private: 349private:
310 void GetCore(HLERequestContext& ctx) { 350 void GetCore(HLERequestContext& ctx) {
311 LOG_DEBUG(Service_BTM, "called"); 351 LOG_WARNING(Service_BTM, "called");
312 352
313 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 353 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
314 rb.Push(ResultSuccess); 354 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 9d05f9801..0507b14e7 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -32,7 +32,7 @@ public:
32 {10200, nullptr, "SendFriendRequestForApplication"}, 32 {10200, nullptr, "SendFriendRequestForApplication"},
33 {10211, nullptr, "AddFacedFriendRequestForApplication"}, 33 {10211, nullptr, "AddFacedFriendRequestForApplication"},
34 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, 34 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
35 {10420, nullptr, "IsBlockedUserListCacheAvailable"}, 35 {10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
36 {10421, nullptr, "EnsureBlockedUserListAvailable"}, 36 {10421, nullptr, "EnsureBlockedUserListAvailable"},
37 {10500, nullptr, "GetProfileList"}, 37 {10500, nullptr, "GetProfileList"},
38 {10600, nullptr, "DeclareOpenOnlinePlaySession"}, 38 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
@@ -206,6 +206,17 @@ private:
206 rb.Push(true); 206 rb.Push(true);
207 } 207 }
208 208
209 void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
210 IPC::RequestParser rp{ctx};
211 const auto uuid{rp.PopRaw<Common::UUID>()};
212
213 LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
214
215 IPC::ResponseBuilder rb{ctx, 3};
216 rb.Push(ResultSuccess);
217 rb.Push(true);
218 }
219
209 KernelHelpers::ServiceContext service_context; 220 KernelHelpers::ServiceContext service_context;
210 221
211 Kernel::KEvent* completion_event; 222 Kernel::KEvent* completion_event;
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
new file mode 100644
index 000000000..b2bf1d78d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_six_axis.h"
9#include "core/memory.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
13
14ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
15 : ControllerBase{hid_core_} {
16 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21}
22
23ConsoleSixAxis::~ConsoleSixAxis() = default;
24
25void ConsoleSixAxis::OnInit() {}
26
27void ConsoleSixAxis::OnRelease() {}
28
29void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated()) {
31 return;
32 }
33
34 const auto motion_status = console->GetMotion();
35
36 shared_memory->sampling_number++;
37 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
38 shared_memory->verticalization_error = motion_status.verticalization_error;
39 shared_memory->gyro_bias = motion_status.gyro_bias;
40}
41
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
new file mode 100644
index 000000000..5b7c6a29a
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/vector_math.h"
7#include "core/hle/service/hid/controllers/controller_base.h"
8
9namespace Core::HID {
10class EmulatedConsole;
11} // namespace Core::HID
12
13namespace Service::HID {
14class ConsoleSixAxis final : public ControllerBase {
15public:
16 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
17 ~ConsoleSixAxis() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
27
28private:
29 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
30 struct ConsoleSharedMemory {
31 u64 sampling_number{};
32 bool is_seven_six_axis_sensor_at_rest{};
33 INSERT_PADDING_BYTES(3); // padding
34 f32 verticalization_error{};
35 Common::Vec3f gyro_bias{};
36 INSERT_PADDING_BYTES(4); // padding
37 };
38 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
39
40 ConsoleSharedMemory* shared_memory = nullptr;
41 Core::HID::EmulatedConsole* console = nullptr;
42};
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index c58d67d7d..0bcd87062 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -8,12 +8,17 @@ namespace Service::HID {
8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} 8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
9ControllerBase::~ControllerBase() = default; 9ControllerBase::~ControllerBase() = default;
10 10
11void ControllerBase::ActivateController() { 11Result ControllerBase::Activate() {
12 if (is_activated) { 12 if (is_activated) {
13 return; 13 return ResultSuccess;
14 } 14 }
15 is_activated = true; 15 is_activated = true;
16 OnInit(); 16 OnInit();
17 return ResultSuccess;
18}
19
20Result ControllerBase::Activate(u64 aruid) {
21 return Activate();
17} 22}
18 23
19void ControllerBase::DeactivateController() { 24void ControllerBase::DeactivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index d6f7a5073..9a44ee41e 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/result.h"
7 8
8namespace Core::Timing { 9namespace Core::Timing {
9class CoreTiming; 10class CoreTiming;
@@ -31,7 +32,8 @@ public:
31 // When the controller is requesting a motion update for the shared memory 32 // When the controller is requesting a motion update for the shared memory
32 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} 33 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
33 34
34 void ActivateController(); 35 Result Activate();
36 Result Activate(u64 aruid);
35 37
36 void DeactivateController(); 38 void DeactivateController();
37 39
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 8ec9f4a95..9de19ebfc 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -13,7 +13,7 @@
13namespace Service::HID { 13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
15 15
16Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 16DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
17 : ControllerBase{hid_core_} { 17 : ControllerBase{hid_core_} {
18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, 18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
19 "DebugPadSharedMemory is bigger than the shared memory"); 19 "DebugPadSharedMemory is bigger than the shared memory");
@@ -22,13 +22,13 @@ Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_
22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
23} 23}
24 24
25Controller_DebugPad::~Controller_DebugPad() = default; 25DebugPad::~DebugPad() = default;
26 26
27void Controller_DebugPad::OnInit() {} 27void DebugPad::OnInit() {}
28 28
29void Controller_DebugPad::OnRelease() {} 29void DebugPad::OnRelease() {}
30 30
31void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 31void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
32 if (!IsControllerActivated()) { 32 if (!IsControllerActivated()) {
33 shared_memory->debug_pad_lifo.buffer_count = 0; 33 shared_memory->debug_pad_lifo.buffer_count = 0;
34 shared_memory->debug_pad_lifo.buffer_tail = 0; 34 shared_memory->debug_pad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 68ff0ea79..5566dba77 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -15,10 +15,10 @@ struct AnalogStickState;
15} // namespace Core::HID 15} // namespace Core::HID
16 16
17namespace Service::HID { 17namespace Service::HID {
18class Controller_DebugPad final : public ControllerBase { 18class DebugPad final : public ControllerBase {
19public: 19public:
20 explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 20 explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
21 ~Controller_DebugPad() override; 21 ~DebugPad() override;
22 22
23 // Called when the controller is initialized 23 // Called when the controller is initialized
24 void OnInit() override; 24 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 63eecd42b..59b2ec73c 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 23 return static_cast<f32>(num * num);
24} 24}
25 25
26Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 26Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
27 : ControllerBase(hid_core_) { 27 : ControllerBase(hid_core_) {
28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, 28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
29 "GestureSharedMemory is bigger than the shared memory"); 29 "GestureSharedMemory is bigger than the shared memory");
@@ -31,17 +31,17 @@ Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_sh
31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); 31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
32 console = hid_core.GetEmulatedConsole(); 32 console = hid_core.GetEmulatedConsole();
33} 33}
34Controller_Gesture::~Controller_Gesture() = default; 34Gesture::~Gesture() = default;
35 35
36void Controller_Gesture::OnInit() { 36void Gesture::OnInit() {
37 shared_memory->gesture_lifo.buffer_count = 0; 37 shared_memory->gesture_lifo.buffer_count = 0;
38 shared_memory->gesture_lifo.buffer_tail = 0; 38 shared_memory->gesture_lifo.buffer_tail = 0;
39 force_update = true; 39 force_update = true;
40} 40}
41 41
42void Controller_Gesture::OnRelease() {} 42void Gesture::OnRelease() {}
43 43
44void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 44void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
45 if (!IsControllerActivated()) { 45 if (!IsControllerActivated()) {
46 shared_memory->gesture_lifo.buffer_count = 0; 46 shared_memory->gesture_lifo.buffer_count = 0;
47 shared_memory->gesture_lifo.buffer_tail = 0; 47 shared_memory->gesture_lifo.buffer_tail = 0;
@@ -64,7 +64,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
64 UpdateGestureSharedMemory(gesture, time_difference); 64 UpdateGestureSharedMemory(gesture, time_difference);
65} 65}
66 66
67void Controller_Gesture::ReadTouchInput() { 67void Gesture::ReadTouchInput() {
68 if (!Settings::values.touchscreen.enabled) { 68 if (!Settings::values.touchscreen.enabled) {
69 fingers = {}; 69 fingers = {};
70 return; 70 return;
@@ -76,8 +76,7 @@ void Controller_Gesture::ReadTouchInput() {
76 } 76 }
77} 77}
78 78
79bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 79bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
80 f32 time_difference) {
81 const auto& last_entry = GetLastGestureEntry(); 80 const auto& last_entry = GetLastGestureEntry();
82 if (force_update) { 81 if (force_update) {
83 force_update = false; 82 force_update = false;
@@ -100,8 +99,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
100 return false; 99 return false;
101} 100}
102 101
103void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, 102void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
104 f32 time_difference) {
105 GestureType type = GestureType::Idle; 103 GestureType type = GestureType::Idle;
106 GestureAttribute attributes{}; 104 GestureAttribute attributes{};
107 105
@@ -138,8 +136,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
138 shared_memory->gesture_lifo.WriteNextEntry(next_state); 136 shared_memory->gesture_lifo.WriteNextEntry(next_state);
139} 137}
140 138
141void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, 139void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
142 GestureAttribute& attributes) { 140 GestureAttribute& attributes) {
143 const auto& last_entry = GetLastGestureEntry(); 141 const auto& last_entry = GetLastGestureEntry();
144 142
145 gesture.detection_count++; 143 gesture.detection_count++;
@@ -152,8 +150,8 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& typ
152 } 150 }
153} 151}
154 152
155void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, 153void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
156 f32 time_difference) { 154 f32 time_difference) {
157 const auto& last_entry = GetLastGestureEntry(); 155 const auto& last_entry = GetLastGestureEntry();
158 156
159 // Promote to pan type if touch moved 157 // Promote to pan type if touch moved
@@ -186,9 +184,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu
186 } 184 }
187} 185}
188 186
189void Controller_Gesture::EndGesture(GestureProperties& gesture, 187void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
190 GestureProperties& last_gesture_props, GestureType& type, 188 GestureType& type, GestureAttribute& attributes, f32 time_difference) {
191 GestureAttribute& attributes, f32 time_difference) {
192 const auto& last_entry = GetLastGestureEntry(); 189 const auto& last_entry = GetLastGestureEntry();
193 190
194 if (last_gesture_props.active_points != 0) { 191 if (last_gesture_props.active_points != 0) {
@@ -222,9 +219,8 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
222 } 219 }
223} 220}
224 221
225void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 222void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
226 GestureProperties& last_gesture_props, GestureType& type, 223 GestureType& type, GestureAttribute& attributes) {
227 GestureAttribute& attributes) {
228 type = GestureType::Tap; 224 type = GestureType::Tap;
229 gesture = last_gesture_props; 225 gesture = last_gesture_props;
230 force_update = true; 226 force_update = true;
@@ -236,9 +232,8 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
236 } 232 }
237} 233}
238 234
239void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 235void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
240 GestureProperties& last_gesture_props, GestureType& type, 236 GestureType& type, f32 time_difference) {
241 f32 time_difference) {
242 const auto& last_entry = GetLastGestureEntry(); 237 const auto& last_entry = GetLastGestureEntry();
243 238
244 next_state.delta = gesture.mid_point - last_entry.pos; 239 next_state.delta = gesture.mid_point - last_entry.pos;
@@ -263,9 +258,8 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
263 } 258 }
264} 259}
265 260
266void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 261void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
267 GestureProperties& last_gesture_props, GestureType& type, 262 GestureType& type, f32 time_difference) {
268 f32 time_difference) {
269 const auto& last_entry = GetLastGestureEntry(); 263 const auto& last_entry = GetLastGestureEntry();
270 next_state.vel_x = 264 next_state.vel_x =
271 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 265 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
@@ -287,8 +281,8 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
287 force_update = true; 281 force_update = true;
288} 282}
289 283
290void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 284void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
291 GestureProperties& last_gesture_props, GestureType& type) { 285 GestureType& type) {
292 const auto& last_entry = GetLastGestureEntry(); 286 const auto& last_entry = GetLastGestureEntry();
293 287
294 type = GestureType::Swipe; 288 type = GestureType::Swipe;
@@ -311,11 +305,11 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
311 next_state.direction = GestureDirection::Up; 305 next_state.direction = GestureDirection::Up;
312} 306}
313 307
314const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { 308const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
315 return shared_memory->gesture_lifo.ReadCurrentEntry().state; 309 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
316} 310}
317 311
318Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 312Gesture::GestureProperties Gesture::GetGestureProperties() {
319 GestureProperties gesture; 313 GestureProperties gesture;
320 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; 314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
321 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 0d6099ea0..4c6f8ee07 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -12,10 +12,10 @@
12#include "core/hle/service/hid/ring_lifo.h" 12#include "core/hle/service/hid/ring_lifo.h"
13 13
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 15class Gesture final : public ControllerBase {
16public: 16public:
17 explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 17 explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
18 ~Controller_Gesture() override; 18 ~Gesture() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
21 void OnInit() override; 21 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 117d87433..ddb1b0ba4 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
14 14
15Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
16 : ControllerBase{hid_core_} { 16 : ControllerBase{hid_core_} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, 17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
18 "KeyboardSharedMemory is bigger than the shared memory"); 18 "KeyboardSharedMemory is bigger than the shared memory");
@@ -21,13 +21,13 @@ Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_
21 emulated_devices = hid_core.GetEmulatedDevices(); 21 emulated_devices = hid_core.GetEmulatedDevices();
22} 22}
23 23
24Controller_Keyboard::~Controller_Keyboard() = default; 24Keyboard::~Keyboard() = default;
25 25
26void Controller_Keyboard::OnInit() {} 26void Keyboard::OnInit() {}
27 27
28void Controller_Keyboard::OnRelease() {} 28void Keyboard::OnRelease() {}
29 29
30void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 31 if (!IsControllerActivated()) {
32 shared_memory->keyboard_lifo.buffer_count = 0; 32 shared_memory->keyboard_lifo.buffer_count = 0;
33 shared_memory->keyboard_lifo.buffer_tail = 0; 33 shared_memory->keyboard_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 7532f53c6..172ec1309 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -14,10 +14,10 @@ struct KeyboardKey;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Keyboard final : public ControllerBase { 17class Keyboard final : public ControllerBase {
18public: 18public:
19 explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Keyboard() override; 20 ~Keyboard() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 0afc66681..6e5a04e34 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -12,8 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
14 14
15Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
16 : ControllerBase{hid_core_} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, 16 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
18 "MouseSharedMemory is bigger than the shared memory"); 17 "MouseSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at( 18 shared_memory = std::construct_at(
@@ -21,12 +20,12 @@ Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared
21 emulated_devices = hid_core.GetEmulatedDevices(); 20 emulated_devices = hid_core.GetEmulatedDevices();
22} 21}
23 22
24Controller_Mouse::~Controller_Mouse() = default; 23Mouse::~Mouse() = default;
25 24
26void Controller_Mouse::OnInit() {} 25void Mouse::OnInit() {}
27void Controller_Mouse::OnRelease() {} 26void Mouse::OnRelease() {}
28 27
29void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 28void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
31 shared_memory->mouse_lifo.buffer_count = 0; 30 shared_memory->mouse_lifo.buffer_count = 0;
32 shared_memory->mouse_lifo.buffer_tail = 0; 31 shared_memory->mouse_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 733d00577..a80f3823f 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -14,10 +14,10 @@ struct AnalogStickState;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Mouse final : public ControllerBase { 17class Mouse final : public ControllerBase {
18public: 18public:
19 explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Mouse() override; 20 ~Mouse() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index bc822f19e..08ee9de9c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/kernel/k_readable_event.h" 18#include "core/hle/kernel/k_readable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/errors.h" 20#include "core/hle/service/hid/errors.h"
21#include "core/hle/service/hid/hid_util.h"
21#include "core/hle/service/kernel_helpers.h" 22#include "core/hle/service/kernel_helpers.h"
22 23
23namespace Service::HID { 24namespace Service::HID {
@@ -29,60 +30,8 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
29 Core::HID::NpadIdType::Handheld, 30 Core::HID::NpadIdType::Handheld,
30}; 31};
31 32
32bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { 33NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
33 switch (npad_id) { 34 KernelHelpers::ServiceContext& service_context_)
34 case Core::HID::NpadIdType::Player1:
35 case Core::HID::NpadIdType::Player2:
36 case Core::HID::NpadIdType::Player3:
37 case Core::HID::NpadIdType::Player4:
38 case Core::HID::NpadIdType::Player5:
39 case Core::HID::NpadIdType::Player6:
40 case Core::HID::NpadIdType::Player7:
41 case Core::HID::NpadIdType::Player8:
42 case Core::HID::NpadIdType::Other:
43 case Core::HID::NpadIdType::Handheld:
44 return true;
45 default:
46 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
47 return false;
48 }
49}
50
51Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
52 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
53 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
54 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
55
56 if (!npad_type) {
57 return VibrationInvalidStyleIndex;
58 }
59 if (!npad_id) {
60 return VibrationInvalidNpadId;
61 }
62 if (!device_index) {
63 return VibrationDeviceIndexOutOfRange;
64 }
65
66 return ResultSuccess;
67}
68
69Result Controller_NPad::VerifyValidSixAxisSensorHandle(
70 const Core::HID::SixAxisSensorHandle& device_handle) {
71 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
72 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
73
74 if (!npad_id) {
75 return InvalidNpadId;
76 }
77 if (!device_index) {
78 return NpadDeviceIndexOutOfRange;
79 }
80
81 return ResultSuccess;
82}
83
84Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
85 KernelHelpers::ServiceContext& service_context_)
86 : ControllerBase{hid_core_}, service_context{service_context_} { 35 : ControllerBase{hid_core_}, service_context{service_context_} {
87 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); 36 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
88 for (std::size_t i = 0; i < controller_data.size(); ++i) { 37 for (std::size_t i = 0; i < controller_data.size(); ++i) {
@@ -103,7 +52,7 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_m
103 } 52 }
104} 53}
105 54
106Controller_NPad::~Controller_NPad() { 55NPad::~NPad() {
107 for (std::size_t i = 0; i < controller_data.size(); ++i) { 56 for (std::size_t i = 0; i < controller_data.size(); ++i) {
108 auto& controller = controller_data[i]; 57 auto& controller = controller_data[i];
109 controller.device->DeleteCallback(controller.callback_key); 58 controller.device->DeleteCallback(controller.callback_key);
@@ -111,8 +60,7 @@ Controller_NPad::~Controller_NPad() {
111 OnRelease(); 60 OnRelease();
112} 61}
113 62
114void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, 63void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
115 std::size_t controller_idx) {
116 if (type == Core::HID::ControllerTriggerType::All) { 64 if (type == Core::HID::ControllerTriggerType::All) {
117 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); 65 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
118 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); 66 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
@@ -150,7 +98,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
150 } 98 }
151} 99}
152 100
153void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { 101void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
154 auto& controller = GetControllerFromNpadIdType(npad_id); 102 auto& controller = GetControllerFromNpadIdType(npad_id);
155 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { 103 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
156 return; 104 return;
@@ -344,12 +292,13 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
344 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, 292 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
345 Common::Input::PollingMode::Active); 293 Common::Input::PollingMode::Active);
346 } 294 }
295
347 SignalStyleSetChangedEvent(npad_id); 296 SignalStyleSetChangedEvent(npad_id);
348 WriteEmptyEntry(controller.shared_memory); 297 WriteEmptyEntry(controller.shared_memory);
349 hid_core.SetLastActiveController(npad_id); 298 hid_core.SetLastActiveController(npad_id);
350} 299}
351 300
352void Controller_NPad::OnInit() { 301void NPad::OnInit() {
353 if (!IsControllerActivated()) { 302 if (!IsControllerActivated()) {
354 return; 303 return;
355 } 304 }
@@ -383,7 +332,7 @@ void Controller_NPad::OnInit() {
383 } 332 }
384} 333}
385 334
386void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { 335void NPad::WriteEmptyEntry(NpadInternalState* npad) {
387 NPadGenericState dummy_pad_state{}; 336 NPadGenericState dummy_pad_state{};
388 NpadGcTriggerState dummy_gc_state{}; 337 NpadGcTriggerState dummy_gc_state{};
389 dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; 338 dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
@@ -404,7 +353,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
404 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); 353 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
405} 354}
406 355
407void Controller_NPad::OnRelease() { 356void NPad::OnRelease() {
408 is_controller_initialized = false; 357 is_controller_initialized = false;
409 for (std::size_t i = 0; i < controller_data.size(); ++i) { 358 for (std::size_t i = 0; i < controller_data.size(); ++i) {
410 auto& controller = controller_data[i]; 359 auto& controller = controller_data[i];
@@ -415,7 +364,7 @@ void Controller_NPad::OnRelease() {
415 } 364 }
416} 365}
417 366
418void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 367void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
419 std::scoped_lock lock{mutex}; 368 std::scoped_lock lock{mutex};
420 auto& controller = GetControllerFromNpadIdType(npad_id); 369 auto& controller = GetControllerFromNpadIdType(npad_id);
421 const auto controller_type = controller.device->GetNpadStyleIndex(); 370 const auto controller_type = controller.device->GetNpadStyleIndex();
@@ -457,12 +406,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
457 pad_entry.l_stick = stick_state.left; 406 pad_entry.l_stick = stick_state.left;
458 } 407 }
459 408
460 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { 409 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft ||
410 controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
461 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); 411 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
462 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); 412 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
463 } 413 }
464 414
465 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { 415 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight ||
416 controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
466 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); 417 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
467 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); 418 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
468 } 419 }
@@ -482,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
482 } 433 }
483} 434}
484 435
485void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 436void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
486 if (!IsControllerActivated()) { 437 if (!IsControllerActivated()) {
487 return; 438 return;
488 } 439 }
@@ -612,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
612 } 563 }
613} 564}
614 565
615void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { 566void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
616 if (!IsControllerActivated()) {
617 return;
618 }
619
620 for (std::size_t i = 0; i < controller_data.size(); ++i) {
621 auto& controller = controller_data[i];
622
623 const auto& controller_type = controller.device->GetNpadStyleIndex();
624
625 if (controller_type == Core::HID::NpadStyleIndex::None ||
626 !controller.device->IsConnected()) {
627 continue;
628 }
629
630 auto* npad = controller.shared_memory;
631 const auto& motion_state = controller.device->GetMotions();
632 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
633 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
634 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
635 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
636 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
637 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
638
639 // Clear previous state
640 sixaxis_fullkey_state = {};
641 sixaxis_handheld_state = {};
642 sixaxis_dual_left_state = {};
643 sixaxis_dual_right_state = {};
644 sixaxis_left_lifo_state = {};
645 sixaxis_right_lifo_state = {};
646
647 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
648 controller.sixaxis_at_rest = true;
649 for (std::size_t e = 0; e < motion_state.size(); ++e) {
650 controller.sixaxis_at_rest =
651 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
652 }
653 }
654
655 const auto set_motion_state = [&](SixAxisSensorState& state,
656 const Core::HID::ControllerMotion& hid_state) {
657 using namespace std::literals::chrono_literals;
658 static constexpr SixAxisSensorState default_motion_state = {
659 .delta_time = std::chrono::nanoseconds(5ms).count(),
660 .accel = {0, 0, -1.0f},
661 .orientation =
662 {
663 Common::Vec3f{1.0f, 0, 0},
664 Common::Vec3f{0, 1.0f, 0},
665 Common::Vec3f{0, 0, 1.0f},
666 },
667 .attribute = {1},
668 };
669 if (!controller.sixaxis_sensor_enabled) {
670 state = default_motion_state;
671 return;
672 }
673 if (!Settings::values.motion_enabled.GetValue()) {
674 state = default_motion_state;
675 return;
676 }
677 state.attribute.is_connected.Assign(1);
678 state.delta_time = std::chrono::nanoseconds(5ms).count();
679 state.accel = hid_state.accel;
680 state.gyro = hid_state.gyro;
681 state.rotation = hid_state.rotation;
682 state.orientation = hid_state.orientation;
683 };
684
685 switch (controller_type) {
686 case Core::HID::NpadStyleIndex::None:
687 ASSERT(false);
688 break;
689 case Core::HID::NpadStyleIndex::ProController:
690 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
691 break;
692 case Core::HID::NpadStyleIndex::Handheld:
693 set_motion_state(sixaxis_handheld_state, motion_state[0]);
694 break;
695 case Core::HID::NpadStyleIndex::JoyconDual:
696 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
697 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
698 break;
699 case Core::HID::NpadStyleIndex::JoyconLeft:
700 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
701 break;
702 case Core::HID::NpadStyleIndex::JoyconRight:
703 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
704 break;
705 case Core::HID::NpadStyleIndex::Pokeball:
706 using namespace std::literals::chrono_literals;
707 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
708 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
709 break;
710 default:
711 break;
712 }
713
714 sixaxis_fullkey_state.sampling_number =
715 npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
716 sixaxis_handheld_state.sampling_number =
717 npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
718 sixaxis_dual_left_state.sampling_number =
719 npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
720 sixaxis_dual_right_state.sampling_number =
721 npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
722 sixaxis_left_lifo_state.sampling_number =
723 npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
724 sixaxis_right_lifo_state.sampling_number =
725 npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
726
727 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
728 // This buffer only is updated on handheld on HW
729 npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
730 } else {
731 // Handheld doesn't update this buffer on HW
732 npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
733 }
734
735 npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
736 npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
737 npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
738 npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
739 }
740}
741
742void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
743 hid_core.SetSupportedStyleTag(style_set); 567 hid_core.SetSupportedStyleTag(style_set);
744 568
745 if (is_controller_initialized) { 569 if (is_controller_initialized) {
@@ -750,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
750 is_controller_initialized = true; 574 is_controller_initialized = true;
751} 575}
752 576
753Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 577Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const {
754 if (!is_controller_initialized) { 578 if (!is_controller_initialized) {
755 return {Core::HID::NpadStyleSet::None}; 579 return {Core::HID::NpadStyleSet::None};
756 } 580 }
757 return hid_core.GetSupportedStyleTag(); 581 return hid_core.GetSupportedStyleTag();
758} 582}
759 583
760Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { 584Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
761 constexpr std::size_t max_number_npad_ids = 0xa; 585 constexpr std::size_t max_number_npad_ids = 0xa;
762 const auto length = data.size(); 586 const auto length = data.size();
763 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 587 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
@@ -773,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
773 return ResultSuccess; 597 return ResultSuccess;
774} 598}
775 599
776void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 600void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
777 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); 601 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
778 ASSERT(max_length <= copy_amount); 602 ASSERT(max_length <= copy_amount);
779 std::memcpy(data, supported_npad_id_types.data(), copy_amount); 603 std::memcpy(data, supported_npad_id_types.data(), copy_amount);
780} 604}
781 605
782std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { 606std::size_t NPad::GetSupportedNpadIdTypesSize() const {
783 return supported_npad_id_types.size(); 607 return supported_npad_id_types.size();
784} 608}
785 609
786void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 610void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
787 if (joy_hold_type != NpadJoyHoldType::Horizontal && 611 if (joy_hold_type != NpadJoyHoldType::Horizontal &&
788 joy_hold_type != NpadJoyHoldType::Vertical) { 612 joy_hold_type != NpadJoyHoldType::Vertical) {
789 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", 613 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
@@ -793,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
793 hold_type = joy_hold_type; 617 hold_type = joy_hold_type;
794} 618}
795 619
796Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { 620NPad::NpadJoyHoldType NPad::GetHoldType() const {
797 return hold_type; 621 return hold_type;
798} 622}
799 623
800void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { 624void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
801 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { 625 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
802 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); 626 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
803 return; 627 return;
@@ -806,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a
806 handheld_activation_mode = activation_mode; 630 handheld_activation_mode = activation_mode;
807} 631}
808 632
809Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { 633NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
810 return handheld_activation_mode; 634 return handheld_activation_mode;
811} 635}
812 636
813void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { 637void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
814 communication_mode = communication_mode_; 638 communication_mode = communication_mode_;
815} 639}
816 640
817Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { 641NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
818 return communication_mode; 642 return communication_mode;
819} 643}
820 644
821bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, 645bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
822 NpadJoyDeviceType npad_device_type, 646 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
823 NpadJoyAssignmentMode assignment_mode) {
824 if (!IsNpadIdValid(npad_id)) { 647 if (!IsNpadIdValid(npad_id)) {
825 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 648 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
826 return false; 649 return false;
@@ -889,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID:
889 return true; 712 return true;
890} 713}
891 714
892bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, 715bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
893 std::size_t device_index, 716 const Core::HID::VibrationValue& vibration_value) {
894 const Core::HID::VibrationValue& vibration_value) {
895 auto& controller = GetControllerFromNpadIdType(npad_id); 717 auto& controller = GetControllerFromNpadIdType(npad_id);
896 if (!controller.device->IsConnected()) { 718 if (!controller.device->IsConnected()) {
897 return false; 719 return false;
@@ -935,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
935 return controller.device->SetVibration(device_index, vibration); 757 return controller.device->SetVibration(device_index, vibration);
936} 758}
937 759
938void Controller_NPad::VibrateController( 760void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
939 const Core::HID::VibrationDeviceHandle& vibration_device_handle, 761 const Core::HID::VibrationValue& vibration_value) {
940 const Core::HID::VibrationValue& vibration_value) { 762 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
941 if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
942 return; 763 return;
943 } 764 }
944 765
@@ -982,7 +803,7 @@ void Controller_NPad::VibrateController(
982 } 803 }
983} 804}
984 805
985void Controller_NPad::VibrateControllers( 806void NPad::VibrateControllers(
986 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, 807 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
987 std::span<const Core::HID::VibrationValue> vibration_values) { 808 std::span<const Core::HID::VibrationValue> vibration_values) {
988 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 809 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
@@ -999,9 +820,9 @@ void Controller_NPad::VibrateControllers(
999 } 820 }
1000} 821}
1001 822
1002Core::HID::VibrationValue Controller_NPad::GetLastVibration( 823Core::HID::VibrationValue NPad::GetLastVibration(
1003 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 824 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1004 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 825 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1005 return {}; 826 return {};
1006 } 827 }
1007 828
@@ -1010,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
1010 return controller.vibration[device_index].latest_vibration_value; 831 return controller.vibration[device_index].latest_vibration_value;
1011} 832}
1012 833
1013void Controller_NPad::InitializeVibrationDevice( 834void NPad::InitializeVibrationDevice(
1014 const Core::HID::VibrationDeviceHandle& vibration_device_handle) { 835 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
1015 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 836 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1016 return; 837 return;
1017 } 838 }
1018 839
@@ -1021,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice(
1021 InitializeVibrationDeviceAtIndex(npad_index, device_index); 842 InitializeVibrationDeviceAtIndex(npad_index, device_index);
1022} 843}
1023 844
1024void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, 845void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
1025 std::size_t device_index) { 846 std::size_t device_index) {
1026 auto& controller = GetControllerFromNpadIdType(npad_id); 847 auto& controller = GetControllerFromNpadIdType(npad_id);
1027 if (!Settings::values.vibration_enabled.GetValue()) { 848 if (!Settings::values.vibration_enabled.GetValue()) {
1028 controller.vibration[device_index].device_mounted = false; 849 controller.vibration[device_index].device_mounted = false;
@@ -1033,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
1033 controller.device->IsVibrationEnabled(device_index); 854 controller.device->IsVibrationEnabled(device_index);
1034} 855}
1035 856
1036void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 857void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
1037 permit_vibration_session_enabled = permit_vibration_session; 858 permit_vibration_session_enabled = permit_vibration_session;
1038} 859}
1039 860
1040bool Controller_NPad::IsVibrationDeviceMounted( 861bool NPad::IsVibrationDeviceMounted(
1041 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 862 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1042 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 863 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1043 return false; 864 return false;
1044 } 865 }
1045 866
@@ -1048,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted(
1048 return controller.vibration[device_index].device_mounted; 869 return controller.vibration[device_index].device_mounted;
1049} 870}
1050 871
1051Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { 872Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
1052 if (!IsNpadIdValid(npad_id)) { 873 if (!IsNpadIdValid(npad_id)) {
1053 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 874 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1054 // Fallback to player 1 875 // Fallback to player 1
@@ -1060,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
1060 return controller.styleset_changed_event->GetReadableEvent(); 881 return controller.styleset_changed_event->GetReadableEvent();
1061} 882}
1062 883
1063void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { 884void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
1064 const auto& controller = GetControllerFromNpadIdType(npad_id); 885 const auto& controller = GetControllerFromNpadIdType(npad_id);
1065 controller.styleset_changed_event->Signal(); 886 controller.styleset_changed_event->Signal();
1066} 887}
1067 888
1068void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, 889void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) {
1069 Core::HID::NpadIdType npad_id) {
1070 UpdateControllerAt(controller, npad_id, true); 890 UpdateControllerAt(controller, npad_id, true);
1071} 891}
1072 892
1073void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, 893void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id,
1074 Core::HID::NpadIdType npad_id, bool connected) { 894 bool connected) {
1075 auto& controller = GetControllerFromNpadIdType(npad_id); 895 auto& controller = GetControllerFromNpadIdType(npad_id);
1076 if (!connected) { 896 if (!connected) {
1077 DisconnectNpad(npad_id); 897 DisconnectNpad(npad_id);
@@ -1082,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
1082 InitNewlyAddedController(npad_id); 902 InitNewlyAddedController(npad_id);
1083} 903}
1084 904
1085Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 905Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1086 if (!IsNpadIdValid(npad_id)) { 906 if (!IsNpadIdValid(npad_id)) {
1087 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 907 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1088 return InvalidNpadId; 908 return InvalidNpadId;
@@ -1108,9 +928,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1108 shared_memory->sixaxis_dual_right_properties.raw = 0; 928 shared_memory->sixaxis_dual_right_properties.raw = 0;
1109 shared_memory->sixaxis_left_properties.raw = 0; 929 shared_memory->sixaxis_left_properties.raw = 0;
1110 shared_memory->sixaxis_right_properties.raw = 0; 930 shared_memory->sixaxis_right_properties.raw = 0;
1111 shared_memory->battery_level_dual = 0; 931 shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty;
1112 shared_memory->battery_level_left = 0; 932 shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty;
1113 shared_memory->battery_level_right = 0; 933 shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty;
1114 shared_memory->fullkey_color = { 934 shared_memory->fullkey_color = {
1115 .attribute = ColorAttribute::NoController, 935 .attribute = ColorAttribute::NoController,
1116 .fullkey = {}, 936 .fullkey = {},
@@ -1131,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1131 return ResultSuccess; 951 return ResultSuccess;
1132} 952}
1133 953
1134Result Controller_NPad::SetGyroscopeZeroDriftMode( 954Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1135 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1136 Core::HID::GyroscopeZeroDriftMode drift_mode) {
1137 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1138 if (is_valid.IsError()) {
1139 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1140 return is_valid;
1141 }
1142
1143 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1144 auto& controller = GetControllerFromHandle(sixaxis_handle);
1145 sixaxis.gyroscope_zero_drift_mode = drift_mode;
1146 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
1147
1148 return ResultSuccess;
1149}
1150
1151Result Controller_NPad::GetGyroscopeZeroDriftMode(
1152 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1153 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
1154 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1155 if (is_valid.IsError()) {
1156 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1157 return is_valid;
1158 }
1159
1160 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1161 drift_mode = sixaxis.gyroscope_zero_drift_mode;
1162
1163 return ResultSuccess;
1164}
1165
1166Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1167 bool& is_at_rest) const {
1168 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1169 if (is_valid.IsError()) {
1170 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1171 return is_valid;
1172 }
1173
1174 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1175 is_at_rest = controller.sixaxis_at_rest;
1176 return ResultSuccess;
1177}
1178
1179Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1180 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { 955 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
1181 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 956 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1182 if (is_valid.IsError()) { 957 if (is_valid.IsError()) {
1183 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 958 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1184 return is_valid; 959 return is_valid;
@@ -1189,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1189 return ResultSuccess; 964 return ResultSuccess;
1190} 965}
1191 966
1192Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( 967Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1193 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
1194 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1195 if (is_valid.IsError()) {
1196 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1197 return is_valid;
1198 }
1199
1200 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1201 sixaxis.unaltered_passtrough = is_enabled;
1202 return ResultSuccess;
1203}
1204
1205Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
1206 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
1207 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1208 if (is_valid.IsError()) {
1209 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1210 return is_valid;
1211 }
1212
1213 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1214 is_enabled = sixaxis.unaltered_passtrough;
1215 return ResultSuccess;
1216}
1217
1218Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
1219 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1220 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
1221 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1222 if (is_valid.IsError()) {
1223 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1224 return is_valid;
1225 }
1226
1227 // TODO: Request this data to the controller. On error return 0xd8ca
1228 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1229 calibration = sixaxis.calibration;
1230 return ResultSuccess;
1231}
1232
1233Result Controller_NPad::GetSixAxisSensorIcInformation(
1234 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1235 Core::HID::SixAxisSensorIcInformation& ic_information) const {
1236 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1237 if (is_valid.IsError()) {
1238 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1239 return is_valid;
1240 }
1241
1242 // TODO: Request this data to the controller. On error return 0xd8ca
1243 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1244 ic_information = sixaxis.ic_information;
1245 return ResultSuccess;
1246}
1247
1248Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1249 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 968 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1250 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 969 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1251 if (is_valid.IsError()) { 970 if (is_valid.IsError()) {
1252 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 971 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1253 return is_valid; 972 return is_valid;
@@ -1259,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1259 return ResultSuccess; 978 return ResultSuccess;
1260} 979}
1261 980
1262Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 981NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
1263 bool sixaxis_status) { 982 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
1264 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1265 if (is_valid.IsError()) {
1266 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1267 return is_valid;
1268 }
1269
1270 auto& controller = GetControllerFromHandle(sixaxis_handle);
1271 controller.sixaxis_sensor_enabled = sixaxis_status;
1272 return ResultSuccess;
1273} 983}
1274 984
1275Result Controller_NPad::IsSixAxisSensorFusionEnabled( 985NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
1276 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { 986 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
1277 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1278 if (is_valid.IsError()) {
1279 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1280 return is_valid;
1281 }
1282
1283 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1284 is_fusion_enabled = sixaxis.is_fusion_enabled;
1285
1286 return ResultSuccess;
1287} 987}
1288Result Controller_NPad::SetSixAxisFusionEnabled(
1289 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
1290 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1291 if (is_valid.IsError()) {
1292 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1293 return is_valid;
1294 }
1295 988
1296 auto& sixaxis = GetSixaxisState(sixaxis_handle); 989NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
1297 sixaxis.is_fusion_enabled = is_fusion_enabled; 990 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
1298
1299 return ResultSuccess;
1300} 991}
1301 992
1302Result Controller_NPad::SetSixAxisFusionParameters( 993NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
1303 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 994 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
1304 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1305 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1306 if (is_valid.IsError()) {
1307 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1308 return is_valid;
1309 }
1310
1311 const auto param1 = sixaxis_fusion_parameters.parameter1;
1312 if (param1 < 0.0f || param1 > 1.0f) {
1313 return InvalidSixAxisFusionRange;
1314 }
1315
1316 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1317 sixaxis.fusion = sixaxis_fusion_parameters;
1318
1319 return ResultSuccess;
1320} 995}
1321 996
1322Result Controller_NPad::GetSixAxisFusionParameters( 997NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
1323 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 998 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
1324 Core::HID::SixAxisSensorFusionParameters& parameters) const { 999}
1325 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1326 if (is_valid.IsError()) {
1327 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1328 return is_valid;
1329 }
1330
1331 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1332 parameters = sixaxis.fusion;
1333 1000
1334 return ResultSuccess; 1001NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1002 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1335} 1003}
1336 1004
1337Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1005Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1338 Core::HID::NpadIdType npad_id_2) { 1006 Core::HID::NpadIdType npad_id_2) {
1339 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1007 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1340 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1008 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1341 npad_id_2); 1009 npad_id_2);
@@ -1397,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1397 return ResultSuccess; 1065 return ResultSuccess;
1398} 1066}
1399 1067
1400void Controller_NPad::StartLRAssignmentMode() { 1068void NPad::StartLRAssignmentMode() {
1401 // Nothing internally is used for lr assignment mode. Since we have the ability to set the 1069 // Nothing internally is used for lr assignment mode. Since we have the ability to set the
1402 // controller types from boot, it doesn't really matter about showing a selection screen 1070 // controller types from boot, it doesn't really matter about showing a selection screen
1403 is_in_lr_assignment_mode = true; 1071 is_in_lr_assignment_mode = true;
1404} 1072}
1405 1073
1406void Controller_NPad::StopLRAssignmentMode() { 1074void NPad::StopLRAssignmentMode() {
1407 is_in_lr_assignment_mode = false; 1075 is_in_lr_assignment_mode = false;
1408} 1076}
1409 1077
1410Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, 1078Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) {
1411 Core::HID::NpadIdType npad_id_2) {
1412 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1079 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1413 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1080 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1414 npad_id_2); 1081 npad_id_2);
@@ -1439,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1439 return ResultSuccess; 1106 return ResultSuccess;
1440} 1107}
1441 1108
1442Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, 1109Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
1443 Core::HID::LedPattern& pattern) const {
1444 if (!IsNpadIdValid(npad_id)) { 1110 if (!IsNpadIdValid(npad_id)) {
1445 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1111 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1446 return InvalidNpadId; 1112 return InvalidNpadId;
@@ -1450,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
1450 return ResultSuccess; 1116 return ResultSuccess;
1451} 1117}
1452 1118
1453Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 1119Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
1454 bool& is_valid) const { 1120 bool& is_valid) const {
1455 if (!IsNpadIdValid(npad_id)) { 1121 if (!IsNpadIdValid(npad_id)) {
1456 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1122 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1457 return InvalidNpadId; 1123 return InvalidNpadId;
@@ -1461,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::
1461 return ResultSuccess; 1127 return ResultSuccess;
1462} 1128}
1463 1129
1464Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( 1130Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1465 bool is_protection_enabled, Core::HID::NpadIdType npad_id) { 1131 Core::HID::NpadIdType npad_id) {
1466 if (!IsNpadIdValid(npad_id)) { 1132 if (!IsNpadIdValid(npad_id)) {
1467 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1133 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1468 return InvalidNpadId; 1134 return InvalidNpadId;
@@ -1472,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
1472 return ResultSuccess; 1138 return ResultSuccess;
1473} 1139}
1474 1140
1475void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1141void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1476 analog_stick_use_center_clamp = use_center_clamp; 1142 analog_stick_use_center_clamp = use_center_clamp;
1477} 1143}
1478 1144
1479void Controller_NPad::ClearAllConnectedControllers() { 1145void NPad::ClearAllConnectedControllers() {
1480 for (auto& controller : controller_data) { 1146 for (auto& controller : controller_data) {
1481 if (controller.device->IsConnected() && 1147 if (controller.device->IsConnected() &&
1482 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { 1148 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
@@ -1486,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
1486 } 1152 }
1487} 1153}
1488 1154
1489void Controller_NPad::DisconnectAllConnectedControllers() { 1155void NPad::DisconnectAllConnectedControllers() {
1490 for (auto& controller : controller_data) { 1156 for (auto& controller : controller_data) {
1491 controller.device->Disconnect(); 1157 controller.device->Disconnect();
1492 } 1158 }
1493} 1159}
1494 1160
1495void Controller_NPad::ConnectAllDisconnectedControllers() { 1161void NPad::ConnectAllDisconnectedControllers() {
1496 for (auto& controller : controller_data) { 1162 for (auto& controller : controller_data) {
1497 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && 1163 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1498 !controller.device->IsConnected()) { 1164 !controller.device->IsConnected()) {
@@ -1501,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
1501 } 1167 }
1502} 1168}
1503 1169
1504void Controller_NPad::ClearAllControllers() { 1170void NPad::ClearAllControllers() {
1505 for (auto& controller : controller_data) { 1171 for (auto& controller : controller_data) {
1506 controller.device->Disconnect(); 1172 controller.device->Disconnect();
1507 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); 1173 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1508 } 1174 }
1509} 1175}
1510 1176
1511Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { 1177Core::HID::NpadButton NPad::GetAndResetPressState() {
1512 return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); 1178 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1513} 1179}
1514 1180
1515void Controller_NPad::ApplyNpadSystemCommonPolicy() { 1181void NPad::ApplyNpadSystemCommonPolicy() {
1516 Core::HID::NpadStyleTag styletag{}; 1182 Core::HID::NpadStyleTag styletag{};
1517 styletag.fullkey.Assign(1); 1183 styletag.fullkey.Assign(1);
1518 styletag.handheld.Assign(1); 1184 styletag.handheld.Assign(1);
@@ -1537,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() {
1537 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; 1203 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
1538} 1204}
1539 1205
1540bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { 1206bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1541 if (controller == Core::HID::NpadStyleIndex::Handheld) { 1207 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1542 const bool support_handheld = 1208 const bool support_handheld =
1543 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1209 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
@@ -1588,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1588 return false; 1254 return false;
1589} 1255}
1590 1256
1591Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1257NPad::NpadControllerData& NPad::GetControllerFromHandle(
1592 const Core::HID::SixAxisSensorHandle& device_handle) { 1258 const Core::HID::VibrationDeviceHandle& device_handle) {
1593 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1259 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1594 return GetControllerFromNpadIdType(npad_id); 1260 return GetControllerFromNpadIdType(npad_id);
1595} 1261}
1596 1262
1597const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1263const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1598 const Core::HID::SixAxisSensorHandle& device_handle) const { 1264 const Core::HID::VibrationDeviceHandle& device_handle) const {
1599 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1265 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1600 return GetControllerFromNpadIdType(npad_id); 1266 return GetControllerFromNpadIdType(npad_id);
1601} 1267}
1602 1268
1603Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1269NPad::NpadControllerData& NPad::GetControllerFromHandle(
1604 const Core::HID::VibrationDeviceHandle& device_handle) { 1270 const Core::HID::SixAxisSensorHandle& device_handle) {
1605 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1271 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1606 return GetControllerFromNpadIdType(npad_id); 1272 return GetControllerFromNpadIdType(npad_id);
1607} 1273}
1608 1274
1609const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1275const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1610 const Core::HID::VibrationDeviceHandle& device_handle) const { 1276 const Core::HID::SixAxisSensorHandle& device_handle) const {
1611 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1277 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1612 return GetControllerFromNpadIdType(npad_id); 1278 return GetControllerFromNpadIdType(npad_id);
1613} 1279}
1614 1280
1615Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1281NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
1616 Core::HID::NpadIdType npad_id) {
1617 if (!IsNpadIdValid(npad_id)) { 1282 if (!IsNpadIdValid(npad_id)) {
1618 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1283 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1619 npad_id = Core::HID::NpadIdType::Player1; 1284 npad_id = Core::HID::NpadIdType::Player1;
1620 } 1285 }
1621 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1286 const auto npad_index = NpadIdTypeToIndex(npad_id);
1622 return controller_data[npad_index]; 1287 return controller_data[npad_index];
1623} 1288}
1624 1289
1625const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1290const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
1626 Core::HID::NpadIdType npad_id) const { 1291 Core::HID::NpadIdType npad_id) const {
1627 if (!IsNpadIdValid(npad_id)) { 1292 if (!IsNpadIdValid(npad_id)) {
1628 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1293 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1629 npad_id = Core::HID::NpadIdType::Player1; 1294 npad_id = Core::HID::NpadIdType::Player1;
1630 } 1295 }
1631 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1296 const auto npad_index = NpadIdTypeToIndex(npad_id);
1632 return controller_data[npad_index]; 1297 return controller_data[npad_index];
1633} 1298}
1634 1299
1635Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1300Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1636 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1301 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1637 auto& controller = GetControllerFromHandle(sixaxis_handle); 1302 auto& controller = GetControllerFromHandle(sixaxis_handle);
1638 switch (sixaxis_handle.npad_type) { 1303 switch (sixaxis_handle.npad_type) {
@@ -1655,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1655 } 1320 }
1656} 1321}
1657 1322
1658const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1323const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1659 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1324 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1660 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1325 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1661 switch (sixaxis_handle.npad_type) { 1326 switch (sixaxis_handle.npad_type) {
@@ -1678,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1678 } 1343 }
1679} 1344}
1680 1345
1681Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1346NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
1682 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1347 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
1683 auto& controller = GetControllerFromHandle(sixaxis_handle);
1684 switch (sixaxis_handle.npad_type) {
1685 case Core::HID::NpadStyleIndex::ProController:
1686 case Core::HID::NpadStyleIndex::Pokeball:
1687 return controller.sixaxis_fullkey;
1688 case Core::HID::NpadStyleIndex::Handheld:
1689 return controller.sixaxis_handheld;
1690 case Core::HID::NpadStyleIndex::JoyconDual:
1691 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1692 return controller.sixaxis_dual_left;
1693 }
1694 return controller.sixaxis_dual_right;
1695 case Core::HID::NpadStyleIndex::JoyconLeft:
1696 return controller.sixaxis_left;
1697 case Core::HID::NpadStyleIndex::JoyconRight:
1698 return controller.sixaxis_right;
1699 default:
1700 return controller.sixaxis_unknown;
1701 }
1702}
1703 1348
1704const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1349 return {
1705 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1350 .ui_variant = 0,
1706 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1351 .footer = shared_memory->applet_footer_type,
1707 switch (sixaxis_handle.npad_type) { 1352 };
1708 case Core::HID::NpadStyleIndex::ProController:
1709 case Core::HID::NpadStyleIndex::Pokeball:
1710 return controller.sixaxis_fullkey;
1711 case Core::HID::NpadStyleIndex::Handheld:
1712 return controller.sixaxis_handheld;
1713 case Core::HID::NpadStyleIndex::JoyconDual:
1714 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1715 return controller.sixaxis_dual_left;
1716 }
1717 return controller.sixaxis_dual_right;
1718 case Core::HID::NpadStyleIndex::JoyconLeft:
1719 return controller.sixaxis_left;
1720 case Core::HID::NpadStyleIndex::JoyconRight:
1721 return controller.sixaxis_right;
1722 default:
1723 return controller.sixaxis_unknown;
1724 }
1725} 1353}
1726 1354
1727} // namespace Service::HID 1355} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 949e58a4c..9167c93f0 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -10,7 +10,6 @@
10 10
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/vector_math.h"
14 13
15#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
16#include "core/hle/service/hid/controllers/controller_base.h" 15#include "core/hle/service/hid/controllers/controller_base.h"
@@ -34,11 +33,11 @@ union Result;
34 33
35namespace Service::HID { 34namespace Service::HID {
36 35
37class Controller_NPad final : public ControllerBase { 36class NPad final : public ControllerBase {
38public: 37public:
39 explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 38 explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
40 KernelHelpers::ServiceContext& service_context_); 39 KernelHelpers::ServiceContext& service_context_);
41 ~Controller_NPad() override; 40 ~NPad() override;
42 41
43 // Called when the controller is initialized 42 // Called when the controller is initialized
44 void OnInit() override; 43 void OnInit() override;
@@ -49,9 +48,6 @@ public:
49 // When the controller is requesting an update for the shared memory 48 // When the controller is requesting an update for the shared memory
50 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 49 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
51 50
52 // When the controller is requesting a motion update for the shared memory
53 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
54
55 // This is nn::hid::NpadJoyHoldType 51 // This is nn::hid::NpadJoyHoldType
56 enum class NpadJoyHoldType : u64 { 52 enum class NpadJoyHoldType : u64 {
57 Vertical = 0, 53 Vertical = 0,
@@ -78,6 +74,46 @@ public:
78 MaxActivationMode = 3, 74 MaxActivationMode = 3,
79 }; 75 };
80 76
77 // This is nn::hid::system::AppletFooterUiAttributesSet
78 struct AppletFooterUiAttributes {
79 INSERT_PADDING_BYTES(0x4);
80 };
81
82 // This is nn::hid::system::AppletFooterUiType
83 enum class AppletFooterUiType : u8 {
84 None = 0,
85 HandheldNone = 1,
86 HandheldJoyConLeftOnly = 2,
87 HandheldJoyConRightOnly = 3,
88 HandheldJoyConLeftJoyConRight = 4,
89 JoyDual = 5,
90 JoyDualLeftOnly = 6,
91 JoyDualRightOnly = 7,
92 JoyLeftHorizontal = 8,
93 JoyLeftVertical = 9,
94 JoyRightHorizontal = 10,
95 JoyRightVertical = 11,
96 SwitchProController = 12,
97 CompatibleProController = 13,
98 CompatibleJoyCon = 14,
99 LarkHvc1 = 15,
100 LarkHvc2 = 16,
101 LarkNesLeft = 17,
102 LarkNesRight = 18,
103 Lucia = 19,
104 Verification = 20,
105 Lagon = 21,
106 };
107
108 using AppletFooterUiVariant = u8;
109
110 // This is "nn::hid::system::AppletDetailedUiType".
111 struct AppletDetailedUiType {
112 AppletFooterUiVariant ui_variant;
113 INSERT_PADDING_BYTES(0x2);
114 AppletFooterUiType footer;
115 };
116 static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
81 // This is nn::hid::NpadCommunicationMode 117 // This is nn::hid::NpadCommunicationMode
82 enum class NpadCommunicationMode : u64 { 118 enum class NpadCommunicationMode : u64 {
83 Mode_5ms = 0, 119 Mode_5ms = 0,
@@ -86,6 +122,15 @@ public:
86 Default = 3, 122 Default = 3,
87 }; 123 };
88 124
125 enum class NpadRevision : u32 {
126 Revision0 = 0,
127 Revision1 = 1,
128 Revision2 = 2,
129 Revision3 = 3,
130 };
131
132 using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
133
89 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 134 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
90 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 135 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
91 136
@@ -138,37 +183,18 @@ public:
138 183
139 Result DisconnectNpad(Core::HID::NpadIdType npad_id); 184 Result DisconnectNpad(Core::HID::NpadIdType npad_id);
140 185
141 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
142 Core::HID::GyroscopeZeroDriftMode drift_mode);
143 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
144 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
145 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
146 bool& is_at_rest) const;
147 Result IsFirmwareUpdateAvailableForSixAxisSensor( 186 Result IsFirmwareUpdateAvailableForSixAxisSensor(
148 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; 187 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
149 Result EnableSixAxisSensorUnalteredPassthrough(
150 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
151 Result IsSixAxisSensorUnalteredPassthroughEnabled(
152 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
153 Result LoadSixAxisSensorCalibrationParameter(
154 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
155 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
156 Result GetSixAxisSensorIcInformation(
157 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
158 Core::HID::SixAxisSensorIcInformation& ic_information) const;
159 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 188 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
160 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 189 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
161 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 190
162 bool sixaxis_status); 191 SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
163 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 192 SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
164 bool& is_fusion_enabled) const; 193 SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
165 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 194 SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
166 bool is_fusion_enabled); 195 SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
167 Result SetSixAxisFusionParameters( 196 SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
168 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 197
169 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
170 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
171 Core::HID::SixAxisSensorFusionParameters& parameters) const;
172 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; 198 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
173 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 199 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
174 bool& is_enabled) const; 200 bool& is_enabled) const;
@@ -192,10 +218,7 @@ public:
192 218
193 void ApplyNpadSystemCommonPolicy(); 219 void ApplyNpadSystemCommonPolicy();
194 220
195 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); 221 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
196 static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
197 static Result VerifyValidSixAxisSensorHandle(
198 const Core::HID::SixAxisSensorHandle& device_handle);
199 222
200private: 223private:
201 static constexpr std::size_t NPAD_COUNT = 10; 224 static constexpr std::size_t NPAD_COUNT = 10;
@@ -254,29 +277,6 @@ private:
254 }; 277 };
255 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 278 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
256 279
257 // This is nn::hid::SixAxisSensorAttribute
258 struct SixAxisSensorAttribute {
259 union {
260 u32 raw{};
261 BitField<0, 1, u32> is_connected;
262 BitField<1, 1, u32> is_interpolated;
263 };
264 };
265 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
266
267 // This is nn::hid::SixAxisSensorState
268 struct SixAxisSensorState {
269 s64 delta_time{};
270 s64 sampling_number{};
271 Common::Vec3f accel{};
272 Common::Vec3f gyro{};
273 Common::Vec3f rotation{};
274 std::array<Common::Vec3f, 3> orientation{};
275 SixAxisSensorAttribute attribute{};
276 INSERT_PADDING_BYTES(4); // Reserved
277 };
278 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
279
280 // This is nn::hid::server::NpadGcTriggerState 280 // This is nn::hid::server::NpadGcTriggerState
281 struct NpadGcTriggerState { 281 struct NpadGcTriggerState {
282 s64 sampling_number{}; 282 s64 sampling_number{};
@@ -353,37 +353,6 @@ private:
353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
354 "NfcXcdDeviceHandleStateImpl is an invalid size"); 354 "NfcXcdDeviceHandleStateImpl is an invalid size");
355 355
356 // This is nn::hid::system::AppletFooterUiAttributesSet
357 struct AppletFooterUiAttributes {
358 INSERT_PADDING_BYTES(0x4);
359 };
360
361 // This is nn::hid::system::AppletFooterUiType
362 enum class AppletFooterUiType : u8 {
363 None = 0,
364 HandheldNone = 1,
365 HandheldJoyConLeftOnly = 2,
366 HandheldJoyConRightOnly = 3,
367 HandheldJoyConLeftJoyConRight = 4,
368 JoyDual = 5,
369 JoyDualLeftOnly = 6,
370 JoyDualRightOnly = 7,
371 JoyLeftHorizontal = 8,
372 JoyLeftVertical = 9,
373 JoyRightHorizontal = 10,
374 JoyRightVertical = 11,
375 SwitchProController = 12,
376 CompatibleProController = 13,
377 CompatibleJoyCon = 14,
378 LarkHvc1 = 15,
379 LarkHvc2 = 16,
380 LarkNesLeft = 17,
381 LarkNesRight = 18,
382 Lucia = 19,
383 Verification = 20,
384 Lagon = 21,
385 };
386
387 // This is nn::hid::NpadLarkType 356 // This is nn::hid::NpadLarkType
388 enum class NpadLarkType : u32 { 357 enum class NpadLarkType : u32 {
389 Invalid, 358 Invalid,
@@ -427,12 +396,12 @@ private:
427 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; 396 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
428 Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; 397 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
429 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; 398 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
430 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; 399 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
431 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; 400 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
432 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; 401 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
433 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; 402 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
434 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; 403 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
435 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; 404 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
436 DeviceType device_type{}; 405 DeviceType device_type{};
437 INSERT_PADDING_BYTES(0x4); // Reserved 406 INSERT_PADDING_BYTES(0x4); // Reserved
438 NPadSystemProperties system_properties{}; 407 NPadSystemProperties system_properties{};
@@ -466,16 +435,6 @@ private:
466 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 435 std::chrono::steady_clock::time_point last_vibration_timepoint{};
467 }; 436 };
468 437
469 struct SixaxisParameters {
470 bool is_fusion_enabled{true};
471 bool unaltered_passtrough{false};
472 Core::HID::SixAxisSensorFusionParameters fusion{};
473 Core::HID::SixAxisSensorCalibrationParameter calibration{};
474 Core::HID::SixAxisSensorIcInformation ic_information{};
475 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
476 Core::HID::GyroscopeZeroDriftMode::Standard};
477 };
478
479 struct NpadControllerData { 438 struct NpadControllerData {
480 Kernel::KEvent* styleset_changed_event{}; 439 Kernel::KEvent* styleset_changed_event{};
481 NpadInternalState* shared_memory = nullptr; 440 NpadInternalState* shared_memory = nullptr;
@@ -489,27 +448,10 @@ private:
489 bool is_dual_left_connected{true}; 448 bool is_dual_left_connected{true};
490 bool is_dual_right_connected{true}; 449 bool is_dual_right_connected{true};
491 450
492 // Motion parameters
493 bool sixaxis_at_rest{true};
494 bool sixaxis_sensor_enabled{true};
495 SixaxisParameters sixaxis_fullkey{};
496 SixaxisParameters sixaxis_handheld{};
497 SixaxisParameters sixaxis_dual_left{};
498 SixaxisParameters sixaxis_dual_right{};
499 SixaxisParameters sixaxis_left{};
500 SixaxisParameters sixaxis_right{};
501 SixaxisParameters sixaxis_unknown{};
502
503 // Current pad state 451 // Current pad state
504 NPadGenericState npad_pad_state{}; 452 NPadGenericState npad_pad_state{};
505 NPadGenericState npad_libnx_state{}; 453 NPadGenericState npad_libnx_state{};
506 NpadGcTriggerState npad_trigger_state{}; 454 NpadGcTriggerState npad_trigger_state{};
507 SixAxisSensorState sixaxis_fullkey_state{};
508 SixAxisSensorState sixaxis_handheld_state{};
509 SixAxisSensorState sixaxis_dual_left_state{};
510 SixAxisSensorState sixaxis_dual_right_state{};
511 SixAxisSensorState sixaxis_left_lifo_state{};
512 SixAxisSensorState sixaxis_right_lifo_state{};
513 int callback_key{}; 455 int callback_key{};
514 }; 456 };
515 457
@@ -520,13 +462,13 @@ private:
520 void WriteEmptyEntry(NpadInternalState* npad); 462 void WriteEmptyEntry(NpadInternalState* npad);
521 463
522 NpadControllerData& GetControllerFromHandle( 464 NpadControllerData& GetControllerFromHandle(
523 const Core::HID::SixAxisSensorHandle& device_handle);
524 const NpadControllerData& GetControllerFromHandle(
525 const Core::HID::SixAxisSensorHandle& device_handle) const;
526 NpadControllerData& GetControllerFromHandle(
527 const Core::HID::VibrationDeviceHandle& device_handle); 465 const Core::HID::VibrationDeviceHandle& device_handle);
528 const NpadControllerData& GetControllerFromHandle( 466 const NpadControllerData& GetControllerFromHandle(
529 const Core::HID::VibrationDeviceHandle& device_handle) const; 467 const Core::HID::VibrationDeviceHandle& device_handle) const;
468 NpadControllerData& GetControllerFromHandle(
469 const Core::HID::SixAxisSensorHandle& device_handle);
470 const NpadControllerData& GetControllerFromHandle(
471 const Core::HID::SixAxisSensorHandle& device_handle) const;
530 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 472 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
531 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 473 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
532 474
@@ -534,9 +476,6 @@ private:
534 const Core::HID::SixAxisSensorHandle& device_handle); 476 const Core::HID::SixAxisSensorHandle& device_handle);
535 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( 477 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
536 const Core::HID::SixAxisSensorHandle& device_handle) const; 478 const Core::HID::SixAxisSensorHandle& device_handle) const;
537 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
538 const SixaxisParameters& GetSixaxisState(
539 const Core::HID::SixAxisSensorHandle& device_handle) const;
540 479
541 std::atomic<u64> press_state{}; 480 std::atomic<u64> press_state{};
542 481
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 73a2a2b91..588ff9d62 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -12,43 +12,43 @@
12 12
13namespace Service::HID { 13namespace Service::HID {
14 14
15Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 15Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
16 KernelHelpers::ServiceContext& service_context_) 16 KernelHelpers::ServiceContext& service_context_)
17 : ControllerBase{hid_core_}, service_context{service_context_} { 17 : ControllerBase{hid_core_}, service_context{service_context_} {
18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); 19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
20} 20}
21 21
22Controller_Palma::~Controller_Palma() { 22Palma::~Palma() {
23 service_context.CloseEvent(operation_complete_event); 23 service_context.CloseEvent(operation_complete_event);
24}; 24};
25 25
26void Controller_Palma::OnInit() {} 26void Palma::OnInit() {}
27 27
28void Controller_Palma::OnRelease() {} 28void Palma::OnRelease() {}
29 29
30void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 31 if (!IsControllerActivated()) {
32 return; 32 return;
33 } 33 }
34} 34}
35 35
36Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, 36Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
37 PalmaConnectionHandle& handle) { 37 PalmaConnectionHandle& handle) {
38 active_handle.npad_id = npad_id; 38 active_handle.npad_id = npad_id;
39 handle = active_handle; 39 handle = active_handle;
40 return ResultSuccess; 40 return ResultSuccess;
41} 41}
42 42
43Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { 43Result Palma::InitializePalma(const PalmaConnectionHandle& handle) {
44 if (handle.npad_id != active_handle.npad_id) { 44 if (handle.npad_id != active_handle.npad_id) {
45 return InvalidPalmaHandle; 45 return InvalidPalmaHandle;
46 } 46 }
47 ActivateController(); 47 Activate();
48 return ResultSuccess; 48 return ResultSuccess;
49} 49}
50 50
51Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( 51Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent(
52 const PalmaConnectionHandle& handle) const { 52 const PalmaConnectionHandle& handle) const {
53 if (handle.npad_id != active_handle.npad_id) { 53 if (handle.npad_id != active_handle.npad_id) {
54 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); 54 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
@@ -56,9 +56,9 @@ Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
56 return operation_complete_event->GetReadableEvent(); 56 return operation_complete_event->GetReadableEvent();
57} 57}
58 58
59Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, 59Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
60 PalmaOperationType& operation_type, 60 PalmaOperationType& operation_type,
61 PalmaOperationData& data) const { 61 PalmaOperationData& data) const {
62 if (handle.npad_id != active_handle.npad_id) { 62 if (handle.npad_id != active_handle.npad_id) {
63 return InvalidPalmaHandle; 63 return InvalidPalmaHandle;
64 } 64 }
@@ -67,8 +67,7 @@ Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& hand
67 return ResultSuccess; 67 return ResultSuccess;
68} 68}
69 69
70Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, 70Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) {
71 u64 palma_activity) {
72 if (handle.npad_id != active_handle.npad_id) { 71 if (handle.npad_id != active_handle.npad_id) {
73 return InvalidPalmaHandle; 72 return InvalidPalmaHandle;
74 } 73 }
@@ -79,8 +78,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
79 return ResultSuccess; 78 return ResultSuccess;
80} 79}
81 80
82Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, 81Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) {
83 PalmaFrModeType fr_mode_) {
84 if (handle.npad_id != active_handle.npad_id) { 82 if (handle.npad_id != active_handle.npad_id) {
85 return InvalidPalmaHandle; 83 return InvalidPalmaHandle;
86 } 84 }
@@ -88,7 +86,7 @@ Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
88 return ResultSuccess; 86 return ResultSuccess;
89} 87}
90 88
91Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { 89Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
92 if (handle.npad_id != active_handle.npad_id) { 90 if (handle.npad_id != active_handle.npad_id) {
93 return InvalidPalmaHandle; 91 return InvalidPalmaHandle;
94 } 92 }
@@ -99,25 +97,25 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
99 return ResultSuccess; 97 return ResultSuccess;
100} 98}
101 99
102Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { 100Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
103 if (handle.npad_id != active_handle.npad_id) { 101 if (handle.npad_id != active_handle.npad_id) {
104 return InvalidPalmaHandle; 102 return InvalidPalmaHandle;
105 } 103 }
106 return ResultSuccess; 104 return ResultSuccess;
107} 105}
108 106
109Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { 107Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
110 if (handle.npad_id != active_handle.npad_id) { 108 if (handle.npad_id != active_handle.npad_id) {
111 return InvalidPalmaHandle; 109 return InvalidPalmaHandle;
112 } 110 }
113 return ResultSuccess; 111 return ResultSuccess;
114} 112}
115 113
116void Controller_Palma::ReadPalmaApplicationSection() {} 114void Palma::ReadPalmaApplicationSection() {}
117 115
118void Controller_Palma::WritePalmaApplicationSection() {} 116void Palma::WritePalmaApplicationSection() {}
119 117
120Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { 118Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
121 if (handle.npad_id != active_handle.npad_id) { 119 if (handle.npad_id != active_handle.npad_id) {
122 return InvalidPalmaHandle; 120 return InvalidPalmaHandle;
123 } 121 }
@@ -128,7 +126,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle
128 return ResultSuccess; 126 return ResultSuccess;
129} 127}
130 128
131Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { 129Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
132 if (handle.npad_id != active_handle.npad_id) { 130 if (handle.npad_id != active_handle.npad_id) {
133 return InvalidPalmaHandle; 131 return InvalidPalmaHandle;
134 } 132 }
@@ -139,10 +137,9 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle&
139 return ResultSuccess; 137 return ResultSuccess;
140} 138}
141 139
142void Controller_Palma::WritePalmaActivityEntry() {} 140void Palma::WritePalmaActivityEntry() {}
143 141
144Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, 142Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) {
145 u64 unknown) {
146 if (handle.npad_id != active_handle.npad_id) { 143 if (handle.npad_id != active_handle.npad_id) {
147 return InvalidPalmaHandle; 144 return InvalidPalmaHandle;
148 } 145 }
@@ -153,8 +150,8 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl
153 return ResultSuccess; 150 return ResultSuccess;
154} 151}
155 152
156Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, 153Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
157 Common::ProcessAddress t_mem, u64 size) { 154 Common::ProcessAddress t_mem, u64 size) {
158 if (handle.npad_id != active_handle.npad_id) { 155 if (handle.npad_id != active_handle.npad_id) {
159 return InvalidPalmaHandle; 156 return InvalidPalmaHandle;
160 } 157 }
@@ -165,8 +162,8 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle
165 return ResultSuccess; 162 return ResultSuccess;
166} 163}
167 164
168Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, 165Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
169 s32 database_id_version_) { 166 s32 database_id_version_) {
170 if (handle.npad_id != active_handle.npad_id) { 167 if (handle.npad_id != active_handle.npad_id) {
171 return InvalidPalmaHandle; 168 return InvalidPalmaHandle;
172 } 169 }
@@ -178,8 +175,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec
178 return ResultSuccess; 175 return ResultSuccess;
179} 176}
180 177
181Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( 178Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) {
182 const PalmaConnectionHandle& handle) {
183 if (handle.npad_id != active_handle.npad_id) { 179 if (handle.npad_id != active_handle.npad_id) {
184 return InvalidPalmaHandle; 180 return InvalidPalmaHandle;
185 } 181 }
@@ -191,26 +187,26 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
191 return ResultSuccess; 187 return ResultSuccess;
192} 188}
193 189
194void Controller_Palma::SuspendPalmaFeature() {} 190void Palma::SuspendPalmaFeature() {}
195 191
196Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { 192Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
197 if (handle.npad_id != active_handle.npad_id) { 193 if (handle.npad_id != active_handle.npad_id) {
198 return InvalidPalmaHandle; 194 return InvalidPalmaHandle;
199 } 195 }
200 return operation.result; 196 return operation.result;
201} 197}
202void Controller_Palma::ReadPalmaPlayLog() {} 198void Palma::ReadPalmaPlayLog() {}
203 199
204void Controller_Palma::ResetPalmaPlayLog() {} 200void Palma::ResetPalmaPlayLog() {}
205 201
206void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { 202void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
207 // If true controllers are able to be paired 203 // If true controllers are able to be paired
208 is_connectable = is_all_connectable; 204 is_connectable = is_all_connectable;
209} 205}
210 206
211void Controller_Palma::SetIsPalmaPairedConnectable() {} 207void Palma::SetIsPalmaPairedConnectable() {}
212 208
213Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { 209Result Palma::PairPalma(const PalmaConnectionHandle& handle) {
214 if (handle.npad_id != active_handle.npad_id) { 210 if (handle.npad_id != active_handle.npad_id) {
215 return InvalidPalmaHandle; 211 return InvalidPalmaHandle;
216 } 212 }
@@ -218,14 +214,14 @@ Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
218 return ResultSuccess; 214 return ResultSuccess;
219} 215}
220 216
221void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} 217void Palma::SetPalmaBoostMode(bool boost_mode) {}
222 218
223void Controller_Palma::CancelWritePalmaWaveEntry() {} 219void Palma::CancelWritePalmaWaveEntry() {}
224 220
225void Controller_Palma::EnablePalmaBoostMode() {} 221void Palma::EnablePalmaBoostMode() {}
226 222
227void Controller_Palma::GetPalmaBluetoothAddress() {} 223void Palma::GetPalmaBluetoothAddress() {}
228 224
229void Controller_Palma::SetDisallowedPalmaConnection() {} 225void Palma::SetDisallowedPalmaConnection() {}
230 226
231} // namespace Service::HID 227} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h
index a0491a819..a6047f36a 100644
--- a/src/core/hle/service/hid/controllers/palma.h
+++ b/src/core/hle/service/hid/controllers/palma.h
@@ -23,7 +23,7 @@ class EmulatedController;
23} // namespace Core::HID 23} // namespace Core::HID
24 24
25namespace Service::HID { 25namespace Service::HID {
26class Controller_Palma final : public ControllerBase { 26class Palma final : public ControllerBase {
27public: 27public:
28 using PalmaOperationData = std::array<u8, 0x140>; 28 using PalmaOperationData = std::array<u8, 0x140>;
29 29
@@ -97,9 +97,9 @@ public:
97 static_assert(sizeof(PalmaConnectionHandle) == 0x8, 97 static_assert(sizeof(PalmaConnectionHandle) == 0x8,
98 "PalmaConnectionHandle has incorrect size."); 98 "PalmaConnectionHandle has incorrect size.");
99 99
100 explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 100 explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
101 KernelHelpers::ServiceContext& service_context_); 101 KernelHelpers::ServiceContext& service_context_);
102 ~Controller_Palma() override; 102 ~Palma() override;
103 103
104 // Called when the controller is initialized 104 // Called when the controller is initialized
105 void OnInit() override; 105 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/seven_six_axis.cpp
index bcb272eaf..495568484 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/seven_six_axis.cpp
@@ -1,32 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <cstring>
5#include "common/common_types.h"
4#include "core/core.h" 6#include "core/core.h"
5#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_console.h" 9#include "core/hid/emulated_console.h"
10#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 11#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_sixaxis.h" 12#include "core/hle/service/hid/controllers/seven_six_axis.h"
9#include "core/memory.h" 13#include "core/memory.h"
10 14
11namespace Service::HID { 15namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; 16SevenSixAxis::SevenSixAxis(Core::System& system_)
13
14Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_)
15 : ControllerBase{system_.HIDCore()}, system{system_} { 17 : ControllerBase{system_.HIDCore()}, system{system_} {
16 console = hid_core.GetEmulatedConsole(); 18 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21} 19}
22 20
23Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; 21SevenSixAxis::~SevenSixAxis() = default;
24
25void Controller_ConsoleSixAxis::OnInit() {}
26 22
27void Controller_ConsoleSixAxis::OnRelease() {} 23void SevenSixAxis::OnInit() {}
24void SevenSixAxis::OnRelease() {}
28 25
29void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 26void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated() || transfer_memory == 0) { 27 if (!IsControllerActivated() || transfer_memory == 0) {
31 seven_sixaxis_lifo.buffer_count = 0; 28 seven_sixaxis_lifo.buffer_count = 0;
32 seven_sixaxis_lifo.buffer_tail = 0; 29 seven_sixaxis_lifo.buffer_tail = 0;
@@ -53,22 +50,17 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
53 -motion_status.quaternion.xyz.z, 50 -motion_status.quaternion.xyz.z,
54 }; 51 };
55 52
56 shared_memory->sampling_number++;
57 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
58 shared_memory->verticalization_error = motion_status.verticalization_error;
59 shared_memory->gyro_bias = motion_status.gyro_bias;
60
61 // Update seven six axis transfer memory
62 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); 53 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
63 system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, 54 system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
64 sizeof(seven_sixaxis_lifo)); 55 sizeof(seven_sixaxis_lifo));
65} 56}
66 57
67void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { 58void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
68 transfer_memory = t_mem; 59 transfer_memory = t_mem;
69} 60}
70 61
71void Controller_ConsoleSixAxis::ResetTimestamp() { 62void SevenSixAxis::ResetTimestamp() {
72 last_saved_timestamp = last_global_timestamp; 63 last_saved_timestamp = last_global_timestamp;
73} 64}
65
74} // namespace Service::HID 66} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/seven_six_axis.h
index 7015d924c..40e3f5d12 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/seven_six_axis.h
@@ -1,10 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <array> 6#include "common/common_types.h"
7
8#include "common/quaternion.h" 7#include "common/quaternion.h"
9#include "common/typed_address.h" 8#include "common/typed_address.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
@@ -19,10 +18,10 @@ class EmulatedConsole;
19} // namespace Core::HID 18} // namespace Core::HID
20 19
21namespace Service::HID { 20namespace Service::HID {
22class Controller_ConsoleSixAxis final : public ControllerBase { 21class SevenSixAxis final : public ControllerBase {
23public: 22public:
24 explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_); 23 explicit SevenSixAxis(Core::System& system_);
25 ~Controller_ConsoleSixAxis() override; 24 ~SevenSixAxis() override;
26 25
27 // Called when the controller is initialized 26 // Called when the controller is initialized
28 void OnInit() override; 27 void OnInit() override;
@@ -51,28 +50,16 @@ private:
51 }; 50 };
52 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); 51 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
53 52
54 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
55 struct ConsoleSharedMemory {
56 u64 sampling_number{};
57 bool is_seven_six_axis_sensor_at_rest{};
58 INSERT_PADDING_BYTES(3); // padding
59 f32 verticalization_error{};
60 Common::Vec3f gyro_bias{};
61 INSERT_PADDING_BYTES(4); // padding
62 };
63 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
64
65 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; 53 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
66 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); 54 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
67 55
56 u64 last_saved_timestamp{};
57 u64 last_global_timestamp{};
58
68 SevenSixAxisState next_seven_sixaxis_state{}; 59 SevenSixAxisState next_seven_sixaxis_state{};
69 Common::ProcessAddress transfer_memory{}; 60 Common::ProcessAddress transfer_memory{};
70 ConsoleSharedMemory* shared_memory = nullptr;
71 Core::HID::EmulatedConsole* console = nullptr; 61 Core::HID::EmulatedConsole* console = nullptr;
72 62
73 u64 last_saved_timestamp{};
74 u64 last_global_timestamp{};
75
76 Core::System& system; 63 Core::System& system;
77}; 64};
78} // namespace Service::HID 65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
new file mode 100644
index 000000000..3d24a5c04
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -0,0 +1,413 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/common_types.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/controllers/six_axis.h"
10#include "core/hle/service/hid/errors.h"
11#include "core/hle/service/hid/hid_util.h"
12
13namespace Service::HID {
14
15SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_)
16 : ControllerBase{hid_core_}, npad{npad_} {
17 for (std::size_t i = 0; i < controller_data.size(); ++i) {
18 auto& controller = controller_data[i];
19 controller.device = hid_core.GetEmulatedControllerByIndex(i);
20 }
21}
22
23SixAxis::~SixAxis() = default;
24
25void SixAxis::OnInit() {}
26void SixAxis::OnRelease() {}
27
28void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
29 if (!IsControllerActivated()) {
30 return;
31 }
32
33 for (std::size_t i = 0; i < controller_data.size(); ++i) {
34 auto& controller = controller_data[i];
35
36 const auto npad_id = IndexToNpadIdType(i);
37 const auto& controller_type = controller.device->GetNpadStyleIndex();
38
39 if (controller_type == Core::HID::NpadStyleIndex::None ||
40 !controller.device->IsConnected()) {
41 continue;
42 }
43
44 const auto& motion_state = controller.device->GetMotions();
45 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
46 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
47 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
48 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
49 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
50 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
51
52 auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id);
53 auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id);
54 auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id);
55 auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id);
56 auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id);
57 auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id);
58
59 // Clear previous state
60 sixaxis_fullkey_state = {};
61 sixaxis_handheld_state = {};
62 sixaxis_dual_left_state = {};
63 sixaxis_dual_right_state = {};
64 sixaxis_left_lifo_state = {};
65 sixaxis_right_lifo_state = {};
66
67 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
68 controller.sixaxis_at_rest = true;
69 for (std::size_t e = 0; e < motion_state.size(); ++e) {
70 controller.sixaxis_at_rest =
71 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
72 }
73 }
74
75 const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
76 const Core::HID::ControllerMotion& hid_state) {
77 using namespace std::literals::chrono_literals;
78 static constexpr Core::HID::SixAxisSensorState default_motion_state = {
79 .delta_time = std::chrono::nanoseconds(5ms).count(),
80 .accel = {0, 0, -1.0f},
81 .orientation =
82 {
83 Common::Vec3f{1.0f, 0, 0},
84 Common::Vec3f{0, 1.0f, 0},
85 Common::Vec3f{0, 0, 1.0f},
86 },
87 .attribute = {1},
88 };
89 if (!controller.sixaxis_sensor_enabled) {
90 state = default_motion_state;
91 return;
92 }
93 if (!Settings::values.motion_enabled.GetValue()) {
94 state = default_motion_state;
95 return;
96 }
97 state.attribute.is_connected.Assign(1);
98 state.delta_time = std::chrono::nanoseconds(5ms).count();
99 state.accel = hid_state.accel;
100 state.gyro = hid_state.gyro;
101 state.rotation = hid_state.rotation;
102 state.orientation = hid_state.orientation;
103 };
104
105 switch (controller_type) {
106 case Core::HID::NpadStyleIndex::None:
107 ASSERT(false);
108 break;
109 case Core::HID::NpadStyleIndex::ProController:
110 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
111 break;
112 case Core::HID::NpadStyleIndex::Handheld:
113 set_motion_state(sixaxis_handheld_state, motion_state[0]);
114 break;
115 case Core::HID::NpadStyleIndex::JoyconDual:
116 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
117 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
118 break;
119 case Core::HID::NpadStyleIndex::JoyconLeft:
120 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
121 break;
122 case Core::HID::NpadStyleIndex::JoyconRight:
123 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
124 break;
125 case Core::HID::NpadStyleIndex::Pokeball:
126 using namespace std::literals::chrono_literals;
127 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
128 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
129 break;
130 default:
131 break;
132 }
133
134 sixaxis_fullkey_state.sampling_number =
135 sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
136 sixaxis_handheld_state.sampling_number =
137 sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
138 sixaxis_dual_left_state.sampling_number =
139 sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
140 sixaxis_dual_right_state.sampling_number =
141 sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
142 sixaxis_left_lifo_state.sampling_number =
143 sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
144 sixaxis_right_lifo_state.sampling_number =
145 sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
146
147 if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
148 // This buffer only is updated on handheld on HW
149 sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
150 } else {
151 // Handheld doesn't update this buffer on HW
152 sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
153 }
154
155 sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
156 sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
157 sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
158 sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
159 }
160}
161
162Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
163 Core::HID::GyroscopeZeroDriftMode drift_mode) {
164 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
165 if (is_valid.IsError()) {
166 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
167 return is_valid;
168 }
169
170 auto& sixaxis = GetSixaxisState(sixaxis_handle);
171 auto& controller = GetControllerFromHandle(sixaxis_handle);
172 sixaxis.gyroscope_zero_drift_mode = drift_mode;
173 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
174
175 return ResultSuccess;
176}
177
178Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
179 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
180 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
181 if (is_valid.IsError()) {
182 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
183 return is_valid;
184 }
185
186 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
187 drift_mode = sixaxis.gyroscope_zero_drift_mode;
188
189 return ResultSuccess;
190}
191
192Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
193 bool& is_at_rest) const {
194 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
195 if (is_valid.IsError()) {
196 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
197 return is_valid;
198 }
199
200 const auto& controller = GetControllerFromHandle(sixaxis_handle);
201 is_at_rest = controller.sixaxis_at_rest;
202 return ResultSuccess;
203}
204
205Result SixAxis::LoadSixAxisSensorCalibrationParameter(
206 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
207 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
208 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
209 if (is_valid.IsError()) {
210 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
211 return is_valid;
212 }
213
214 // TODO: Request this data to the controller. On error return 0xd8ca
215 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
216 calibration = sixaxis.calibration;
217 return ResultSuccess;
218}
219
220Result SixAxis::GetSixAxisSensorIcInformation(
221 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
222 Core::HID::SixAxisSensorIcInformation& ic_information) const {
223 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
224 if (is_valid.IsError()) {
225 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
226 return is_valid;
227 }
228
229 // TODO: Request this data to the controller. On error return 0xd8ca
230 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
231 ic_information = sixaxis.ic_information;
232 return ResultSuccess;
233}
234
235Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
236 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
237 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
238 if (is_valid.IsError()) {
239 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
240 return is_valid;
241 }
242
243 auto& sixaxis = GetSixaxisState(sixaxis_handle);
244 sixaxis.unaltered_passtrough = is_enabled;
245 return ResultSuccess;
246}
247
248Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
249 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
250 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
251 if (is_valid.IsError()) {
252 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
253 return is_valid;
254 }
255
256 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
257 is_enabled = sixaxis.unaltered_passtrough;
258 return ResultSuccess;
259}
260
261Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
262 bool sixaxis_status) {
263 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
264 if (is_valid.IsError()) {
265 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
266 return is_valid;
267 }
268
269 auto& controller = GetControllerFromHandle(sixaxis_handle);
270 controller.sixaxis_sensor_enabled = sixaxis_status;
271 return ResultSuccess;
272}
273
274Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
275 bool& is_fusion_enabled) const {
276 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
277 if (is_valid.IsError()) {
278 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
279 return is_valid;
280 }
281
282 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
283 is_fusion_enabled = sixaxis.is_fusion_enabled;
284
285 return ResultSuccess;
286}
287Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
288 bool is_fusion_enabled) {
289 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
290 if (is_valid.IsError()) {
291 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
292 return is_valid;
293 }
294
295 auto& sixaxis = GetSixaxisState(sixaxis_handle);
296 sixaxis.is_fusion_enabled = is_fusion_enabled;
297
298 return ResultSuccess;
299}
300
301Result SixAxis::SetSixAxisFusionParameters(
302 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
303 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
304 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
305 if (is_valid.IsError()) {
306 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
307 return is_valid;
308 }
309
310 const auto param1 = sixaxis_fusion_parameters.parameter1;
311 if (param1 < 0.0f || param1 > 1.0f) {
312 return InvalidSixAxisFusionRange;
313 }
314
315 auto& sixaxis = GetSixaxisState(sixaxis_handle);
316 sixaxis.fusion = sixaxis_fusion_parameters;
317
318 return ResultSuccess;
319}
320
321Result SixAxis::GetSixAxisFusionParameters(
322 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
323 Core::HID::SixAxisSensorFusionParameters& parameters) const {
324 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
325 if (is_valid.IsError()) {
326 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
327 return is_valid;
328 }
329
330 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
331 parameters = sixaxis.fusion;
332
333 return ResultSuccess;
334}
335
336SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
337 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
338 auto& controller = GetControllerFromHandle(sixaxis_handle);
339 switch (sixaxis_handle.npad_type) {
340 case Core::HID::NpadStyleIndex::ProController:
341 case Core::HID::NpadStyleIndex::Pokeball:
342 return controller.sixaxis_fullkey;
343 case Core::HID::NpadStyleIndex::Handheld:
344 return controller.sixaxis_handheld;
345 case Core::HID::NpadStyleIndex::JoyconDual:
346 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
347 return controller.sixaxis_dual_left;
348 }
349 return controller.sixaxis_dual_right;
350 case Core::HID::NpadStyleIndex::JoyconLeft:
351 return controller.sixaxis_left;
352 case Core::HID::NpadStyleIndex::JoyconRight:
353 return controller.sixaxis_right;
354 default:
355 return controller.sixaxis_unknown;
356 }
357}
358
359const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
360 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
361 const auto& controller = GetControllerFromHandle(sixaxis_handle);
362 switch (sixaxis_handle.npad_type) {
363 case Core::HID::NpadStyleIndex::ProController:
364 case Core::HID::NpadStyleIndex::Pokeball:
365 return controller.sixaxis_fullkey;
366 case Core::HID::NpadStyleIndex::Handheld:
367 return controller.sixaxis_handheld;
368 case Core::HID::NpadStyleIndex::JoyconDual:
369 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
370 return controller.sixaxis_dual_left;
371 }
372 return controller.sixaxis_dual_right;
373 case Core::HID::NpadStyleIndex::JoyconLeft:
374 return controller.sixaxis_left;
375 case Core::HID::NpadStyleIndex::JoyconRight:
376 return controller.sixaxis_right;
377 default:
378 return controller.sixaxis_unknown;
379 }
380}
381
382SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
383 const Core::HID::SixAxisSensorHandle& device_handle) {
384 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
385 return GetControllerFromNpadIdType(npad_id);
386}
387
388const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
389 const Core::HID::SixAxisSensorHandle& device_handle) const {
390 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
391 return GetControllerFromNpadIdType(npad_id);
392}
393
394SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
395 if (!IsNpadIdValid(npad_id)) {
396 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
397 npad_id = Core::HID::NpadIdType::Player1;
398 }
399 const auto npad_index = NpadIdTypeToIndex(npad_id);
400 return controller_data[npad_index];
401}
402
403const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(
404 Core::HID::NpadIdType npad_id) const {
405 if (!IsNpadIdValid(npad_id)) {
406 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
407 npad_id = Core::HID::NpadIdType::Player1;
408 }
409 const auto npad_index = NpadIdTypeToIndex(npad_id);
410 return controller_data[npad_index];
411}
412
413} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.h b/src/core/hle/service/hid/controllers/six_axis.h
new file mode 100644
index 000000000..4c4f5dc7b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/six_axis.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/hid_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/hid/ring_lifo.h"
10
11namespace Core::HID {
12class EmulatedController;
13} // namespace Core::HID
14
15namespace Service::HID {
16class NPad;
17
18class SixAxis final : public ControllerBase {
19public:
20 explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_);
21 ~SixAxis() override;
22
23 // Called when the controller is initialized
24 void OnInit() override;
25
26 // When the controller is released
27 void OnRelease() override;
28
29 // When the controller is requesting an update for the shared memory
30 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
31
32 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
33 Core::HID::GyroscopeZeroDriftMode drift_mode);
34 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
35 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
36 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
37 bool& is_at_rest) const;
38 Result EnableSixAxisSensorUnalteredPassthrough(
39 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
40 Result IsSixAxisSensorUnalteredPassthroughEnabled(
41 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
42 Result LoadSixAxisSensorCalibrationParameter(
43 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
44 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
45 Result GetSixAxisSensorIcInformation(
46 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
47 Core::HID::SixAxisSensorIcInformation& ic_information) const;
48 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
49 bool sixaxis_status);
50 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
51 bool& is_fusion_enabled) const;
52 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
53 bool is_fusion_enabled);
54 Result SetSixAxisFusionParameters(
55 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
56 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
57 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
58 Core::HID::SixAxisSensorFusionParameters& parameters) const;
59
60private:
61 static constexpr std::size_t NPAD_COUNT = 10;
62
63 struct SixaxisParameters {
64 bool is_fusion_enabled{true};
65 bool unaltered_passtrough{false};
66 Core::HID::SixAxisSensorFusionParameters fusion{};
67 Core::HID::SixAxisSensorCalibrationParameter calibration{};
68 Core::HID::SixAxisSensorIcInformation ic_information{};
69 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
70 Core::HID::GyroscopeZeroDriftMode::Standard};
71 };
72
73 struct NpadControllerData {
74 Core::HID::EmulatedController* device = nullptr;
75
76 // Motion parameters
77 bool sixaxis_at_rest{true};
78 bool sixaxis_sensor_enabled{true};
79 SixaxisParameters sixaxis_fullkey{};
80 SixaxisParameters sixaxis_handheld{};
81 SixaxisParameters sixaxis_dual_left{};
82 SixaxisParameters sixaxis_dual_right{};
83 SixaxisParameters sixaxis_left{};
84 SixaxisParameters sixaxis_right{};
85 SixaxisParameters sixaxis_unknown{};
86
87 // Current pad state
88 Core::HID::SixAxisSensorState sixaxis_fullkey_state{};
89 Core::HID::SixAxisSensorState sixaxis_handheld_state{};
90 Core::HID::SixAxisSensorState sixaxis_dual_left_state{};
91 Core::HID::SixAxisSensorState sixaxis_dual_right_state{};
92 Core::HID::SixAxisSensorState sixaxis_left_lifo_state{};
93 Core::HID::SixAxisSensorState sixaxis_right_lifo_state{};
94 int callback_key{};
95 };
96
97 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
98 const SixaxisParameters& GetSixaxisState(
99 const Core::HID::SixAxisSensorHandle& device_handle) const;
100
101 NpadControllerData& GetControllerFromHandle(
102 const Core::HID::SixAxisSensorHandle& device_handle);
103 const NpadControllerData& GetControllerFromHandle(
104 const Core::HID::SixAxisSensorHandle& device_handle) const;
105 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
106 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
107
108 std::shared_ptr<NPad> npad;
109 std::array<NpadControllerData, NPAD_COUNT> controller_data{};
110};
111} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 3ef91df4b..3bcf0ee9f 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -15,8 +15,7 @@
15namespace Service::HID { 15namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 17
18Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, 18TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
19 u8* raw_shared_memory_)
20 : ControllerBase{hid_core_} { 19 : ControllerBase{hid_core_} {
21 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, 20 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
22 "TouchSharedMemory is bigger than the shared memory"); 21 "TouchSharedMemory is bigger than the shared memory");
@@ -25,13 +24,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
25 console = hid_core.GetEmulatedConsole(); 24 console = hid_core.GetEmulatedConsole();
26} 25}
27 26
28Controller_Touchscreen::~Controller_Touchscreen() = default; 27TouchScreen::~TouchScreen() = default;
29 28
30void Controller_Touchscreen::OnInit() {} 29void TouchScreen::OnInit() {}
31 30
32void Controller_Touchscreen::OnRelease() {} 31void TouchScreen::OnRelease() {}
33 32
34void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 33void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
35 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 34 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
36 35
37 if (!IsControllerActivated()) { 36 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index dd00921fd..cd342ce91 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -14,10 +14,10 @@ class EmulatedConsole;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Touchscreen final : public ControllerBase { 17class TouchScreen final : public ControllerBase {
18public: 18public:
19 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Touchscreen() override; 20 ~TouchScreen() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 62119e2c5..0aaed1fa7 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,20 +10,19 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 12
13Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 13XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
14 : ControllerBase{hid_core_} {
15 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, 14 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
16 "XpadSharedMemory is bigger than the shared memory"); 15 "XpadSharedMemory is bigger than the shared memory");
17 shared_memory = std::construct_at( 16 shared_memory = std::construct_at(
18 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); 17 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
19} 18}
20Controller_XPad::~Controller_XPad() = default; 19XPad::~XPad() = default;
21 20
22void Controller_XPad::OnInit() {} 21void XPad::OnInit() {}
23 22
24void Controller_XPad::OnRelease() {} 23void XPad::OnRelease() {}
25 24
26void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 25void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
27 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
28 shared_memory->basic_xpad_lifo.buffer_count = 0; 27 shared_memory->basic_xpad_lifo.buffer_count = 0;
29 shared_memory->basic_xpad_lifo.buffer_tail = 0; 28 shared_memory->basic_xpad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index d01dee5fc..9e63a317a 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -10,10 +10,10 @@
10#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/ring_lifo.h"
11 11
12namespace Service::HID { 12namespace Service::HID {
13class Controller_XPad final : public ControllerBase { 13class XPad final : public ControllerBase {
14public: 14public:
15 explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 15 explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
16 ~Controller_XPad() override; 16 ~XPad() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
19 void OnInit() override; 19 void OnInit() override;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 929dd5f03..1b7381d8d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1,2864 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/core.h"
9#include "core/core_timing.h"
10#include "core/hid/hid_core.h"
11#include "core/hle/kernel/k_readable_event.h"
12#include "core/hle/kernel/k_shared_memory.h"
13#include "core/hle/kernel/k_transfer_memory.h"
14#include "core/hle/kernel/kernel.h"
15#include "core/hle/service/hid/errors.h"
16#include "core/hle/service/hid/hid.h" 4#include "core/hle/service/hid/hid.h"
5#include "core/hle/service/hid/hid_debug_server.h"
6#include "core/hle/service/hid/hid_firmware_settings.h"
7#include "core/hle/service/hid/hid_server.h"
8#include "core/hle/service/hid/hid_system_server.h"
17#include "core/hle/service/hid/hidbus.h" 9#include "core/hle/service/hid/hidbus.h"
18#include "core/hle/service/hid/irs.h" 10#include "core/hle/service/hid/irs.h"
11#include "core/hle/service/hid/resource_manager.h"
19#include "core/hle/service/hid/xcd.h" 12#include "core/hle/service/hid/xcd.h"
20#include "core/hle/service/ipc_helpers.h"
21#include "core/hle/service/server_manager.h" 13#include "core/hle/service/server_manager.h"
22#include "core/memory.h"
23
24#include "core/hle/service/hid/controllers/console_sixaxis.h"
25#include "core/hle/service/hid/controllers/controller_base.h"
26#include "core/hle/service/hid/controllers/debug_pad.h"
27#include "core/hle/service/hid/controllers/gesture.h"
28#include "core/hle/service/hid/controllers/keyboard.h"
29#include "core/hle/service/hid/controllers/mouse.h"
30#include "core/hle/service/hid/controllers/npad.h"
31#include "core/hle/service/hid/controllers/palma.h"
32#include "core/hle/service/hid/controllers/stubbed.h"
33#include "core/hle/service/hid/controllers/touchscreen.h"
34#include "core/hle/service/hid/controllers/xpad.h"
35 14
36namespace Service::HID { 15namespace Service::HID {
37 16
38// Updating period for each HID device.
39// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
40// Correct npad_update_ns is 4ms this is overclocked to lower input lag
41constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
42constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
43constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
44constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
45
46IAppletResource::IAppletResource(Core::System& system_,
47 KernelHelpers::ServiceContext& service_context_)
48 : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} {
49 static const FunctionInfo functions[] = {
50 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
51 };
52 RegisterHandlers(functions);
53 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
54 MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory);
55 MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory);
56 MakeController<Controller_Mouse>(HidController::Mouse, shared_memory);
57 MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory);
58 MakeController<Controller_XPad>(HidController::XPad, shared_memory);
59 MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory);
60 MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory);
61 MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory);
62 MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory);
63 MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory);
64 MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
65 MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
66 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
67 MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory);
68 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
69
70 // Homebrew doesn't try to activate some controllers, so we activate them by default
71 GetController<Controller_NPad>(HidController::NPad).ActivateController();
72 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
73
74 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
75 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
76 GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
77 GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
78 GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
79 GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00);
80
81 // Register update callbacks
82 npad_update_event = Core::Timing::CreateEvent(
83 "HID::UpdatePadCallback",
84 [this](std::uintptr_t user_data, s64 time,
85 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
86 const auto guard = LockService();
87 UpdateNpad(user_data, ns_late);
88 return std::nullopt;
89 });
90 default_update_event = Core::Timing::CreateEvent(
91 "HID::UpdateDefaultCallback",
92 [this](std::uintptr_t user_data, s64 time,
93 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
94 const auto guard = LockService();
95 UpdateControllers(user_data, ns_late);
96 return std::nullopt;
97 });
98 mouse_keyboard_update_event = Core::Timing::CreateEvent(
99 "HID::UpdateMouseKeyboardCallback",
100 [this](std::uintptr_t user_data, s64 time,
101 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
102 const auto guard = LockService();
103 UpdateMouseKeyboard(user_data, ns_late);
104 return std::nullopt;
105 });
106 motion_update_event = Core::Timing::CreateEvent(
107 "HID::UpdateMotionCallback",
108 [this](std::uintptr_t user_data, s64 time,
109 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
110 const auto guard = LockService();
111 UpdateMotion(user_data, ns_late);
112 return std::nullopt;
113 });
114
115 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
116 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
117 default_update_event);
118 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
119 mouse_keyboard_update_event);
120 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
121 motion_update_event);
122
123 system.HIDCore().ReloadInputDevices();
124}
125
126void IAppletResource::ActivateController(HidController controller) {
127 controllers[static_cast<size_t>(controller)]->ActivateController();
128}
129
130void IAppletResource::DeactivateController(HidController controller) {
131 controllers[static_cast<size_t>(controller)]->DeactivateController();
132}
133
134IAppletResource::~IAppletResource() {
135 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
136 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
137 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
138 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
139}
140
141void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
142 LOG_DEBUG(Service_HID, "called");
143
144 IPC::ResponseBuilder rb{ctx, 2, 1};
145 rb.Push(ResultSuccess);
146 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
147}
148
149void IAppletResource::UpdateControllers(std::uintptr_t user_data,
150 std::chrono::nanoseconds ns_late) {
151 auto& core_timing = system.CoreTiming();
152
153 for (const auto& controller : controllers) {
154 // Keyboard has it's own update event
155 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
156 continue;
157 }
158 // Mouse has it's own update event
159 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
160 continue;
161 }
162 // Npad has it's own update event
163 if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
164 continue;
165 }
166 controller->OnUpdate(core_timing);
167 }
168}
169
170void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
171 auto& core_timing = system.CoreTiming();
172
173 controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
174}
175
176void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
177 std::chrono::nanoseconds ns_late) {
178 auto& core_timing = system.CoreTiming();
179
180 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
181 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
182}
183
184void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
185 auto& core_timing = system.CoreTiming();
186
187 controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
188}
189
190class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
191public:
192 explicit IActiveVibrationDeviceList(Core::System& system_,
193 std::shared_ptr<IAppletResource> applet_resource_)
194 : ServiceFramework{system_, "IActiveVibrationDeviceList"},
195 applet_resource(applet_resource_) {
196 // clang-format off
197 static const FunctionInfo functions[] = {
198 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
199 };
200 // clang-format on
201
202 RegisterHandlers(functions);
203 }
204
205private:
206 void InitializeVibrationDevice(HLERequestContext& ctx) {
207 IPC::RequestParser rp{ctx};
208 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
209
210 if (applet_resource != nullptr) {
211 applet_resource->GetController<Controller_NPad>(HidController::NPad)
212 .InitializeVibrationDevice(vibration_device_handle);
213 }
214
215 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
216 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
217 vibration_device_handle.device_index);
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 rb.Push(ResultSuccess);
221 }
222
223 std::shared_ptr<IAppletResource> applet_resource;
224};
225
226std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
227 if (applet_resource == nullptr) {
228 applet_resource = std::make_shared<IAppletResource>(system, service_context);
229 }
230
231 return applet_resource;
232}
233
234Hid::Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_)
235 : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{
236 system_,
237 service_name} {
238 // clang-format off
239 static const FunctionInfo functions[] = {
240 {0, &Hid::CreateAppletResource, "CreateAppletResource"},
241 {1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
242 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
243 {21, &Hid::ActivateMouse, "ActivateMouse"},
244 {26, nullptr, "ActivateDebugMouse"},
245 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
246 {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
247 {40, nullptr, "AcquireXpadIdEventHandle"},
248 {41, nullptr, "ReleaseXpadIdEventHandle"},
249 {51, &Hid::ActivateXpad, "ActivateXpad"},
250 {55, &Hid::GetXpadIDs, "GetXpadIds"},
251 {56, nullptr, "ActivateJoyXpad"},
252 {58, nullptr, "GetJoyXpadLifoHandle"},
253 {59, nullptr, "GetJoyXpadIds"},
254 {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
255 {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
256 {62, nullptr, "GetSixAxisSensorLifoHandle"},
257 {63, nullptr, "ActivateJoySixAxisSensor"},
258 {64, nullptr, "DeactivateJoySixAxisSensor"},
259 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
260 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
261 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
262 {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
263 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
264 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
265 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
266 {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
267 {73, nullptr, "SetAccelerometerParameters"},
268 {74, nullptr, "GetAccelerometerParameters"},
269 {75, nullptr, "ResetAccelerometerParameters"},
270 {76, nullptr, "SetAccelerometerPlayMode"},
271 {77, nullptr, "GetAccelerometerPlayMode"},
272 {78, nullptr, "ResetAccelerometerPlayMode"},
273 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
274 {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
275 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
276 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
277 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
278 {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
279 {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
280 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
281 {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
282 {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
283 {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
284 {91, &Hid::ActivateGesture, "ActivateGesture"},
285 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
286 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
287 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
288 {103, &Hid::ActivateNpad, "ActivateNpad"},
289 {104, &Hid::DeactivateNpad, "DeactivateNpad"},
290 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
291 {107, &Hid::DisconnectNpad, "DisconnectNpad"},
292 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
293 {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
294 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
295 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
296 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
297 {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
298 {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
299 {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
300 {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"},
301 {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"},
302 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
303 {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
304 {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
305 {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
306 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
307 {133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
308 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
309 {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
310 {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
311 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
312 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
313 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
314 {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
315 {204, &Hid::PermitVibration, "PermitVibration"},
316 {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
317 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
318 {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
319 {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
320 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
321 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
322 {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
323 {212, nullptr, "SendVibrationValueInBool"},
324 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
325 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
326 {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
327 {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
328 {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
329 {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
330 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
331 {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
332 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
333 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
334 {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
335 {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
336 {401, nullptr, "EnableUsbFullKeyController"},
337 {402, nullptr, "IsUsbFullKeyControllerConnected"},
338 {403, nullptr, "HasBattery"},
339 {404, nullptr, "HasLeftRightBattery"},
340 {405, nullptr, "GetNpadInterfaceType"},
341 {406, nullptr, "GetNpadLeftRightInterfaceType"},
342 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
343 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
344 {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
345 {501, &Hid::InitializePalma, "InitializePalma"},
346 {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
347 {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
348 {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"},
349 {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"},
350 {506, &Hid::ReadPalmaStep, "ReadPalmaStep"},
351 {507, &Hid::EnablePalmaStep, "EnablePalmaStep"},
352 {508, &Hid::ResetPalmaStep, "ResetPalmaStep"},
353 {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
354 {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
355 {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
356 {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
357 {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
358 {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
359 {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
360 {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
361 {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
362 {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"},
363 {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"},
364 {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
365 {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
366 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
367 {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
368 {524, &Hid::PairPalma, "PairPalma"},
369 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
370 {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
371 {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
372 {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
373 {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
374 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
375 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
376 {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
377 {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
378 {2000, nullptr, "ActivateDigitizer"},
379 };
380 // clang-format on
381
382 RegisterHandlers(functions);
383}
384
385Hid::~Hid() = default;
386
387void Hid::CreateAppletResource(HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto applet_resource_user_id{rp.Pop<u64>()};
390
391 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
392
393 if (applet_resource == nullptr) {
394 applet_resource = std::make_shared<IAppletResource>(system, service_context);
395 }
396
397 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
398 rb.Push(ResultSuccess);
399 rb.PushIpcInterface<IAppletResource>(applet_resource);
400}
401
402void Hid::ActivateDebugPad(HLERequestContext& ctx) {
403 IPC::RequestParser rp{ctx};
404 const auto applet_resource_user_id{rp.Pop<u64>()};
405
406 applet_resource->ActivateController(HidController::DebugPad);
407
408 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
409
410 IPC::ResponseBuilder rb{ctx, 2};
411 rb.Push(ResultSuccess);
412}
413
414void Hid::ActivateTouchScreen(HLERequestContext& ctx) {
415 IPC::RequestParser rp{ctx};
416 const auto applet_resource_user_id{rp.Pop<u64>()};
417
418 applet_resource->ActivateController(HidController::Touchscreen);
419
420 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
421
422 IPC::ResponseBuilder rb{ctx, 2};
423 rb.Push(ResultSuccess);
424}
425
426void Hid::ActivateMouse(HLERequestContext& ctx) {
427 IPC::RequestParser rp{ctx};
428 const auto applet_resource_user_id{rp.Pop<u64>()};
429
430 applet_resource->ActivateController(HidController::Mouse);
431
432 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
433
434 IPC::ResponseBuilder rb{ctx, 2};
435 rb.Push(ResultSuccess);
436}
437
438void Hid::ActivateKeyboard(HLERequestContext& ctx) {
439 IPC::RequestParser rp{ctx};
440 const auto applet_resource_user_id{rp.Pop<u64>()};
441
442 applet_resource->ActivateController(HidController::Keyboard);
443
444 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
445
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(ResultSuccess);
448}
449
450void Hid::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto flags{rp.Pop<u32>()};
453
454 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
455
456 IPC::ResponseBuilder rb{ctx, 2};
457 rb.Push(ResultSuccess);
458}
459
460void Hid::ActivateXpad(HLERequestContext& ctx) {
461 IPC::RequestParser rp{ctx};
462 struct Parameters {
463 u32 basic_xpad_id;
464 INSERT_PADDING_WORDS_NOINIT(1);
465 u64 applet_resource_user_id;
466 };
467 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
468
469 const auto parameters{rp.PopRaw<Parameters>()};
470
471 applet_resource->ActivateController(HidController::XPad);
472
473 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
474 parameters.basic_xpad_id, parameters.applet_resource_user_id);
475
476 IPC::ResponseBuilder rb{ctx, 2};
477 rb.Push(ResultSuccess);
478}
479
480void Hid::GetXpadIDs(HLERequestContext& ctx) {
481 IPC::RequestParser rp{ctx};
482 const auto applet_resource_user_id{rp.Pop<u64>()};
483
484 LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
485
486 IPC::ResponseBuilder rb{ctx, 3};
487 rb.Push(ResultSuccess);
488 rb.Push(0);
489}
490
491void Hid::ActivateSixAxisSensor(HLERequestContext& ctx) {
492 IPC::RequestParser rp{ctx};
493 struct Parameters {
494 u32 basic_xpad_id;
495 INSERT_PADDING_WORDS_NOINIT(1);
496 u64 applet_resource_user_id;
497 };
498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
499
500 const auto parameters{rp.PopRaw<Parameters>()};
501
502 // This function does nothing on 10.0.0+
503
504 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
505 parameters.basic_xpad_id, parameters.applet_resource_user_id);
506
507 IPC::ResponseBuilder rb{ctx, 2};
508 rb.Push(ResultSuccess);
509}
510
511void Hid::DeactivateSixAxisSensor(HLERequestContext& ctx) {
512 IPC::RequestParser rp{ctx};
513 struct Parameters {
514 u32 basic_xpad_id;
515 INSERT_PADDING_WORDS_NOINIT(1);
516 u64 applet_resource_user_id;
517 };
518 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
519
520 const auto parameters{rp.PopRaw<Parameters>()};
521
522 // This function does nothing on 10.0.0+
523
524 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
525 parameters.basic_xpad_id, parameters.applet_resource_user_id);
526
527 IPC::ResponseBuilder rb{ctx, 2};
528 rb.Push(ResultSuccess);
529}
530
531void Hid::StartSixAxisSensor(HLERequestContext& ctx) {
532 IPC::RequestParser rp{ctx};
533 struct Parameters {
534 Core::HID::SixAxisSensorHandle sixaxis_handle;
535 INSERT_PADDING_WORDS_NOINIT(1);
536 u64 applet_resource_user_id;
537 };
538 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
539
540 const auto parameters{rp.PopRaw<Parameters>()};
541
542 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
543 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
544
545 LOG_DEBUG(Service_HID,
546 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
547 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
548 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
549
550 IPC::ResponseBuilder rb{ctx, 2};
551 rb.Push(result);
552}
553
554void Hid::StopSixAxisSensor(HLERequestContext& ctx) {
555 IPC::RequestParser rp{ctx};
556 struct Parameters {
557 Core::HID::SixAxisSensorHandle sixaxis_handle;
558 INSERT_PADDING_WORDS_NOINIT(1);
559 u64 applet_resource_user_id;
560 };
561 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
562
563 const auto parameters{rp.PopRaw<Parameters>()};
564
565 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
566 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
567
568 LOG_DEBUG(Service_HID,
569 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
570 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
571 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
572
573 IPC::ResponseBuilder rb{ctx, 2};
574 rb.Push(result);
575}
576
577void Hid::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
578 IPC::RequestParser rp{ctx};
579 struct Parameters {
580 Core::HID::SixAxisSensorHandle sixaxis_handle;
581 INSERT_PADDING_WORDS_NOINIT(1);
582 u64 applet_resource_user_id;
583 };
584 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
585
586 const auto parameters{rp.PopRaw<Parameters>()};
587
588 bool is_enabled{};
589 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
590 const auto result =
591 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
592
593 LOG_DEBUG(Service_HID,
594 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
595 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
596 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
597
598 IPC::ResponseBuilder rb{ctx, 3};
599 rb.Push(result);
600 rb.Push(is_enabled);
601}
602
603void Hid::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
604 IPC::RequestParser rp{ctx};
605 struct Parameters {
606 bool enable_sixaxis_sensor_fusion;
607 INSERT_PADDING_BYTES_NOINIT(3);
608 Core::HID::SixAxisSensorHandle sixaxis_handle;
609 u64 applet_resource_user_id;
610 };
611 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
612
613 const auto parameters{rp.PopRaw<Parameters>()};
614
615 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
616 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
617 parameters.enable_sixaxis_sensor_fusion);
618
619 LOG_DEBUG(Service_HID,
620 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
621 "device_index={}, applet_resource_user_id={}",
622 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
623 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
624 parameters.applet_resource_user_id);
625
626 IPC::ResponseBuilder rb{ctx, 2};
627 rb.Push(result);
628}
629
630void Hid::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
631 IPC::RequestParser rp{ctx};
632 struct Parameters {
633 Core::HID::SixAxisSensorHandle sixaxis_handle;
634 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
635 INSERT_PADDING_WORDS_NOINIT(1);
636 u64 applet_resource_user_id;
637 };
638 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
639
640 const auto parameters{rp.PopRaw<Parameters>()};
641
642 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
643 const auto result =
644 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
645
646 LOG_DEBUG(Service_HID,
647 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
648 "parameter2={}, applet_resource_user_id={}",
649 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
650 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
651 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
652
653 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(result);
655}
656
657void Hid::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
658 IPC::RequestParser rp{ctx};
659 struct Parameters {
660 Core::HID::SixAxisSensorHandle sixaxis_handle;
661 INSERT_PADDING_WORDS_NOINIT(1);
662 u64 applet_resource_user_id;
663 };
664 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
665
666 const auto parameters{rp.PopRaw<Parameters>()};
667
668 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
669 const auto& controller =
670 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
671 const auto result =
672 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
673
674 LOG_DEBUG(Service_HID,
675 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
676 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
677 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
678
679 IPC::ResponseBuilder rb{ctx, 4};
680 rb.Push(result);
681 rb.PushRaw(fusion_parameters);
682}
683
684void Hid::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
685 IPC::RequestParser rp{ctx};
686 struct Parameters {
687 Core::HID::SixAxisSensorHandle sixaxis_handle;
688 INSERT_PADDING_WORDS_NOINIT(1);
689 u64 applet_resource_user_id;
690 };
691 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
692
693 const auto parameters{rp.PopRaw<Parameters>()};
694
695 // Since these parameters are unknown just use what HW outputs
696 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
697 .parameter1 = 0.03f,
698 .parameter2 = 0.4f,
699 };
700 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
701 const auto result1 =
702 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
703 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
704
705 LOG_DEBUG(Service_HID,
706 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
707 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
708 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
709
710 IPC::ResponseBuilder rb{ctx, 2};
711 if (result1.IsError()) {
712 rb.Push(result1);
713 return;
714 }
715 rb.Push(result2);
716}
717
718void Hid::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
719 IPC::RequestParser rp{ctx};
720 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
721 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
722 const auto applet_resource_user_id{rp.Pop<u64>()};
723
724 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
725 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
726
727 LOG_DEBUG(Service_HID,
728 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
729 "applet_resource_user_id={}",
730 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
731 drift_mode, applet_resource_user_id);
732
733 IPC::ResponseBuilder rb{ctx, 2};
734 rb.Push(result);
735}
736
737void Hid::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
738 IPC::RequestParser rp{ctx};
739 struct Parameters {
740 Core::HID::SixAxisSensorHandle sixaxis_handle;
741 INSERT_PADDING_WORDS_NOINIT(1);
742 u64 applet_resource_user_id;
743 };
744 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
745
746 const auto parameters{rp.PopRaw<Parameters>()};
747
748 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
749 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
750 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
751
752 LOG_DEBUG(Service_HID,
753 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
754 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
755 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
756
757 IPC::ResponseBuilder rb{ctx, 3};
758 rb.Push(result);
759 rb.PushEnum(drift_mode);
760}
761
762void Hid::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
763 IPC::RequestParser rp{ctx};
764 struct Parameters {
765 Core::HID::SixAxisSensorHandle sixaxis_handle;
766 INSERT_PADDING_WORDS_NOINIT(1);
767 u64 applet_resource_user_id;
768 };
769 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
770
771 const auto parameters{rp.PopRaw<Parameters>()};
772
773 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
774 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
775 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
776
777 LOG_DEBUG(Service_HID,
778 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
779 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
780 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
781
782 IPC::ResponseBuilder rb{ctx, 2};
783 rb.Push(result);
784}
785
786void Hid::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
787 IPC::RequestParser rp{ctx};
788 struct Parameters {
789 Core::HID::SixAxisSensorHandle sixaxis_handle;
790 INSERT_PADDING_WORDS_NOINIT(1);
791 u64 applet_resource_user_id;
792 };
793 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
794
795 const auto parameters{rp.PopRaw<Parameters>()};
796
797 bool is_at_rest{};
798 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
799 controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
800
801 LOG_DEBUG(Service_HID,
802 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
803 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
804 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
805
806 IPC::ResponseBuilder rb{ctx, 3};
807 rb.Push(ResultSuccess);
808 rb.Push(is_at_rest);
809}
810
811void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) {
812 IPC::RequestParser rp{ctx};
813 struct Parameters {
814 Core::HID::SixAxisSensorHandle sixaxis_handle;
815 INSERT_PADDING_WORDS_NOINIT(1);
816 u64 applet_resource_user_id;
817 };
818 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
819
820 const auto parameters{rp.PopRaw<Parameters>()};
821
822 bool is_firmware_available{};
823 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
824 controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
825 is_firmware_available);
826
827 LOG_WARNING(
828 Service_HID,
829 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
830 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
831 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
832
833 IPC::ResponseBuilder rb{ctx, 3};
834 rb.Push(ResultSuccess);
835 rb.Push(is_firmware_available);
836}
837
838void Hid::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) {
839 IPC::RequestParser rp{ctx};
840 struct Parameters {
841 bool enabled;
842 Core::HID::SixAxisSensorHandle sixaxis_handle;
843 u64 applet_resource_user_id;
844 };
845 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
846
847 const auto parameters{rp.PopRaw<Parameters>()};
848
849 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
850 const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
851 parameters.sixaxis_handle, parameters.enabled);
852
853 LOG_DEBUG(Service_HID,
854 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
855 "applet_resource_user_id={}",
856 parameters.enabled, parameters.sixaxis_handle.npad_type,
857 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
858 parameters.applet_resource_user_id);
859
860 IPC::ResponseBuilder rb{ctx, 2};
861 rb.Push(result);
862}
863
864void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) {
865 IPC::RequestParser rp{ctx};
866 struct Parameters {
867 Core::HID::SixAxisSensorHandle sixaxis_handle;
868 INSERT_PADDING_WORDS_NOINIT(1);
869 u64 applet_resource_user_id;
870 };
871 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
872
873 const auto parameters{rp.PopRaw<Parameters>()};
874
875 bool is_unaltered_sisxaxis_enabled{};
876 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
877 const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
878 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
879
880 LOG_DEBUG(
881 Service_HID,
882 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
883 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
884 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
885
886 IPC::ResponseBuilder rb{ctx, 3};
887 rb.Push(result);
888 rb.Push(is_unaltered_sisxaxis_enabled);
889}
890
891void Hid::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx};
893 struct Parameters {
894 Core::HID::SixAxisSensorHandle sixaxis_handle;
895 INSERT_PADDING_WORDS_NOINIT(1);
896 u64 applet_resource_user_id;
897 };
898 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
899
900 const auto parameters{rp.PopRaw<Parameters>()};
901
902 Core::HID::SixAxisSensorCalibrationParameter calibration{};
903 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
904 const auto result =
905 controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
906
907 LOG_WARNING(
908 Service_HID,
909 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
910 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
911 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
912
913 if (result.IsSuccess()) {
914 ctx.WriteBuffer(calibration);
915 }
916
917 IPC::ResponseBuilder rb{ctx, 2};
918 rb.Push(result);
919}
920
921void Hid::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
922 IPC::RequestParser rp{ctx};
923 struct Parameters {
924 Core::HID::SixAxisSensorHandle sixaxis_handle;
925 INSERT_PADDING_WORDS_NOINIT(1);
926 u64 applet_resource_user_id;
927 };
928 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
929
930 const auto parameters{rp.PopRaw<Parameters>()};
931
932 Core::HID::SixAxisSensorIcInformation ic_information{};
933 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
934 const auto result =
935 controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
936
937 LOG_WARNING(
938 Service_HID,
939 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
940 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
941 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
942
943 if (result.IsSuccess()) {
944 ctx.WriteBuffer(ic_information);
945 }
946
947 IPC::ResponseBuilder rb{ctx, 2};
948 rb.Push(result);
949}
950
951void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) {
952 IPC::RequestParser rp{ctx};
953 struct Parameters {
954 Core::HID::SixAxisSensorHandle sixaxis_handle;
955 INSERT_PADDING_WORDS_NOINIT(1);
956 u64 applet_resource_user_id;
957 };
958 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
959
960 const auto parameters{rp.PopRaw<Parameters>()};
961
962 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
963 const auto result =
964 controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
965
966 LOG_WARNING(
967 Service_HID,
968 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
969 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
970 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
971
972 IPC::ResponseBuilder rb{ctx, 2};
973 rb.Push(result);
974}
975
976void Hid::ActivateGesture(HLERequestContext& ctx) {
977 IPC::RequestParser rp{ctx};
978 struct Parameters {
979 u32 unknown;
980 INSERT_PADDING_WORDS_NOINIT(1);
981 u64 applet_resource_user_id;
982 };
983 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
984
985 const auto parameters{rp.PopRaw<Parameters>()};
986
987 applet_resource->ActivateController(HidController::Gesture);
988
989 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
990 parameters.unknown, parameters.applet_resource_user_id);
991
992 IPC::ResponseBuilder rb{ctx, 2};
993 rb.Push(ResultSuccess);
994}
995
996void Hid::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
997 IPC::RequestParser rp{ctx};
998 struct Parameters {
999 Core::HID::NpadStyleSet supported_styleset;
1000 INSERT_PADDING_WORDS_NOINIT(1);
1001 u64 applet_resource_user_id;
1002 };
1003 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1004
1005 const auto parameters{rp.PopRaw<Parameters>()};
1006
1007 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1008 .SetSupportedStyleSet({parameters.supported_styleset});
1009
1010 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
1011 parameters.supported_styleset, parameters.applet_resource_user_id);
1012
1013 IPC::ResponseBuilder rb{ctx, 2};
1014 rb.Push(ResultSuccess);
1015}
1016
1017void Hid::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
1018 IPC::RequestParser rp{ctx};
1019 const auto applet_resource_user_id{rp.Pop<u64>()};
1020
1021 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1022
1023 IPC::ResponseBuilder rb{ctx, 3};
1024 rb.Push(ResultSuccess);
1025 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1026 .GetSupportedStyleSet()
1027 .raw);
1028}
1029
1030void Hid::SetSupportedNpadIdType(HLERequestContext& ctx) {
1031 IPC::RequestParser rp{ctx};
1032 const auto applet_resource_user_id{rp.Pop<u64>()};
1033
1034 const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1035 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1036
1037 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1038
1039 IPC::ResponseBuilder rb{ctx, 2};
1040 rb.Push(result);
1041}
1042
1043void Hid::ActivateNpad(HLERequestContext& ctx) {
1044 IPC::RequestParser rp{ctx};
1045 const auto applet_resource_user_id{rp.Pop<u64>()};
1046
1047 applet_resource->ActivateController(HidController::NPad);
1048
1049 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1050
1051 IPC::ResponseBuilder rb{ctx, 2};
1052 rb.Push(ResultSuccess);
1053}
1054
1055void Hid::DeactivateNpad(HLERequestContext& ctx) {
1056 IPC::RequestParser rp{ctx};
1057 const auto applet_resource_user_id{rp.Pop<u64>()};
1058
1059 applet_resource->DeactivateController(HidController::NPad);
1060
1061 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1062
1063 IPC::ResponseBuilder rb{ctx, 2};
1064 rb.Push(ResultSuccess);
1065}
1066
1067void Hid::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1068 IPC::RequestParser rp{ctx};
1069 struct Parameters {
1070 Core::HID::NpadIdType npad_id;
1071 INSERT_PADDING_WORDS_NOINIT(1);
1072 u64 applet_resource_user_id;
1073 u64 unknown;
1074 };
1075 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1076
1077 const auto parameters{rp.PopRaw<Parameters>()};
1078
1079 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1080 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1081
1082 // Games expect this event to be signaled after calling this function
1083 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1084 .SignalStyleSetChangedEvent(parameters.npad_id);
1085
1086 IPC::ResponseBuilder rb{ctx, 2, 1};
1087 rb.Push(ResultSuccess);
1088 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1089 .GetStyleSetChangedEvent(parameters.npad_id));
1090}
1091
1092void Hid::DisconnectNpad(HLERequestContext& ctx) {
1093 IPC::RequestParser rp{ctx};
1094 struct Parameters {
1095 Core::HID::NpadIdType npad_id;
1096 INSERT_PADDING_WORDS_NOINIT(1);
1097 u64 applet_resource_user_id;
1098 };
1099 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1100
1101 const auto parameters{rp.PopRaw<Parameters>()};
1102
1103 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1104 controller.DisconnectNpad(parameters.npad_id);
1105
1106 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1107 parameters.applet_resource_user_id);
1108
1109 IPC::ResponseBuilder rb{ctx, 2};
1110 rb.Push(ResultSuccess);
1111}
1112
1113void Hid::GetPlayerLedPattern(HLERequestContext& ctx) {
1114 IPC::RequestParser rp{ctx};
1115 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1116
1117 Core::HID::LedPattern pattern{0, 0, 0, 0};
1118 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1119 const auto result = controller.GetLedPattern(npad_id, pattern);
1120
1121 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1122
1123 IPC::ResponseBuilder rb{ctx, 4};
1124 rb.Push(result);
1125 rb.Push(pattern.raw);
1126}
1127
1128void Hid::ActivateNpadWithRevision(HLERequestContext& ctx) {
1129 // Should have no effect with how our npad sets up the data
1130 IPC::RequestParser rp{ctx};
1131 struct Parameters {
1132 s32 revision;
1133 INSERT_PADDING_WORDS_NOINIT(1);
1134 u64 applet_resource_user_id;
1135 };
1136 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1137
1138 const auto parameters{rp.PopRaw<Parameters>()};
1139
1140 applet_resource->ActivateController(HidController::NPad);
1141
1142 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1143 parameters.applet_resource_user_id);
1144
1145 IPC::ResponseBuilder rb{ctx, 2};
1146 rb.Push(ResultSuccess);
1147}
1148
1149void Hid::SetNpadJoyHoldType(HLERequestContext& ctx) {
1150 IPC::RequestParser rp{ctx};
1151 const auto applet_resource_user_id{rp.Pop<u64>()};
1152 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
1153
1154 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
1155
1156 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1157 applet_resource_user_id, hold_type);
1158
1159 IPC::ResponseBuilder rb{ctx, 2};
1160 rb.Push(ResultSuccess);
1161}
1162
1163void Hid::GetNpadJoyHoldType(HLERequestContext& ctx) {
1164 IPC::RequestParser rp{ctx};
1165 const auto applet_resource_user_id{rp.Pop<u64>()};
1166
1167 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1168
1169 IPC::ResponseBuilder rb{ctx, 4};
1170 rb.Push(ResultSuccess);
1171 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
1172}
1173
1174void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
1175 IPC::RequestParser rp{ctx};
1176 struct Parameters {
1177 Core::HID::NpadIdType npad_id;
1178 INSERT_PADDING_WORDS_NOINIT(1);
1179 u64 applet_resource_user_id;
1180 };
1181 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1182
1183 const auto parameters{rp.PopRaw<Parameters>()};
1184
1185 Core::HID::NpadIdType new_npad_id{};
1186 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1187 controller.SetNpadMode(new_npad_id, parameters.npad_id,
1188 Controller_NPad::NpadJoyDeviceType::Left,
1189 Controller_NPad::NpadJoyAssignmentMode::Single);
1190
1191 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1192 parameters.applet_resource_user_id);
1193
1194 IPC::ResponseBuilder rb{ctx, 2};
1195 rb.Push(ResultSuccess);
1196}
1197
1198void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1199 IPC::RequestParser rp{ctx};
1200 struct Parameters {
1201 Core::HID::NpadIdType npad_id;
1202 INSERT_PADDING_WORDS_NOINIT(1);
1203 u64 applet_resource_user_id;
1204 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
1205 };
1206 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1207
1208 const auto parameters{rp.PopRaw<Parameters>()};
1209
1210 Core::HID::NpadIdType new_npad_id{};
1211 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1212 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1213 Controller_NPad::NpadJoyAssignmentMode::Single);
1214
1215 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1216 parameters.npad_id, parameters.applet_resource_user_id,
1217 parameters.npad_joy_device_type);
1218
1219 IPC::ResponseBuilder rb{ctx, 2};
1220 rb.Push(ResultSuccess);
1221}
1222
1223void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1224 IPC::RequestParser rp{ctx};
1225 struct Parameters {
1226 Core::HID::NpadIdType npad_id;
1227 INSERT_PADDING_WORDS_NOINIT(1);
1228 u64 applet_resource_user_id;
1229 };
1230 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1231
1232 const auto parameters{rp.PopRaw<Parameters>()};
1233
1234 Core::HID::NpadIdType new_npad_id{};
1235 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1236 controller.SetNpadMode(new_npad_id, parameters.npad_id, {},
1237 Controller_NPad::NpadJoyAssignmentMode::Dual);
1238
1239 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1240 parameters.applet_resource_user_id);
1241
1242 IPC::ResponseBuilder rb{ctx, 2};
1243 rb.Push(ResultSuccess);
1244}
1245
1246void Hid::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1247 IPC::RequestParser rp{ctx};
1248 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1249 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1250 const auto applet_resource_user_id{rp.Pop<u64>()};
1251
1252 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1253 const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1254
1255 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1256 npad_id_1, npad_id_2, applet_resource_user_id);
1257
1258 IPC::ResponseBuilder rb{ctx, 2};
1259 rb.Push(result);
1260}
1261
1262void Hid::StartLrAssignmentMode(HLERequestContext& ctx) {
1263 IPC::RequestParser rp{ctx};
1264 const auto applet_resource_user_id{rp.Pop<u64>()};
1265
1266 applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode();
1267
1268 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1269
1270 IPC::ResponseBuilder rb{ctx, 2};
1271 rb.Push(ResultSuccess);
1272}
1273
1274void Hid::StopLrAssignmentMode(HLERequestContext& ctx) {
1275 IPC::RequestParser rp{ctx};
1276 const auto applet_resource_user_id{rp.Pop<u64>()};
1277
1278 applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode();
1279
1280 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1281
1282 IPC::ResponseBuilder rb{ctx, 2};
1283 rb.Push(ResultSuccess);
1284}
1285
1286void Hid::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1287 IPC::RequestParser rp{ctx};
1288 const auto applet_resource_user_id{rp.Pop<u64>()};
1289 const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()};
1290
1291 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1292 .SetNpadHandheldActivationMode(activation_mode);
1293
1294 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1295 applet_resource_user_id, activation_mode);
1296
1297 IPC::ResponseBuilder rb{ctx, 2};
1298 rb.Push(ResultSuccess);
1299}
1300
1301void Hid::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1302 IPC::RequestParser rp{ctx};
1303 const auto applet_resource_user_id{rp.Pop<u64>()};
1304
1305 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1306
1307 IPC::ResponseBuilder rb{ctx, 4};
1308 rb.Push(ResultSuccess);
1309 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1310 .GetNpadHandheldActivationMode());
1311}
1312
1313void Hid::SwapNpadAssignment(HLERequestContext& ctx) {
1314 IPC::RequestParser rp{ctx};
1315 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1316 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1317 const auto applet_resource_user_id{rp.Pop<u64>()};
1318
1319 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1320 const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
1321
1322 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1323 npad_id_1, npad_id_2, applet_resource_user_id);
1324
1325 IPC::ResponseBuilder rb{ctx, 2};
1326 rb.Push(result);
1327}
1328
1329void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
1330 IPC::RequestParser rp{ctx};
1331 struct Parameters {
1332 Core::HID::NpadIdType npad_id;
1333 INSERT_PADDING_WORDS_NOINIT(1);
1334 u64 applet_resource_user_id;
1335 };
1336 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1337
1338 const auto parameters{rp.PopRaw<Parameters>()};
1339
1340 bool is_enabled = false;
1341 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1342 const auto result =
1343 controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1344
1345 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1346 parameters.npad_id, parameters.applet_resource_user_id);
1347
1348 IPC::ResponseBuilder rb{ctx, 3};
1349 rb.Push(result);
1350 rb.Push(is_enabled);
1351}
1352
1353void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
1354 IPC::RequestParser rp{ctx};
1355 struct Parameters {
1356 bool unintended_home_button_input_protection;
1357 INSERT_PADDING_BYTES_NOINIT(3);
1358 Core::HID::NpadIdType npad_id;
1359 u64 applet_resource_user_id;
1360 };
1361 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1362
1363 const auto parameters{rp.PopRaw<Parameters>()};
1364
1365 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1366 const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
1367 parameters.unintended_home_button_input_protection, parameters.npad_id);
1368
1369 LOG_WARNING(Service_HID,
1370 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
1371 "applet_resource_user_id={}",
1372 parameters.unintended_home_button_input_protection, parameters.npad_id,
1373 parameters.applet_resource_user_id);
1374
1375 IPC::ResponseBuilder rb{ctx, 2};
1376 rb.Push(result);
1377}
1378
1379void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
1380 IPC::RequestParser rp{ctx};
1381 struct Parameters {
1382 Core::HID::NpadIdType npad_id;
1383 INSERT_PADDING_WORDS_NOINIT(1);
1384 u64 applet_resource_user_id;
1385 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
1386 };
1387 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1388
1389 const auto parameters{rp.PopRaw<Parameters>()};
1390
1391 Core::HID::NpadIdType new_npad_id{};
1392 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1393 const auto is_reassigned =
1394 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1395 Controller_NPad::NpadJoyAssignmentMode::Single);
1396
1397 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1398 parameters.npad_id, parameters.applet_resource_user_id,
1399 parameters.npad_joy_device_type);
1400
1401 IPC::ResponseBuilder rb{ctx, 4};
1402 rb.Push(ResultSuccess);
1403 rb.Push(is_reassigned);
1404 rb.PushEnum(new_npad_id);
1405}
1406
1407void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1408 IPC::RequestParser rp{ctx};
1409 struct Parameters {
1410 bool analog_stick_use_center_clamp;
1411 INSERT_PADDING_BYTES_NOINIT(7);
1412 u64 applet_resource_user_id;
1413 };
1414 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1415
1416 const auto parameters{rp.PopRaw<Parameters>()};
1417
1418 GetAppletResource()
1419 ->GetController<Controller_NPad>(HidController::NPad)
1420 .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp);
1421
1422 LOG_WARNING(Service_HID,
1423 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
1424 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1425
1426 IPC::ResponseBuilder rb{ctx, 2};
1427 rb.Push(ResultSuccess);
1428}
1429
1430void Hid::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1431 IPC::RequestParser rp{ctx};
1432 struct Parameters {
1433 Core::HID::NpadStyleSet npad_styleset;
1434 INSERT_PADDING_WORDS_NOINIT(1);
1435 u64 applet_resource_user_id;
1436 Core::HID::NpadButton button;
1437 };
1438 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1439
1440 const auto parameters{rp.PopRaw<Parameters>()};
1441
1442 LOG_WARNING(Service_HID,
1443 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1444 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1445
1446 IPC::ResponseBuilder rb{ctx, 2};
1447 rb.Push(ResultSuccess);
1448}
1449
1450void Hid::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1451 IPC::RequestParser rp{ctx};
1452 const auto applet_resource_user_id{rp.Pop<u64>()};
1453
1454 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1455 applet_resource_user_id);
1456
1457 IPC::ResponseBuilder rb{ctx, 2};
1458 rb.Push(ResultSuccess);
1459}
1460
1461void Hid::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1462 IPC::RequestParser rp{ctx};
1463 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1464 const auto& controller =
1465 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1466
1467 Core::HID::VibrationDeviceInfo vibration_device_info;
1468 bool check_device_index = false;
1469
1470 switch (vibration_device_handle.npad_type) {
1471 case Core::HID::NpadStyleIndex::ProController:
1472 case Core::HID::NpadStyleIndex::Handheld:
1473 case Core::HID::NpadStyleIndex::JoyconDual:
1474 case Core::HID::NpadStyleIndex::JoyconLeft:
1475 case Core::HID::NpadStyleIndex::JoyconRight:
1476 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1477 check_device_index = true;
1478 break;
1479 case Core::HID::NpadStyleIndex::GameCube:
1480 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1481 break;
1482 case Core::HID::NpadStyleIndex::N64:
1483 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1484 break;
1485 default:
1486 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1487 break;
1488 }
1489
1490 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1491 if (check_device_index) {
1492 switch (vibration_device_handle.device_index) {
1493 case Core::HID::DeviceIndex::Left:
1494 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1495 break;
1496 case Core::HID::DeviceIndex::Right:
1497 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1498 break;
1499 case Core::HID::DeviceIndex::None:
1500 default:
1501 ASSERT_MSG(false, "DeviceIndex should never be None!");
1502 break;
1503 }
1504 }
1505
1506 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1507 vibration_device_info.type, vibration_device_info.position);
1508
1509 const auto result = controller.IsDeviceHandleValid(vibration_device_handle);
1510 if (result.IsError()) {
1511 IPC::ResponseBuilder rb{ctx, 2};
1512 rb.Push(result);
1513 return;
1514 }
1515
1516 IPC::ResponseBuilder rb{ctx, 4};
1517 rb.Push(ResultSuccess);
1518 rb.PushRaw(vibration_device_info);
1519}
1520
1521void Hid::SendVibrationValue(HLERequestContext& ctx) {
1522 IPC::RequestParser rp{ctx};
1523 struct Parameters {
1524 Core::HID::VibrationDeviceHandle vibration_device_handle;
1525 Core::HID::VibrationValue vibration_value;
1526 INSERT_PADDING_WORDS_NOINIT(1);
1527 u64 applet_resource_user_id;
1528 };
1529 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1530
1531 const auto parameters{rp.PopRaw<Parameters>()};
1532
1533 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1534 .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
1535
1536 LOG_DEBUG(Service_HID,
1537 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1538 parameters.vibration_device_handle.npad_type,
1539 parameters.vibration_device_handle.npad_id,
1540 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1541
1542 IPC::ResponseBuilder rb{ctx, 2};
1543 rb.Push(ResultSuccess);
1544}
1545
1546void Hid::GetActualVibrationValue(HLERequestContext& ctx) {
1547 IPC::RequestParser rp{ctx};
1548 struct Parameters {
1549 Core::HID::VibrationDeviceHandle vibration_device_handle;
1550 INSERT_PADDING_WORDS_NOINIT(1);
1551 u64 applet_resource_user_id;
1552 };
1553 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1554
1555 const auto parameters{rp.PopRaw<Parameters>()};
1556
1557 LOG_DEBUG(Service_HID,
1558 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1559 parameters.vibration_device_handle.npad_type,
1560 parameters.vibration_device_handle.npad_id,
1561 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1562
1563 IPC::ResponseBuilder rb{ctx, 6};
1564 rb.Push(ResultSuccess);
1565 rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1566 .GetLastVibration(parameters.vibration_device_handle));
1567}
1568
1569void Hid::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1570 LOG_DEBUG(Service_HID, "called");
1571
1572 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1573 rb.Push(ResultSuccess);
1574 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, applet_resource);
1575}
1576
1577void Hid::PermitVibration(HLERequestContext& ctx) {
1578 IPC::RequestParser rp{ctx};
1579 const auto can_vibrate{rp.Pop<bool>()};
1580
1581 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1582 // by converting it to a bool
1583 Settings::values.vibration_enabled.SetValue(can_vibrate);
1584
1585 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1586
1587 IPC::ResponseBuilder rb{ctx, 2};
1588 rb.Push(ResultSuccess);
1589}
1590
1591void Hid::IsVibrationPermitted(HLERequestContext& ctx) {
1592 LOG_DEBUG(Service_HID, "called");
1593
1594 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1595 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1596
1597 IPC::ResponseBuilder rb{ctx, 3};
1598 rb.Push(ResultSuccess);
1599 rb.Push(is_enabled);
1600}
1601
1602void Hid::SendVibrationValues(HLERequestContext& ctx) {
1603 IPC::RequestParser rp{ctx};
1604 const auto applet_resource_user_id{rp.Pop<u64>()};
1605
1606 const auto handle_data = ctx.ReadBuffer(0);
1607 const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
1608 const auto vibration_data = ctx.ReadBuffer(1);
1609 const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
1610
1611 auto vibration_device_handles =
1612 std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
1613 handle_count);
1614 auto vibration_values = std::span(
1615 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1616
1617 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1618 .VibrateControllers(vibration_device_handles, vibration_values);
1619
1620 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1621
1622 IPC::ResponseBuilder rb{ctx, 2};
1623 rb.Push(ResultSuccess);
1624}
1625
1626void Hid::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1627 IPC::RequestParser rp{ctx};
1628 struct Parameters {
1629 Core::HID::VibrationDeviceHandle vibration_device_handle;
1630 INSERT_PADDING_WORDS_NOINIT(1);
1631 u64 applet_resource_user_id;
1632 Core::HID::VibrationGcErmCommand gc_erm_command;
1633 };
1634 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1635
1636 const auto parameters{rp.PopRaw<Parameters>()};
1637
1638 /**
1639 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1640 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1641 * in order to differentiate between Stop and StopHard commands.
1642 * This is done to reuse the controller vibration functions made for regular controllers.
1643 */
1644 const auto vibration_value = [parameters] {
1645 switch (parameters.gc_erm_command) {
1646 case Core::HID::VibrationGcErmCommand::Stop:
1647 return Core::HID::VibrationValue{
1648 .low_amplitude = 0.0f,
1649 .low_frequency = 160.0f,
1650 .high_amplitude = 0.0f,
1651 .high_frequency = 320.0f,
1652 };
1653 case Core::HID::VibrationGcErmCommand::Start:
1654 return Core::HID::VibrationValue{
1655 .low_amplitude = 1.0f,
1656 .low_frequency = 160.0f,
1657 .high_amplitude = 1.0f,
1658 .high_frequency = 320.0f,
1659 };
1660 case Core::HID::VibrationGcErmCommand::StopHard:
1661 return Core::HID::VibrationValue{
1662 .low_amplitude = 0.0f,
1663 .low_frequency = 0.0f,
1664 .high_amplitude = 0.0f,
1665 .high_frequency = 0.0f,
1666 };
1667 default:
1668 return Core::HID::DEFAULT_VIBRATION_VALUE;
1669 }
1670 }();
1671
1672 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1673 .VibrateController(parameters.vibration_device_handle, vibration_value);
1674
1675 LOG_DEBUG(Service_HID,
1676 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1677 "gc_erm_command={}",
1678 parameters.vibration_device_handle.npad_type,
1679 parameters.vibration_device_handle.npad_id,
1680 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1681 parameters.gc_erm_command);
1682
1683 IPC::ResponseBuilder rb{ctx, 2};
1684 rb.Push(ResultSuccess);
1685}
1686
1687void Hid::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1688 IPC::RequestParser rp{ctx};
1689 struct Parameters {
1690 Core::HID::VibrationDeviceHandle vibration_device_handle;
1691 INSERT_PADDING_WORDS_NOINIT(1);
1692 u64 applet_resource_user_id;
1693 };
1694
1695 const auto parameters{rp.PopRaw<Parameters>()};
1696
1697 const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1698 .GetLastVibration(parameters.vibration_device_handle);
1699
1700 const auto gc_erm_command = [last_vibration] {
1701 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1702 return Core::HID::VibrationGcErmCommand::Start;
1703 }
1704
1705 /**
1706 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1707 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1708 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1709 * This is done to reuse the controller vibration functions made for regular controllers.
1710 */
1711 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1712 return Core::HID::VibrationGcErmCommand::StopHard;
1713 }
1714
1715 return Core::HID::VibrationGcErmCommand::Stop;
1716 }();
1717
1718 LOG_DEBUG(Service_HID,
1719 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1720 parameters.vibration_device_handle.npad_type,
1721 parameters.vibration_device_handle.npad_id,
1722 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1723
1724 IPC::ResponseBuilder rb{ctx, 4};
1725 rb.Push(ResultSuccess);
1726 rb.PushEnum(gc_erm_command);
1727}
1728
1729void Hid::BeginPermitVibrationSession(HLERequestContext& ctx) {
1730 IPC::RequestParser rp{ctx};
1731 const auto applet_resource_user_id{rp.Pop<u64>()};
1732
1733 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1734 .SetPermitVibrationSession(true);
1735
1736 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1737
1738 IPC::ResponseBuilder rb{ctx, 2};
1739 rb.Push(ResultSuccess);
1740}
1741
1742void Hid::EndPermitVibrationSession(HLERequestContext& ctx) {
1743 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1744 .SetPermitVibrationSession(false);
1745
1746 LOG_DEBUG(Service_HID, "called");
1747
1748 IPC::ResponseBuilder rb{ctx, 2};
1749 rb.Push(ResultSuccess);
1750}
1751
1752void Hid::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1753 IPC::RequestParser rp{ctx};
1754 struct Parameters {
1755 Core::HID::VibrationDeviceHandle vibration_device_handle;
1756 INSERT_PADDING_WORDS_NOINIT(1);
1757 u64 applet_resource_user_id;
1758 };
1759 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1760
1761 const auto parameters{rp.PopRaw<Parameters>()};
1762
1763 LOG_DEBUG(Service_HID,
1764 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1765 parameters.vibration_device_handle.npad_type,
1766 parameters.vibration_device_handle.npad_id,
1767 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1768
1769 IPC::ResponseBuilder rb{ctx, 3};
1770 rb.Push(ResultSuccess);
1771 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1772 .IsVibrationDeviceMounted(parameters.vibration_device_handle));
1773}
1774
1775void Hid::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1776 IPC::RequestParser rp{ctx};
1777 const auto applet_resource_user_id{rp.Pop<u64>()};
1778
1779 applet_resource->ActivateController(HidController::ConsoleSixAxisSensor);
1780
1781 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1782
1783 IPC::ResponseBuilder rb{ctx, 2};
1784 rb.Push(ResultSuccess);
1785}
1786
1787void Hid::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
1788 IPC::RequestParser rp{ctx};
1789 struct Parameters {
1790 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1791 INSERT_PADDING_WORDS_NOINIT(1);
1792 u64 applet_resource_user_id;
1793 };
1794 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1795
1796 const auto parameters{rp.PopRaw<Parameters>()};
1797
1798 LOG_WARNING(Service_HID,
1799 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1800 parameters.console_sixaxis_handle.unknown_1,
1801 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1802
1803 IPC::ResponseBuilder rb{ctx, 2};
1804 rb.Push(ResultSuccess);
1805}
1806
1807void Hid::StopConsoleSixAxisSensor(HLERequestContext& ctx) {
1808 IPC::RequestParser rp{ctx};
1809 struct Parameters {
1810 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1811 INSERT_PADDING_WORDS_NOINIT(1);
1812 u64 applet_resource_user_id;
1813 };
1814 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1815
1816 const auto parameters{rp.PopRaw<Parameters>()};
1817
1818 LOG_WARNING(Service_HID,
1819 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1820 parameters.console_sixaxis_handle.unknown_1,
1821 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1822
1823 IPC::ResponseBuilder rb{ctx, 2};
1824 rb.Push(ResultSuccess);
1825}
1826
1827void Hid::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1828 IPC::RequestParser rp{ctx};
1829 const auto applet_resource_user_id{rp.Pop<u64>()};
1830
1831 applet_resource->ActivateController(HidController::ConsoleSixAxisSensor);
1832
1833 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1834
1835 IPC::ResponseBuilder rb{ctx, 2};
1836 rb.Push(ResultSuccess);
1837}
1838
1839void Hid::StartSevenSixAxisSensor(HLERequestContext& ctx) {
1840 IPC::RequestParser rp{ctx};
1841 const auto applet_resource_user_id{rp.Pop<u64>()};
1842
1843 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1844 applet_resource_user_id);
1845
1846 IPC::ResponseBuilder rb{ctx, 2};
1847 rb.Push(ResultSuccess);
1848}
1849
1850void Hid::StopSevenSixAxisSensor(HLERequestContext& ctx) {
1851 IPC::RequestParser rp{ctx};
1852 const auto applet_resource_user_id{rp.Pop<u64>()};
1853
1854 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1855 applet_resource_user_id);
1856
1857 IPC::ResponseBuilder rb{ctx, 2};
1858 rb.Push(ResultSuccess);
1859}
1860
1861void Hid::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1862 IPC::RequestParser rp{ctx};
1863 const auto applet_resource_user_id{rp.Pop<u64>()};
1864 const auto t_mem_1_size{rp.Pop<u64>()};
1865 const auto t_mem_2_size{rp.Pop<u64>()};
1866 const auto t_mem_1_handle{ctx.GetCopyHandle(0)};
1867 const auto t_mem_2_handle{ctx.GetCopyHandle(1)};
1868
1869 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1870 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1871
1872 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1873 t_mem_1_handle);
1874
1875 if (t_mem_1.IsNull()) {
1876 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
1877 IPC::ResponseBuilder rb{ctx, 2};
1878 rb.Push(ResultUnknown);
1879 return;
1880 }
1881
1882 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1883 t_mem_2_handle);
1884
1885 if (t_mem_2.IsNull()) {
1886 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
1887 IPC::ResponseBuilder rb{ctx, 2};
1888 rb.Push(ResultUnknown);
1889 return;
1890 }
1891
1892 ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size");
1893 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1894
1895 // Activate console six axis controller
1896 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1897 .ActivateController();
1898
1899 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1900 .SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1901
1902 LOG_WARNING(Service_HID,
1903 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
1904 "applet_resource_user_id={}",
1905 t_mem_1_handle, t_mem_2_handle, applet_resource_user_id);
1906
1907 IPC::ResponseBuilder rb{ctx, 2};
1908 rb.Push(ResultSuccess);
1909}
1910
1911void Hid::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) {
1912 IPC::RequestParser rp{ctx};
1913 const auto applet_resource_user_id{rp.Pop<u64>()};
1914
1915 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1916 applet_resource_user_id);
1917
1918 IPC::ResponseBuilder rb{ctx, 2};
1919 rb.Push(ResultSuccess);
1920}
1921
1922void Hid::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1923 IPC::RequestParser rp{ctx};
1924 const auto applet_resource_user_id{rp.Pop<u64>()};
1925
1926 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1927 .ResetTimestamp();
1928
1929 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1930
1931 IPC::ResponseBuilder rb{ctx, 2};
1932 rb.Push(ResultSuccess);
1933}
1934
1935void Hid::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
1936 IPC::RequestParser rp{ctx};
1937
1938 LOG_WARNING(Service_HID, "(STUBBED) called");
1939
1940 IPC::ResponseBuilder rb{ctx, 3};
1941 rb.Push(ResultSuccess);
1942 rb.Push(false);
1943}
1944
1945void Hid::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1946 IPC::RequestParser rp{ctx};
1947 struct Parameters {
1948 Core::HID::NpadIdType npad_id;
1949 INSERT_PADDING_WORDS_NOINIT(1);
1950 u64 applet_resource_user_id;
1951 };
1952 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1953
1954 const auto parameters{rp.PopRaw<Parameters>()};
1955
1956 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1957 parameters.npad_id, parameters.applet_resource_user_id);
1958
1959 Controller_Palma::PalmaConnectionHandle handle;
1960 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1961 const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle);
1962
1963 IPC::ResponseBuilder rb{ctx, 4};
1964 rb.Push(result);
1965 rb.PushRaw(handle);
1966}
1967
1968void Hid::InitializePalma(HLERequestContext& ctx) {
1969 IPC::RequestParser rp{ctx};
1970 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1971
1972 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1973
1974 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1975 const auto result = controller.InitializePalma(connection_handle);
1976
1977 IPC::ResponseBuilder rb{ctx, 2};
1978 rb.Push(result);
1979}
1980
1981void Hid::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
1982 IPC::RequestParser rp{ctx};
1983 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1984
1985 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1986
1987 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1988
1989 IPC::ResponseBuilder rb{ctx, 2, 1};
1990 rb.Push(ResultSuccess);
1991 rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle));
1992}
1993
1994void Hid::GetPalmaOperationInfo(HLERequestContext& ctx) {
1995 IPC::RequestParser rp{ctx};
1996 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1997
1998 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1999
2000 Controller_Palma::PalmaOperationType operation_type;
2001 Controller_Palma::PalmaOperationData data;
2002 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2003 const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data);
2004
2005 if (result.IsError()) {
2006 IPC::ResponseBuilder rb{ctx, 2};
2007 rb.Push(result);
2008 }
2009
2010 ctx.WriteBuffer(data);
2011 IPC::ResponseBuilder rb{ctx, 4};
2012 rb.Push(result);
2013 rb.Push(static_cast<u64>(operation_type));
2014}
2015
2016void Hid::PlayPalmaActivity(HLERequestContext& ctx) {
2017 IPC::RequestParser rp{ctx};
2018 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2019 const auto palma_activity{rp.Pop<u64>()};
2020
2021 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
2022 connection_handle.npad_id, palma_activity);
2023
2024 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2025 const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity);
2026
2027 IPC::ResponseBuilder rb{ctx, 2};
2028 rb.Push(result);
2029}
2030
2031void Hid::SetPalmaFrModeType(HLERequestContext& ctx) {
2032 IPC::RequestParser rp{ctx};
2033 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2034 const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()};
2035
2036 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2037 connection_handle.npad_id, fr_mode);
2038
2039 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2040 const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode);
2041
2042 IPC::ResponseBuilder rb{ctx, 2};
2043 rb.Push(result);
2044}
2045
2046void Hid::ReadPalmaStep(HLERequestContext& ctx) {
2047 IPC::RequestParser rp{ctx};
2048 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2049
2050 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2051
2052 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2053 const auto result = controller.ReadPalmaStep(connection_handle);
2054
2055 IPC::ResponseBuilder rb{ctx, 2};
2056 rb.Push(result);
2057}
2058
2059void Hid::EnablePalmaStep(HLERequestContext& ctx) {
2060 IPC::RequestParser rp{ctx};
2061 struct Parameters {
2062 bool is_enabled;
2063 INSERT_PADDING_WORDS_NOINIT(1);
2064 Controller_Palma::PalmaConnectionHandle connection_handle;
2065 };
2066 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2067
2068 const auto parameters{rp.PopRaw<Parameters>()};
2069
2070 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2071 parameters.connection_handle.npad_id, parameters.is_enabled);
2072
2073 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2074 const auto result =
2075 controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2076
2077 IPC::ResponseBuilder rb{ctx, 2};
2078 rb.Push(result);
2079}
2080
2081void Hid::ResetPalmaStep(HLERequestContext& ctx) {
2082 IPC::RequestParser rp{ctx};
2083 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2084
2085 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2086
2087 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2088 const auto result = controller.ResetPalmaStep(connection_handle);
2089
2090 IPC::ResponseBuilder rb{ctx, 2};
2091 rb.Push(result);
2092}
2093
2094void Hid::ReadPalmaApplicationSection(HLERequestContext& ctx) {
2095 LOG_WARNING(Service_HID, "(STUBBED) called");
2096
2097 IPC::ResponseBuilder rb{ctx, 2};
2098 rb.Push(ResultSuccess);
2099}
2100
2101void Hid::WritePalmaApplicationSection(HLERequestContext& ctx) {
2102 LOG_WARNING(Service_HID, "(STUBBED) called");
2103
2104 IPC::ResponseBuilder rb{ctx, 2};
2105 rb.Push(ResultSuccess);
2106}
2107
2108void Hid::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2109 IPC::RequestParser rp{ctx};
2110 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2111
2112 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2113
2114 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2115 .ReadPalmaUniqueCode(connection_handle);
2116
2117 IPC::ResponseBuilder rb{ctx, 2};
2118 rb.Push(ResultSuccess);
2119}
2120
2121void Hid::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2122 IPC::RequestParser rp{ctx};
2123 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2124
2125 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2126
2127 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2128 .SetPalmaUniqueCodeInvalid(connection_handle);
2129
2130 IPC::ResponseBuilder rb{ctx, 2};
2131 rb.Push(ResultSuccess);
2132}
2133
2134void Hid::WritePalmaActivityEntry(HLERequestContext& ctx) {
2135 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2136
2137 IPC::ResponseBuilder rb{ctx, 2};
2138 rb.Push(ResultSuccess);
2139}
2140
2141void Hid::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2142 IPC::RequestParser rp{ctx};
2143 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2144 const auto unknown{rp.Pop<u64>()};
2145
2146 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2147
2148 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2149 connection_handle.npad_id, unknown);
2150
2151 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2152 .WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2153
2154 IPC::ResponseBuilder rb{ctx, 2};
2155 rb.Push(ResultSuccess);
2156}
2157
2158void Hid::WritePalmaWaveEntry(HLERequestContext& ctx) {
2159 IPC::RequestParser rp{ctx};
2160 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2161 const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()};
2162 const auto unknown{rp.Pop<u64>()};
2163 const auto t_mem_size{rp.Pop<u64>()};
2164 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2165 const auto size{rp.Pop<u64>()};
2166
2167 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2168
2169 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
2170 t_mem_handle);
2171
2172 if (t_mem.IsNull()) {
2173 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2174 IPC::ResponseBuilder rb{ctx, 2};
2175 rb.Push(ResultUnknown);
2176 return;
2177 }
2178
2179 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2180
2181 LOG_WARNING(Service_HID,
2182 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2183 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2184 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2185
2186 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2187 .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size);
2188
2189 IPC::ResponseBuilder rb{ctx, 2};
2190 rb.Push(ResultSuccess);
2191}
2192
2193void Hid::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2194 IPC::RequestParser rp{ctx};
2195 struct Parameters {
2196 s32 database_id_version;
2197 INSERT_PADDING_WORDS_NOINIT(1);
2198 Controller_Palma::PalmaConnectionHandle connection_handle;
2199 };
2200 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2201
2202 const auto parameters{rp.PopRaw<Parameters>()};
2203
2204 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2205 parameters.connection_handle.npad_id, parameters.database_id_version);
2206
2207 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2208 .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle,
2209 parameters.database_id_version);
2210
2211 IPC::ResponseBuilder rb{ctx, 2};
2212 rb.Push(ResultSuccess);
2213}
2214
2215void Hid::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2216 IPC::RequestParser rp{ctx};
2217 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2218
2219 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2220
2221 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2222 .GetPalmaDataBaseIdentificationVersion(connection_handle);
2223
2224 IPC::ResponseBuilder rb{ctx, 2};
2225 rb.Push(ResultSuccess);
2226}
2227
2228void Hid::SuspendPalmaFeature(HLERequestContext& ctx) {
2229 LOG_WARNING(Service_HID, "(STUBBED) called");
2230
2231 IPC::ResponseBuilder rb{ctx, 2};
2232 rb.Push(ResultSuccess);
2233}
2234
2235void Hid::GetPalmaOperationResult(HLERequestContext& ctx) {
2236 IPC::RequestParser rp{ctx};
2237 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2238
2239 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2240
2241 const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma)
2242 .GetPalmaOperationResult(connection_handle);
2243
2244 IPC::ResponseBuilder rb{ctx, 2};
2245 rb.Push(result);
2246}
2247
2248void Hid::ReadPalmaPlayLog(HLERequestContext& ctx) {
2249 LOG_WARNING(Service_HID, "(STUBBED) called");
2250
2251 IPC::ResponseBuilder rb{ctx, 2};
2252 rb.Push(ResultSuccess);
2253}
2254
2255void Hid::ResetPalmaPlayLog(HLERequestContext& ctx) {
2256 LOG_WARNING(Service_HID, "(STUBBED) called");
2257
2258 IPC::ResponseBuilder rb{ctx, 2};
2259 rb.Push(ResultSuccess);
2260}
2261
2262void Hid::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2263 IPC::RequestParser rp{ctx};
2264 struct Parameters {
2265 bool is_palma_all_connectable;
2266 INSERT_PADDING_BYTES_NOINIT(7);
2267 u64 applet_resource_user_id;
2268 };
2269 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2270
2271 const auto parameters{rp.PopRaw<Parameters>()};
2272
2273 LOG_WARNING(Service_HID,
2274 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2275 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2276
2277 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2278 .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2279
2280 IPC::ResponseBuilder rb{ctx, 2};
2281 rb.Push(ResultSuccess);
2282}
2283
2284void Hid::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2285 LOG_WARNING(Service_HID, "(STUBBED) called");
2286
2287 IPC::ResponseBuilder rb{ctx, 2};
2288 rb.Push(ResultSuccess);
2289}
2290
2291void Hid::PairPalma(HLERequestContext& ctx) {
2292 IPC::RequestParser rp{ctx};
2293 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2294
2295 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2296
2297 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2298 .PairPalma(connection_handle);
2299
2300 IPC::ResponseBuilder rb{ctx, 2};
2301 rb.Push(ResultSuccess);
2302}
2303
2304void Hid::SetPalmaBoostMode(HLERequestContext& ctx) {
2305 IPC::RequestParser rp{ctx};
2306 const auto palma_boost_mode{rp.Pop<bool>()};
2307
2308 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2309
2310 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2311 .SetPalmaBoostMode(palma_boost_mode);
2312
2313 IPC::ResponseBuilder rb{ctx, 2};
2314 rb.Push(ResultSuccess);
2315}
2316
2317void Hid::CancelWritePalmaWaveEntry(HLERequestContext& ctx) {
2318 LOG_WARNING(Service_HID, "(STUBBED) called");
2319
2320 IPC::ResponseBuilder rb{ctx, 2};
2321 rb.Push(ResultSuccess);
2322}
2323
2324void Hid::EnablePalmaBoostMode(HLERequestContext& ctx) {
2325 LOG_WARNING(Service_HID, "(STUBBED) called");
2326
2327 IPC::ResponseBuilder rb{ctx, 2};
2328 rb.Push(ResultSuccess);
2329}
2330
2331void Hid::GetPalmaBluetoothAddress(HLERequestContext& ctx) {
2332 LOG_WARNING(Service_HID, "(STUBBED) called");
2333
2334 IPC::ResponseBuilder rb{ctx, 2};
2335 rb.Push(ResultSuccess);
2336}
2337
2338void Hid::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2339 LOG_WARNING(Service_HID, "(STUBBED) called");
2340
2341 IPC::ResponseBuilder rb{ctx, 2};
2342 rb.Push(ResultSuccess);
2343}
2344
2345void Hid::SetNpadCommunicationMode(HLERequestContext& ctx) {
2346 IPC::RequestParser rp{ctx};
2347 const auto applet_resource_user_id{rp.Pop<u64>()};
2348 const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()};
2349
2350 applet_resource->GetController<Controller_NPad>(HidController::NPad)
2351 .SetNpadCommunicationMode(communication_mode);
2352
2353 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2354 applet_resource_user_id, communication_mode);
2355
2356 IPC::ResponseBuilder rb{ctx, 2};
2357 rb.Push(ResultSuccess);
2358}
2359
2360void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
2361 IPC::RequestParser rp{ctx};
2362
2363 LOG_WARNING(Service_HID, "(STUBBED) called");
2364
2365 IPC::ResponseBuilder rb{ctx, 4};
2366 rb.Push(ResultSuccess);
2367 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
2368 .GetNpadCommunicationMode());
2369}
2370
2371void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2372 IPC::RequestParser rp{ctx};
2373 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2374 const auto applet_resource_user_id{rp.Pop<u64>()};
2375
2376 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
2377 touchscreen_mode.mode, applet_resource_user_id);
2378
2379 IPC::ResponseBuilder rb{ctx, 2};
2380 rb.Push(ResultSuccess);
2381}
2382
2383void Hid::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2384 IPC::RequestParser rp{ctx};
2385 struct Parameters {
2386 s32 unknown;
2387 INSERT_PADDING_WORDS_NOINIT(1);
2388 u64 applet_resource_user_id;
2389 };
2390 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2391
2392 const auto parameters{rp.PopRaw<Parameters>()};
2393
2394 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
2395 parameters.unknown, parameters.applet_resource_user_id);
2396
2397 IPC::ResponseBuilder rb{ctx, 3};
2398 rb.Push(ResultSuccess);
2399 rb.Push(false);
2400}
2401
2402class HidDbg final : public ServiceFramework<HidDbg> {
2403public:
2404 explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
2405 // clang-format off
2406 static const FunctionInfo functions[] = {
2407 {0, nullptr, "DeactivateDebugPad"},
2408 {1, nullptr, "SetDebugPadAutoPilotState"},
2409 {2, nullptr, "UnsetDebugPadAutoPilotState"},
2410 {10, nullptr, "DeactivateTouchScreen"},
2411 {11, nullptr, "SetTouchScreenAutoPilotState"},
2412 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
2413 {13, nullptr, "GetTouchScreenConfiguration"},
2414 {14, nullptr, "ProcessTouchScreenAutoTune"},
2415 {15, nullptr, "ForceStopTouchScreenManagement"},
2416 {16, nullptr, "ForceRestartTouchScreenManagement"},
2417 {17, nullptr, "IsTouchScreenManaged"},
2418 {20, nullptr, "DeactivateMouse"},
2419 {21, nullptr, "SetMouseAutoPilotState"},
2420 {22, nullptr, "UnsetMouseAutoPilotState"},
2421 {25, nullptr, "SetDebugMouseAutoPilotState"},
2422 {26, nullptr, "UnsetDebugMouseAutoPilotState"},
2423 {30, nullptr, "DeactivateKeyboard"},
2424 {31, nullptr, "SetKeyboardAutoPilotState"},
2425 {32, nullptr, "UnsetKeyboardAutoPilotState"},
2426 {50, nullptr, "DeactivateXpad"},
2427 {51, nullptr, "SetXpadAutoPilotState"},
2428 {52, nullptr, "UnsetXpadAutoPilotState"},
2429 {53, nullptr, "DeactivateJoyXpad"},
2430 {60, nullptr, "ClearNpadSystemCommonPolicy"},
2431 {61, nullptr, "DeactivateNpad"},
2432 {62, nullptr, "ForceDisconnectNpad"},
2433 {91, nullptr, "DeactivateGesture"},
2434 {110, nullptr, "DeactivateHomeButton"},
2435 {111, nullptr, "SetHomeButtonAutoPilotState"},
2436 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
2437 {120, nullptr, "DeactivateSleepButton"},
2438 {121, nullptr, "SetSleepButtonAutoPilotState"},
2439 {122, nullptr, "UnsetSleepButtonAutoPilotState"},
2440 {123, nullptr, "DeactivateInputDetector"},
2441 {130, nullptr, "DeactivateCaptureButton"},
2442 {131, nullptr, "SetCaptureButtonAutoPilotState"},
2443 {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
2444 {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
2445 {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
2446 {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
2447 {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
2448 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
2449 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
2450 {142, nullptr, "DeactivateSevenSixAxisSensor"},
2451 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
2452 {144, nullptr, "GetAccelerometerFsr"},
2453 {145, nullptr, "SetAccelerometerFsr"},
2454 {146, nullptr, "GetAccelerometerOdr"},
2455 {147, nullptr, "SetAccelerometerOdr"},
2456 {148, nullptr, "GetGyroscopeFsr"},
2457 {149, nullptr, "SetGyroscopeFsr"},
2458 {150, nullptr, "GetGyroscopeOdr"},
2459 {151, nullptr, "SetGyroscopeOdr"},
2460 {152, nullptr, "GetWhoAmI"},
2461 {201, nullptr, "ActivateFirmwareUpdate"},
2462 {202, nullptr, "DeactivateFirmwareUpdate"},
2463 {203, nullptr, "StartFirmwareUpdate"},
2464 {204, nullptr, "GetFirmwareUpdateStage"},
2465 {205, nullptr, "GetFirmwareVersion"},
2466 {206, nullptr, "GetDestinationFirmwareVersion"},
2467 {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
2468 {208, nullptr, "StartFirmwareUpdateForRevert"},
2469 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
2470 {210, nullptr, "IsFirmwareUpdatingDevice"},
2471 {211, nullptr, "StartFirmwareUpdateIndividual"},
2472 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
2473 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
2474 {221, nullptr, "UpdateControllerColor"},
2475 {222, nullptr, "ConnectUsbPadsAsync"},
2476 {223, nullptr, "DisconnectUsbPadsAsync"},
2477 {224, nullptr, "UpdateDesignInfo"},
2478 {225, nullptr, "GetUniquePadDriverState"},
2479 {226, nullptr, "GetSixAxisSensorDriverStates"},
2480 {227, nullptr, "GetRxPacketHistory"},
2481 {228, nullptr, "AcquireOperationEventHandle"},
2482 {229, nullptr, "ReadSerialFlash"},
2483 {230, nullptr, "WriteSerialFlash"},
2484 {231, nullptr, "GetOperationResult"},
2485 {232, nullptr, "EnableShipmentMode"},
2486 {233, nullptr, "ClearPairingInfo"},
2487 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
2488 {235, nullptr, "EnableAnalogStickPower"},
2489 {236, nullptr, "RequestKuinaUartClockCal"},
2490 {237, nullptr, "GetKuinaUartClockCal"},
2491 {238, nullptr, "SetKuinaUartClockTrim"},
2492 {239, nullptr, "KuinaLoopbackTest"},
2493 {240, nullptr, "RequestBatteryVoltage"},
2494 {241, nullptr, "GetBatteryVoltage"},
2495 {242, nullptr, "GetUniquePadPowerInfo"},
2496 {243, nullptr, "RebootUniquePad"},
2497 {244, nullptr, "RequestKuinaFirmwareVersion"},
2498 {245, nullptr, "GetKuinaFirmwareVersion"},
2499 {246, nullptr, "GetVidPid"},
2500 {247, nullptr, "GetAnalogStickCalibrationValue"},
2501 {248, nullptr, "GetUniquePadIdsFull"},
2502 {249, nullptr, "ConnectUniquePad"},
2503 {250, nullptr, "IsVirtual"},
2504 {251, nullptr, "GetAnalogStickModuleParam"},
2505 {301, nullptr, "GetAbstractedPadHandles"},
2506 {302, nullptr, "GetAbstractedPadState"},
2507 {303, nullptr, "GetAbstractedPadsState"},
2508 {321, nullptr, "SetAutoPilotVirtualPadState"},
2509 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
2510 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
2511 {324, nullptr, "AttachHdlsWorkBuffer"},
2512 {325, nullptr, "ReleaseHdlsWorkBuffer"},
2513 {326, nullptr, "DumpHdlsNpadAssignmentState"},
2514 {327, nullptr, "DumpHdlsStates"},
2515 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
2516 {329, nullptr, "ApplyHdlsStateList"},
2517 {330, nullptr, "AttachHdlsVirtualDevice"},
2518 {331, nullptr, "DetachHdlsVirtualDevice"},
2519 {332, nullptr, "SetHdlsState"},
2520 {350, nullptr, "AddRegisteredDevice"},
2521 {400, nullptr, "DisableExternalMcuOnNxDevice"},
2522 {401, nullptr, "DisableRailDeviceFiltering"},
2523 {402, nullptr, "EnableWiredPairing"},
2524 {403, nullptr, "EnableShipmentModeAutoClear"},
2525 {404, nullptr, "SetRailEnabled"},
2526 {500, nullptr, "SetFactoryInt"},
2527 {501, nullptr, "IsFactoryBootEnabled"},
2528 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
2529 {551, nullptr, "GetAnalogStickModelData"},
2530 {552, nullptr, "ResetAnalogStickModelData"},
2531 {600, nullptr, "ConvertPadState"},
2532 {650, nullptr, "AddButtonPlayData"},
2533 {651, nullptr, "StartButtonPlayData"},
2534 {652, nullptr, "StopButtonPlayData"},
2535 {2000, nullptr, "DeactivateDigitizer"},
2536 {2001, nullptr, "SetDigitizerAutoPilotState"},
2537 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
2538 {2002, nullptr, "ReloadFirmwareDebugSettings"},
2539 };
2540 // clang-format on
2541
2542 RegisterHandlers(functions);
2543 }
2544};
2545
2546class HidSys final : public ServiceFramework<HidSys> {
2547public:
2548 explicit HidSys(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_)
2549 : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"},
2550 applet_resource{applet_resource_} {
2551 // clang-format off
2552 static const FunctionInfo functions[] = {
2553 {31, nullptr, "SendKeyboardLockKeyEvent"},
2554 {101, nullptr, "AcquireHomeButtonEventHandle"},
2555 {111, nullptr, "ActivateHomeButton"},
2556 {121, nullptr, "AcquireSleepButtonEventHandle"},
2557 {131, nullptr, "ActivateSleepButton"},
2558 {141, nullptr, "AcquireCaptureButtonEventHandle"},
2559 {151, nullptr, "ActivateCaptureButton"},
2560 {161, nullptr, "GetPlatformConfig"},
2561 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
2562 {211, nullptr, "GetNpadsWithNfc"},
2563 {212, nullptr, "AcquireNfcActivateEventHandle"},
2564 {213, nullptr, "ActivateNfc"},
2565 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
2566 {215, nullptr, "IsNfcActivated"},
2567 {230, nullptr, "AcquireIrSensorEventHandle"},
2568 {231, nullptr, "ActivateIrSensor"},
2569 {232, nullptr, "GetIrSensorState"},
2570 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
2571 {301, nullptr, "ActivateNpadSystem"},
2572 {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
2573 {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
2574 {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
2575 {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
2576 {307, nullptr, "GetNpadSystemExtStyle"},
2577 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
2578 {309, nullptr, "GetNpadFullKeyGripColor"},
2579 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
2580 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
2581 {312, nullptr, "SetSupportedNpadStyleSetAll"},
2582 {313, nullptr, "GetNpadCaptureButtonAssignment"},
2583 {314, nullptr, "GetAppletFooterUiType"},
2584 {315, nullptr, "GetAppletDetailedUiType"},
2585 {316, nullptr, "GetNpadInterfaceType"},
2586 {317, nullptr, "GetNpadLeftRightInterfaceType"},
2587 {318, nullptr, "HasBattery"},
2588 {319, nullptr, "HasLeftRightBattery"},
2589 {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
2590 {322, nullptr, "GetIrSensorState"},
2591 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
2592 {324, nullptr, "GetUniquePadButtonSet"},
2593 {325, nullptr, "GetUniquePadColor"},
2594 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
2595 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
2596 {328, nullptr, "AttachAbstractedPadToNpad"},
2597 {329, nullptr, "DetachAbstractedPadAll"},
2598 {330, nullptr, "CheckAbstractedPadConnection"},
2599 {500, nullptr, "SetAppletResourceUserId"},
2600 {501, nullptr, "RegisterAppletResourceUserId"},
2601 {502, nullptr, "UnregisterAppletResourceUserId"},
2602 {503, nullptr, "EnableAppletToGetInput"},
2603 {504, nullptr, "SetAruidValidForVibration"},
2604 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
2605 {506, nullptr, "EnableAppletToGetPadInput"},
2606 {507, nullptr, "EnableAppletToGetTouchScreen"},
2607 {510, nullptr, "SetVibrationMasterVolume"},
2608 {511, nullptr, "GetVibrationMasterVolume"},
2609 {512, nullptr, "BeginPermitVibrationSession"},
2610 {513, nullptr, "EndPermitVibrationSession"},
2611 {514, nullptr, "Unknown514"},
2612 {520, nullptr, "EnableHandheldHids"},
2613 {521, nullptr, "DisableHandheldHids"},
2614 {522, nullptr, "SetJoyConRailEnabled"},
2615 {523, nullptr, "IsJoyConRailEnabled"},
2616 {524, nullptr, "IsHandheldHidsEnabled"},
2617 {525, nullptr, "IsJoyConAttachedOnAllRail"},
2618 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
2619 {541, nullptr, "GetPlayReportControllerUsages"},
2620 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
2621 {543, nullptr, "GetRegisteredDevicesOld"},
2622 {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"},
2623 {545, nullptr, "SendConnectionTrigger"},
2624 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
2625 {547, nullptr, "GetAllowedBluetoothLinksCount"},
2626 {548, nullptr, "GetRegisteredDevices"},
2627 {549, nullptr, "GetConnectableRegisteredDevices"},
2628 {700, nullptr, "ActivateUniquePad"},
2629 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
2630 {703, nullptr, "GetUniquePadIds"},
2631 {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
2632 {800, nullptr, "ListSixAxisSensorHandles"},
2633 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
2634 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
2635 {803, nullptr, "StartSixAxisSensorUserCalibration"},
2636 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
2637 {805, nullptr, "GetUniquePadBluetoothAddress"},
2638 {806, nullptr, "DisconnectUniquePad"},
2639 {807, nullptr, "GetUniquePadType"},
2640 {808, nullptr, "GetUniquePadInterface"},
2641 {809, nullptr, "GetUniquePadSerialNumber"},
2642 {810, nullptr, "GetUniquePadControllerNumber"},
2643 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
2644 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
2645 {821, nullptr, "StartAnalogStickManualCalibration"},
2646 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
2647 {823, nullptr, "CancelAnalogStickManualCalibration"},
2648 {824, nullptr, "ResetAnalogStickManualCalibration"},
2649 {825, nullptr, "GetAnalogStickState"},
2650 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
2651 {827, nullptr, "IsAnalogStickButtonPressed"},
2652 {828, nullptr, "IsAnalogStickInReleasePosition"},
2653 {829, nullptr, "IsAnalogStickInCircumference"},
2654 {830, nullptr, "SetNotificationLedPattern"},
2655 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
2656 {832, nullptr, "PrepareHidsForNotificationWake"},
2657 {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
2658 {851, nullptr, "EnableUsbFullKeyController"},
2659 {852, nullptr, "IsUsbConnected"},
2660 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
2661 {900, nullptr, "ActivateInputDetector"},
2662 {901, nullptr, "NotifyInputDetector"},
2663 {1000, nullptr, "InitializeFirmwareUpdate"},
2664 {1001, nullptr, "GetFirmwareVersion"},
2665 {1002, nullptr, "GetAvailableFirmwareVersion"},
2666 {1003, nullptr, "IsFirmwareUpdateAvailable"},
2667 {1004, nullptr, "CheckFirmwareUpdateRequired"},
2668 {1005, nullptr, "StartFirmwareUpdate"},
2669 {1006, nullptr, "AbortFirmwareUpdate"},
2670 {1007, nullptr, "GetFirmwareUpdateState"},
2671 {1008, nullptr, "ActivateAudioControl"},
2672 {1009, nullptr, "AcquireAudioControlEventHandle"},
2673 {1010, nullptr, "GetAudioControlStates"},
2674 {1011, nullptr, "DeactivateAudioControl"},
2675 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
2676 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
2677 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
2678 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
2679 {1100, nullptr, "GetHidbusSystemServiceObject"},
2680 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
2681 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
2682 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
2683 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
2684 {1133, nullptr, "StartUsbFirmwareUpdate"},
2685 {1134, nullptr, "GetUsbFirmwareUpdateState"},
2686 {1150, nullptr, "SetTouchScreenMagnification"},
2687 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
2688 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
2689 {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
2690 {1154, nullptr, "IsFirmwareAvailableForNotification"},
2691 {1155, nullptr, "SetForceHandheldStyleVibration"},
2692 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
2693 {1157, nullptr, "CancelConnectionTrigger"},
2694 {1200, nullptr, "IsButtonConfigSupported"},
2695 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
2696 {1202, nullptr, "DeleteButtonConfig"},
2697 {1203, nullptr, "DeleteButtonConfigEmbedded"},
2698 {1204, nullptr, "SetButtonConfigEnabled"},
2699 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
2700 {1206, nullptr, "IsButtonConfigEnabled"},
2701 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
2702 {1208, nullptr, "SetButtonConfigEmbedded"},
2703 {1209, nullptr, "SetButtonConfigFull"},
2704 {1210, nullptr, "SetButtonConfigLeft"},
2705 {1211, nullptr, "SetButtonConfigRight"},
2706 {1212, nullptr, "GetButtonConfigEmbedded"},
2707 {1213, nullptr, "GetButtonConfigFull"},
2708 {1214, nullptr, "GetButtonConfigLeft"},
2709 {1215, nullptr, "GetButtonConfigRight"},
2710 {1250, nullptr, "IsCustomButtonConfigSupported"},
2711 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
2712 {1252, nullptr, "IsDefaultButtonConfigFull"},
2713 {1253, nullptr, "IsDefaultButtonConfigLeft"},
2714 {1254, nullptr, "IsDefaultButtonConfigRight"},
2715 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
2716 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
2717 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
2718 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
2719 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
2720 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
2721 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
2722 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
2723 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
2724 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
2725 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
2726 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
2727 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
2728 {1268, nullptr, "DeleteButtonConfigStorageFull"},
2729 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
2730 {1270, nullptr, "DeleteButtonConfigStorageRight"},
2731 {1271, nullptr, "IsUsingCustomButtonConfig"},
2732 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
2733 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
2734 {1274, nullptr, "SetDefaultButtonConfig"},
2735 {1275, nullptr, "SetAllDefaultButtonConfig"},
2736 {1276, nullptr, "SetHidButtonConfigEmbedded"},
2737 {1277, nullptr, "SetHidButtonConfigFull"},
2738 {1278, nullptr, "SetHidButtonConfigLeft"},
2739 {1279, nullptr, "SetHidButtonConfigRight"},
2740 {1280, nullptr, "GetHidButtonConfigEmbedded"},
2741 {1281, nullptr, "GetHidButtonConfigFull"},
2742 {1282, nullptr, "GetHidButtonConfigLeft"},
2743 {1283, nullptr, "GetHidButtonConfigRight"},
2744 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
2745 {1285, nullptr, "GetButtonConfigStorageFull"},
2746 {1286, nullptr, "GetButtonConfigStorageLeft"},
2747 {1287, nullptr, "GetButtonConfigStorageRight"},
2748 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
2749 {1289, nullptr, "SetButtonConfigStorageFull"},
2750 {1290, nullptr, "DeleteButtonConfigStorageRight"},
2751 {1291, nullptr, "DeleteButtonConfigStorageRight"},
2752 };
2753 // clang-format on
2754
2755 RegisterHandlers(functions);
2756
2757 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
2758 }
2759
2760 ~HidSys() {
2761 service_context.CloseEvent(joy_detach_event);
2762 };
2763
2764private:
2765 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
2766 LOG_WARNING(Service_HID, "called");
2767
2768 GetAppletResource()
2769 ->GetController<Controller_NPad>(HidController::NPad)
2770 .ApplyNpadSystemCommonPolicy();
2771
2772 IPC::ResponseBuilder rb{ctx, 2};
2773 rb.Push(ResultSuccess);
2774 }
2775
2776 void GetLastActiveNpad(HLERequestContext& ctx) {
2777 LOG_DEBUG(Service_HID, "(STUBBED) called");
2778
2779 IPC::ResponseBuilder rb{ctx, 3};
2780 rb.Push(ResultSuccess);
2781 rb.PushEnum(system.HIDCore().GetLastActiveController());
2782 }
2783
2784 void GetUniquePadsFromNpad(HLERequestContext& ctx) {
2785 IPC::RequestParser rp{ctx};
2786 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2787
2788 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2789
2790 const std::vector<Core::HID::UniquePadId> unique_pads{};
2791
2792 ctx.WriteBuffer(unique_pads);
2793
2794 IPC::ResponseBuilder rb{ctx, 3};
2795 rb.Push(ResultSuccess);
2796 rb.Push(static_cast<u32>(unique_pads.size()));
2797 }
2798
2799 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
2800 LOG_INFO(Service_AM, "called");
2801
2802 IPC::ResponseBuilder rb{ctx, 2, 1};
2803 rb.Push(ResultSuccess);
2804 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
2805 }
2806
2807 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
2808 const bool is_enabled = false;
2809
2810 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
2811
2812 IPC::ResponseBuilder rb{ctx, 3};
2813 rb.Push(ResultSuccess);
2814 rb.Push(is_enabled);
2815 }
2816
2817 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
2818 LOG_WARNING(Service_HID, "(STUBBED) called");
2819
2820 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
2821 .mode = Core::HID::TouchScreenModeForNx::Finger,
2822 };
2823
2824 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
2825 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
2826 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
2827 }
2828
2829 IPC::ResponseBuilder rb{ctx, 6};
2830 rb.Push(ResultSuccess);
2831 rb.PushRaw(touchscreen_config);
2832 }
2833
2834 std::shared_ptr<IAppletResource> GetAppletResource() {
2835 if (applet_resource == nullptr) {
2836 applet_resource = std::make_shared<IAppletResource>(system, service_context);
2837 }
2838
2839 return applet_resource;
2840 }
2841
2842 Kernel::KEvent* joy_detach_event;
2843 KernelHelpers::ServiceContext service_context;
2844 std::shared_ptr<IAppletResource> applet_resource;
2845};
2846
2847void LoopProcess(Core::System& system) { 17void LoopProcess(Core::System& system) {
2848 auto server_manager = std::make_unique<ServerManager>(system); 18 auto server_manager = std::make_unique<ServerManager>(system);
2849 std::shared_ptr<IAppletResource> applet_resource; 19 std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system);
20 std::shared_ptr<HidFirmwareSettings> firmware_settings =
21 std::make_shared<HidFirmwareSettings>();
22
23 server_manager->RegisterNamedService(
24 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
25 server_manager->RegisterNamedService(
26 "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager));
27 server_manager->RegisterNamedService(
28 "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager));
2850 29
2851 server_manager->RegisterNamedService("hid", std::make_shared<Hid>(system, applet_resource));
2852 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); 30 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
2853 server_manager->RegisterNamedService("hid:dbg", std::make_shared<HidDbg>(system));
2854 server_manager->RegisterNamedService("hid:sys",
2855 std::make_shared<HidSys>(system, applet_resource));
2856 31
2857 server_manager->RegisterNamedService("irs", std::make_shared<Service::IRS::IRS>(system)); 32 server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
2858 server_manager->RegisterNamedService("irs:sys", 33 server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));
2859 std::make_shared<Service::IRS::IRS_SYS>(system));
2860 34
2861 server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system)); 35 server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system));
36
2862 system.RunServer(std::move(server_manager)); 37 system.RunServer(std::move(server_manager));
2863} 38}
2864 39
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 0ca43de93..ec5463f4e 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -3,220 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono> 6namespace Core {
7 7class System;
8#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/service.h"
11
12namespace Core::Timing {
13struct EventType;
14}
15
16namespace Service::SM {
17class ServiceManager;
18} 8}
19 9
20namespace Service::HID { 10namespace Service::HID {
21 11
22enum class HidController : std::size_t {
23 DebugPad,
24 Touchscreen,
25 Mouse,
26 Keyboard,
27 XPad,
28 HomeButton,
29 SleepButton,
30 CaptureButton,
31 InputDetector,
32 UniquePad,
33 NPad,
34 Gesture,
35 ConsoleSixAxisSensor,
36 DebugMouse,
37 Palma,
38
39 MaxControllers,
40};
41
42class IAppletResource final : public ServiceFramework<IAppletResource> {
43public:
44 explicit IAppletResource(Core::System& system_,
45 KernelHelpers::ServiceContext& service_context_);
46 ~IAppletResource() override;
47
48 void ActivateController(HidController controller);
49 void DeactivateController(HidController controller);
50
51 template <typename T>
52 T& GetController(HidController controller) {
53 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
54 }
55
56 template <typename T>
57 const T& GetController(HidController controller) const {
58 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
59 }
60
61private:
62 template <typename T>
63 void MakeController(HidController controller, u8* shared_memory) {
64 if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
65 controllers[static_cast<std::size_t>(controller)] =
66 std::make_unique<T>(system, shared_memory);
67 } else {
68 controllers[static_cast<std::size_t>(controller)] =
69 std::make_unique<T>(system.HIDCore(), shared_memory);
70 }
71 }
72
73 template <typename T>
74 void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
75 controllers[static_cast<std::size_t>(controller)] =
76 std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
77 }
78
79 void GetSharedMemoryHandle(HLERequestContext& ctx);
80 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
81 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
82 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
83 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
84
85 KernelHelpers::ServiceContext& service_context;
86
87 std::shared_ptr<Core::Timing::EventType> npad_update_event;
88 std::shared_ptr<Core::Timing::EventType> default_update_event;
89 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
90 std::shared_ptr<Core::Timing::EventType> motion_update_event;
91
92 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
93 controllers{};
94};
95
96class Hid final : public ServiceFramework<Hid> {
97public:
98 explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
99 ~Hid() override;
100
101 std::shared_ptr<IAppletResource> GetAppletResource();
102
103private:
104 void CreateAppletResource(HLERequestContext& ctx);
105 void ActivateDebugPad(HLERequestContext& ctx);
106 void ActivateTouchScreen(HLERequestContext& ctx);
107 void ActivateMouse(HLERequestContext& ctx);
108 void ActivateKeyboard(HLERequestContext& ctx);
109 void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
110 void ActivateXpad(HLERequestContext& ctx);
111 void GetXpadIDs(HLERequestContext& ctx);
112 void ActivateSixAxisSensor(HLERequestContext& ctx);
113 void DeactivateSixAxisSensor(HLERequestContext& ctx);
114 void StartSixAxisSensor(HLERequestContext& ctx);
115 void StopSixAxisSensor(HLERequestContext& ctx);
116 void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
117 void EnableSixAxisSensorFusion(HLERequestContext& ctx);
118 void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
119 void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
120 void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
121 void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
122 void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
123 void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
124 void IsSixAxisSensorAtRest(HLERequestContext& ctx);
125 void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
126 void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
127 void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
128 void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
129 void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
130 void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
131 void ActivateGesture(HLERequestContext& ctx);
132 void SetSupportedNpadStyleSet(HLERequestContext& ctx);
133 void GetSupportedNpadStyleSet(HLERequestContext& ctx);
134 void SetSupportedNpadIdType(HLERequestContext& ctx);
135 void ActivateNpad(HLERequestContext& ctx);
136 void DeactivateNpad(HLERequestContext& ctx);
137 void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
138 void DisconnectNpad(HLERequestContext& ctx);
139 void GetPlayerLedPattern(HLERequestContext& ctx);
140 void ActivateNpadWithRevision(HLERequestContext& ctx);
141 void SetNpadJoyHoldType(HLERequestContext& ctx);
142 void GetNpadJoyHoldType(HLERequestContext& ctx);
143 void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
144 void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
145 void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
146 void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
147 void StartLrAssignmentMode(HLERequestContext& ctx);
148 void StopLrAssignmentMode(HLERequestContext& ctx);
149 void SetNpadHandheldActivationMode(HLERequestContext& ctx);
150 void GetNpadHandheldActivationMode(HLERequestContext& ctx);
151 void SwapNpadAssignment(HLERequestContext& ctx);
152 void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
153 void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
154 void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
155 void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
156 void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
157 void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
158 void GetVibrationDeviceInfo(HLERequestContext& ctx);
159 void SendVibrationValue(HLERequestContext& ctx);
160 void GetActualVibrationValue(HLERequestContext& ctx);
161 void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
162 void PermitVibration(HLERequestContext& ctx);
163 void IsVibrationPermitted(HLERequestContext& ctx);
164 void SendVibrationValues(HLERequestContext& ctx);
165 void SendVibrationGcErmCommand(HLERequestContext& ctx);
166 void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
167 void BeginPermitVibrationSession(HLERequestContext& ctx);
168 void EndPermitVibrationSession(HLERequestContext& ctx);
169 void IsVibrationDeviceMounted(HLERequestContext& ctx);
170 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
171 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
172 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
173 void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
174 void StartSevenSixAxisSensor(HLERequestContext& ctx);
175 void StopSevenSixAxisSensor(HLERequestContext& ctx);
176 void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
177 void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
178 void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
179 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
180 void GetPalmaConnectionHandle(HLERequestContext& ctx);
181 void InitializePalma(HLERequestContext& ctx);
182 void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
183 void GetPalmaOperationInfo(HLERequestContext& ctx);
184 void PlayPalmaActivity(HLERequestContext& ctx);
185 void SetPalmaFrModeType(HLERequestContext& ctx);
186 void ReadPalmaStep(HLERequestContext& ctx);
187 void EnablePalmaStep(HLERequestContext& ctx);
188 void ResetPalmaStep(HLERequestContext& ctx);
189 void ReadPalmaApplicationSection(HLERequestContext& ctx);
190 void WritePalmaApplicationSection(HLERequestContext& ctx);
191 void ReadPalmaUniqueCode(HLERequestContext& ctx);
192 void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
193 void WritePalmaActivityEntry(HLERequestContext& ctx);
194 void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
195 void WritePalmaWaveEntry(HLERequestContext& ctx);
196 void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
197 void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
198 void SuspendPalmaFeature(HLERequestContext& ctx);
199 void GetPalmaOperationResult(HLERequestContext& ctx);
200 void ReadPalmaPlayLog(HLERequestContext& ctx);
201 void ResetPalmaPlayLog(HLERequestContext& ctx);
202 void SetIsPalmaAllConnectable(HLERequestContext& ctx);
203 void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
204 void PairPalma(HLERequestContext& ctx);
205 void SetPalmaBoostMode(HLERequestContext& ctx);
206 void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
207 void EnablePalmaBoostMode(HLERequestContext& ctx);
208 void GetPalmaBluetoothAddress(HLERequestContext& ctx);
209 void SetDisallowedPalmaConnection(HLERequestContext& ctx);
210 void SetNpadCommunicationMode(HLERequestContext& ctx);
211 void GetNpadCommunicationMode(HLERequestContext& ctx);
212 void SetTouchScreenConfiguration(HLERequestContext& ctx);
213 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
214
215 std::shared_ptr<IAppletResource> applet_resource;
216
217 KernelHelpers::ServiceContext service_context;
218};
219
220void LoopProcess(Core::System& system); 12void LoopProcess(Core::System& system);
221 13
222} // namespace Service::HID 14} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp
new file mode 100644
index 000000000..6294f3dfb
--- /dev/null
+++ b/src/core/hle/service/hid/hid_debug_server.cpp
@@ -0,0 +1,159 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_debug_server.h"
5#include "core/hle/service/hid/resource_manager.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::HID {
9
10IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
11 : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, nullptr, "DeactivateDebugPad"},
15 {1, nullptr, "SetDebugPadAutoPilotState"},
16 {2, nullptr, "UnsetDebugPadAutoPilotState"},
17 {10, nullptr, "DeactivateTouchScreen"},
18 {11, nullptr, "SetTouchScreenAutoPilotState"},
19 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
20 {13, nullptr, "GetTouchScreenConfiguration"},
21 {14, nullptr, "ProcessTouchScreenAutoTune"},
22 {15, nullptr, "ForceStopTouchScreenManagement"},
23 {16, nullptr, "ForceRestartTouchScreenManagement"},
24 {17, nullptr, "IsTouchScreenManaged"},
25 {20, nullptr, "DeactivateMouse"},
26 {21, nullptr, "SetMouseAutoPilotState"},
27 {22, nullptr, "UnsetMouseAutoPilotState"},
28 {25, nullptr, "SetDebugMouseAutoPilotState"},
29 {26, nullptr, "UnsetDebugMouseAutoPilotState"},
30 {30, nullptr, "DeactivateKeyboard"},
31 {31, nullptr, "SetKeyboardAutoPilotState"},
32 {32, nullptr, "UnsetKeyboardAutoPilotState"},
33 {50, nullptr, "DeactivateXpad"},
34 {51, nullptr, "SetXpadAutoPilotState"},
35 {52, nullptr, "UnsetXpadAutoPilotState"},
36 {53, nullptr, "DeactivateJoyXpad"},
37 {60, nullptr, "ClearNpadSystemCommonPolicy"},
38 {61, nullptr, "DeactivateNpad"},
39 {62, nullptr, "ForceDisconnectNpad"},
40 {91, nullptr, "DeactivateGesture"},
41 {110, nullptr, "DeactivateHomeButton"},
42 {111, nullptr, "SetHomeButtonAutoPilotState"},
43 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
44 {120, nullptr, "DeactivateSleepButton"},
45 {121, nullptr, "SetSleepButtonAutoPilotState"},
46 {122, nullptr, "UnsetSleepButtonAutoPilotState"},
47 {123, nullptr, "DeactivateInputDetector"},
48 {130, nullptr, "DeactivateCaptureButton"},
49 {131, nullptr, "SetCaptureButtonAutoPilotState"},
50 {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
51 {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
52 {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
53 {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
54 {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
55 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
56 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
57 {142, nullptr, "DeactivateSevenSixAxisSensor"},
58 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
59 {144, nullptr, "GetAccelerometerFsr"},
60 {145, nullptr, "SetAccelerometerFsr"},
61 {146, nullptr, "GetAccelerometerOdr"},
62 {147, nullptr, "SetAccelerometerOdr"},
63 {148, nullptr, "GetGyroscopeFsr"},
64 {149, nullptr, "SetGyroscopeFsr"},
65 {150, nullptr, "GetGyroscopeOdr"},
66 {151, nullptr, "SetGyroscopeOdr"},
67 {152, nullptr, "GetWhoAmI"},
68 {201, nullptr, "ActivateFirmwareUpdate"},
69 {202, nullptr, "DeactivateFirmwareUpdate"},
70 {203, nullptr, "StartFirmwareUpdate"},
71 {204, nullptr, "GetFirmwareUpdateStage"},
72 {205, nullptr, "GetFirmwareVersion"},
73 {206, nullptr, "GetDestinationFirmwareVersion"},
74 {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
75 {208, nullptr, "StartFirmwareUpdateForRevert"},
76 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
77 {210, nullptr, "IsFirmwareUpdatingDevice"},
78 {211, nullptr, "StartFirmwareUpdateIndividual"},
79 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
80 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
81 {221, nullptr, "UpdateControllerColor"},
82 {222, nullptr, "ConnectUsbPadsAsync"},
83 {223, nullptr, "DisconnectUsbPadsAsync"},
84 {224, nullptr, "UpdateDesignInfo"},
85 {225, nullptr, "GetUniquePadDriverState"},
86 {226, nullptr, "GetSixAxisSensorDriverStates"},
87 {227, nullptr, "GetRxPacketHistory"},
88 {228, nullptr, "AcquireOperationEventHandle"},
89 {229, nullptr, "ReadSerialFlash"},
90 {230, nullptr, "WriteSerialFlash"},
91 {231, nullptr, "GetOperationResult"},
92 {232, nullptr, "EnableShipmentMode"},
93 {233, nullptr, "ClearPairingInfo"},
94 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
95 {235, nullptr, "EnableAnalogStickPower"},
96 {236, nullptr, "RequestKuinaUartClockCal"},
97 {237, nullptr, "GetKuinaUartClockCal"},
98 {238, nullptr, "SetKuinaUartClockTrim"},
99 {239, nullptr, "KuinaLoopbackTest"},
100 {240, nullptr, "RequestBatteryVoltage"},
101 {241, nullptr, "GetBatteryVoltage"},
102 {242, nullptr, "GetUniquePadPowerInfo"},
103 {243, nullptr, "RebootUniquePad"},
104 {244, nullptr, "RequestKuinaFirmwareVersion"},
105 {245, nullptr, "GetKuinaFirmwareVersion"},
106 {246, nullptr, "GetVidPid"},
107 {247, nullptr, "GetAnalogStickCalibrationValue"},
108 {248, nullptr, "GetUniquePadIdsFull"},
109 {249, nullptr, "ConnectUniquePad"},
110 {250, nullptr, "IsVirtual"},
111 {251, nullptr, "GetAnalogStickModuleParam"},
112 {301, nullptr, "GetAbstractedPadHandles"},
113 {302, nullptr, "GetAbstractedPadState"},
114 {303, nullptr, "GetAbstractedPadsState"},
115 {321, nullptr, "SetAutoPilotVirtualPadState"},
116 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
117 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
118 {324, nullptr, "AttachHdlsWorkBuffer"},
119 {325, nullptr, "ReleaseHdlsWorkBuffer"},
120 {326, nullptr, "DumpHdlsNpadAssignmentState"},
121 {327, nullptr, "DumpHdlsStates"},
122 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
123 {329, nullptr, "ApplyHdlsStateList"},
124 {330, nullptr, "AttachHdlsVirtualDevice"},
125 {331, nullptr, "DetachHdlsVirtualDevice"},
126 {332, nullptr, "SetHdlsState"},
127 {350, nullptr, "AddRegisteredDevice"},
128 {400, nullptr, "DisableExternalMcuOnNxDevice"},
129 {401, nullptr, "DisableRailDeviceFiltering"},
130 {402, nullptr, "EnableWiredPairing"},
131 {403, nullptr, "EnableShipmentModeAutoClear"},
132 {404, nullptr, "SetRailEnabled"},
133 {500, nullptr, "SetFactoryInt"},
134 {501, nullptr, "IsFactoryBootEnabled"},
135 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
136 {551, nullptr, "GetAnalogStickModelData"},
137 {552, nullptr, "ResetAnalogStickModelData"},
138 {600, nullptr, "ConvertPadState"},
139 {650, nullptr, "AddButtonPlayData"},
140 {651, nullptr, "StartButtonPlayData"},
141 {652, nullptr, "StopButtonPlayData"},
142 {2000, nullptr, "DeactivateDigitizer"},
143 {2001, nullptr, "SetDigitizerAutoPilotState"},
144 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
145 {2002, nullptr, "ReloadFirmwareDebugSettings"},
146 };
147 // clang-format on
148
149 RegisterHandlers(functions);
150}
151
152IHidDebugServer::~IHidDebugServer() = default;
153
154std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
155 resource_manager->Initialize();
156 return resource_manager;
157}
158
159} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h
new file mode 100644
index 000000000..406db2211
--- /dev/null
+++ b/src/core/hle/service/hid/hid_debug_server.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class ResourceManager;
14
15class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
16public:
17 explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
18 ~IHidDebugServer() override;
19
20private:
21 std::shared_ptr<ResourceManager> GetResourceManager();
22
23 std::shared_ptr<ResourceManager> resource_manager;
24};
25
26} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp
new file mode 100644
index 000000000..59bd6825c
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_firmware_settings.h"
5
6namespace Service::HID {
7
8HidFirmwareSettings::HidFirmwareSettings() {
9 LoadSettings(true);
10}
11
12void HidFirmwareSettings::Reload() {
13 LoadSettings(true);
14}
15
16void HidFirmwareSettings::LoadSettings(bool reload_config) {
17 if (is_initalized && !reload_config) {
18 return;
19 }
20
21 // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
22
23 is_debug_pad_enabled = true;
24 is_device_managed = true;
25 is_touch_i2c_managed = is_device_managed;
26 is_future_devices_emulated = false;
27 is_mcu_hardware_error_emulated = false;
28 is_rail_enabled = true;
29 is_firmware_update_failure_emulated = false;
30 is_firmware_update_failure = {};
31 is_ble_disabled = false;
32 is_dscale_disabled = false;
33 is_handheld_forced = true;
34 features_per_id_disabled = {};
35 is_touch_firmware_auto_update_disabled = false;
36 is_initalized = true;
37}
38
39bool HidFirmwareSettings::IsDebugPadEnabled() {
40 LoadSettings(false);
41 return is_debug_pad_enabled;
42}
43
44bool HidFirmwareSettings::IsDeviceManaged() {
45 LoadSettings(false);
46 return is_device_managed;
47}
48
49bool HidFirmwareSettings::IsEmulateFutureDevice() {
50 LoadSettings(false);
51 return is_future_devices_emulated;
52}
53
54bool HidFirmwareSettings::IsTouchI2cManaged() {
55 LoadSettings(false);
56 return is_touch_i2c_managed;
57}
58
59bool HidFirmwareSettings::IsHandheldForced() {
60 LoadSettings(false);
61 return is_handheld_forced;
62}
63
64bool HidFirmwareSettings::IsRailEnabled() {
65 LoadSettings(false);
66 return is_rail_enabled;
67}
68
69bool HidFirmwareSettings::IsHardwareErrorEmulated() {
70 LoadSettings(false);
71 return is_mcu_hardware_error_emulated;
72}
73
74bool HidFirmwareSettings::IsBleDisabled() {
75 LoadSettings(false);
76 return is_ble_disabled;
77}
78
79bool HidFirmwareSettings::IsDscaleDisabled() {
80 LoadSettings(false);
81 return is_dscale_disabled;
82}
83
84bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
85 LoadSettings(false);
86 return is_touch_firmware_auto_update_disabled;
87}
88
89HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
90 LoadSettings(false);
91 return is_firmware_update_failure;
92}
93
94HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
95 LoadSettings(false);
96 return features_per_id_disabled;
97}
98
99} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h
new file mode 100644
index 000000000..6c10c440b
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::HID {
9
10/// Loads firmware config from nn::settings::fwdbg
11class HidFirmwareSettings {
12public:
13 using FirmwareSetting = std::array<u8, 4>;
14 using FeaturesPerId = std::array<bool, 0xA8>;
15
16 HidFirmwareSettings();
17
18 void Reload();
19 void LoadSettings(bool reload_config);
20
21 bool IsDebugPadEnabled();
22 bool IsDeviceManaged();
23 bool IsEmulateFutureDevice();
24 bool IsTouchI2cManaged();
25 bool IsHandheldForced();
26 bool IsRailEnabled();
27 bool IsHardwareErrorEmulated();
28 bool IsBleDisabled();
29 bool IsDscaleDisabled();
30 bool IsTouchAutoUpdateDisabled();
31
32 FirmwareSetting GetFirmwareUpdateFailure();
33 FeaturesPerId FeaturesDisabledPerId();
34
35private:
36 bool is_initalized{};
37
38 // Debug settings
39 bool is_debug_pad_enabled{};
40 bool is_device_managed{};
41 bool is_touch_i2c_managed{};
42 bool is_future_devices_emulated{};
43 bool is_mcu_hardware_error_emulated{};
44 bool is_rail_enabled{};
45 bool is_firmware_update_failure_emulated{};
46 bool is_ble_disabled{};
47 bool is_dscale_disabled{};
48 bool is_handheld_forced{};
49 bool is_touch_firmware_auto_update_disabled{};
50 FirmwareSetting is_firmware_update_failure{};
51 FeaturesPerId features_per_id_disabled{};
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
new file mode 100644
index 000000000..583142e35
--- /dev/null
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -0,0 +1,2371 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <array>
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/hid/hid_core.h"
9#include "core/hle/kernel/k_shared_memory.h"
10#include "core/hle/kernel/k_transfer_memory.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/hid/errors.h"
13#include "core/hle/service/hid/hid_firmware_settings.h"
14#include "core/hle/service/hid/hid_server.h"
15#include "core/hle/service/hid/hid_util.h"
16#include "core/hle/service/hid/resource_manager.h"
17#include "core/hle/service/ipc_helpers.h"
18#include "core/memory.h"
19
20#include "core/hle/service/hid/controllers/console_six_axis.h"
21#include "core/hle/service/hid/controllers/controller_base.h"
22#include "core/hle/service/hid/controllers/debug_pad.h"
23#include "core/hle/service/hid/controllers/gesture.h"
24#include "core/hle/service/hid/controllers/keyboard.h"
25#include "core/hle/service/hid/controllers/mouse.h"
26#include "core/hle/service/hid/controllers/npad.h"
27#include "core/hle/service/hid/controllers/palma.h"
28#include "core/hle/service/hid/controllers/seven_six_axis.h"
29#include "core/hle/service/hid/controllers/six_axis.h"
30#include "core/hle/service/hid/controllers/touchscreen.h"
31
32namespace Service::HID {
33
34class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
35public:
36 explicit IActiveVibrationDeviceList(Core::System& system_,
37 std::shared_ptr<ResourceManager> resource)
38 : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
39 // clang-format off
40 static const FunctionInfo functions[] = {
41 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
42 };
43 // clang-format on
44
45 RegisterHandlers(functions);
46 }
47
48private:
49 void InitializeVibrationDevice(HLERequestContext& ctx) {
50 IPC::RequestParser rp{ctx};
51 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
52
53 if (resource_manager != nullptr) {
54 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
55 }
56
57 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
58 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
59 vibration_device_handle.device_index);
60
61 IPC::ResponseBuilder rb{ctx, 2};
62 rb.Push(ResultSuccess);
63 }
64
65 std::shared_ptr<ResourceManager> resource_manager;
66};
67
68IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
69 std::shared_ptr<HidFirmwareSettings> settings)
70 : ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} {
71 // clang-format off
72 static const FunctionInfo functions[] = {
73 {0, &IHidServer::CreateAppletResource, "CreateAppletResource"},
74 {1, &IHidServer::ActivateDebugPad, "ActivateDebugPad"},
75 {11, &IHidServer::ActivateTouchScreen, "ActivateTouchScreen"},
76 {21, &IHidServer::ActivateMouse, "ActivateMouse"},
77 {26, nullptr, "ActivateDebugMouse"},
78 {31, &IHidServer::ActivateKeyboard, "ActivateKeyboard"},
79 {32, &IHidServer::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
80 {40, &IHidServer::AcquireXpadIdEventHandle, "AcquireXpadIdEventHandle"},
81 {41, &IHidServer::ReleaseXpadIdEventHandle, "ReleaseXpadIdEventHandle"},
82 {51, &IHidServer::ActivateXpad, "ActivateXpad"},
83 {55, &IHidServer::GetXpadIds, "GetXpadIds"},
84 {56, &IHidServer::ActivateJoyXpad, "ActivateJoyXpad"},
85 {58, &IHidServer::GetJoyXpadLifoHandle, "GetJoyXpadLifoHandle"},
86 {59, &IHidServer::GetJoyXpadIds, "GetJoyXpadIds"},
87 {60, &IHidServer::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
88 {61, &IHidServer::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
89 {62, &IHidServer::GetSixAxisSensorLifoHandle, "GetSixAxisSensorLifoHandle"},
90 {63, &IHidServer::ActivateJoySixAxisSensor, "ActivateJoySixAxisSensor"},
91 {64, &IHidServer::DeactivateJoySixAxisSensor, "DeactivateJoySixAxisSensor"},
92 {65, &IHidServer::GetJoySixAxisSensorLifoHandle, "GetJoySixAxisSensorLifoHandle"},
93 {66, &IHidServer::StartSixAxisSensor, "StartSixAxisSensor"},
94 {67, &IHidServer::StopSixAxisSensor, "StopSixAxisSensor"},
95 {68, &IHidServer::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
96 {69, &IHidServer::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
97 {70, &IHidServer::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
98 {71, &IHidServer::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
99 {72, &IHidServer::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
100 {73, nullptr, "SetAccelerometerParameters"},
101 {74, nullptr, "GetAccelerometerParameters"},
102 {75, nullptr, "ResetAccelerometerParameters"},
103 {76, nullptr, "SetAccelerometerPlayMode"},
104 {77, nullptr, "GetAccelerometerPlayMode"},
105 {78, nullptr, "ResetAccelerometerPlayMode"},
106 {79, &IHidServer::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
107 {80, &IHidServer::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
108 {81, &IHidServer::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
109 {82, &IHidServer::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
110 {83, &IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
111 {84, &IHidServer::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
112 {85, &IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
113 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
114 {87, &IHidServer::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
115 {88, &IHidServer::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
116 {89, &IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
117 {91, &IHidServer::ActivateGesture, "ActivateGesture"},
118 {100, &IHidServer::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
119 {101, &IHidServer::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
120 {102, &IHidServer::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
121 {103, &IHidServer::ActivateNpad, "ActivateNpad"},
122 {104, &IHidServer::DeactivateNpad, "DeactivateNpad"},
123 {106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
124 {107, &IHidServer::DisconnectNpad, "DisconnectNpad"},
125 {108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"},
126 {109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
127 {120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
128 {121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
129 {122, &IHidServer::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
130 {123, &IHidServer::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
131 {124, &IHidServer::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
132 {125, &IHidServer::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
133 {126, &IHidServer::StartLrAssignmentMode, "StartLrAssignmentMode"},
134 {127, &IHidServer::StopLrAssignmentMode, "StopLrAssignmentMode"},
135 {128, &IHidServer::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
136 {129, &IHidServer::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
137 {130, &IHidServer::SwapNpadAssignment, "SwapNpadAssignment"},
138 {131, &IHidServer::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
139 {132, &IHidServer::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
140 {133, &IHidServer::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
141 {134, &IHidServer::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
142 {135, &IHidServer::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
143 {136, &IHidServer::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
144 {200, &IHidServer::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
145 {201, &IHidServer::SendVibrationValue, "SendVibrationValue"},
146 {202, &IHidServer::GetActualVibrationValue, "GetActualVibrationValue"},
147 {203, &IHidServer::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
148 {204, &IHidServer::PermitVibration, "PermitVibration"},
149 {205, &IHidServer::IsVibrationPermitted, "IsVibrationPermitted"},
150 {206, &IHidServer::SendVibrationValues, "SendVibrationValues"},
151 {207, &IHidServer::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
152 {208, &IHidServer::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
153 {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
154 {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
155 {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
156 {212, nullptr, "SendVibrationValueInBool"},
157 {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
158 {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
159 {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
160 {303, &IHidServer::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
161 {304, &IHidServer::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
162 {305, &IHidServer::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
163 {306, &IHidServer::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
164 {307, &IHidServer::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
165 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
166 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
167 {310, &IHidServer::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
168 {400, &IHidServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
169 {401, nullptr, "EnableUsbFullKeyController"},
170 {402, nullptr, "IsUsbFullKeyControllerConnected"},
171 {403, nullptr, "HasBattery"},
172 {404, nullptr, "HasLeftRightBattery"},
173 {405, nullptr, "GetNpadInterfaceType"},
174 {406, nullptr, "GetNpadLeftRightInterfaceType"},
175 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
176 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
177 {500, &IHidServer::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
178 {501, &IHidServer::InitializePalma, "InitializePalma"},
179 {502, &IHidServer::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
180 {503, &IHidServer::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
181 {504, &IHidServer::PlayPalmaActivity, "PlayPalmaActivity"},
182 {505, &IHidServer::SetPalmaFrModeType, "SetPalmaFrModeType"},
183 {506, &IHidServer::ReadPalmaStep, "ReadPalmaStep"},
184 {507, &IHidServer::EnablePalmaStep, "EnablePalmaStep"},
185 {508, &IHidServer::ResetPalmaStep, "ResetPalmaStep"},
186 {509, &IHidServer::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
187 {510, &IHidServer::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
188 {511, &IHidServer::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
189 {512, &IHidServer::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
190 {513, &IHidServer::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
191 {514, &IHidServer::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
192 {515, &IHidServer::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
193 {516, &IHidServer::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
194 {517, &IHidServer::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
195 {518, &IHidServer::SuspendPalmaFeature, "SuspendPalmaFeature"},
196 {519, &IHidServer::GetPalmaOperationResult, "GetPalmaOperationResult"},
197 {520, &IHidServer::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
198 {521, &IHidServer::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
199 {522, &IHidServer::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
200 {523, &IHidServer::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
201 {524, &IHidServer::PairPalma, "PairPalma"},
202 {525, &IHidServer::SetPalmaBoostMode, "SetPalmaBoostMode"},
203 {526, &IHidServer::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
204 {527, &IHidServer::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
205 {528, &IHidServer::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
206 {529, &IHidServer::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
207 {1000, &IHidServer::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
208 {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
209 {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
210 {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
211 {2000, nullptr, "ActivateDigitizer"},
212 };
213 // clang-format on
214
215 RegisterHandlers(functions);
216}
217
218IHidServer::~IHidServer() = default;
219
220void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
221 IPC::RequestParser rp{ctx};
222 const auto applet_resource_user_id{rp.Pop<u64>()};
223
224 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
225
226 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
227 rb.Push(ResultSuccess);
228 rb.PushIpcInterface<IAppletResource>(system, resource_manager);
229}
230
231void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
232 IPC::RequestParser rp{ctx};
233 const auto applet_resource_user_id{rp.Pop<u64>()};
234
235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
236
237 Result result = ResultSuccess;
238 auto debug_pad = GetResourceManager()->GetDebugPad();
239
240 if (!firmware_settings->IsDeviceManaged()) {
241 result = debug_pad->Activate();
242 }
243
244 if (result.IsSuccess()) {
245 result = debug_pad->Activate(applet_resource_user_id);
246 }
247
248 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(result);
250}
251
252void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) {
253 IPC::RequestParser rp{ctx};
254 const auto applet_resource_user_id{rp.Pop<u64>()};
255
256 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
257
258 Result result = ResultSuccess;
259 auto touch_screen = GetResourceManager()->GetTouchScreen();
260
261 if (!firmware_settings->IsDeviceManaged()) {
262 result = touch_screen->Activate();
263 }
264
265 if (result.IsSuccess()) {
266 result = touch_screen->Activate(applet_resource_user_id);
267 }
268
269 IPC::ResponseBuilder rb{ctx, 2};
270 rb.Push(result);
271}
272
273void IHidServer::ActivateMouse(HLERequestContext& ctx) {
274 IPC::RequestParser rp{ctx};
275 const auto applet_resource_user_id{rp.Pop<u64>()};
276
277 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
278
279 Result result = ResultSuccess;
280 auto mouse = GetResourceManager()->GetMouse();
281
282 if (!firmware_settings->IsDeviceManaged()) {
283 result = mouse->Activate();
284 }
285
286 if (result.IsSuccess()) {
287 result = mouse->Activate(applet_resource_user_id);
288 }
289
290 IPC::ResponseBuilder rb{ctx, 2};
291 rb.Push(result);
292}
293
294void IHidServer::ActivateKeyboard(HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 const auto applet_resource_user_id{rp.Pop<u64>()};
297
298 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
299
300 Result result = ResultSuccess;
301 auto keyboard = GetResourceManager()->GetKeyboard();
302
303 if (!firmware_settings->IsDeviceManaged()) {
304 result = keyboard->Activate();
305 }
306
307 if (result.IsSuccess()) {
308 result = keyboard->Activate(applet_resource_user_id);
309 }
310
311 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(result);
313}
314
315void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
316 IPC::RequestParser rp{ctx};
317 const auto flags{rp.Pop<u32>()};
318
319 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323}
324
325void IHidServer::AcquireXpadIdEventHandle(HLERequestContext& ctx) {
326 IPC::RequestParser rp{ctx};
327 const auto applet_resource_user_id{rp.Pop<u64>()};
328
329 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
330
331 // This function has been stubbed since 10.0.0+
332
333 IPC::ResponseBuilder rb{ctx, 2, 1};
334 rb.Push(ResultSuccess);
335 // Handle returned is null here
336}
337
338void IHidServer::ReleaseXpadIdEventHandle(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340 const auto applet_resource_user_id{rp.Pop<u64>()};
341
342 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
343
344 // This function has been stubbed since 10.0.0+
345
346 IPC::ResponseBuilder rb{ctx, 2};
347 rb.Push(ResultSuccess);
348}
349
350void IHidServer::ActivateXpad(HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 struct Parameters {
353 u32 basic_xpad_id;
354 INSERT_PADDING_WORDS_NOINIT(1);
355 u64 applet_resource_user_id;
356 };
357 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
358
359 const auto parameters{rp.PopRaw<Parameters>()};
360
361 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
362 parameters.basic_xpad_id, parameters.applet_resource_user_id);
363
364 // This function has been stubbed since 10.0.0+
365
366 IPC::ResponseBuilder rb{ctx, 2};
367 rb.Push(ResultSuccess);
368}
369
370void IHidServer::GetXpadIds(HLERequestContext& ctx) {
371 LOG_DEBUG(Service_HID, "called");
372
373 // This function has been hardcoded since 10.0.0+
374 const std::array<u32, 4> basic_xpad_id{0, 1, 2, 3};
375 ctx.WriteBuffer(basic_xpad_id);
376
377 IPC::ResponseBuilder rb{ctx, 4};
378 rb.Push(ResultSuccess);
379 rb.Push<s64>(basic_xpad_id.size());
380}
381
382void IHidServer::ActivateJoyXpad(HLERequestContext& ctx) {
383 IPC::RequestParser rp{ctx};
384 const auto joy_xpad_id{rp.Pop<u32>()};
385
386 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
387
388 // This function has been stubbed since 10.0.0+
389
390 IPC::ResponseBuilder rb{ctx, 2};
391 rb.Push(ResultSuccess);
392}
393
394void IHidServer::GetJoyXpadLifoHandle(HLERequestContext& ctx) {
395 IPC::RequestParser rp{ctx};
396 const auto joy_xpad_id{rp.Pop<u32>()};
397
398 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
399
400 // This function has been stubbed since 10.0.0+
401
402 IPC::ResponseBuilder rb{ctx, 2, 1};
403 rb.Push(ResultSuccess);
404 // Handle returned is null here
405}
406
407void IHidServer::GetJoyXpadIds(HLERequestContext& ctx) {
408 LOG_DEBUG(Service_HID, "called");
409
410 // This function has been hardcoded since 10.0.0+
411 const s64 basic_xpad_id_count{};
412
413 IPC::ResponseBuilder rb{ctx, 4};
414 rb.Push(ResultSuccess);
415 rb.Push(basic_xpad_id_count);
416}
417
418void IHidServer::ActivateSixAxisSensor(HLERequestContext& ctx) {
419 IPC::RequestParser rp{ctx};
420 const auto joy_xpad_id{rp.Pop<u32>()};
421
422 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
423
424 // This function has been stubbed since 10.0.0+
425
426 IPC::ResponseBuilder rb{ctx, 2};
427 rb.Push(ResultSuccess);
428}
429
430void IHidServer::DeactivateSixAxisSensor(HLERequestContext& ctx) {
431 IPC::RequestParser rp{ctx};
432 const auto joy_xpad_id{rp.Pop<u32>()};
433
434 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
435
436 // This function has been stubbed since 10.0.0+
437
438 IPC::ResponseBuilder rb{ctx, 2, 1};
439 rb.Push(ResultSuccess);
440}
441
442void IHidServer::GetSixAxisSensorLifoHandle(HLERequestContext& ctx) {
443 IPC::RequestParser rp{ctx};
444 const auto joy_xpad_id{rp.Pop<u32>()};
445
446 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
447
448 // This function has been stubbed since 10.0.0+
449
450 IPC::ResponseBuilder rb{ctx, 2};
451 rb.Push(ResultSuccess);
452}
453
454void IHidServer::ActivateJoySixAxisSensor(HLERequestContext& ctx) {
455 IPC::RequestParser rp{ctx};
456 const auto joy_xpad_id{rp.Pop<u32>()};
457
458 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
459
460 // This function has been stubbed since 10.0.0+
461
462 IPC::ResponseBuilder rb{ctx, 2};
463 rb.Push(ResultSuccess);
464}
465
466void IHidServer::DeactivateJoySixAxisSensor(HLERequestContext& ctx) {
467 IPC::RequestParser rp{ctx};
468 const auto joy_xpad_id{rp.Pop<u32>()};
469
470 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
471
472 // This function has been stubbed since 10.0.0+
473
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(ResultSuccess);
476}
477
478void IHidServer::GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx) {
479 IPC::RequestParser rp{ctx};
480 const auto joy_xpad_id{rp.Pop<u32>()};
481
482 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
483
484 // This function has been stubbed since 10.0.0+
485
486 IPC::ResponseBuilder rb{ctx, 2, 1};
487 rb.Push(ResultSuccess);
488 // Handle returned is null here
489}
490
491void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) {
492 IPC::RequestParser rp{ctx};
493 struct Parameters {
494 Core::HID::SixAxisSensorHandle sixaxis_handle;
495 INSERT_PADDING_WORDS_NOINIT(1);
496 u64 applet_resource_user_id;
497 };
498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
499
500 const auto parameters{rp.PopRaw<Parameters>()};
501
502 auto six_axis = GetResourceManager()->GetSixAxis();
503 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true);
504
505 LOG_DEBUG(Service_HID,
506 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
507 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
508 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
509
510 IPC::ResponseBuilder rb{ctx, 2};
511 rb.Push(result);
512}
513
514void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) {
515 IPC::RequestParser rp{ctx};
516 struct Parameters {
517 Core::HID::SixAxisSensorHandle sixaxis_handle;
518 INSERT_PADDING_WORDS_NOINIT(1);
519 u64 applet_resource_user_id;
520 };
521 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
522
523 const auto parameters{rp.PopRaw<Parameters>()};
524
525 auto six_axis = GetResourceManager()->GetSixAxis();
526 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false);
527
528 LOG_DEBUG(Service_HID,
529 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
530 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
531 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
532
533 IPC::ResponseBuilder rb{ctx, 2};
534 rb.Push(result);
535}
536
537void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
538 IPC::RequestParser rp{ctx};
539 struct Parameters {
540 Core::HID::SixAxisSensorHandle sixaxis_handle;
541 INSERT_PADDING_WORDS_NOINIT(1);
542 u64 applet_resource_user_id;
543 };
544 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
545
546 const auto parameters{rp.PopRaw<Parameters>()};
547
548 bool is_enabled{};
549 auto six_axis = GetResourceManager()->GetSixAxis();
550 const auto result =
551 six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
552
553 LOG_DEBUG(Service_HID,
554 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
555 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
556 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
557
558 IPC::ResponseBuilder rb{ctx, 3};
559 rb.Push(result);
560 rb.Push(is_enabled);
561}
562
563void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
564 IPC::RequestParser rp{ctx};
565 struct Parameters {
566 bool enable_sixaxis_sensor_fusion;
567 INSERT_PADDING_BYTES_NOINIT(3);
568 Core::HID::SixAxisSensorHandle sixaxis_handle;
569 u64 applet_resource_user_id;
570 };
571 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
572
573 const auto parameters{rp.PopRaw<Parameters>()};
574
575 auto six_axis = GetResourceManager()->GetSixAxis();
576 const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle,
577 parameters.enable_sixaxis_sensor_fusion);
578
579 LOG_DEBUG(Service_HID,
580 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
581 "device_index={}, applet_resource_user_id={}",
582 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
583 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
584 parameters.applet_resource_user_id);
585
586 IPC::ResponseBuilder rb{ctx, 2};
587 rb.Push(result);
588}
589
590void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
591 IPC::RequestParser rp{ctx};
592 struct Parameters {
593 Core::HID::SixAxisSensorHandle sixaxis_handle;
594 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
595 INSERT_PADDING_WORDS_NOINIT(1);
596 u64 applet_resource_user_id;
597 };
598 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
599
600 const auto parameters{rp.PopRaw<Parameters>()};
601
602 auto six_axis = GetResourceManager()->GetSixAxis();
603 const auto result =
604 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
605
606 LOG_DEBUG(Service_HID,
607 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
608 "parameter2={}, applet_resource_user_id={}",
609 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
610 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
611 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
612
613 IPC::ResponseBuilder rb{ctx, 2};
614 rb.Push(result);
615}
616
617void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
618 IPC::RequestParser rp{ctx};
619 struct Parameters {
620 Core::HID::SixAxisSensorHandle sixaxis_handle;
621 INSERT_PADDING_WORDS_NOINIT(1);
622 u64 applet_resource_user_id;
623 };
624 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
625
626 const auto parameters{rp.PopRaw<Parameters>()};
627
628 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
629 auto six_axis = GetResourceManager()->GetSixAxis();
630 const auto result =
631 six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
632
633 LOG_DEBUG(Service_HID,
634 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
635 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
636 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
637
638 IPC::ResponseBuilder rb{ctx, 4};
639 rb.Push(result);
640 rb.PushRaw(fusion_parameters);
641}
642
643void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
644 IPC::RequestParser rp{ctx};
645 struct Parameters {
646 Core::HID::SixAxisSensorHandle sixaxis_handle;
647 INSERT_PADDING_WORDS_NOINIT(1);
648 u64 applet_resource_user_id;
649 };
650 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
651
652 const auto parameters{rp.PopRaw<Parameters>()};
653
654 // Since these parameters are unknown just use what HW outputs
655 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
656 .parameter1 = 0.03f,
657 .parameter2 = 0.4f,
658 };
659 auto six_axis = GetResourceManager()->GetSixAxis();
660 const auto result1 =
661 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
662 const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
663
664 LOG_DEBUG(Service_HID,
665 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
666 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
667 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
668
669 IPC::ResponseBuilder rb{ctx, 2};
670 if (result1.IsError()) {
671 rb.Push(result1);
672 return;
673 }
674 rb.Push(result2);
675}
676
677void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
678 IPC::RequestParser rp{ctx};
679 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
680 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
681 const auto applet_resource_user_id{rp.Pop<u64>()};
682
683 auto six_axis = GetResourceManager()->GetSixAxis();
684 const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
685
686 LOG_DEBUG(Service_HID,
687 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
688 "applet_resource_user_id={}",
689 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
690 drift_mode, applet_resource_user_id);
691
692 IPC::ResponseBuilder rb{ctx, 2};
693 rb.Push(result);
694}
695
696void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
697 IPC::RequestParser rp{ctx};
698 struct Parameters {
699 Core::HID::SixAxisSensorHandle sixaxis_handle;
700 INSERT_PADDING_WORDS_NOINIT(1);
701 u64 applet_resource_user_id;
702 };
703 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
704
705 const auto parameters{rp.PopRaw<Parameters>()};
706
707 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
708 auto six_axis = GetResourceManager()->GetSixAxis();
709 const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
710
711 LOG_DEBUG(Service_HID,
712 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
713 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
714 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
715
716 IPC::ResponseBuilder rb{ctx, 3};
717 rb.Push(result);
718 rb.PushEnum(drift_mode);
719}
720
721void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
722 IPC::RequestParser rp{ctx};
723 struct Parameters {
724 Core::HID::SixAxisSensorHandle sixaxis_handle;
725 INSERT_PADDING_WORDS_NOINIT(1);
726 u64 applet_resource_user_id;
727 };
728 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
729
730 const auto parameters{rp.PopRaw<Parameters>()};
731
732 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
733 auto six_axis = GetResourceManager()->GetSixAxis();
734 const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
735
736 LOG_DEBUG(Service_HID,
737 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
738 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
739 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
740
741 IPC::ResponseBuilder rb{ctx, 2};
742 rb.Push(result);
743}
744
745void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
746 IPC::RequestParser rp{ctx};
747 struct Parameters {
748 Core::HID::SixAxisSensorHandle sixaxis_handle;
749 INSERT_PADDING_WORDS_NOINIT(1);
750 u64 applet_resource_user_id;
751 };
752 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
753
754 const auto parameters{rp.PopRaw<Parameters>()};
755
756 bool is_at_rest{};
757 auto six_axis = GetResourceManager()->GetSixAxis();
758 six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
759
760 LOG_DEBUG(Service_HID,
761 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
762 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
763 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
764
765 IPC::ResponseBuilder rb{ctx, 3};
766 rb.Push(ResultSuccess);
767 rb.Push(is_at_rest);
768}
769
770void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) {
771 IPC::RequestParser rp{ctx};
772 struct Parameters {
773 Core::HID::SixAxisSensorHandle sixaxis_handle;
774 INSERT_PADDING_WORDS_NOINIT(1);
775 u64 applet_resource_user_id;
776 };
777 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
778
779 const auto parameters{rp.PopRaw<Parameters>()};
780
781 bool is_firmware_available{};
782 auto controller = GetResourceManager()->GetNpad();
783 controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
784 is_firmware_available);
785
786 LOG_WARNING(
787 Service_HID,
788 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
789 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
790 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
791
792 IPC::ResponseBuilder rb{ctx, 3};
793 rb.Push(ResultSuccess);
794 rb.Push(is_firmware_available);
795}
796
797void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) {
798 IPC::RequestParser rp{ctx};
799 struct Parameters {
800 bool enabled;
801 Core::HID::SixAxisSensorHandle sixaxis_handle;
802 u64 applet_resource_user_id;
803 };
804 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
805
806 const auto parameters{rp.PopRaw<Parameters>()};
807
808 auto six_axis = GetResourceManager()->GetSixAxis();
809 const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle,
810 parameters.enabled);
811
812 LOG_DEBUG(Service_HID,
813 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
814 "applet_resource_user_id={}",
815 parameters.enabled, parameters.sixaxis_handle.npad_type,
816 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
817 parameters.applet_resource_user_id);
818
819 IPC::ResponseBuilder rb{ctx, 2};
820 rb.Push(result);
821}
822
823void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) {
824 IPC::RequestParser rp{ctx};
825 struct Parameters {
826 Core::HID::SixAxisSensorHandle sixaxis_handle;
827 INSERT_PADDING_WORDS_NOINIT(1);
828 u64 applet_resource_user_id;
829 };
830 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
831
832 const auto parameters{rp.PopRaw<Parameters>()};
833
834 bool is_unaltered_sisxaxis_enabled{};
835 auto six_axis = GetResourceManager()->GetSixAxis();
836 const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled(
837 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
838
839 LOG_DEBUG(
840 Service_HID,
841 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
842 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
843 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
844
845 IPC::ResponseBuilder rb{ctx, 3};
846 rb.Push(result);
847 rb.Push(is_unaltered_sisxaxis_enabled);
848}
849
850void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
851 IPC::RequestParser rp{ctx};
852 struct Parameters {
853 Core::HID::SixAxisSensorHandle sixaxis_handle;
854 INSERT_PADDING_WORDS_NOINIT(1);
855 u64 applet_resource_user_id;
856 };
857 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
858
859 const auto parameters{rp.PopRaw<Parameters>()};
860
861 Core::HID::SixAxisSensorCalibrationParameter calibration{};
862 auto six_axis = GetResourceManager()->GetSixAxis();
863 const auto result =
864 six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
865
866 LOG_WARNING(
867 Service_HID,
868 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
869 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
870 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
871
872 if (result.IsSuccess()) {
873 ctx.WriteBuffer(calibration);
874 }
875
876 IPC::ResponseBuilder rb{ctx, 2};
877 rb.Push(result);
878}
879
880void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
881 IPC::RequestParser rp{ctx};
882 struct Parameters {
883 Core::HID::SixAxisSensorHandle sixaxis_handle;
884 INSERT_PADDING_WORDS_NOINIT(1);
885 u64 applet_resource_user_id;
886 };
887 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
888
889 const auto parameters{rp.PopRaw<Parameters>()};
890
891 Core::HID::SixAxisSensorIcInformation ic_information{};
892 auto six_axis = GetResourceManager()->GetSixAxis();
893 const auto result =
894 six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
895
896 LOG_WARNING(
897 Service_HID,
898 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
899 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
900 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
901
902 if (result.IsSuccess()) {
903 ctx.WriteBuffer(ic_information);
904 }
905
906 IPC::ResponseBuilder rb{ctx, 2};
907 rb.Push(result);
908}
909
910void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) {
911 IPC::RequestParser rp{ctx};
912 struct Parameters {
913 Core::HID::SixAxisSensorHandle sixaxis_handle;
914 INSERT_PADDING_WORDS_NOINIT(1);
915 u64 applet_resource_user_id;
916 };
917 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
918
919 const auto parameters{rp.PopRaw<Parameters>()};
920
921 auto controller = GetResourceManager()->GetNpad();
922 const auto result =
923 controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
924
925 LOG_WARNING(
926 Service_HID,
927 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
928 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
929 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
930
931 IPC::ResponseBuilder rb{ctx, 2};
932 rb.Push(result);
933}
934
935void IHidServer::ActivateGesture(HLERequestContext& ctx) {
936 IPC::RequestParser rp{ctx};
937 struct Parameters {
938 u32 basic_gesture_id;
939 INSERT_PADDING_WORDS_NOINIT(1);
940 u64 applet_resource_user_id;
941 };
942 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
943
944 const auto parameters{rp.PopRaw<Parameters>()};
945
946 LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
947 parameters.basic_gesture_id, parameters.applet_resource_user_id);
948
949 Result result = ResultSuccess;
950 auto gesture = GetResourceManager()->GetGesture();
951
952 if (!firmware_settings->IsDeviceManaged()) {
953 result = gesture->Activate();
954 }
955
956 if (result.IsSuccess()) {
957 // TODO: Use gesture id here
958 result = gesture->Activate(parameters.applet_resource_user_id);
959 }
960
961 IPC::ResponseBuilder rb{ctx, 2};
962 rb.Push(result);
963}
964
965void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
966 IPC::RequestParser rp{ctx};
967 struct Parameters {
968 Core::HID::NpadStyleSet supported_styleset;
969 INSERT_PADDING_WORDS_NOINIT(1);
970 u64 applet_resource_user_id;
971 };
972 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
973
974 const auto parameters{rp.PopRaw<Parameters>()};
975
976 GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset});
977
978 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
979 parameters.supported_styleset, parameters.applet_resource_user_id);
980
981 IPC::ResponseBuilder rb{ctx, 2};
982 rb.Push(ResultSuccess);
983}
984
985void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
986 IPC::RequestParser rp{ctx};
987 const auto applet_resource_user_id{rp.Pop<u64>()};
988
989 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
990
991 IPC::ResponseBuilder rb{ctx, 3};
992 rb.Push(ResultSuccess);
993 rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw);
994}
995
996void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) {
997 IPC::RequestParser rp{ctx};
998 const auto applet_resource_user_id{rp.Pop<u64>()};
999
1000 const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer());
1001
1002 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1003
1004 IPC::ResponseBuilder rb{ctx, 2};
1005 rb.Push(result);
1006}
1007
1008void IHidServer::ActivateNpad(HLERequestContext& ctx) {
1009 IPC::RequestParser rp{ctx};
1010 const auto applet_resource_user_id{rp.Pop<u64>()};
1011
1012 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1013
1014 auto npad = GetResourceManager()->GetNpad();
1015
1016 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
1017 const Result result = npad->Activate(applet_resource_user_id);
1018
1019 IPC::ResponseBuilder rb{ctx, 2};
1020 rb.Push(result);
1021}
1022
1023void IHidServer::DeactivateNpad(HLERequestContext& ctx) {
1024 IPC::RequestParser rp{ctx};
1025 const auto applet_resource_user_id{rp.Pop<u64>()};
1026
1027 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1028
1029 // This function does nothing since 10.0.0+
1030
1031 IPC::ResponseBuilder rb{ctx, 2};
1032 rb.Push(ResultSuccess);
1033}
1034
1035void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1036 IPC::RequestParser rp{ctx};
1037 struct Parameters {
1038 Core::HID::NpadIdType npad_id;
1039 INSERT_PADDING_WORDS_NOINIT(1);
1040 u64 applet_resource_user_id;
1041 u64 unknown;
1042 };
1043 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1044
1045 const auto parameters{rp.PopRaw<Parameters>()};
1046
1047 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1048 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1049
1050 // Games expect this event to be signaled after calling this function
1051 GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id);
1052
1053 IPC::ResponseBuilder rb{ctx, 2, 1};
1054 rb.Push(ResultSuccess);
1055 rb.PushCopyObjects(
1056 GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id));
1057}
1058
1059void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
1060 IPC::RequestParser rp{ctx};
1061 struct Parameters {
1062 Core::HID::NpadIdType npad_id;
1063 INSERT_PADDING_WORDS_NOINIT(1);
1064 u64 applet_resource_user_id;
1065 };
1066 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1067
1068 const auto parameters{rp.PopRaw<Parameters>()};
1069
1070 auto controller = GetResourceManager()->GetNpad();
1071 controller->DisconnectNpad(parameters.npad_id);
1072
1073 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1074 parameters.applet_resource_user_id);
1075
1076 IPC::ResponseBuilder rb{ctx, 2};
1077 rb.Push(ResultSuccess);
1078}
1079
1080void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1081 IPC::RequestParser rp{ctx};
1082 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1083
1084 Core::HID::LedPattern pattern{0, 0, 0, 0};
1085 auto controller = GetResourceManager()->GetNpad();
1086 const auto result = controller->GetLedPattern(npad_id, pattern);
1087
1088 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1089
1090 IPC::ResponseBuilder rb{ctx, 4};
1091 rb.Push(result);
1092 rb.Push(pattern.raw);
1093}
1094
1095void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1096 IPC::RequestParser rp{ctx};
1097 struct Parameters {
1098 NPad::NpadRevision revision;
1099 INSERT_PADDING_WORDS_NOINIT(1);
1100 u64 applet_resource_user_id;
1101 };
1102 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1103
1104 const auto parameters{rp.PopRaw<Parameters>()};
1105
1106 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1107 parameters.applet_resource_user_id);
1108
1109 auto npad = GetResourceManager()->GetNpad();
1110
1111 // TODO: npad->SetRevision(applet_resource_user_id, revision);
1112 const auto result = npad->Activate(parameters.applet_resource_user_id);
1113
1114 IPC::ResponseBuilder rb{ctx, 2};
1115 rb.Push(result);
1116}
1117
1118void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
1119 IPC::RequestParser rp{ctx};
1120 const auto applet_resource_user_id{rp.Pop<u64>()};
1121 const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()};
1122
1123 GetResourceManager()->GetNpad()->SetHoldType(hold_type);
1124
1125 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1126 applet_resource_user_id, hold_type);
1127
1128 IPC::ResponseBuilder rb{ctx, 2};
1129 rb.Push(ResultSuccess);
1130}
1131
1132void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
1133 IPC::RequestParser rp{ctx};
1134 const auto applet_resource_user_id{rp.Pop<u64>()};
1135
1136 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1137
1138 IPC::ResponseBuilder rb{ctx, 4};
1139 rb.Push(ResultSuccess);
1140 rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType());
1141}
1142
1143void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
1144 IPC::RequestParser rp{ctx};
1145 struct Parameters {
1146 Core::HID::NpadIdType npad_id;
1147 INSERT_PADDING_WORDS_NOINIT(1);
1148 u64 applet_resource_user_id;
1149 };
1150 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1151
1152 const auto parameters{rp.PopRaw<Parameters>()};
1153
1154 Core::HID::NpadIdType new_npad_id{};
1155 auto controller = GetResourceManager()->GetNpad();
1156 controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left,
1157 NPad::NpadJoyAssignmentMode::Single);
1158
1159 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1160 parameters.applet_resource_user_id);
1161
1162 IPC::ResponseBuilder rb{ctx, 2};
1163 rb.Push(ResultSuccess);
1164}
1165
1166void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1167 IPC::RequestParser rp{ctx};
1168 struct Parameters {
1169 Core::HID::NpadIdType npad_id;
1170 INSERT_PADDING_WORDS_NOINIT(1);
1171 u64 applet_resource_user_id;
1172 NPad::NpadJoyDeviceType npad_joy_device_type;
1173 };
1174 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1175
1176 const auto parameters{rp.PopRaw<Parameters>()};
1177
1178 Core::HID::NpadIdType new_npad_id{};
1179 auto controller = GetResourceManager()->GetNpad();
1180 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1181 NPad::NpadJoyAssignmentMode::Single);
1182
1183 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1184 parameters.npad_id, parameters.applet_resource_user_id,
1185 parameters.npad_joy_device_type);
1186
1187 IPC::ResponseBuilder rb{ctx, 2};
1188 rb.Push(ResultSuccess);
1189}
1190
1191void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1192 IPC::RequestParser rp{ctx};
1193 struct Parameters {
1194 Core::HID::NpadIdType npad_id;
1195 INSERT_PADDING_WORDS_NOINIT(1);
1196 u64 applet_resource_user_id;
1197 };
1198 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1199
1200 const auto parameters{rp.PopRaw<Parameters>()};
1201
1202 Core::HID::NpadIdType new_npad_id{};
1203 auto controller = GetResourceManager()->GetNpad();
1204 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual);
1205
1206 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1207 parameters.applet_resource_user_id); // Spams a lot when controller applet is open
1208
1209 IPC::ResponseBuilder rb{ctx, 2};
1210 rb.Push(ResultSuccess);
1211}
1212
1213void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1214 IPC::RequestParser rp{ctx};
1215 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1216 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1217 const auto applet_resource_user_id{rp.Pop<u64>()};
1218
1219 auto controller = GetResourceManager()->GetNpad();
1220 const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1221
1222 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1223 npad_id_1, npad_id_2, applet_resource_user_id);
1224
1225 IPC::ResponseBuilder rb{ctx, 2};
1226 rb.Push(result);
1227}
1228
1229void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) {
1230 IPC::RequestParser rp{ctx};
1231 const auto applet_resource_user_id{rp.Pop<u64>()};
1232
1233 GetResourceManager()->GetNpad()->StartLRAssignmentMode();
1234
1235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1236
1237 IPC::ResponseBuilder rb{ctx, 2};
1238 rb.Push(ResultSuccess);
1239}
1240
1241void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1242 IPC::RequestParser rp{ctx};
1243 const auto applet_resource_user_id{rp.Pop<u64>()};
1244
1245 GetResourceManager()->GetNpad()->StopLRAssignmentMode();
1246
1247 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1248
1249 IPC::ResponseBuilder rb{ctx, 2};
1250 rb.Push(ResultSuccess);
1251}
1252
1253void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1254 IPC::RequestParser rp{ctx};
1255 const auto applet_resource_user_id{rp.Pop<u64>()};
1256 const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()};
1257
1258 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
1259
1260 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1261 applet_resource_user_id, activation_mode);
1262
1263 IPC::ResponseBuilder rb{ctx, 2};
1264 rb.Push(ResultSuccess);
1265}
1266
1267void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1268 IPC::RequestParser rp{ctx};
1269 const auto applet_resource_user_id{rp.Pop<u64>()};
1270
1271 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1272
1273 IPC::ResponseBuilder rb{ctx, 4};
1274 rb.Push(ResultSuccess);
1275 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode());
1276}
1277
1278void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
1279 IPC::RequestParser rp{ctx};
1280 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1281 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1282 const auto applet_resource_user_id{rp.Pop<u64>()};
1283
1284 auto controller = GetResourceManager()->GetNpad();
1285 const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2);
1286
1287 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1288 npad_id_1, npad_id_2, applet_resource_user_id);
1289
1290 IPC::ResponseBuilder rb{ctx, 2};
1291 rb.Push(result);
1292}
1293
1294void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
1295 IPC::RequestParser rp{ctx};
1296 struct Parameters {
1297 Core::HID::NpadIdType npad_id;
1298 INSERT_PADDING_WORDS_NOINIT(1);
1299 u64 applet_resource_user_id;
1300 };
1301 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1302
1303 const auto parameters{rp.PopRaw<Parameters>()};
1304
1305 bool is_enabled = false;
1306 auto controller = GetResourceManager()->GetNpad();
1307 const auto result =
1308 controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1309
1310 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1311 parameters.npad_id, parameters.applet_resource_user_id);
1312
1313 IPC::ResponseBuilder rb{ctx, 3};
1314 rb.Push(result);
1315 rb.Push(is_enabled);
1316}
1317
1318void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
1319 IPC::RequestParser rp{ctx};
1320 struct Parameters {
1321 bool is_enabled;
1322 INSERT_PADDING_BYTES_NOINIT(3);
1323 Core::HID::NpadIdType npad_id;
1324 u64 applet_resource_user_id;
1325 };
1326 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1327
1328 const auto parameters{rp.PopRaw<Parameters>()};
1329
1330 auto controller = GetResourceManager()->GetNpad();
1331 const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled(
1332 parameters.is_enabled, parameters.npad_id);
1333
1334 LOG_DEBUG(Service_HID,
1335 "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
1336 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
1337
1338 IPC::ResponseBuilder rb{ctx, 2};
1339 rb.Push(result);
1340}
1341
1342void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
1343 IPC::RequestParser rp{ctx};
1344 struct Parameters {
1345 Core::HID::NpadIdType npad_id;
1346 INSERT_PADDING_WORDS_NOINIT(1);
1347 u64 applet_resource_user_id;
1348 NPad::NpadJoyDeviceType npad_joy_device_type;
1349 };
1350 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1351
1352 const auto parameters{rp.PopRaw<Parameters>()};
1353
1354 Core::HID::NpadIdType new_npad_id{};
1355 auto controller = GetResourceManager()->GetNpad();
1356 const auto is_reassigned =
1357 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1358 NPad::NpadJoyAssignmentMode::Single);
1359
1360 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1361 parameters.npad_id, parameters.applet_resource_user_id,
1362 parameters.npad_joy_device_type);
1363
1364 IPC::ResponseBuilder rb{ctx, 4};
1365 rb.Push(ResultSuccess);
1366 rb.Push(is_reassigned);
1367 rb.PushEnum(new_npad_id);
1368}
1369
1370void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1371 IPC::RequestParser rp{ctx};
1372 struct Parameters {
1373 bool analog_stick_use_center_clamp;
1374 INSERT_PADDING_BYTES_NOINIT(7);
1375 u64 applet_resource_user_id;
1376 };
1377 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1378
1379 const auto parameters{rp.PopRaw<Parameters>()};
1380
1381 GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp(
1382 parameters.analog_stick_use_center_clamp);
1383
1384 LOG_WARNING(Service_HID,
1385 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
1386 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1387
1388 IPC::ResponseBuilder rb{ctx, 2};
1389 rb.Push(ResultSuccess);
1390}
1391
1392void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1393 IPC::RequestParser rp{ctx};
1394 struct Parameters {
1395 Core::HID::NpadStyleSet npad_styleset;
1396 INSERT_PADDING_WORDS_NOINIT(1);
1397 u64 applet_resource_user_id;
1398 Core::HID::NpadButton button;
1399 };
1400 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1401
1402 const auto parameters{rp.PopRaw<Parameters>()};
1403
1404 LOG_WARNING(Service_HID,
1405 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1406 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1407
1408 IPC::ResponseBuilder rb{ctx, 2};
1409 rb.Push(ResultSuccess);
1410}
1411
1412void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1413 IPC::RequestParser rp{ctx};
1414 const auto applet_resource_user_id{rp.Pop<u64>()};
1415
1416 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1417 applet_resource_user_id);
1418
1419 IPC::ResponseBuilder rb{ctx, 2};
1420 rb.Push(ResultSuccess);
1421}
1422
1423void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1424 IPC::RequestParser rp{ctx};
1425 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1426 const auto controller = GetResourceManager()->GetNpad();
1427
1428 Core::HID::VibrationDeviceInfo vibration_device_info;
1429 bool check_device_index = false;
1430
1431 switch (vibration_device_handle.npad_type) {
1432 case Core::HID::NpadStyleIndex::ProController:
1433 case Core::HID::NpadStyleIndex::Handheld:
1434 case Core::HID::NpadStyleIndex::JoyconDual:
1435 case Core::HID::NpadStyleIndex::JoyconLeft:
1436 case Core::HID::NpadStyleIndex::JoyconRight:
1437 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1438 check_device_index = true;
1439 break;
1440 case Core::HID::NpadStyleIndex::GameCube:
1441 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1442 break;
1443 case Core::HID::NpadStyleIndex::N64:
1444 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1445 break;
1446 default:
1447 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1448 break;
1449 }
1450
1451 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1452 if (check_device_index) {
1453 switch (vibration_device_handle.device_index) {
1454 case Core::HID::DeviceIndex::Left:
1455 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1456 break;
1457 case Core::HID::DeviceIndex::Right:
1458 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1459 break;
1460 case Core::HID::DeviceIndex::None:
1461 default:
1462 ASSERT_MSG(false, "DeviceIndex should never be None!");
1463 break;
1464 }
1465 }
1466
1467 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1468 vibration_device_info.type, vibration_device_info.position);
1469
1470 const auto result = IsVibrationHandleValid(vibration_device_handle);
1471 if (result.IsError()) {
1472 IPC::ResponseBuilder rb{ctx, 2};
1473 rb.Push(result);
1474 return;
1475 }
1476
1477 IPC::ResponseBuilder rb{ctx, 4};
1478 rb.Push(ResultSuccess);
1479 rb.PushRaw(vibration_device_info);
1480}
1481
1482void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
1483 IPC::RequestParser rp{ctx};
1484 struct Parameters {
1485 Core::HID::VibrationDeviceHandle vibration_device_handle;
1486 Core::HID::VibrationValue vibration_value;
1487 INSERT_PADDING_WORDS_NOINIT(1);
1488 u64 applet_resource_user_id;
1489 };
1490 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1491
1492 const auto parameters{rp.PopRaw<Parameters>()};
1493
1494 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1495 parameters.vibration_value);
1496
1497 LOG_DEBUG(Service_HID,
1498 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1499 parameters.vibration_device_handle.npad_type,
1500 parameters.vibration_device_handle.npad_id,
1501 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1502
1503 IPC::ResponseBuilder rb{ctx, 2};
1504 rb.Push(ResultSuccess);
1505}
1506
1507void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
1508 IPC::RequestParser rp{ctx};
1509 struct Parameters {
1510 Core::HID::VibrationDeviceHandle vibration_device_handle;
1511 INSERT_PADDING_WORDS_NOINIT(1);
1512 u64 applet_resource_user_id;
1513 };
1514 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1515
1516 const auto parameters{rp.PopRaw<Parameters>()};
1517
1518 LOG_DEBUG(Service_HID,
1519 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1520 parameters.vibration_device_handle.npad_type,
1521 parameters.vibration_device_handle.npad_id,
1522 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1523
1524 IPC::ResponseBuilder rb{ctx, 6};
1525 rb.Push(ResultSuccess);
1526 rb.PushRaw(
1527 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle));
1528}
1529
1530void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1531 LOG_DEBUG(Service_HID, "called");
1532
1533 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1534 rb.Push(ResultSuccess);
1535 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager());
1536}
1537
1538void IHidServer::PermitVibration(HLERequestContext& ctx) {
1539 IPC::RequestParser rp{ctx};
1540 const auto can_vibrate{rp.Pop<bool>()};
1541
1542 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1543 // by converting it to a bool
1544 Settings::values.vibration_enabled.SetValue(can_vibrate);
1545
1546 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1547
1548 IPC::ResponseBuilder rb{ctx, 2};
1549 rb.Push(ResultSuccess);
1550}
1551
1552void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
1553 LOG_DEBUG(Service_HID, "called");
1554
1555 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1556 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1557
1558 IPC::ResponseBuilder rb{ctx, 3};
1559 rb.Push(ResultSuccess);
1560 rb.Push(is_enabled);
1561}
1562
1563void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
1564 IPC::RequestParser rp{ctx};
1565 const auto applet_resource_user_id{rp.Pop<u64>()};
1566
1567 const auto handle_data = ctx.ReadBuffer(0);
1568 const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
1569 const auto vibration_data = ctx.ReadBuffer(1);
1570 const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
1571
1572 auto vibration_device_handles =
1573 std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
1574 handle_count);
1575 auto vibration_values = std::span(
1576 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1577
1578 GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values);
1579
1580 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1581
1582 IPC::ResponseBuilder rb{ctx, 2};
1583 rb.Push(ResultSuccess);
1584}
1585
1586void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1587 IPC::RequestParser rp{ctx};
1588 struct Parameters {
1589 Core::HID::VibrationDeviceHandle vibration_device_handle;
1590 INSERT_PADDING_WORDS_NOINIT(1);
1591 u64 applet_resource_user_id;
1592 Core::HID::VibrationGcErmCommand gc_erm_command;
1593 };
1594 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1595
1596 const auto parameters{rp.PopRaw<Parameters>()};
1597
1598 /**
1599 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1600 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1601 * in order to differentiate between Stop and StopHard commands.
1602 * This is done to reuse the controller vibration functions made for regular controllers.
1603 */
1604 const auto vibration_value = [parameters] {
1605 switch (parameters.gc_erm_command) {
1606 case Core::HID::VibrationGcErmCommand::Stop:
1607 return Core::HID::VibrationValue{
1608 .low_amplitude = 0.0f,
1609 .low_frequency = 160.0f,
1610 .high_amplitude = 0.0f,
1611 .high_frequency = 320.0f,
1612 };
1613 case Core::HID::VibrationGcErmCommand::Start:
1614 return Core::HID::VibrationValue{
1615 .low_amplitude = 1.0f,
1616 .low_frequency = 160.0f,
1617 .high_amplitude = 1.0f,
1618 .high_frequency = 320.0f,
1619 };
1620 case Core::HID::VibrationGcErmCommand::StopHard:
1621 return Core::HID::VibrationValue{
1622 .low_amplitude = 0.0f,
1623 .low_frequency = 0.0f,
1624 .high_amplitude = 0.0f,
1625 .high_frequency = 0.0f,
1626 };
1627 default:
1628 return Core::HID::DEFAULT_VIBRATION_VALUE;
1629 }
1630 }();
1631
1632 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1633 vibration_value);
1634
1635 LOG_DEBUG(Service_HID,
1636 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1637 "gc_erm_command={}",
1638 parameters.vibration_device_handle.npad_type,
1639 parameters.vibration_device_handle.npad_id,
1640 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1641 parameters.gc_erm_command);
1642
1643 IPC::ResponseBuilder rb{ctx, 2};
1644 rb.Push(ResultSuccess);
1645}
1646
1647void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1648 IPC::RequestParser rp{ctx};
1649 struct Parameters {
1650 Core::HID::VibrationDeviceHandle vibration_device_handle;
1651 INSERT_PADDING_WORDS_NOINIT(1);
1652 u64 applet_resource_user_id;
1653 };
1654
1655 const auto parameters{rp.PopRaw<Parameters>()};
1656
1657 const auto last_vibration =
1658 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle);
1659
1660 const auto gc_erm_command = [last_vibration] {
1661 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1662 return Core::HID::VibrationGcErmCommand::Start;
1663 }
1664
1665 /**
1666 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1667 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1668 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1669 * This is done to reuse the controller vibration functions made for regular controllers.
1670 */
1671 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1672 return Core::HID::VibrationGcErmCommand::StopHard;
1673 }
1674
1675 return Core::HID::VibrationGcErmCommand::Stop;
1676 }();
1677
1678 LOG_DEBUG(Service_HID,
1679 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1680 parameters.vibration_device_handle.npad_type,
1681 parameters.vibration_device_handle.npad_id,
1682 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1683
1684 IPC::ResponseBuilder rb{ctx, 4};
1685 rb.Push(ResultSuccess);
1686 rb.PushEnum(gc_erm_command);
1687}
1688
1689void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
1690 IPC::RequestParser rp{ctx};
1691 const auto applet_resource_user_id{rp.Pop<u64>()};
1692
1693 GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
1694
1695 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1696
1697 IPC::ResponseBuilder rb{ctx, 2};
1698 rb.Push(ResultSuccess);
1699}
1700
1701void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
1702 GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
1703
1704 LOG_DEBUG(Service_HID, "called");
1705
1706 IPC::ResponseBuilder rb{ctx, 2};
1707 rb.Push(ResultSuccess);
1708}
1709
1710void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1711 IPC::RequestParser rp{ctx};
1712 struct Parameters {
1713 Core::HID::VibrationDeviceHandle vibration_device_handle;
1714 INSERT_PADDING_WORDS_NOINIT(1);
1715 u64 applet_resource_user_id;
1716 };
1717 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1718
1719 const auto parameters{rp.PopRaw<Parameters>()};
1720
1721 LOG_DEBUG(Service_HID,
1722 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1723 parameters.vibration_device_handle.npad_type,
1724 parameters.vibration_device_handle.npad_id,
1725 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1726
1727 IPC::ResponseBuilder rb{ctx, 3};
1728 rb.Push(ResultSuccess);
1729 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
1730 parameters.vibration_device_handle));
1731}
1732
1733void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1734 IPC::RequestParser rp{ctx};
1735 const auto applet_resource_user_id{rp.Pop<u64>()};
1736
1737 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1738
1739 Result result = ResultSuccess;
1740 auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis();
1741
1742 if (!firmware_settings->IsDeviceManaged()) {
1743 result = console_sixaxis->Activate();
1744 }
1745
1746 if (result.IsSuccess()) {
1747 result = console_sixaxis->Activate(applet_resource_user_id);
1748 }
1749
1750 IPC::ResponseBuilder rb{ctx, 2};
1751 rb.Push(result);
1752}
1753
1754void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
1755 IPC::RequestParser rp{ctx};
1756 struct Parameters {
1757 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1758 INSERT_PADDING_WORDS_NOINIT(1);
1759 u64 applet_resource_user_id;
1760 };
1761 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1762
1763 const auto parameters{rp.PopRaw<Parameters>()};
1764
1765 LOG_WARNING(Service_HID,
1766 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1767 parameters.console_sixaxis_handle.unknown_1,
1768 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1769
1770 IPC::ResponseBuilder rb{ctx, 2};
1771 rb.Push(ResultSuccess);
1772}
1773
1774void IHidServer::StopConsoleSixAxisSensor(HLERequestContext& ctx) {
1775 IPC::RequestParser rp{ctx};
1776 struct Parameters {
1777 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1778 INSERT_PADDING_WORDS_NOINIT(1);
1779 u64 applet_resource_user_id;
1780 };
1781 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1782
1783 const auto parameters{rp.PopRaw<Parameters>()};
1784
1785 LOG_WARNING(Service_HID,
1786 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1787 parameters.console_sixaxis_handle.unknown_1,
1788 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1789
1790 IPC::ResponseBuilder rb{ctx, 2};
1791 rb.Push(ResultSuccess);
1792}
1793
1794void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1795 IPC::RequestParser rp{ctx};
1796 const auto applet_resource_user_id{rp.Pop<u64>()};
1797
1798 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1799
1800 Result result = ResultSuccess;
1801 auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis();
1802
1803 if (!firmware_settings->IsDeviceManaged()) {
1804 result = seven_sixaxis->Activate();
1805 }
1806
1807 if (result.IsSuccess()) {
1808 seven_sixaxis->Activate(applet_resource_user_id);
1809 }
1810
1811 IPC::ResponseBuilder rb{ctx, 2};
1812 rb.Push(ResultSuccess);
1813}
1814
1815void IHidServer::StartSevenSixAxisSensor(HLERequestContext& ctx) {
1816 IPC::RequestParser rp{ctx};
1817 const auto applet_resource_user_id{rp.Pop<u64>()};
1818
1819 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1820 applet_resource_user_id);
1821
1822 IPC::ResponseBuilder rb{ctx, 2};
1823 rb.Push(ResultSuccess);
1824}
1825
1826void IHidServer::StopSevenSixAxisSensor(HLERequestContext& ctx) {
1827 IPC::RequestParser rp{ctx};
1828 const auto applet_resource_user_id{rp.Pop<u64>()};
1829
1830 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1831 applet_resource_user_id);
1832
1833 IPC::ResponseBuilder rb{ctx, 2};
1834 rb.Push(ResultSuccess);
1835}
1836
1837void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1838 IPC::RequestParser rp{ctx};
1839 const auto applet_resource_user_id{rp.Pop<u64>()};
1840 const auto t_mem_1_size{rp.Pop<u64>()};
1841 const auto t_mem_2_size{rp.Pop<u64>()};
1842 const auto t_mem_1_handle{ctx.GetCopyHandle(0)};
1843 const auto t_mem_2_handle{ctx.GetCopyHandle(1)};
1844
1845 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1846 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1847
1848 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1849 t_mem_1_handle);
1850
1851 if (t_mem_1.IsNull()) {
1852 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
1853 IPC::ResponseBuilder rb{ctx, 2};
1854 rb.Push(ResultUnknown);
1855 return;
1856 }
1857
1858 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1859 t_mem_2_handle);
1860
1861 if (t_mem_2.IsNull()) {
1862 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
1863 IPC::ResponseBuilder rb{ctx, 2};
1864 rb.Push(ResultUnknown);
1865 return;
1866 }
1867
1868 ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size");
1869 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1870
1871 // Activate console six axis controller
1872 GetResourceManager()->GetConsoleSixAxis()->Activate();
1873 GetResourceManager()->GetSevenSixAxis()->Activate();
1874
1875 GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1876
1877 LOG_WARNING(Service_HID,
1878 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
1879 "applet_resource_user_id={}",
1880 t_mem_1_handle, t_mem_2_handle, applet_resource_user_id);
1881
1882 IPC::ResponseBuilder rb{ctx, 2};
1883 rb.Push(ResultSuccess);
1884}
1885
1886void IHidServer::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) {
1887 IPC::RequestParser rp{ctx};
1888 const auto applet_resource_user_id{rp.Pop<u64>()};
1889
1890 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1891 applet_resource_user_id);
1892
1893 IPC::ResponseBuilder rb{ctx, 2};
1894 rb.Push(ResultSuccess);
1895}
1896
1897void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1898 IPC::RequestParser rp{ctx};
1899 const auto applet_resource_user_id{rp.Pop<u64>()};
1900
1901 GetResourceManager()->GetSevenSixAxis()->ResetTimestamp();
1902
1903 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1904
1905 IPC::ResponseBuilder rb{ctx, 2};
1906 rb.Push(ResultSuccess);
1907}
1908
1909void IHidServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
1910 IPC::RequestParser rp{ctx};
1911
1912 LOG_WARNING(Service_HID, "(STUBBED) called");
1913
1914 IPC::ResponseBuilder rb{ctx, 3};
1915 rb.Push(ResultSuccess);
1916 rb.Push(false);
1917}
1918
1919void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1920 IPC::RequestParser rp{ctx};
1921 struct Parameters {
1922 Core::HID::NpadIdType npad_id;
1923 INSERT_PADDING_WORDS_NOINIT(1);
1924 u64 applet_resource_user_id;
1925 };
1926 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1927
1928 const auto parameters{rp.PopRaw<Parameters>()};
1929
1930 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1931 parameters.npad_id, parameters.applet_resource_user_id);
1932
1933 Palma::PalmaConnectionHandle handle;
1934 auto controller = GetResourceManager()->GetPalma();
1935 const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle);
1936
1937 IPC::ResponseBuilder rb{ctx, 4};
1938 rb.Push(result);
1939 rb.PushRaw(handle);
1940}
1941
1942void IHidServer::InitializePalma(HLERequestContext& ctx) {
1943 IPC::RequestParser rp{ctx};
1944 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1945
1946 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1947
1948 auto controller = GetResourceManager()->GetPalma();
1949 const auto result = controller->InitializePalma(connection_handle);
1950
1951 IPC::ResponseBuilder rb{ctx, 2};
1952 rb.Push(result);
1953}
1954
1955void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
1956 IPC::RequestParser rp{ctx};
1957 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1958
1959 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1960
1961 auto controller = GetResourceManager()->GetPalma();
1962
1963 IPC::ResponseBuilder rb{ctx, 2, 1};
1964 rb.Push(ResultSuccess);
1965 rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle));
1966}
1967
1968void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) {
1969 IPC::RequestParser rp{ctx};
1970 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1971
1972 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1973
1974 Palma::PalmaOperationType operation_type;
1975 Palma::PalmaOperationData data;
1976 auto controller = GetResourceManager()->GetPalma();
1977 const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data);
1978
1979 if (result.IsError()) {
1980 IPC::ResponseBuilder rb{ctx, 2};
1981 rb.Push(result);
1982 }
1983
1984 ctx.WriteBuffer(data);
1985 IPC::ResponseBuilder rb{ctx, 4};
1986 rb.Push(result);
1987 rb.Push(static_cast<u64>(operation_type));
1988}
1989
1990void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) {
1991 IPC::RequestParser rp{ctx};
1992 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1993 const auto palma_activity{rp.Pop<u64>()};
1994
1995 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
1996 connection_handle.npad_id, palma_activity);
1997
1998 auto controller = GetResourceManager()->GetPalma();
1999 const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity);
2000
2001 IPC::ResponseBuilder rb{ctx, 2};
2002 rb.Push(result);
2003}
2004
2005void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) {
2006 IPC::RequestParser rp{ctx};
2007 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2008 const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()};
2009
2010 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2011 connection_handle.npad_id, fr_mode);
2012
2013 auto controller = GetResourceManager()->GetPalma();
2014 const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode);
2015
2016 IPC::ResponseBuilder rb{ctx, 2};
2017 rb.Push(result);
2018}
2019
2020void IHidServer::ReadPalmaStep(HLERequestContext& ctx) {
2021 IPC::RequestParser rp{ctx};
2022 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2023
2024 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2025
2026 auto controller = GetResourceManager()->GetPalma();
2027 const auto result = controller->ReadPalmaStep(connection_handle);
2028
2029 IPC::ResponseBuilder rb{ctx, 2};
2030 rb.Push(result);
2031}
2032
2033void IHidServer::EnablePalmaStep(HLERequestContext& ctx) {
2034 IPC::RequestParser rp{ctx};
2035 struct Parameters {
2036 bool is_enabled;
2037 INSERT_PADDING_WORDS_NOINIT(1);
2038 Palma::PalmaConnectionHandle connection_handle;
2039 };
2040 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2041
2042 const auto parameters{rp.PopRaw<Parameters>()};
2043
2044 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2045 parameters.connection_handle.npad_id, parameters.is_enabled);
2046
2047 auto controller = GetResourceManager()->GetPalma();
2048 const auto result =
2049 controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2050
2051 IPC::ResponseBuilder rb{ctx, 2};
2052 rb.Push(result);
2053}
2054
2055void IHidServer::ResetPalmaStep(HLERequestContext& ctx) {
2056 IPC::RequestParser rp{ctx};
2057 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2058
2059 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2060
2061 auto controller = GetResourceManager()->GetPalma();
2062 const auto result = controller->ResetPalmaStep(connection_handle);
2063
2064 IPC::ResponseBuilder rb{ctx, 2};
2065 rb.Push(result);
2066}
2067
2068void IHidServer::ReadPalmaApplicationSection(HLERequestContext& ctx) {
2069 LOG_WARNING(Service_HID, "(STUBBED) called");
2070
2071 IPC::ResponseBuilder rb{ctx, 2};
2072 rb.Push(ResultSuccess);
2073}
2074
2075void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) {
2076 LOG_WARNING(Service_HID, "(STUBBED) called");
2077
2078 IPC::ResponseBuilder rb{ctx, 2};
2079 rb.Push(ResultSuccess);
2080}
2081
2082void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2083 IPC::RequestParser rp{ctx};
2084 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2085
2086 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2087
2088 GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle);
2089
2090 IPC::ResponseBuilder rb{ctx, 2};
2091 rb.Push(ResultSuccess);
2092}
2093
2094void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2095 IPC::RequestParser rp{ctx};
2096 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2097
2098 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2099
2100 GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle);
2101
2102 IPC::ResponseBuilder rb{ctx, 2};
2103 rb.Push(ResultSuccess);
2104}
2105
2106void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) {
2107 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2108
2109 IPC::ResponseBuilder rb{ctx, 2};
2110 rb.Push(ResultSuccess);
2111}
2112
2113void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2114 IPC::RequestParser rp{ctx};
2115 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2116 const auto unknown{rp.Pop<u64>()};
2117
2118 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2119
2120 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2121 connection_handle.npad_id, unknown);
2122
2123 GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2124
2125 IPC::ResponseBuilder rb{ctx, 2};
2126 rb.Push(ResultSuccess);
2127}
2128
2129void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2130 IPC::RequestParser rp{ctx};
2131 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2132 const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()};
2133 const auto unknown{rp.Pop<u64>()};
2134 const auto t_mem_size{rp.Pop<u64>()};
2135 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2136 const auto size{rp.Pop<u64>()};
2137
2138 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2139
2140 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
2141 t_mem_handle);
2142
2143 if (t_mem.IsNull()) {
2144 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2145 IPC::ResponseBuilder rb{ctx, 2};
2146 rb.Push(ResultUnknown);
2147 return;
2148 }
2149
2150 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2151
2152 LOG_WARNING(Service_HID,
2153 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2154 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2155 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2156
2157 GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set,
2158 t_mem->GetSourceAddress(), t_mem_size);
2159
2160 IPC::ResponseBuilder rb{ctx, 2};
2161 rb.Push(ResultSuccess);
2162}
2163
2164void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2165 IPC::RequestParser rp{ctx};
2166 struct Parameters {
2167 s32 database_id_version;
2168 INSERT_PADDING_WORDS_NOINIT(1);
2169 Palma::PalmaConnectionHandle connection_handle;
2170 };
2171 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2172
2173 const auto parameters{rp.PopRaw<Parameters>()};
2174
2175 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2176 parameters.connection_handle.npad_id, parameters.database_id_version);
2177
2178 GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion(
2179 parameters.connection_handle, parameters.database_id_version);
2180
2181 IPC::ResponseBuilder rb{ctx, 2};
2182 rb.Push(ResultSuccess);
2183}
2184
2185void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2186 IPC::RequestParser rp{ctx};
2187 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2188
2189 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2190
2191 GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle);
2192
2193 IPC::ResponseBuilder rb{ctx, 2};
2194 rb.Push(ResultSuccess);
2195}
2196
2197void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) {
2198 LOG_WARNING(Service_HID, "(STUBBED) called");
2199
2200 IPC::ResponseBuilder rb{ctx, 2};
2201 rb.Push(ResultSuccess);
2202}
2203
2204void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) {
2205 IPC::RequestParser rp{ctx};
2206 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2207
2208 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2209
2210 const auto result =
2211 GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle);
2212
2213 IPC::ResponseBuilder rb{ctx, 2};
2214 rb.Push(result);
2215}
2216
2217void IHidServer::ReadPalmaPlayLog(HLERequestContext& ctx) {
2218 LOG_WARNING(Service_HID, "(STUBBED) called");
2219
2220 IPC::ResponseBuilder rb{ctx, 2};
2221 rb.Push(ResultSuccess);
2222}
2223
2224void IHidServer::ResetPalmaPlayLog(HLERequestContext& ctx) {
2225 LOG_WARNING(Service_HID, "(STUBBED) called");
2226
2227 IPC::ResponseBuilder rb{ctx, 2};
2228 rb.Push(ResultSuccess);
2229}
2230
2231void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2232 IPC::RequestParser rp{ctx};
2233 struct Parameters {
2234 bool is_palma_all_connectable;
2235 INSERT_PADDING_BYTES_NOINIT(7);
2236 u64 applet_resource_user_id;
2237 };
2238 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2239
2240 const auto parameters{rp.PopRaw<Parameters>()};
2241
2242 LOG_WARNING(Service_HID,
2243 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2244 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2245
2246 GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2247
2248 IPC::ResponseBuilder rb{ctx, 2};
2249 rb.Push(ResultSuccess);
2250}
2251
2252void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2253 LOG_WARNING(Service_HID, "(STUBBED) called");
2254
2255 IPC::ResponseBuilder rb{ctx, 2};
2256 rb.Push(ResultSuccess);
2257}
2258
2259void IHidServer::PairPalma(HLERequestContext& ctx) {
2260 IPC::RequestParser rp{ctx};
2261 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2262
2263 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2264
2265 GetResourceManager()->GetPalma()->PairPalma(connection_handle);
2266
2267 IPC::ResponseBuilder rb{ctx, 2};
2268 rb.Push(ResultSuccess);
2269}
2270
2271void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) {
2272 IPC::RequestParser rp{ctx};
2273 const auto palma_boost_mode{rp.Pop<bool>()};
2274
2275 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2276
2277 GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode);
2278
2279 IPC::ResponseBuilder rb{ctx, 2};
2280 rb.Push(ResultSuccess);
2281}
2282
2283void IHidServer::CancelWritePalmaWaveEntry(HLERequestContext& ctx) {
2284 LOG_WARNING(Service_HID, "(STUBBED) called");
2285
2286 IPC::ResponseBuilder rb{ctx, 2};
2287 rb.Push(ResultSuccess);
2288}
2289
2290void IHidServer::EnablePalmaBoostMode(HLERequestContext& ctx) {
2291 LOG_WARNING(Service_HID, "(STUBBED) called");
2292
2293 IPC::ResponseBuilder rb{ctx, 2};
2294 rb.Push(ResultSuccess);
2295}
2296
2297void IHidServer::GetPalmaBluetoothAddress(HLERequestContext& ctx) {
2298 LOG_WARNING(Service_HID, "(STUBBED) called");
2299
2300 IPC::ResponseBuilder rb{ctx, 2};
2301 rb.Push(ResultSuccess);
2302}
2303
2304void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2305 LOG_WARNING(Service_HID, "(STUBBED) called");
2306
2307 IPC::ResponseBuilder rb{ctx, 2};
2308 rb.Push(ResultSuccess);
2309}
2310
2311void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2312 IPC::RequestParser rp{ctx};
2313 const auto applet_resource_user_id{rp.Pop<u64>()};
2314 const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()};
2315
2316 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);
2317
2318 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2319 applet_resource_user_id, communication_mode);
2320
2321 IPC::ResponseBuilder rb{ctx, 2};
2322 rb.Push(ResultSuccess);
2323}
2324
2325void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
2326 IPC::RequestParser rp{ctx};
2327
2328 LOG_WARNING(Service_HID, "(STUBBED) called");
2329
2330 IPC::ResponseBuilder rb{ctx, 4};
2331 rb.Push(ResultSuccess);
2332 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode());
2333}
2334
2335void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2336 IPC::RequestParser rp{ctx};
2337 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2338 const auto applet_resource_user_id{rp.Pop<u64>()};
2339
2340 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
2341 touchscreen_mode.mode, applet_resource_user_id);
2342
2343 IPC::ResponseBuilder rb{ctx, 2};
2344 rb.Push(ResultSuccess);
2345}
2346
2347void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2348 IPC::RequestParser rp{ctx};
2349 struct Parameters {
2350 s32 unknown;
2351 INSERT_PADDING_WORDS_NOINIT(1);
2352 u64 applet_resource_user_id;
2353 };
2354 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2355
2356 const auto parameters{rp.PopRaw<Parameters>()};
2357
2358 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
2359 parameters.unknown, parameters.applet_resource_user_id);
2360
2361 IPC::ResponseBuilder rb{ctx, 3};
2362 rb.Push(ResultSuccess);
2363 rb.Push(false);
2364}
2365
2366std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
2367 resource_manager->Initialize();
2368 return resource_manager;
2369}
2370
2371} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h
new file mode 100644
index 000000000..eb2e8e7f4
--- /dev/null
+++ b/src/core/hle/service/hid/hid_server.h
@@ -0,0 +1,149 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class ResourceManager;
14class HidFirmwareSettings;
15
16class IHidServer final : public ServiceFramework<IHidServer> {
17public:
18 explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
19 std::shared_ptr<HidFirmwareSettings> settings);
20 ~IHidServer() override;
21
22 std::shared_ptr<ResourceManager> GetResourceManager();
23
24private:
25 void CreateAppletResource(HLERequestContext& ctx);
26 void ActivateDebugPad(HLERequestContext& ctx);
27 void ActivateTouchScreen(HLERequestContext& ctx);
28 void ActivateMouse(HLERequestContext& ctx);
29 void ActivateKeyboard(HLERequestContext& ctx);
30 void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
31 void AcquireXpadIdEventHandle(HLERequestContext& ctx);
32 void ReleaseXpadIdEventHandle(HLERequestContext& ctx);
33 void ActivateXpad(HLERequestContext& ctx);
34 void GetXpadIds(HLERequestContext& ctx);
35 void ActivateJoyXpad(HLERequestContext& ctx);
36 void GetJoyXpadLifoHandle(HLERequestContext& ctx);
37 void GetJoyXpadIds(HLERequestContext& ctx);
38 void ActivateSixAxisSensor(HLERequestContext& ctx);
39 void DeactivateSixAxisSensor(HLERequestContext& ctx);
40 void GetSixAxisSensorLifoHandle(HLERequestContext& ctx);
41 void ActivateJoySixAxisSensor(HLERequestContext& ctx);
42 void DeactivateJoySixAxisSensor(HLERequestContext& ctx);
43 void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx);
44 void StartSixAxisSensor(HLERequestContext& ctx);
45 void StopSixAxisSensor(HLERequestContext& ctx);
46 void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
47 void EnableSixAxisSensorFusion(HLERequestContext& ctx);
48 void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
49 void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
50 void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
51 void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
52 void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
53 void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
54 void IsSixAxisSensorAtRest(HLERequestContext& ctx);
55 void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
56 void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
57 void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
58 void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
59 void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
60 void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
61 void ActivateGesture(HLERequestContext& ctx);
62 void SetSupportedNpadStyleSet(HLERequestContext& ctx);
63 void GetSupportedNpadStyleSet(HLERequestContext& ctx);
64 void SetSupportedNpadIdType(HLERequestContext& ctx);
65 void ActivateNpad(HLERequestContext& ctx);
66 void DeactivateNpad(HLERequestContext& ctx);
67 void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
68 void DisconnectNpad(HLERequestContext& ctx);
69 void GetPlayerLedPattern(HLERequestContext& ctx);
70 void ActivateNpadWithRevision(HLERequestContext& ctx);
71 void SetNpadJoyHoldType(HLERequestContext& ctx);
72 void GetNpadJoyHoldType(HLERequestContext& ctx);
73 void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
74 void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
75 void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
76 void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
77 void StartLrAssignmentMode(HLERequestContext& ctx);
78 void StopLrAssignmentMode(HLERequestContext& ctx);
79 void SetNpadHandheldActivationMode(HLERequestContext& ctx);
80 void GetNpadHandheldActivationMode(HLERequestContext& ctx);
81 void SwapNpadAssignment(HLERequestContext& ctx);
82 void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
83 void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
84 void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
85 void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
86 void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
87 void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
88 void GetVibrationDeviceInfo(HLERequestContext& ctx);
89 void SendVibrationValue(HLERequestContext& ctx);
90 void GetActualVibrationValue(HLERequestContext& ctx);
91 void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
92 void PermitVibration(HLERequestContext& ctx);
93 void IsVibrationPermitted(HLERequestContext& ctx);
94 void SendVibrationValues(HLERequestContext& ctx);
95 void SendVibrationGcErmCommand(HLERequestContext& ctx);
96 void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
97 void BeginPermitVibrationSession(HLERequestContext& ctx);
98 void EndPermitVibrationSession(HLERequestContext& ctx);
99 void IsVibrationDeviceMounted(HLERequestContext& ctx);
100 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
101 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
102 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
103 void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
104 void StartSevenSixAxisSensor(HLERequestContext& ctx);
105 void StopSevenSixAxisSensor(HLERequestContext& ctx);
106 void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
107 void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
108 void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
109 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
110 void GetPalmaConnectionHandle(HLERequestContext& ctx);
111 void InitializePalma(HLERequestContext& ctx);
112 void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
113 void GetPalmaOperationInfo(HLERequestContext& ctx);
114 void PlayPalmaActivity(HLERequestContext& ctx);
115 void SetPalmaFrModeType(HLERequestContext& ctx);
116 void ReadPalmaStep(HLERequestContext& ctx);
117 void EnablePalmaStep(HLERequestContext& ctx);
118 void ResetPalmaStep(HLERequestContext& ctx);
119 void ReadPalmaApplicationSection(HLERequestContext& ctx);
120 void WritePalmaApplicationSection(HLERequestContext& ctx);
121 void ReadPalmaUniqueCode(HLERequestContext& ctx);
122 void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
123 void WritePalmaActivityEntry(HLERequestContext& ctx);
124 void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
125 void WritePalmaWaveEntry(HLERequestContext& ctx);
126 void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
127 void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
128 void SuspendPalmaFeature(HLERequestContext& ctx);
129 void GetPalmaOperationResult(HLERequestContext& ctx);
130 void ReadPalmaPlayLog(HLERequestContext& ctx);
131 void ResetPalmaPlayLog(HLERequestContext& ctx);
132 void SetIsPalmaAllConnectable(HLERequestContext& ctx);
133 void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
134 void PairPalma(HLERequestContext& ctx);
135 void SetPalmaBoostMode(HLERequestContext& ctx);
136 void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
137 void EnablePalmaBoostMode(HLERequestContext& ctx);
138 void GetPalmaBluetoothAddress(HLERequestContext& ctx);
139 void SetDisallowedPalmaConnection(HLERequestContext& ctx);
140 void SetNpadCommunicationMode(HLERequestContext& ctx);
141 void GetNpadCommunicationMode(HLERequestContext& ctx);
142 void SetTouchScreenConfiguration(HLERequestContext& ctx);
143 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
144
145 std::shared_ptr<ResourceManager> resource_manager;
146 std::shared_ptr<HidFirmwareSettings> firmware_settings;
147};
148
149} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
new file mode 100644
index 000000000..b56d0347a
--- /dev/null
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -0,0 +1,539 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hid/hid_core.h"
5#include "core/hle/service/hid/controllers/npad.h"
6#include "core/hle/service/hid/controllers/touchscreen.h"
7#include "core/hle/service/hid/errors.h"
8#include "core/hle/service/hid/hid_system_server.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h"
11
12namespace Service::HID {
13
14IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
15 : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
16 resource_manager{resource} {
17 // clang-format off
18 static const FunctionInfo functions[] = {
19 {31, nullptr, "SendKeyboardLockKeyEvent"},
20 {101, nullptr, "AcquireHomeButtonEventHandle"},
21 {111, nullptr, "ActivateHomeButton"},
22 {121, nullptr, "AcquireSleepButtonEventHandle"},
23 {131, nullptr, "ActivateSleepButton"},
24 {141, nullptr, "AcquireCaptureButtonEventHandle"},
25 {151, nullptr, "ActivateCaptureButton"},
26 {161, nullptr, "GetPlatformConfig"},
27 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
28 {211, nullptr, "GetNpadsWithNfc"},
29 {212, nullptr, "AcquireNfcActivateEventHandle"},
30 {213, nullptr, "ActivateNfc"},
31 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
32 {215, nullptr, "IsNfcActivated"},
33 {230, nullptr, "AcquireIrSensorEventHandle"},
34 {231, nullptr, "ActivateIrSensor"},
35 {232, nullptr, "GetIrSensorState"},
36 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
37 {301, nullptr, "ActivateNpadSystem"},
38 {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
39 {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
40 {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
41 {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
42 {307, nullptr, "GetNpadSystemExtStyle"},
43 {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
44 {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
45 {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
46 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
47 {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
48 {313, nullptr, "GetNpadCaptureButtonAssignment"},
49 {314, nullptr, "GetAppletFooterUiType"},
50 {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
51 {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
52 {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
53 {318, &IHidSystemServer::HasBattery, "HasBattery"},
54 {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
55 {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
56 {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"},
57 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
58 {324, nullptr, "GetUniquePadButtonSet"},
59 {325, nullptr, "GetUniquePadColor"},
60 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
61 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
62 {328, nullptr, "AttachAbstractedPadToNpad"},
63 {329, nullptr, "DetachAbstractedPadAll"},
64 {330, nullptr, "CheckAbstractedPadConnection"},
65 {500, nullptr, "SetAppletResourceUserId"},
66 {501, nullptr, "RegisterAppletResourceUserId"},
67 {502, nullptr, "UnregisterAppletResourceUserId"},
68 {503, nullptr, "EnableAppletToGetInput"},
69 {504, nullptr, "SetAruidValidForVibration"},
70 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
71 {506, nullptr, "EnableAppletToGetPadInput"},
72 {507, nullptr, "EnableAppletToGetTouchScreen"},
73 {510, nullptr, "SetVibrationMasterVolume"},
74 {511, nullptr, "GetVibrationMasterVolume"},
75 {512, nullptr, "BeginPermitVibrationSession"},
76 {513, nullptr, "EndPermitVibrationSession"},
77 {514, nullptr, "Unknown514"},
78 {520, nullptr, "EnableHandheldHids"},
79 {521, nullptr, "DisableHandheldHids"},
80 {522, nullptr, "SetJoyConRailEnabled"},
81 {523, nullptr, "IsJoyConRailEnabled"},
82 {524, nullptr, "IsHandheldHidsEnabled"},
83 {525, nullptr, "IsJoyConAttachedOnAllRail"},
84 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
85 {541, nullptr, "GetPlayReportControllerUsages"},
86 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
87 {543, nullptr, "GetRegisteredDevicesOld"},
88 {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
89 {545, nullptr, "SendConnectionTrigger"},
90 {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
91 {547, nullptr, "GetAllowedBluetoothLinksCount"},
92 {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
93 {549, nullptr, "GetConnectableRegisteredDevices"},
94 {700, nullptr, "ActivateUniquePad"},
95 {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
96 {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
97 {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
98 {800, nullptr, "ListSixAxisSensorHandles"},
99 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
100 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
101 {803, nullptr, "StartSixAxisSensorUserCalibration"},
102 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
103 {805, nullptr, "GetUniquePadBluetoothAddress"},
104 {806, nullptr, "DisconnectUniquePad"},
105 {807, nullptr, "GetUniquePadType"},
106 {808, nullptr, "GetUniquePadInterface"},
107 {809, nullptr, "GetUniquePadSerialNumber"},
108 {810, nullptr, "GetUniquePadControllerNumber"},
109 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
110 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
111 {821, nullptr, "StartAnalogStickManualCalibration"},
112 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
113 {823, nullptr, "CancelAnalogStickManualCalibration"},
114 {824, nullptr, "ResetAnalogStickManualCalibration"},
115 {825, nullptr, "GetAnalogStickState"},
116 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
117 {827, nullptr, "IsAnalogStickButtonPressed"},
118 {828, nullptr, "IsAnalogStickInReleasePosition"},
119 {829, nullptr, "IsAnalogStickInCircumference"},
120 {830, nullptr, "SetNotificationLedPattern"},
121 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
122 {832, nullptr, "PrepareHidsForNotificationWake"},
123 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
124 {851, nullptr, "EnableUsbFullKeyController"},
125 {852, nullptr, "IsUsbConnected"},
126 {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
127 {900, nullptr, "ActivateInputDetector"},
128 {901, nullptr, "NotifyInputDetector"},
129 {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
130 {1001, nullptr, "GetFirmwareVersion"},
131 {1002, nullptr, "GetAvailableFirmwareVersion"},
132 {1003, nullptr, "IsFirmwareUpdateAvailable"},
133 {1004, nullptr, "CheckFirmwareUpdateRequired"},
134 {1005, nullptr, "StartFirmwareUpdate"},
135 {1006, nullptr, "AbortFirmwareUpdate"},
136 {1007, nullptr, "GetFirmwareUpdateState"},
137 {1008, nullptr, "ActivateAudioControl"},
138 {1009, nullptr, "AcquireAudioControlEventHandle"},
139 {1010, nullptr, "GetAudioControlStates"},
140 {1011, nullptr, "DeactivateAudioControl"},
141 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
142 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
143 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
144 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
145 {1100, nullptr, "GetHidbusSystemServiceObject"},
146 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
147 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
148 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
149 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
150 {1133, nullptr, "StartUsbFirmwareUpdate"},
151 {1134, nullptr, "GetUsbFirmwareUpdateState"},
152 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
153 {1150, nullptr, "SetTouchScreenMagnification"},
154 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
155 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
156 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
157 {1154, nullptr, "IsFirmwareAvailableForNotification"},
158 {1155, nullptr, "SetForceHandheldStyleVibration"},
159 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
160 {1157, nullptr, "CancelConnectionTrigger"},
161 {1200, nullptr, "IsButtonConfigSupported"},
162 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
163 {1202, nullptr, "DeleteButtonConfig"},
164 {1203, nullptr, "DeleteButtonConfigEmbedded"},
165 {1204, nullptr, "SetButtonConfigEnabled"},
166 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
167 {1206, nullptr, "IsButtonConfigEnabled"},
168 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
169 {1208, nullptr, "SetButtonConfigEmbedded"},
170 {1209, nullptr, "SetButtonConfigFull"},
171 {1210, nullptr, "SetButtonConfigLeft"},
172 {1211, nullptr, "SetButtonConfigRight"},
173 {1212, nullptr, "GetButtonConfigEmbedded"},
174 {1213, nullptr, "GetButtonConfigFull"},
175 {1214, nullptr, "GetButtonConfigLeft"},
176 {1215, nullptr, "GetButtonConfigRight"},
177 {1250, nullptr, "IsCustomButtonConfigSupported"},
178 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
179 {1252, nullptr, "IsDefaultButtonConfigFull"},
180 {1253, nullptr, "IsDefaultButtonConfigLeft"},
181 {1254, nullptr, "IsDefaultButtonConfigRight"},
182 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
183 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
184 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
185 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
186 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
187 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
188 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
189 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
190 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
191 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
192 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
193 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
194 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
195 {1268, nullptr, "DeleteButtonConfigStorageFull"},
196 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
197 {1270, nullptr, "DeleteButtonConfigStorageRight"},
198 {1271, nullptr, "IsUsingCustomButtonConfig"},
199 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
200 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
201 {1274, nullptr, "SetDefaultButtonConfig"},
202 {1275, nullptr, "SetAllDefaultButtonConfig"},
203 {1276, nullptr, "SetHidButtonConfigEmbedded"},
204 {1277, nullptr, "SetHidButtonConfigFull"},
205 {1278, nullptr, "SetHidButtonConfigLeft"},
206 {1279, nullptr, "SetHidButtonConfigRight"},
207 {1280, nullptr, "GetHidButtonConfigEmbedded"},
208 {1281, nullptr, "GetHidButtonConfigFull"},
209 {1282, nullptr, "GetHidButtonConfigLeft"},
210 {1283, nullptr, "GetHidButtonConfigRight"},
211 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
212 {1285, nullptr, "GetButtonConfigStorageFull"},
213 {1286, nullptr, "GetButtonConfigStorageLeft"},
214 {1287, nullptr, "GetButtonConfigStorageRight"},
215 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
216 {1289, nullptr, "SetButtonConfigStorageFull"},
217 {1290, nullptr, "DeleteButtonConfigStorageRight"},
218 {1291, nullptr, "DeleteButtonConfigStorageRight"},
219 };
220 // clang-format on
221
222 RegisterHandlers(functions);
223
224 joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent");
225 acquire_device_registered_event =
226 service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent");
227 acquire_connection_trigger_timeout_event =
228 service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent");
229 unique_pad_connection_event =
230 service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle");
231}
232
233IHidSystemServer::~IHidSystemServer() {
234 service_context.CloseEvent(joy_detach_event);
235 service_context.CloseEvent(acquire_device_registered_event);
236 service_context.CloseEvent(acquire_connection_trigger_timeout_event);
237 service_context.CloseEvent(unique_pad_connection_event);
238};
239
240void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
241 LOG_WARNING(Service_HID, "called");
242
243 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
244
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(ResultSuccess);
247}
248
249void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
250 LOG_WARNING(Service_HID, "(STUBBED) called");
251
252 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(ResultSuccess);
254}
255
256void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
257 LOG_WARNING(Service_HID, "(STUBBED) called");
258
259 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(ResultSuccess);
261}
262
263void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
264 LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running
265
266 IPC::ResponseBuilder rb{ctx, 3};
267 rb.Push(ResultSuccess);
268 rb.PushEnum(system.HIDCore().GetLastActiveController());
269}
270
271void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
272 LOG_WARNING(Service_HID, "called");
273
274 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
275
276 IPC::ResponseBuilder rb{ctx, 2};
277 rb.Push(ResultSuccess);
278}
279
280void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
281 IPC::RequestParser rp{ctx};
282 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
283
284 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
285 npad_id_type); // Spams a lot when controller applet is running
286
287 Core::HID::NpadColor left_color{};
288 Core::HID::NpadColor right_color{};
289 // TODO: Get colors from Npad
290
291 IPC::ResponseBuilder rb{ctx, 4};
292 rb.Push(ResultSuccess);
293 rb.PushRaw(left_color);
294 rb.PushRaw(right_color);
295}
296
297void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
298 IPC::RequestParser rp{ctx};
299
300 LOG_INFO(Service_HID, "(STUBBED) called");
301
302 Core::HID::NpadStyleSet supported_styleset =
303 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
304
305 IPC::ResponseBuilder rb{ctx, 3};
306 rb.Push(ResultSuccess);
307 rb.PushEnum(supported_styleset);
308}
309
310void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
311 IPC::RequestParser rp{ctx};
312
313 LOG_INFO(Service_HID, "(STUBBED) called");
314
315 Core::HID::NpadStyleSet supported_styleset =
316 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
317
318 IPC::ResponseBuilder rb{ctx, 3};
319 rb.Push(ResultSuccess);
320 rb.PushEnum(supported_styleset);
321}
322
323void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
324 IPC::RequestParser rp{ctx};
325 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
326
327 LOG_DEBUG(Service_HID, "called, npad_id_type={}",
328 npad_id_type); // Spams a lot when controller applet is running
329
330 const NPad::AppletDetailedUiType detailed_ui_type =
331 GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type);
332
333 IPC::ResponseBuilder rb{ctx, 3};
334 rb.Push(ResultSuccess);
335 rb.PushRaw(detailed_ui_type);
336}
337
338void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
341
342 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
343 npad_id_type); // Spams a lot when controller applet is running
344
345 IPC::ResponseBuilder rb{ctx, 3};
346 rb.Push(ResultSuccess);
347 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
348}
349
350void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
353
354 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
355 npad_id_type); // Spams a lot when controller applet is running
356
357 IPC::ResponseBuilder rb{ctx, 4};
358 rb.Push(ResultSuccess);
359 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
360 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
361}
362
363void IHidSystemServer::HasBattery(HLERequestContext& ctx) {
364 IPC::RequestParser rp{ctx};
365 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
366
367 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
368 npad_id_type); // Spams a lot when controller applet is running
369
370 IPC::ResponseBuilder rb{ctx, 3};
371 rb.Push(ResultSuccess);
372 rb.Push(false);
373}
374
375void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) {
376 IPC::RequestParser rp{ctx};
377 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
378
379 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
380 npad_id_type); // Spams a lot when controller applet is running
381
382 struct LeftRightBattery {
383 bool left;
384 bool right;
385 };
386
387 LeftRightBattery left_right_battery{
388 .left = false,
389 .right = false,
390 };
391
392 IPC::ResponseBuilder rb{ctx, 3};
393 rb.Push(ResultSuccess);
394 rb.PushRaw(left_right_battery);
395}
396
397void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
398 IPC::RequestParser rp{ctx};
399 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
400
401 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
402 npad_id_type); // Spams a lot when controller applet is running
403
404 const std::vector<Core::HID::UniquePadId> unique_pads{};
405
406 if (!unique_pads.empty()) {
407 ctx.WriteBuffer(unique_pads);
408 }
409
410 IPC::ResponseBuilder rb{ctx, 3};
411 rb.Push(ResultSuccess);
412 rb.Push(static_cast<u32>(unique_pads.size()));
413}
414
415void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) {
416 IPC::RequestParser rp{ctx};
417
418 LOG_WARNING(Service_HID, "(STUBBED) called");
419
420 IPC::ResponseBuilder rb{ctx, 2};
421 rb.Push(ResultSuccess);
422}
423
424void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
425 LOG_INFO(Service_AM, "(STUBBED) called");
426
427 IPC::ResponseBuilder rb{ctx, 2, 1};
428 rb.Push(ResultSuccess);
429 rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
430}
431
432void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
433 LOG_INFO(Service_HID, "(STUBBED) called");
434
435 IPC::ResponseBuilder rb{ctx, 2, 1};
436 rb.Push(ResultSuccess);
437 rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
438}
439
440void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
441 LOG_WARNING(Service_HID, "(STUBBED) called");
442
443 struct RegisterData {
444 std::array<u8, 0x68> data;
445 };
446 static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size");
447 std::vector<RegisterData> registered_devices{};
448
449 if (!registered_devices.empty()) {
450 ctx.WriteBuffer(registered_devices);
451 }
452
453 IPC::ResponseBuilder rb{ctx, 4};
454 rb.Push(ResultSuccess);
455 rb.Push<u64>(registered_devices.size());
456}
457
458void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) {
459 LOG_WARNING(Service_HID, "(STUBBED) called");
460
461 IPC::ResponseBuilder rb{ctx, 2, 1};
462 rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
463 rb.Push(ResultSuccess);
464}
465
466void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
467 LOG_WARNING(Service_HID, "(STUBBED) called");
468
469 IPC::ResponseBuilder rb{ctx, 4};
470 rb.Push(ResultSuccess);
471 rb.Push<u64>(0);
472}
473
474void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
475 LOG_INFO(Service_AM, "called");
476
477 IPC::ResponseBuilder rb{ctx, 2, 1};
478 rb.Push(ResultSuccess);
479 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
480}
481
482void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
483 const bool is_enabled = false;
484
485 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
486
487 IPC::ResponseBuilder rb{ctx, 3};
488 rb.Push(ResultSuccess);
489 rb.Push(is_enabled);
490}
491
492void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
493 const bool button_pressed = false;
494
495 LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}",
496 button_pressed); // Spams a lot when controller applet is open
497
498 IPC::ResponseBuilder rb{ctx, 3};
499 rb.Push(ResultSuccess);
500 rb.Push(button_pressed);
501}
502
503void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
504 LOG_WARNING(Service_HID, "(STUBBED) called");
505
506 IPC::ResponseBuilder rb{ctx, 2};
507 rb.Push(ResultSuccess);
508}
509
510void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
511 LOG_WARNING(Service_HID, "(STUBBED) called");
512
513 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(ResultSuccess);
515}
516
517void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
518 LOG_WARNING(Service_HID, "(STUBBED) called");
519
520 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
521 .mode = Core::HID::TouchScreenModeForNx::Finger,
522 };
523
524 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
525 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
526 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
527 }
528
529 IPC::ResponseBuilder rb{ctx, 6};
530 rb.Push(ResultSuccess);
531 rb.PushRaw(touchscreen_config);
532}
533
534std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
535 resource_manager->Initialize();
536 return resource_manager;
537}
538
539} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
new file mode 100644
index 000000000..822d5e5b9
--- /dev/null
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Kernel {
14class KEvent;
15}
16
17namespace Service::HID {
18class ResourceManager;
19
20class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
21public:
22 explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
23 ~IHidSystemServer() override;
24
25private:
26 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
27 void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
28 void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
29 void GetLastActiveNpad(HLERequestContext& ctx);
30 void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
31 void GetNpadFullKeyGripColor(HLERequestContext& ctx);
32 void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
33 void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
34 void GetAppletDetailedUiType(HLERequestContext& ctx);
35 void GetNpadInterfaceType(HLERequestContext& ctx);
36 void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
37 void HasBattery(HLERequestContext& ctx);
38 void HasLeftRightBattery(HLERequestContext& ctx);
39 void GetUniquePadsFromNpad(HLERequestContext& ctx);
40 void GetIrSensorState(HLERequestContext& ctx);
41 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
42 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
43 void GetRegisteredDevices(HLERequestContext& ctx);
44 void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx);
45 void GetUniquePadIds(HLERequestContext& ctx);
46 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
47 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
48 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
49 void InitializeFirmwareUpdate(HLERequestContext& ctx);
50 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
51 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
52
53 std::shared_ptr<ResourceManager> GetResourceManager();
54
55 Kernel::KEvent* acquire_connection_trigger_timeout_event;
56 Kernel::KEvent* acquire_device_registered_event;
57 Kernel::KEvent* joy_detach_event;
58 Kernel::KEvent* unique_pad_connection_event;
59 KernelHelpers::ServiceContext service_context;
60 std::shared_ptr<ResourceManager> resource_manager;
61};
62
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h
new file mode 100644
index 000000000..b87cc10e3
--- /dev/null
+++ b/src/core/hle/service/hid/hid_util.h
@@ -0,0 +1,146 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hid/hid_types.h"
7#include "core/hle/service/hid/errors.h"
8
9namespace Service::HID {
10
11constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) {
12 switch (npad_id) {
13 case Core::HID::NpadIdType::Player1:
14 case Core::HID::NpadIdType::Player2:
15 case Core::HID::NpadIdType::Player3:
16 case Core::HID::NpadIdType::Player4:
17 case Core::HID::NpadIdType::Player5:
18 case Core::HID::NpadIdType::Player6:
19 case Core::HID::NpadIdType::Player7:
20 case Core::HID::NpadIdType::Player8:
21 case Core::HID::NpadIdType::Other:
22 case Core::HID::NpadIdType::Handheld:
23 return true;
24 default:
25 return false;
26 }
27}
28
29constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) {
30 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id));
31 const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
32
33 if (!npad_id) {
34 return InvalidNpadId;
35 }
36 if (!device_index) {
37 return NpadDeviceIndexOutOfRange;
38 }
39
40 return ResultSuccess;
41}
42
43constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
44 switch (handle.npad_type) {
45 case Core::HID::NpadStyleIndex::ProController:
46 case Core::HID::NpadStyleIndex::Handheld:
47 case Core::HID::NpadStyleIndex::JoyconDual:
48 case Core::HID::NpadStyleIndex::JoyconLeft:
49 case Core::HID::NpadStyleIndex::JoyconRight:
50 case Core::HID::NpadStyleIndex::GameCube:
51 case Core::HID::NpadStyleIndex::N64:
52 case Core::HID::NpadStyleIndex::SystemExt:
53 case Core::HID::NpadStyleIndex::System:
54 // These support vibration
55 break;
56 default:
57 return VibrationInvalidStyleIndex;
58 }
59
60 if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) {
61 return VibrationInvalidNpadId;
62 }
63
64 if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) {
65 return VibrationDeviceIndexOutOfRange;
66 }
67
68 return ResultSuccess;
69}
70
71/// Converts a Core::HID::NpadIdType to an array index.
72constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) {
73 switch (npad_id_type) {
74 case Core::HID::NpadIdType::Player1:
75 return 0;
76 case Core::HID::NpadIdType::Player2:
77 return 1;
78 case Core::HID::NpadIdType::Player3:
79 return 2;
80 case Core::HID::NpadIdType::Player4:
81 return 3;
82 case Core::HID::NpadIdType::Player5:
83 return 4;
84 case Core::HID::NpadIdType::Player6:
85 return 5;
86 case Core::HID::NpadIdType::Player7:
87 return 6;
88 case Core::HID::NpadIdType::Player8:
89 return 7;
90 case Core::HID::NpadIdType::Handheld:
91 return 8;
92 case Core::HID::NpadIdType::Other:
93 return 9;
94 default:
95 return 8;
96 }
97}
98
99/// Converts an array index to a Core::HID::NpadIdType
100constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) {
101 switch (index) {
102 case 0:
103 return Core::HID::NpadIdType::Player1;
104 case 1:
105 return Core::HID::NpadIdType::Player2;
106 case 2:
107 return Core::HID::NpadIdType::Player3;
108 case 3:
109 return Core::HID::NpadIdType::Player4;
110 case 4:
111 return Core::HID::NpadIdType::Player5;
112 case 5:
113 return Core::HID::NpadIdType::Player6;
114 case 6:
115 return Core::HID::NpadIdType::Player7;
116 case 7:
117 return Core::HID::NpadIdType::Player8;
118 case 8:
119 return Core::HID::NpadIdType::Handheld;
120 case 9:
121 return Core::HID::NpadIdType::Other;
122 default:
123 return Core::HID::NpadIdType::Invalid;
124 }
125}
126
127constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) {
128 switch (index) {
129 case 0:
130 return Core::HID::NpadStyleSet::Fullkey;
131 case 1:
132 return Core::HID::NpadStyleSet::Handheld;
133 case 2:
134 return Core::HID::NpadStyleSet::JoyDual;
135 case 3:
136 return Core::HID::NpadStyleSet::JoyLeft;
137 case 4:
138 return Core::HID::NpadStyleSet::JoyRight;
139 case 5:
140 return Core::HID::NpadStyleSet::Palma;
141 default:
142 return Core::HID::NpadStyleSet::None;
143 }
144}
145
146} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 221c33b86..39b9a4474 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/kernel/k_transfer_memory.h" 12#include "core/hle/kernel/k_transfer_memory.h"
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hid/errors.h" 14#include "core/hle/service/hid/errors.h"
15#include "core/hle/service/hid/hid_util.h"
15#include "core/hle/service/hid/irs.h" 16#include "core/hle/service/hid/irs.h"
16#include "core/hle/service/hid/irsensor/clustering_processor.h" 17#include "core/hle/service/hid/irsensor/clustering_processor.h"
17#include "core/hle/service/hid/irsensor/image_transfer_processor.h" 18#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
@@ -138,7 +139,7 @@ void IRS::RunMomentProcessor(HLERequestContext& ctx) {
138 139
139 if (result.IsSuccess()) { 140 if (result.IsSuccess()) {
140 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); 141 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
141 MakeProcessor<MomentProcessor>(parameters.camera_handle, device); 142 MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device);
142 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); 143 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
143 image_transfer_processor.SetConfig(parameters.processor_config); 144 image_transfer_processor.SetConfig(parameters.processor_config);
144 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 145 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
@@ -320,7 +321,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
320 } 321 }
321 322
322 Core::IrSensor::IrCameraHandle camera_handle{ 323 Core::IrSensor::IrCameraHandle camera_handle{
323 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), 324 .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
324 .npad_type = Core::HID::NpadStyleIndex::None, 325 .npad_type = Core::HID::NpadStyleIndex::None,
325 }; 326 };
326 327
@@ -545,7 +546,7 @@ void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) {
545 546
546Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { 547Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
547 if (camera_handle.npad_id > 548 if (camera_handle.npad_id >
548 static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { 549 static_cast<u8>(HID::NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
549 return InvalidIrCameraHandle; 550 return InvalidIrCameraHandle;
550 } 551 }
551 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { 552 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index a8fa19025..c8e6dab17 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -3,15 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/core.h"
6#include "core/hid/hid_types.h" 7#include "core/hid/hid_types.h"
7#include "core/hid/irs_types.h" 8#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h" 9#include "core/hle/service/hid/irsensor/processor_base.h"
9#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
10 11
11namespace Core {
12class System;
13}
14
15namespace Core::HID { 12namespace Core::HID {
16class EmulatedController; 13class EmulatedController;
17} // namespace Core::HID 14} // namespace Core::HID
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
index e2f4ae876..c559eb0d5 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -3,16 +3,18 @@
3 3
4#include <queue> 4#include <queue>
5 5
6#include "core/core.h"
7#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h" 8#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 9#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/irsensor/clustering_processor.h" 10#include "core/hle/service/hid/irsensor/clustering_processor.h"
9 11
10namespace Service::IRS { 12namespace Service::IRS {
11ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, 13ClusteringProcessor::ClusteringProcessor(Core::System& system_,
12 Core::IrSensor::DeviceFormat& device_format, 14 Core::IrSensor::DeviceFormat& device_format,
13 std::size_t npad_index) 15 std::size_t npad_index)
14 : device{device_format} { 16 : device{device_format}, system{system_} {
15 npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); 17 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
16 18
17 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; 19 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
18 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; 20 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
@@ -48,7 +50,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
48 } 50 }
49 51
50 next_state = {}; 52 next_state = {};
51 const auto camera_data = npad_device->GetCamera(); 53 const auto& camera_data = npad_device->GetCamera();
52 auto filtered_image = camera_data.data; 54 auto filtered_image = camera_data.data;
53 55
54 RemoveLowIntensityData(filtered_image); 56 RemoveLowIntensityData(filtered_image);
@@ -83,7 +85,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
83 } 85 }
84 86
85 next_state.sampling_number = camera_data.sample; 87 next_state.sampling_number = camera_data.sample;
86 next_state.timestamp = next_state.timestamp + 131; 88 next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
87 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; 89 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
88 shared_memory->clustering_lifo.WriteNextEntry(next_state); 90 shared_memory->clustering_lifo.WriteNextEntry(next_state);
89 91
@@ -202,14 +204,14 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
202} 204}
203 205
204u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { 206u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
205 if ((y * width) + x > data.size()) { 207 if ((y * width) + x >= data.size()) {
206 return 0; 208 return 0;
207 } 209 }
208 return data[(y * width) + x]; 210 return data[(y * width) + x];
209} 211}
210 212
211void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { 213void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
212 if ((y * width) + x > data.size()) { 214 if ((y * width) + x >= data.size()) {
213 return; 215 return;
214 } 216 }
215 data[(y * width) + x] = value; 217 data[(y * width) + x] = value;
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
index dc01a8ea7..83f34734a 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.h
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -8,6 +8,10 @@
8#include "core/hle/service/hid/irs_ring_lifo.h" 8#include "core/hle/service/hid/irs_ring_lifo.h"
9#include "core/hle/service/hid/irsensor/processor_base.h" 9#include "core/hle/service/hid/irsensor/processor_base.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace Core::HID { 15namespace Core::HID {
12class EmulatedController; 16class EmulatedController;
13} // namespace Core::HID 17} // namespace Core::HID
@@ -15,8 +19,7 @@ class EmulatedController;
15namespace Service::IRS { 19namespace Service::IRS {
16class ClusteringProcessor final : public ProcessorBase { 20class ClusteringProcessor final : public ProcessorBase {
17public: 21public:
18 explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, 22 explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
19 Core::IrSensor::DeviceFormat& device_format,
20 std::size_t npad_index); 23 std::size_t npad_index);
21 ~ClusteringProcessor() override; 24 ~ClusteringProcessor() override;
22 25
@@ -106,5 +109,7 @@ private:
106 Core::IrSensor::DeviceFormat& device; 109 Core::IrSensor::DeviceFormat& device;
107 Core::HID::EmulatedController* npad_device; 110 Core::HID::EmulatedController* npad_device;
108 int callback_key{}; 111 int callback_key{};
112
113 Core::System& system;
109}; 114};
110} // namespace Service::IRS 115} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
index 803a6277c..22067a591 100644
--- a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -49,7 +49,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
49 return; 49 return;
50 } 50 }
51 51
52 const auto camera_data = npad_device->GetCamera(); 52 const auto& camera_data = npad_device->GetCamera();
53 53
54 // This indicates how much ambient light is present 54 // This indicates how much ambient light is present
55 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; 55 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
index dbaca420a..cf045bda7 100644
--- a/src/core/hle/service/hid/irsensor/moment_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -1,24 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
4#include "core/hle/service/hid/irsensor/moment_processor.h" 8#include "core/hle/service/hid/irsensor/moment_processor.h"
5 9
6namespace Service::IRS { 10namespace Service::IRS {
7MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) 11static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
8 : device(device_format) { 12static constexpr std::size_t ImageWidth = 40;
13static constexpr std::size_t ImageHeight = 30;
14
15MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
16 std::size_t npad_index)
17 : device(device_format), system{system_} {
18 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
19
9 device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; 20 device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; 21 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; 22 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
23
24 shared_memory = std::construct_at(
25 reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data));
26
27 Core::HID::ControllerUpdateCallback engine_callback{
28 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
29 .is_npad_service = true,
30 };
31 callback_key = npad_device->SetCallback(engine_callback);
12} 32}
13 33
14MomentProcessor::~MomentProcessor() = default; 34MomentProcessor::~MomentProcessor() {
35 npad_device->DeleteCallback(callback_key);
36};
15 37
16void MomentProcessor::StartProcessor() {} 38void MomentProcessor::StartProcessor() {
39 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
40 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
41}
17 42
18void MomentProcessor::SuspendProcessor() {} 43void MomentProcessor::SuspendProcessor() {}
19 44
20void MomentProcessor::StopProcessor() {} 45void MomentProcessor::StopProcessor() {}
21 46
47void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
48 if (type != Core::HID::ControllerTriggerType::IrSensor) {
49 return;
50 }
51
52 next_state = {};
53 const auto& camera_data = npad_device->GetCamera();
54
55 const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width);
56 const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height);
57 const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
58 const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
59
60 const std::size_t block_width = window_width / Columns;
61 const std::size_t block_height = window_height / Rows;
62
63 for (std::size_t row = 0; row < Rows; row++) {
64 for (std::size_t column = 0; column < Columns; column++) {
65 const size_t x_pos = (column * block_width) + window_start_x;
66 const size_t y_pos = (row * block_height) + window_start_y;
67 auto& statistic = next_state.statistic[column + (row * Columns)];
68 statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height);
69 }
70 }
71
72 next_state.sampling_number = camera_data.sample;
73 next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
74 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
75 shared_memory->moment_lifo.WriteNextEntry(next_state);
76
77 if (!IsProcessorActive()) {
78 StartProcessor();
79 }
80}
81
82u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
83 if ((y * ImageWidth) + x >= data.size()) {
84 return 0;
85 }
86 return data[(y * ImageWidth) + x];
87}
88
89MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data,
90 std::size_t start_x,
91 std::size_t start_y,
92 std::size_t width,
93 std::size_t height) const {
94 // The actual implementation is always 320x240
95 static constexpr std::size_t RealWidth = 320;
96 static constexpr std::size_t RealHeight = 240;
97 static constexpr std::size_t Threshold = 30;
98 MomentStatistic statistic{};
99 std::size_t active_points{};
100
101 // Sum all data points on the block that meet with the threshold
102 for (std::size_t y = 0; y < width; y++) {
103 for (std::size_t x = 0; x < height; x++) {
104 const size_t x_pos = x + start_x;
105 const size_t y_pos = y + start_y;
106 const auto pixel =
107 GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight);
108
109 if (pixel < Threshold) {
110 continue;
111 }
112
113 statistic.average_intensity += pixel;
114
115 statistic.centroid.x += static_cast<float>(x_pos);
116 statistic.centroid.y += static_cast<float>(y_pos);
117
118 active_points++;
119 }
120 }
121
122 // Return an empty field if no points were available
123 if (active_points == 0) {
124 return {};
125 }
126
127 // Finally calculate the actual centroid and average intensity
128 statistic.centroid.x /= static_cast<float>(active_points);
129 statistic.centroid.y /= static_cast<float>(active_points);
130 statistic.average_intensity /= static_cast<f32>(width * height);
131
132 return statistic;
133}
134
22void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { 135void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
23 current_config.camera_config.exposure_time = config.camera_config.exposure_time; 136 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
24 current_config.camera_config.gain = config.camera_config.gain; 137 current_config.camera_config.gain = config.camera_config.gain;
@@ -29,6 +142,8 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf
29 current_config.preprocess = 142 current_config.preprocess =
30 static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); 143 static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
31 current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; 144 current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
145
146 npad_device->SetCameraFormat(format);
32} 147}
33 148
34} // namespace Service::IRS 149} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
index d4bd22e0f..398cfbdc1 100644
--- a/src/core/hle/service/hid/irsensor/moment_processor.h
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -6,12 +6,22 @@
6#include "common/bit_field.h" 6#include "common/bit_field.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hid/irs_types.h" 8#include "core/hid/irs_types.h"
9#include "core/hle/service/hid/irs_ring_lifo.h"
9#include "core/hle/service/hid/irsensor/processor_base.h" 10#include "core/hle/service/hid/irsensor/processor_base.h"
10 11
12namespace Core {
13class System;
14}
15
16namespace Core::HID {
17class EmulatedController;
18} // namespace Core::HID
19
11namespace Service::IRS { 20namespace Service::IRS {
12class MomentProcessor final : public ProcessorBase { 21class MomentProcessor final : public ProcessorBase {
13public: 22public:
14 explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); 23 explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
24 std::size_t npad_index);
15 ~MomentProcessor() override; 25 ~MomentProcessor() override;
16 26
17 // Called when the processor is initialized 27 // Called when the processor is initialized
@@ -27,6 +37,9 @@ public:
27 void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); 37 void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
28 38
29private: 39private:
40 static constexpr std::size_t Columns = 8;
41 static constexpr std::size_t Rows = 6;
42
30 // This is nn::irsensor::MomentProcessorConfig 43 // This is nn::irsensor::MomentProcessorConfig
31 struct MomentProcessorConfig { 44 struct MomentProcessorConfig {
32 Core::IrSensor::CameraConfig camera_config; 45 Core::IrSensor::CameraConfig camera_config;
@@ -50,12 +63,29 @@ private:
50 u64 timestamp; 63 u64 timestamp;
51 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; 64 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
52 INSERT_PADDING_BYTES(4); 65 INSERT_PADDING_BYTES(4);
53 std::array<MomentStatistic, 0x30> stadistic; 66 std::array<MomentStatistic, Columns * Rows> statistic;
54 }; 67 };
55 static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); 68 static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
56 69
70 struct MomentSharedMemory {
71 Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo;
72 };
73 static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size");
74
75 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
76 u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
77 MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x,
78 std::size_t start_y, std::size_t width, std::size_t height) const;
79
80 MomentSharedMemory* shared_memory = nullptr;
81 MomentProcessorState next_state{};
82
57 MomentProcessorConfig current_config{}; 83 MomentProcessorConfig current_config{};
58 Core::IrSensor::DeviceFormat& device; 84 Core::IrSensor::DeviceFormat& device;
85 Core::HID::EmulatedController* npad_device;
86 int callback_key{};
87
88 Core::System& system;
59}; 89};
60 90
61} // namespace Service::IRS 91} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
new file mode 100644
index 000000000..e76d4eea9
--- /dev/null
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -0,0 +1,241 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/kernel/k_shared_memory.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h"
11
12#include "core/hle/service/hid/controllers/console_six_axis.h"
13#include "core/hle/service/hid/controllers/debug_pad.h"
14#include "core/hle/service/hid/controllers/gesture.h"
15#include "core/hle/service/hid/controllers/keyboard.h"
16#include "core/hle/service/hid/controllers/mouse.h"
17#include "core/hle/service/hid/controllers/npad.h"
18#include "core/hle/service/hid/controllers/palma.h"
19#include "core/hle/service/hid/controllers/seven_six_axis.h"
20#include "core/hle/service/hid/controllers/six_axis.h"
21#include "core/hle/service/hid/controllers/stubbed.h"
22#include "core/hle/service/hid/controllers/touchscreen.h"
23#include "core/hle/service/hid/controllers/xpad.h"
24
25namespace Service::HID {
26
27// Updating period for each HID device.
28// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
29// Correct npad_update_ns is 4ms this is overclocked to lower input lag
30constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
31constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
32constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
33constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
34
35ResourceManager::ResourceManager(Core::System& system_)
36 : system{system_}, service_context{system_, "hid"} {}
37
38ResourceManager::~ResourceManager() = default;
39
40void ResourceManager::Initialize() {
41 if (is_initialized) {
42 return;
43 }
44
45 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
46 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory);
47 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory);
48 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory);
49 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory);
50 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory);
51 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context);
52 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory);
53 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory);
54 xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory);
55
56 palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context);
57
58 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory);
59 sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory);
60 capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory);
61
62 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
63 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory);
64 seven_six_axis = std::make_shared<SevenSixAxis>(system);
65
66 home_button->SetCommonHeaderOffset(0x4C00);
67 sleep_button->SetCommonHeaderOffset(0x4E00);
68 capture_button->SetCommonHeaderOffset(0x5000);
69 unique_pad->SetCommonHeaderOffset(0x5A00);
70 debug_mouse->SetCommonHeaderOffset(0x3DC00);
71
72 // Homebrew doesn't try to activate some controllers, so we activate them by default
73 npad->Activate();
74 six_axis->Activate();
75 touch_screen->Activate();
76
77 system.HIDCore().ReloadInputDevices();
78 is_initialized = true;
79}
80std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const {
81 return capture_button;
82}
83
84std::shared_ptr<ConsoleSixAxis> ResourceManager::GetConsoleSixAxis() const {
85 return console_six_axis;
86}
87
88std::shared_ptr<DebugMouse> ResourceManager::GetDebugMouse() const {
89 return debug_mouse;
90}
91
92std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const {
93 return debug_pad;
94}
95
96std::shared_ptr<Gesture> ResourceManager::GetGesture() const {
97 return gesture;
98}
99
100std::shared_ptr<HomeButton> ResourceManager::GetHomeButton() const {
101 return home_button;
102}
103
104std::shared_ptr<Keyboard> ResourceManager::GetKeyboard() const {
105 return keyboard;
106}
107
108std::shared_ptr<Mouse> ResourceManager::GetMouse() const {
109 return mouse;
110}
111
112std::shared_ptr<NPad> ResourceManager::GetNpad() const {
113 return npad;
114}
115
116std::shared_ptr<Palma> ResourceManager::GetPalma() const {
117 return palma;
118}
119
120std::shared_ptr<SevenSixAxis> ResourceManager::GetSevenSixAxis() const {
121 return seven_six_axis;
122}
123
124std::shared_ptr<SixAxis> ResourceManager::GetSixAxis() const {
125 return six_axis;
126}
127
128std::shared_ptr<SleepButton> ResourceManager::GetSleepButton() const {
129 return sleep_button;
130}
131
132std::shared_ptr<TouchScreen> ResourceManager::GetTouchScreen() const {
133 return touch_screen;
134}
135
136std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
137 return unique_pad;
138}
139
140void ResourceManager::UpdateControllers(std::uintptr_t user_data,
141 std::chrono::nanoseconds ns_late) {
142 auto& core_timing = system.CoreTiming();
143 debug_pad->OnUpdate(core_timing);
144 unique_pad->OnUpdate(core_timing);
145 gesture->OnUpdate(core_timing);
146 touch_screen->OnUpdate(core_timing);
147 palma->OnUpdate(core_timing);
148 home_button->OnUpdate(core_timing);
149 sleep_button->OnUpdate(core_timing);
150 capture_button->OnUpdate(core_timing);
151 xpad->OnUpdate(core_timing);
152}
153
154void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
155 auto& core_timing = system.CoreTiming();
156 npad->OnUpdate(core_timing);
157}
158
159void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
160 std::chrono::nanoseconds ns_late) {
161 auto& core_timing = system.CoreTiming();
162 mouse->OnUpdate(core_timing);
163 debug_mouse->OnUpdate(core_timing);
164 keyboard->OnUpdate(core_timing);
165}
166
167void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
168 auto& core_timing = system.CoreTiming();
169 six_axis->OnUpdate(core_timing);
170 seven_six_axis->OnUpdate(core_timing);
171 console_six_axis->OnUpdate(core_timing);
172}
173
174IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
175 : ServiceFramework{system_, "IAppletResource"} {
176 static const FunctionInfo functions[] = {
177 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
178 };
179 RegisterHandlers(functions);
180
181 resource->Initialize();
182
183 // Register update callbacks
184 npad_update_event = Core::Timing::CreateEvent(
185 "HID::UpdatePadCallback",
186 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
187 -> std::optional<std::chrono::nanoseconds> {
188 const auto guard = LockService();
189 resource->UpdateNpad(user_data, ns_late);
190 return std::nullopt;
191 });
192 default_update_event = Core::Timing::CreateEvent(
193 "HID::UpdateDefaultCallback",
194 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
195 -> std::optional<std::chrono::nanoseconds> {
196 const auto guard = LockService();
197 resource->UpdateControllers(user_data, ns_late);
198 return std::nullopt;
199 });
200 mouse_keyboard_update_event = Core::Timing::CreateEvent(
201 "HID::UpdateMouseKeyboardCallback",
202 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
203 -> std::optional<std::chrono::nanoseconds> {
204 const auto guard = LockService();
205 resource->UpdateMouseKeyboard(user_data, ns_late);
206 return std::nullopt;
207 });
208 motion_update_event = Core::Timing::CreateEvent(
209 "HID::UpdateMotionCallback",
210 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
211 -> std::optional<std::chrono::nanoseconds> {
212 const auto guard = LockService();
213 resource->UpdateMotion(user_data, ns_late);
214 return std::nullopt;
215 });
216
217 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
218 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
219 default_update_event);
220 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
221 mouse_keyboard_update_event);
222 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
223 motion_update_event);
224}
225
226IAppletResource::~IAppletResource() {
227 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
228 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
229 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
230 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
231}
232
233void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
234 LOG_DEBUG(Service_HID, "called");
235
236 IPC::ResponseBuilder rb{ctx, 2, 1};
237 rb.Push(ResultSuccess);
238 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
239}
240
241} // namespace Service::HID
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
new file mode 100644
index 000000000..2b6a9b5e6
--- /dev/null
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Core::Timing {
10struct EventType;
11}
12
13namespace Service::HID {
14class Controller_Stubbed;
15class ConsoleSixAxis;
16class DebugPad;
17class Gesture;
18class Keyboard;
19class Mouse;
20class NPad;
21class Palma;
22class SevenSixAxis;
23class SixAxis;
24class TouchScreen;
25class XPad;
26
27using CaptureButton = Controller_Stubbed;
28using DebugMouse = Controller_Stubbed;
29using HomeButton = Controller_Stubbed;
30using SleepButton = Controller_Stubbed;
31using UniquePad = Controller_Stubbed;
32
33class ResourceManager {
34
35public:
36 explicit ResourceManager(Core::System& system_);
37 ~ResourceManager();
38
39 void Initialize();
40
41 std::shared_ptr<CaptureButton> GetCaptureButton() const;
42 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const;
43 std::shared_ptr<DebugMouse> GetDebugMouse() const;
44 std::shared_ptr<DebugPad> GetDebugPad() const;
45 std::shared_ptr<Gesture> GetGesture() const;
46 std::shared_ptr<HomeButton> GetHomeButton() const;
47 std::shared_ptr<Keyboard> GetKeyboard() const;
48 std::shared_ptr<Mouse> GetMouse() const;
49 std::shared_ptr<NPad> GetNpad() const;
50 std::shared_ptr<Palma> GetPalma() const;
51 std::shared_ptr<SevenSixAxis> GetSevenSixAxis() const;
52 std::shared_ptr<SixAxis> GetSixAxis() const;
53 std::shared_ptr<SleepButton> GetSleepButton() const;
54 std::shared_ptr<TouchScreen> GetTouchScreen() const;
55 std::shared_ptr<UniquePad> GetUniquePad() const;
56
57 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
58 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
59 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
60 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
61
62private:
63 bool is_initialized{false};
64
65 std::shared_ptr<CaptureButton> capture_button = nullptr;
66 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
67 std::shared_ptr<DebugMouse> debug_mouse = nullptr;
68 std::shared_ptr<DebugPad> debug_pad = nullptr;
69 std::shared_ptr<Gesture> gesture = nullptr;
70 std::shared_ptr<HomeButton> home_button = nullptr;
71 std::shared_ptr<Keyboard> keyboard = nullptr;
72 std::shared_ptr<Mouse> mouse = nullptr;
73 std::shared_ptr<NPad> npad = nullptr;
74 std::shared_ptr<Palma> palma = nullptr;
75 std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr;
76 std::shared_ptr<SixAxis> six_axis = nullptr;
77 std::shared_ptr<SleepButton> sleep_button = nullptr;
78 std::shared_ptr<TouchScreen> touch_screen = nullptr;
79 std::shared_ptr<UniquePad> unique_pad = nullptr;
80 std::shared_ptr<XPad> xpad = nullptr;
81
82 // TODO: Create these resources
83 // std::shared_ptr<AudioControl> audio_control = nullptr;
84 // std::shared_ptr<ButtonConfig> button_config = nullptr;
85 // std::shared_ptr<Config> config = nullptr;
86 // std::shared_ptr<Connection> connection = nullptr;
87 // std::shared_ptr<CustomConfig> custom_config = nullptr;
88 // std::shared_ptr<Digitizer> digitizer = nullptr;
89 // std::shared_ptr<Hdls> hdls = nullptr;
90 // std::shared_ptr<PlayReport> play_report = nullptr;
91 // std::shared_ptr<Rail> rail = nullptr;
92
93 Core::System& system;
94 KernelHelpers::ServiceContext service_context;
95};
96
97class IAppletResource final : public ServiceFramework<IAppletResource> {
98public:
99 explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
100 ~IAppletResource() override;
101
102private:
103 void GetSharedMemoryHandle(HLERequestContext& ctx);
104
105 std::shared_ptr<Core::Timing::EventType> npad_update_event;
106 std::shared_ptr<Core::Timing::EventType> default_update_event;
107 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
108 std::shared_ptr<Core::Timing::EventType> motion_update_event;
109};
110
111} // namespace Service::HID
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h
index 65eb7ea02..0816784e0 100644
--- a/src/core/hle/service/hid/ring_lifo.h
+++ b/src/core/hle/service/hid/ring_lifo.h
@@ -32,15 +32,15 @@ struct Lifo {
32 } 32 }
33 33
34 std::size_t GetPreviousEntryIndex() const { 34 std::size_t GetPreviousEntryIndex() const {
35 return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count); 35 return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size);
36 } 36 }
37 37
38 std::size_t GetNextEntryIndex() const { 38 std::size_t GetNextEntryIndex() const {
39 return static_cast<size_t>((buffer_tail + 1) % total_buffer_count); 39 return static_cast<size_t>((buffer_tail + 1) % max_buffer_size);
40 } 40 }
41 41
42 void WriteNextEntry(const State& new_state) { 42 void WriteNextEntry(const State& new_state) {
43 if (buffer_count < total_buffer_count - 1) { 43 if (buffer_count < static_cast<s64>(max_buffer_size) - 1) {
44 buffer_count++; 44 buffer_count++;
45 } 45 }
46 buffer_tail = GetNextEntryIndex(); 46 buffer_tail = GetNextEntryIndex();
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 7927f8264..961f89a14 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -115,12 +115,20 @@ public:
115 {400, nullptr, "InitializeSystem"}, 115 {400, nullptr, "InitializeSystem"},
116 {401, nullptr, "FinalizeSystem"}, 116 {401, nullptr, "FinalizeSystem"},
117 {402, nullptr, "SetOperationMode"}, 117 {402, nullptr, "SetOperationMode"},
118 {403, nullptr, "InitializeSystem2"}, 118 {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
119 }; 119 };
120 // clang-format on 120 // clang-format on
121 121
122 RegisterHandlers(functions); 122 RegisterHandlers(functions);
123 } 123 }
124
125private:
126 void InitializeSystem2(HLERequestContext& ctx) {
127 LOG_WARNING(Service_LDN, "(STUBBED) called");
128
129 IPC::ResponseBuilder rb{ctx, 2};
130 rb.Push(ResultSuccess);
131 }
124}; 132};
125 133
126class IUserLocalCommunicationService final 134class IUserLocalCommunicationService final
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index c73035c77..97b6a9385 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -286,9 +286,14 @@ public:
286 rb.Push(ResultSuccess); 286 rb.Push(ResultSuccess);
287 } 287 }
288 288
289 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { 289 bool ValidateRegionForMap(Kernel::KProcessPageTable& page_table, VAddr start,
290 std::size_t size) const {
290 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; 291 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
291 const auto start_info{page_table.QueryInfo(start - 1)}; 292
293 Kernel::KMemoryInfo start_info;
294 Kernel::Svc::PageInfo page_info;
295 R_ASSERT(
296 page_table.QueryInfo(std::addressof(start_info), std::addressof(page_info), start - 1));
292 297
293 if (start_info.GetState() != Kernel::KMemoryState::Free) { 298 if (start_info.GetState() != Kernel::KMemoryState::Free) {
294 return {}; 299 return {};
@@ -298,7 +303,9 @@ public:
298 return {}; 303 return {};
299 } 304 }
300 305
301 const auto end_info{page_table.QueryInfo(start + size)}; 306 Kernel::KMemoryInfo end_info;
307 R_ASSERT(page_table.QueryInfo(std::addressof(end_info), std::addressof(page_info),
308 start + size));
302 309
303 if (end_info.GetState() != Kernel::KMemoryState::Free) { 310 if (end_info.GetState() != Kernel::KMemoryState::Free) {
304 return {}; 311 return {};
@@ -307,7 +314,7 @@ public:
307 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); 314 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
308 } 315 }
309 316
310 Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { 317 Result GetAvailableMapRegion(Kernel::KProcessPageTable& page_table, u64 size, VAddr& out_addr) {
311 size = Common::AlignUp(size, Kernel::PageSize); 318 size = Common::AlignUp(size, Kernel::PageSize);
312 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; 319 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
313 320
@@ -391,12 +398,8 @@ public:
391 398
392 if (bss_size) { 399 if (bss_size) {
393 auto block_guard = detail::ScopeExit([&] { 400 auto block_guard = detail::ScopeExit([&] {
394 page_table.UnmapCodeMemory( 401 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
395 addr + nro_size, bss_addr, bss_size, 402 page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
396 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
397 page_table.UnmapCodeMemory(
398 addr, nro_addr, nro_size,
399 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
400 }); 403 });
401 404
402 const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; 405 const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
@@ -578,21 +581,17 @@ public:
578 auto& page_table{system.ApplicationProcess()->GetPageTable()}; 581 auto& page_table{system.ApplicationProcess()->GetPageTable()};
579 582
580 if (info.bss_size != 0) { 583 if (info.bss_size != 0) {
581 R_TRY(page_table.UnmapCodeMemory( 584 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size +
582 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, 585 info.data_size,
583 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); 586 info.bss_address, info.bss_size));
584 } 587 }
585 588
586 R_TRY(page_table.UnmapCodeMemory( 589 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
587 info.nro_address + info.text_size + info.ro_size, 590 info.src_addr + info.text_size + info.ro_size,
588 info.src_addr + info.text_size + info.ro_size, info.data_size, 591 info.data_size));
589 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); 592 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
590 R_TRY(page_table.UnmapCodeMemory( 593 info.src_addr + info.text_size, info.ro_size));
591 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, 594 R_TRY(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
592 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
593 R_TRY(page_table.UnmapCodeMemory(
594 info.nro_address, info.src_addr, info.text_size,
595 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
596 return ResultSuccess; 595 return ResultSuccess;
597 } 596 }
598 597
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index a71d26157..ad534177d 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -7,6 +7,7 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
9#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/hid/hid_util.h"
10#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
11#include "core/hle/service/nfc/common/device.h" 12#include "core/hle/service/nfc/common/device.h"
12#include "core/hle/service/nfc/common/device_manager.h" 13#include "core/hle/service/nfc/common/device_manager.h"
@@ -24,7 +25,7 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex
24 25
25 for (u32 device_index = 0; device_index < devices.size(); device_index++) { 26 for (u32 device_index = 0; device_index < devices.size(); device_index++) {
26 devices[device_index] = 27 devices[device_index] =
27 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system, 28 std::make_shared<NfcDevice>(HID::IndexToNpadIdType(device_index), system,
28 service_context, availability_change_event); 29 service_context, availability_change_event);
29 } 30 }
30 31
diff --git a/src/core/hle/service/nvdrv/devices/ioctl_serialization.h b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h
new file mode 100644
index 000000000..b12bcd138
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h
@@ -0,0 +1,159 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <vector>
8
9#include "common/concepts.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h"
11
12namespace Service::Nvidia::Devices {
13
14struct IoctlOneArgTraits {
15 template <typename T, typename R, typename A, typename... B>
16 static A GetFirstArgImpl(R (T::*)(A, B...));
17};
18
19struct IoctlTwoArgTraits {
20 template <typename T, typename R, typename A, typename B, typename... C>
21 static A GetFirstArgImpl(R (T::*)(A, B, C...));
22
23 template <typename T, typename R, typename A, typename B, typename... C>
24 static B GetSecondArgImpl(R (T::*)(A, B, C...));
25};
26
27struct Null {};
28
29// clang-format off
30
31template <typename FixedArg, typename VarArg, typename InlInVarArg, typename InlOutVarArg, typename F>
32NvResult WrapGeneric(F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, std::span<u8> inline_output) {
33 constexpr bool HasFixedArg = !std::is_same_v<FixedArg, Null>;
34 constexpr bool HasVarArg = !std::is_same_v<VarArg, Null>;
35 constexpr bool HasInlInVarArg = !std::is_same_v<InlInVarArg, Null>;
36 constexpr bool HasInlOutVarArg = !std::is_same_v<InlOutVarArg, Null>;
37
38 // Declare the fixed-size input value.
39 FixedArg fixed{};
40 size_t var_offset = 0;
41
42 if constexpr (HasFixedArg) {
43 // Read the fixed-size input value.
44 var_offset = std::min(sizeof(FixedArg), input.size());
45 if (var_offset > 0) {
46 std::memcpy(&fixed, input.data(), var_offset);
47 }
48 }
49
50 // Read the variable-sized inputs.
51 const size_t num_var_args = HasVarArg ? ((input.size() - var_offset) / sizeof(VarArg)) : 0;
52 std::vector<VarArg> var_args(num_var_args);
53 if constexpr (HasVarArg) {
54 if (num_var_args > 0) {
55 std::memcpy(var_args.data(), input.data() + var_offset, num_var_args * sizeof(VarArg));
56 }
57 }
58
59 const size_t num_inl_in_var_args = HasInlInVarArg ? (inline_input.size() / sizeof(InlInVarArg)) : 0;
60 std::vector<InlInVarArg> inl_in_var_args(num_inl_in_var_args);
61 if constexpr (HasInlInVarArg) {
62 if (num_inl_in_var_args > 0) {
63 std::memcpy(inl_in_var_args.data(), inline_input.data(), num_inl_in_var_args * sizeof(InlInVarArg));
64 }
65 }
66
67 // Construct inline output data.
68 const size_t num_inl_out_var_args = HasInlOutVarArg ? (inline_output.size() / sizeof(InlOutVarArg)) : 0;
69 std::vector<InlOutVarArg> inl_out_var_args(num_inl_out_var_args);
70
71 // Perform the call.
72 NvResult result = callable(fixed, var_args, inl_in_var_args, inl_out_var_args);
73
74 // Copy outputs.
75 if constexpr (HasFixedArg) {
76 if (output.size() > 0) {
77 std::memcpy(output.data(), &fixed, std::min(output.size(), sizeof(FixedArg)));
78 }
79 }
80
81 if constexpr (HasVarArg) {
82 if (num_var_args > 0 && output.size() > var_offset) {
83 const size_t max_var_size = output.size() - var_offset;
84 std::memcpy(output.data() + var_offset, var_args.data(), std::min(max_var_size, num_var_args * sizeof(VarArg)));
85 }
86 }
87
88 // Copy inline outputs.
89 if constexpr (HasInlOutVarArg) {
90 if (num_inl_out_var_args > 0) {
91 std::memcpy(inline_output.data(), inl_out_var_args.data(), num_inl_out_var_args * sizeof(InlOutVarArg));
92 }
93 }
94
95 // We're done.
96 return result;
97}
98
99template <typename Self, typename F, typename... Rest>
100NvResult WrapFixed(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
101 using FixedArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>;
102
103 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
104 return (self->*callable)(fixed, std::forward<Rest>(rest)...);
105 };
106
107 return WrapGeneric<FixedArg, Null, Null, Null>(std::move(Callable), input, {}, output, {});
108}
109
110template <typename Self, typename F, typename... Rest>
111NvResult WrapFixedInlOut(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output, Rest&&... rest) {
112 using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
113 using InlOutVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
114
115 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
116 return (self->*callable)(fixed, inl_out, std::forward<Rest>(rest)...);
117 };
118
119 return WrapGeneric<FixedArg, Null, Null, InlOutVarArg>(std::move(Callable), input, {}, output, inline_output);
120}
121
122template <typename Self, typename F, typename... Rest>
123NvResult WrapVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
124 using VarArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>::value_type;
125
126 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
127 return (self->*callable)(var, std::forward<Rest>(rest)...);
128 };
129
130 return WrapGeneric<Null, VarArg, Null, Null>(std::move(Callable), input, {}, output, {});
131}
132
133template <typename Self, typename F, typename... Rest>
134NvResult WrapFixedVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
135 using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
136 using VarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
137
138 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
139 return (self->*callable)(fixed, var, std::forward<Rest>(rest)...);
140 };
141
142 return WrapGeneric<FixedArg, VarArg, Null, Null>(std::move(Callable), input, {}, output, {});
143}
144
145template <typename Self, typename F, typename... Rest>
146NvResult WrapFixedInlIn(Self* self, F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, Rest&&... rest) {
147 using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
148 using InlInVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
149
150 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
151 return (self->*callable)(fixed, inl_in, std::forward<Rest>(rest)...);
152 };
153
154 return WrapGeneric<FixedArg, Null, InlInVarArg, Null>(std::move(Callable), input, inline_input, output, {});
155}
156
157// clang-format on
158
159} // namespace Service::Nvidia::Devices
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 7d7bb8687..6b3639008 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -11,6 +11,7 @@
11#include "core/core.h" 11#include "core/core.h"
12#include "core/hle/service/nvdrv/core/container.h" 12#include "core/hle/service/nvdrv/core/container.h"
13#include "core/hle/service/nvdrv/core/nvmap.h" 13#include "core/hle/service/nvdrv/core/nvmap.h"
14#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
14#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 15#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
15#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 16#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
16#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
@@ -33,21 +34,21 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i
33 case 'A': 34 case 'A':
34 switch (command.cmd) { 35 switch (command.cmd) {
35 case 0x1: 36 case 0x1:
36 return BindChannel(input, output); 37 return WrapFixed(this, &nvhost_as_gpu::BindChannel, input, output);
37 case 0x2: 38 case 0x2:
38 return AllocateSpace(input, output); 39 return WrapFixed(this, &nvhost_as_gpu::AllocateSpace, input, output);
39 case 0x3: 40 case 0x3:
40 return FreeSpace(input, output); 41 return WrapFixed(this, &nvhost_as_gpu::FreeSpace, input, output);
41 case 0x5: 42 case 0x5:
42 return UnmapBuffer(input, output); 43 return WrapFixed(this, &nvhost_as_gpu::UnmapBuffer, input, output);
43 case 0x6: 44 case 0x6:
44 return MapBufferEx(input, output); 45 return WrapFixed(this, &nvhost_as_gpu::MapBufferEx, input, output);
45 case 0x8: 46 case 0x8:
46 return GetVARegions(input, output); 47 return WrapFixed(this, &nvhost_as_gpu::GetVARegions1, input, output);
47 case 0x9: 48 case 0x9:
48 return AllocAsEx(input, output); 49 return WrapFixed(this, &nvhost_as_gpu::AllocAsEx, input, output);
49 case 0x14: 50 case 0x14:
50 return Remap(input, output); 51 return WrapVariable(this, &nvhost_as_gpu::Remap, input, output);
51 default: 52 default:
52 break; 53 break;
53 } 54 }
@@ -72,7 +73,8 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
72 case 'A': 73 case 'A':
73 switch (command.cmd) { 74 switch (command.cmd) {
74 case 0x8: 75 case 0x8:
75 return GetVARegions(input, output, inline_output); 76 return WrapFixedInlOut(this, &nvhost_as_gpu::GetVARegions3, input, output,
77 inline_output);
76 default: 78 default:
77 break; 79 break;
78 } 80 }
@@ -87,10 +89,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
87void nvhost_as_gpu::OnOpen(DeviceFD fd) {} 89void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
88void nvhost_as_gpu::OnClose(DeviceFD fd) {} 90void nvhost_as_gpu::OnClose(DeviceFD fd) {}
89 91
90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) { 92NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
91 IoctlAllocAsEx params{};
92 std::memcpy(&params, input.data(), input.size());
93
94 LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); 93 LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
95 94
96 std::scoped_lock lock(mutex); 95 std::scoped_lock lock(mutex);
@@ -141,10 +140,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> outpu
141 return NvResult::Success; 140 return NvResult::Success;
142} 141}
143 142
144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) { 143NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
145 IoctlAllocSpace params{};
146 std::memcpy(&params, input.data(), input.size());
147
148 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 144 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
149 params.page_size, params.flags); 145 params.page_size, params.flags);
150 146
@@ -194,7 +190,6 @@ NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> o
194 .big_pages = params.page_size != VM::YUZU_PAGESIZE, 190 .big_pages = params.page_size != VM::YUZU_PAGESIZE,
195 }; 191 };
196 192
197 std::memcpy(output.data(), &params, output.size());
198 return NvResult::Success; 193 return NvResult::Success;
199} 194}
200 195
@@ -222,10 +217,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
222 mapping_map.erase(offset); 217 mapping_map.erase(offset);
223} 218}
224 219
225NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) { 220NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
226 IoctlFreeSpace params{};
227 std::memcpy(&params, input.data(), input.size());
228
229 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, 221 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
230 params.pages, params.page_size); 222 params.pages, params.page_size);
231 223
@@ -264,18 +256,11 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> outpu
264 return NvResult::BadValue; 256 return NvResult::BadValue;
265 } 257 }
266 258
267 std::memcpy(output.data(), &params, output.size());
268 return NvResult::Success; 259 return NvResult::Success;
269} 260}
270 261
271NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { 262NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
272 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 263 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size());
273
274 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
275
276 std::scoped_lock lock(mutex);
277 entries.resize_destructive(num_entries);
278 std::memcpy(entries.data(), input.data(), input.size());
279 264
280 if (!vm.initialised) { 265 if (!vm.initialised) {
281 return NvResult::BadValue; 266 return NvResult::BadValue;
@@ -317,14 +302,10 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
317 } 302 }
318 } 303 }
319 304
320 std::memcpy(output.data(), entries.data(), output.size());
321 return NvResult::Success; 305 return NvResult::Success;
322} 306}
323 307
324NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) { 308NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
325 IoctlMapBufferEx params{};
326 std::memcpy(&params, input.data(), input.size());
327
328 LOG_DEBUG(Service_NVDRV, 309 LOG_DEBUG(Service_NVDRV,
329 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" 310 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
330 ", offset={}", 311 ", offset={}",
@@ -421,14 +402,10 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> out
421 mapping_map[params.offset] = mapping; 402 mapping_map[params.offset] = mapping;
422 } 403 }
423 404
424 std::memcpy(output.data(), &params, output.size());
425 return NvResult::Success; 405 return NvResult::Success;
426} 406}
427 407
428NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { 408NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
429 IoctlUnmapBuffer params{};
430 std::memcpy(&params, input.data(), input.size());
431
432 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 409 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
433 410
434 std::scoped_lock lock(mutex); 411 std::scoped_lock lock(mutex);
@@ -464,9 +441,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> out
464 return NvResult::Success; 441 return NvResult::Success;
465} 442}
466 443
467NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) { 444NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
468 IoctlBindChannel params{};
469 std::memcpy(&params, input.data(), input.size());
470 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 445 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
471 446
472 auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); 447 auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
@@ -493,10 +468,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
493 }; 468 };
494} 469}
495 470
496NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) { 471NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) {
497 IoctlGetVaRegions params{};
498 std::memcpy(&params, input.data(), input.size());
499
500 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 472 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
501 params.buf_size); 473 params.buf_size);
502 474
@@ -508,15 +480,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
508 480
509 GetVARegionsImpl(params); 481 GetVARegionsImpl(params);
510 482
511 std::memcpy(output.data(), &params, output.size());
512 return NvResult::Success; 483 return NvResult::Success;
513} 484}
514 485
515NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, 486NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions) {
516 std::span<u8> inline_output) {
517 IoctlGetVaRegions params{};
518 std::memcpy(&params, input.data(), input.size());
519
520 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 487 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
521 params.buf_size); 488 params.buf_size);
522 489
@@ -528,9 +495,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
528 495
529 GetVARegionsImpl(params); 496 GetVARegionsImpl(params);
530 497
531 std::memcpy(output.data(), &params, output.size()); 498 const size_t num_regions = std::min(params.regions.size(), regions.size());
532 std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion)); 499 for (size_t i = 0; i < num_regions; i++) {
533 std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion)); 500 regions[i] = params.regions[i];
501 }
534 502
535 return NvResult::Success; 503 return NvResult::Success;
536} 504}
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 2af3e1260..932997e75 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -139,18 +139,17 @@ private:
139 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, 139 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
140 "IoctlGetVaRegions is incorrect size"); 140 "IoctlGetVaRegions is incorrect size");
141 141
142 NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); 142 NvResult AllocAsEx(IoctlAllocAsEx& params);
143 NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); 143 NvResult AllocateSpace(IoctlAllocSpace& params);
144 NvResult Remap(std::span<const u8> input, std::span<u8> output); 144 NvResult Remap(std::span<IoctlRemapEntry> params);
145 NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); 145 NvResult MapBufferEx(IoctlMapBufferEx& params);
146 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); 146 NvResult UnmapBuffer(IoctlUnmapBuffer& params);
147 NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); 147 NvResult FreeSpace(IoctlFreeSpace& params);
148 NvResult BindChannel(std::span<const u8> input, std::span<u8> output); 148 NvResult BindChannel(IoctlBindChannel& params);
149 149
150 void GetVARegionsImpl(IoctlGetVaRegions& params); 150 void GetVARegionsImpl(IoctlGetVaRegions& params);
151 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); 151 NvResult GetVARegions1(IoctlGetVaRegions& params);
152 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, 152 NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
153 std::span<u8> inline_output);
154 153
155 void FreeMappingLocked(u64 offset); 154 void FreeMappingLocked(u64 offset);
156 155
@@ -213,7 +212,6 @@ private:
213 bool initialised{}; 212 bool initialised{};
214 } vm; 213 } vm;
215 std::shared_ptr<Tegra::MemoryManager> gmmu; 214 std::shared_ptr<Tegra::MemoryManager> gmmu;
216 Common::ScratchBuffer<IoctlRemapEntry> entries;
217 215
218 // s32 channel{}; 216 // s32 channel{};
219 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; 217 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 4d55554b4..b8dd34e24 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -14,6 +14,7 @@
14#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
15#include "core/hle/service/nvdrv/core/container.h" 15#include "core/hle/service/nvdrv/core/container.h"
16#include "core/hle/service/nvdrv/core/syncpoint_manager.h" 16#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
17#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
17#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 18#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
18#include "video_core/gpu.h" 19#include "video_core/gpu.h"
19#include "video_core/host1x/host1x.h" 20#include "video_core/host1x/host1x.h"
@@ -40,19 +41,19 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp
40 case 0x0: 41 case 0x0:
41 switch (command.cmd) { 42 switch (command.cmd) {
42 case 0x1b: 43 case 0x1b:
43 return NvOsGetConfigU32(input, output); 44 return WrapFixed(this, &nvhost_ctrl::NvOsGetConfigU32, input, output);
44 case 0x1c: 45 case 0x1c:
45 return IocCtrlClearEventWait(input, output); 46 return WrapFixed(this, &nvhost_ctrl::IocCtrlClearEventWait, input, output);
46 case 0x1d: 47 case 0x1d:
47 return IocCtrlEventWait(input, output, true); 48 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, true);
48 case 0x1e: 49 case 0x1e:
49 return IocCtrlEventWait(input, output, false); 50 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, false);
50 case 0x1f: 51 case 0x1f:
51 return IocCtrlEventRegister(input, output); 52 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventRegister, input, output);
52 case 0x20: 53 case 0x20:
53 return IocCtrlEventUnregister(input, output); 54 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregister, input, output);
54 case 0x21: 55 case 0x21:
55 return IocCtrlEventUnregisterBatch(input, output); 56 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregisterBatch, input, output);
56 } 57 }
57 break; 58 break;
58 default: 59 default:
@@ -79,25 +80,19 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79 80
80void nvhost_ctrl::OnClose(DeviceFD fd) {} 81void nvhost_ctrl::OnClose(DeviceFD fd) {}
81 82
82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) { 83NvResult nvhost_ctrl::NvOsGetConfigU32(IocGetConfigParams& params) {
83 IocGetConfigParams params{};
84 std::memcpy(&params, input.data(), sizeof(params));
85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 84 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
86 params.param_str.data()); 85 params.param_str.data());
87 return NvResult::ConfigVarNotFound; // Returns error on production mode 86 return NvResult::ConfigVarNotFound; // Returns error on production mode
88} 87}
89 88
90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, 89NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation) {
91 bool is_allocation) {
92 IocCtrlEventWaitParams params{};
93 std::memcpy(&params, input.data(), sizeof(params));
94 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", 90 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
95 params.fence.id, params.fence.value, params.timeout, is_allocation); 91 params.fence.id, params.fence.value, params.timeout, is_allocation);
96 92
97 bool must_unmark_fail = !is_allocation; 93 bool must_unmark_fail = !is_allocation;
98 const u32 event_id = params.value.raw; 94 const u32 event_id = params.value.raw;
99 SCOPE_EXIT({ 95 SCOPE_EXIT({
100 std::memcpy(output.data(), &params, sizeof(params));
101 if (must_unmark_fail) { 96 if (must_unmark_fail) {
102 events[event_id].fails = 0; 97 events[event_id].fails = 0;
103 } 98 }
@@ -231,9 +226,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
231 return NvResult::Success; 226 return NvResult::Success;
232} 227}
233 228
234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) { 229NvResult nvhost_ctrl::IocCtrlEventRegister(IocCtrlEventRegisterParams& params) {
235 IocCtrlEventRegisterParams params{};
236 std::memcpy(&params, input.data(), sizeof(params));
237 const u32 event_id = params.user_event_id; 230 const u32 event_id = params.user_event_id;
238 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 231 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
239 if (event_id >= MaxNvEvents) { 232 if (event_id >= MaxNvEvents) {
@@ -252,9 +245,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<
252 return NvResult::Success; 245 return NvResult::Success;
253} 246}
254 247
255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) { 248NvResult nvhost_ctrl::IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params) {
256 IocCtrlEventUnregisterParams params{};
257 std::memcpy(&params, input.data(), sizeof(params));
258 const u32 event_id = params.user_event_id & 0x00FF; 249 const u32 event_id = params.user_event_id & 0x00FF;
259 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 250 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
260 251
@@ -262,9 +253,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::spa
262 return FreeEvent(event_id); 253 return FreeEvent(event_id);
263} 254}
264 255
265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) { 256NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params) {
266 IocCtrlEventUnregisterBatchParams params{};
267 std::memcpy(&params, input.data(), sizeof(params));
268 u64 event_mask = params.user_events; 257 u64 event_mask = params.user_events;
269 LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); 258 LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask);
270 259
@@ -280,10 +269,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std
280 return NvResult::Success; 269 return NvResult::Success;
281} 270}
282 271
283NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) { 272NvResult nvhost_ctrl::IocCtrlClearEventWait(IocCtrlEventClearParams& params) {
284 IocCtrlEventClearParams params{};
285 std::memcpy(&params, input.data(), sizeof(params));
286
287 u32 event_id = params.event_id.slot; 273 u32 event_id = params.event_id.slot;
288 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); 274 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);
289 275
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 2efed4862..992124b60 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -186,12 +186,12 @@ private:
186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size"); 187 "IocCtrlEventKill is incorrect size");
188 188
189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output); 189 NvResult NvOsGetConfigU32(IocGetConfigParams& params);
190 NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation); 190 NvResult IocCtrlEventRegister(IocCtrlEventRegisterParams& params);
191 NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output); 191 NvResult IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params);
192 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output); 192 NvResult IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params);
193 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output); 193 NvResult IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation);
194 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output); 194 NvResult IocCtrlClearEventWait(IocCtrlEventClearParams& params);
195 195
196 NvResult FreeEvent(u32 slot); 196 NvResult FreeEvent(u32 slot);
197 197
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 6081d92e9..61a2df121 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
9#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 10#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 11#include "core/hle/service/nvdrv/nvdrv.h"
11 12
@@ -27,23 +28,23 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>
27 case 'G': 28 case 'G':
28 switch (command.cmd) { 29 switch (command.cmd) {
29 case 0x1: 30 case 0x1:
30 return ZCullGetCtxSize(input, output); 31 return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetCtxSize, input, output);
31 case 0x2: 32 case 0x2:
32 return ZCullGetInfo(input, output); 33 return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetInfo, input, output);
33 case 0x3: 34 case 0x3:
34 return ZBCSetTable(input, output); 35 return WrapFixed(this, &nvhost_ctrl_gpu::ZBCSetTable, input, output);
35 case 0x4: 36 case 0x4:
36 return ZBCQueryTable(input, output); 37 return WrapFixed(this, &nvhost_ctrl_gpu::ZBCQueryTable, input, output);
37 case 0x5: 38 case 0x5:
38 return GetCharacteristics(input, output); 39 return WrapFixed(this, &nvhost_ctrl_gpu::GetCharacteristics1, input, output);
39 case 0x6: 40 case 0x6:
40 return GetTPCMasks(input, output); 41 return WrapFixed(this, &nvhost_ctrl_gpu::GetTPCMasks1, input, output);
41 case 0x7: 42 case 0x7:
42 return FlushL2(input, output); 43 return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output);
43 case 0x14: 44 case 0x14:
44 return GetActiveSlotMask(input, output); 45 return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output);
45 case 0x1c: 46 case 0x1c:
46 return GetGpuTime(input, output); 47 return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output);
47 default: 48 default:
48 break; 49 break;
49 } 50 }
@@ -65,9 +66,11 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
65 case 'G': 66 case 'G':
66 switch (command.cmd) { 67 switch (command.cmd) {
67 case 0x5: 68 case 0x5:
68 return GetCharacteristics(input, output, inline_output); 69 return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetCharacteristics3, input, output,
70 inline_output);
69 case 0x6: 71 case 0x6:
70 return GetTPCMasks(input, output, inline_output); 72 return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetTPCMasks3, input, output,
73 inline_output);
71 default: 74 default:
72 break; 75 break;
73 } 76 }
@@ -82,10 +85,8 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} 85void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} 86void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
84 87
85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) { 88NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {
86 LOG_DEBUG(Service_NVDRV, "called"); 89 LOG_DEBUG(Service_NVDRV, "called");
87 IoctlCharacteristics params{};
88 std::memcpy(&params, input.data(), input.size());
89 params.gc.arch = 0x120; 90 params.gc.arch = 0x120;
90 params.gc.impl = 0xb; 91 params.gc.impl = 0xb;
91 params.gc.rev = 0xa1; 92 params.gc.rev = 0xa1;
@@ -123,15 +124,13 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa
123 params.gc.gr_compbit_store_base_hw = 0x0; 124 params.gc.gr_compbit_store_base_hw = 0x0;
124 params.gpu_characteristics_buf_size = 0xA0; 125 params.gpu_characteristics_buf_size = 0xA0;
125 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 126 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
126 std::memcpy(output.data(), &params, output.size());
127 return NvResult::Success; 127 return NvResult::Success;
128} 128}
129 129
130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output, 130NvResult nvhost_ctrl_gpu::GetCharacteristics3(
131 std::span<u8> inline_output) { 131 IoctlCharacteristics& params, std::span<IoctlGpuCharacteristics> gpu_characteristics) {
132 LOG_DEBUG(Service_NVDRV, "called"); 132 LOG_DEBUG(Service_NVDRV, "called");
133 IoctlCharacteristics params{}; 133
134 std::memcpy(&params, input.data(), input.size());
135 params.gc.arch = 0x120; 134 params.gc.arch = 0x120;
136 params.gc.impl = 0xb; 135 params.gc.impl = 0xb;
137 params.gc.rev = 0xa1; 136 params.gc.rev = 0xa1;
@@ -169,70 +168,47 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa
169 params.gc.gr_compbit_store_base_hw = 0x0; 168 params.gc.gr_compbit_store_base_hw = 0x0;
170 params.gpu_characteristics_buf_size = 0xA0; 169 params.gpu_characteristics_buf_size = 0xA0;
171 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 170 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
172 171 if (!gpu_characteristics.empty()) {
173 std::memcpy(output.data(), &params, output.size()); 172 gpu_characteristics.front() = params.gc;
174 std::memcpy(inline_output.data(), &params.gc, inline_output.size()); 173 }
175 return NvResult::Success; 174 return NvResult::Success;
176} 175}
177 176
178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) { 177NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
179 IoctlGpuGetTpcMasksArgs params{};
180 std::memcpy(&params, input.data(), input.size());
181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 178 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
182 if (params.mask_buffer_size != 0) { 179 if (params.mask_buffer_size != 0) {
183 params.tcp_mask = 3; 180 params.tcp_mask = 3;
184 } 181 }
185 std::memcpy(output.data(), &params, output.size());
186 return NvResult::Success; 182 return NvResult::Success;
187} 183}
188 184
189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output, 185NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) {
190 std::span<u8> inline_output) {
191 IoctlGpuGetTpcMasksArgs params{};
192 std::memcpy(&params, input.data(), input.size());
193 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 186 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
194 if (params.mask_buffer_size != 0) { 187 if (params.mask_buffer_size != 0) {
195 params.tcp_mask = 3; 188 params.tcp_mask = 3;
196 } 189 }
197 std::memcpy(output.data(), &params, output.size()); 190 if (!tpc_mask.empty()) {
198 std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size()); 191 tpc_mask.front() = params.tcp_mask;
192 }
199 return NvResult::Success; 193 return NvResult::Success;
200} 194}
201 195
202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) { 196NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) {
203 LOG_DEBUG(Service_NVDRV, "called"); 197 LOG_DEBUG(Service_NVDRV, "called");
204 198
205 IoctlActiveSlotMask params{};
206 if (input.size() > 0) {
207 std::memcpy(&params, input.data(), input.size());
208 }
209 params.slot = 0x07; 199 params.slot = 0x07;
210 params.mask = 0x01; 200 params.mask = 0x01;
211 std::memcpy(output.data(), &params, output.size());
212 return NvResult::Success; 201 return NvResult::Success;
213} 202}
214 203
215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) { 204NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(IoctlZcullGetCtxSize& params) {
216 LOG_DEBUG(Service_NVDRV, "called"); 205 LOG_DEBUG(Service_NVDRV, "called");
217
218 IoctlZcullGetCtxSize params{};
219 if (input.size() > 0) {
220 std::memcpy(&params, input.data(), input.size());
221 }
222 params.size = 0x1; 206 params.size = 0x1;
223 std::memcpy(output.data(), &params, output.size());
224 return NvResult::Success; 207 return NvResult::Success;
225} 208}
226 209
227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) { 210NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) {
228 LOG_DEBUG(Service_NVDRV, "called"); 211 LOG_DEBUG(Service_NVDRV, "called");
229
230 IoctlNvgpuGpuZcullGetInfoArgs params{};
231
232 if (input.size() > 0) {
233 std::memcpy(&params, input.data(), input.size());
234 }
235
236 params.width_align_pixels = 0x20; 212 params.width_align_pixels = 0x20;
237 params.height_align_pixels = 0x20; 213 params.height_align_pixels = 0x20;
238 params.pixel_squares_by_aliquots = 0x400; 214 params.pixel_squares_by_aliquots = 0x400;
@@ -243,53 +219,28 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8>
243 params.subregion_width_align_pixels = 0x20; 219 params.subregion_width_align_pixels = 0x20;
244 params.subregion_height_align_pixels = 0x40; 220 params.subregion_height_align_pixels = 0x40;
245 params.subregion_count = 0x10; 221 params.subregion_count = 0x10;
246 std::memcpy(output.data(), &params, output.size());
247 return NvResult::Success; 222 return NvResult::Success;
248} 223}
249 224
250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) { 225NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
251 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 226 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
252
253 IoctlZbcSetTable params{};
254 std::memcpy(&params, input.data(), input.size());
255 // TODO(ogniK): What does this even actually do? 227 // TODO(ogniK): What does this even actually do?
256
257 // Prevent null pointer being passed as arg 1
258 if (output.empty()) {
259 LOG_WARNING(Service_NVDRV, "Avoiding passing null pointer to memcpy");
260 } else {
261 std::memcpy(output.data(), &params, output.size());
262 }
263 return NvResult::Success; 228 return NvResult::Success;
264} 229}
265 230
266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) { 231NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
267 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 232 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
268
269 IoctlZbcQueryTable params{};
270 std::memcpy(&params, input.data(), input.size());
271 // TODO : To implement properly
272 std::memcpy(output.data(), &params, output.size());
273 return NvResult::Success; 233 return NvResult::Success;
274} 234}
275 235
276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) { 236NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) {
277 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 237 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
278
279 IoctlFlushL2 params{};
280 std::memcpy(&params, input.data(), input.size());
281 // TODO : To implement properly
282 std::memcpy(output.data(), &params, output.size());
283 return NvResult::Success; 238 return NvResult::Success;
284} 239}
285 240
286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) { 241NvResult nvhost_ctrl_gpu::GetGpuTime(IoctlGetGpuTime& params) {
287 LOG_DEBUG(Service_NVDRV, "called"); 242 LOG_DEBUG(Service_NVDRV, "called");
288
289 IoctlGetGpuTime params{};
290 std::memcpy(&params, input.data(), input.size());
291 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); 243 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
292 std::memcpy(output.data(), &params, output.size());
293 return NvResult::Success; 244 return NvResult::Success;
294} 245}
295 246
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 97995551c..d170299bd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -151,21 +151,20 @@ private:
151 }; 151 };
152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
153 153
154 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output); 154 NvResult GetCharacteristics1(IoctlCharacteristics& params);
155 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output, 155 NvResult GetCharacteristics3(IoctlCharacteristics& params,
156 std::span<u8> inline_output); 156 std::span<IoctlGpuCharacteristics> gpu_characteristics);
157 157
158 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output); 158 NvResult GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params);
159 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output, 159 NvResult GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask);
160 std::span<u8> inline_output); 160
161 161 NvResult GetActiveSlotMask(IoctlActiveSlotMask& params);
162 NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output); 162 NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params);
163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output); 163 NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params);
164 NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output); 164 NvResult ZBCSetTable(IoctlZbcSetTable& params);
165 NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output); 165 NvResult ZBCQueryTable(IoctlZbcQueryTable& params);
166 NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output); 166 NvResult FlushL2(IoctlFlushL2& params);
167 NvResult FlushL2(std::span<const u8> input, std::span<u8> output); 167 NvResult GetGpuTime(IoctlGetGpuTime& params);
168 NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output);
169 168
170 EventInterface& events_interface; 169 EventInterface& events_interface;
171 170
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 46a25fcab..b0395c2f0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/service/nvdrv/core/container.h" 8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/core/nvmap.h" 9#include "core/hle/service/nvdrv/core/nvmap.h"
10#include "core/hle/service/nvdrv/core/syncpoint_manager.h" 10#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
11#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
11#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 12#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
12#include "core/hle/service/nvdrv/nvdrv.h" 13#include "core/hle/service/nvdrv/nvdrv.h"
13#include "core/memory.h" 14#include "core/memory.h"
@@ -52,7 +53,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
52 case 0x0: 53 case 0x0:
53 switch (command.cmd) { 54 switch (command.cmd) {
54 case 0x3: 55 case 0x3:
55 return GetWaitbase(input, output); 56 return WrapFixed(this, &nvhost_gpu::GetWaitbase, input, output);
56 default: 57 default:
57 break; 58 break;
58 } 59 }
@@ -60,25 +61,25 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
60 case 'H': 61 case 'H':
61 switch (command.cmd) { 62 switch (command.cmd) {
62 case 0x1: 63 case 0x1:
63 return SetNVMAPfd(input, output); 64 return WrapFixed(this, &nvhost_gpu::SetNVMAPfd, input, output);
64 case 0x3: 65 case 0x3:
65 return ChannelSetTimeout(input, output); 66 return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
66 case 0x8: 67 case 0x8:
67 return SubmitGPFIFOBase(input, output, false); 68 return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
68 case 0x9: 69 case 0x9:
69 return AllocateObjectContext(input, output); 70 return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
70 case 0xb: 71 case 0xb:
71 return ZCullBind(input, output); 72 return WrapFixed(this, &nvhost_gpu::ZCullBind, input, output);
72 case 0xc: 73 case 0xc:
73 return SetErrorNotifier(input, output); 74 return WrapFixed(this, &nvhost_gpu::SetErrorNotifier, input, output);
74 case 0xd: 75 case 0xd:
75 return SetChannelPriority(input, output); 76 return WrapFixed(this, &nvhost_gpu::SetChannelPriority, input, output);
76 case 0x1a: 77 case 0x1a:
77 return AllocGPFIFOEx2(input, output); 78 return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output);
78 case 0x1b: 79 case 0x1b:
79 return SubmitGPFIFOBase(input, output, true); 80 return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
80 case 0x1d: 81 case 0x1d:
81 return ChannelSetTimeslice(input, output); 82 return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
82 default: 83 default:
83 break; 84 break;
84 } 85 }
@@ -86,9 +87,9 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
86 case 'G': 87 case 'G':
87 switch (command.cmd) { 88 switch (command.cmd) {
88 case 0x14: 89 case 0x14:
89 return SetClientData(input, output); 90 return WrapFixed(this, &nvhost_gpu::SetClientData, input, output);
90 case 0x15: 91 case 0x15:
91 return GetClientData(input, output); 92 return WrapFixed(this, &nvhost_gpu::GetClientData, input, output);
92 default: 93 default:
93 break; 94 break;
94 } 95 }
@@ -104,7 +105,8 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu
104 case 'H': 105 case 'H':
105 switch (command.cmd) { 106 switch (command.cmd) {
106 case 0x1b: 107 case 0x1b:
107 return SubmitGPFIFOBase(input, inline_input, output); 108 return WrapFixedInlIn(this, &nvhost_gpu::SubmitGPFIFOBase2, input, inline_input,
109 output);
108 } 110 }
109 break; 111 break;
110 } 112 }
@@ -121,63 +123,45 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
121void nvhost_gpu::OnOpen(DeviceFD fd) {} 123void nvhost_gpu::OnOpen(DeviceFD fd) {}
122void nvhost_gpu::OnClose(DeviceFD fd) {} 124void nvhost_gpu::OnClose(DeviceFD fd) {}
123 125
124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { 126NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {
125 IoctlSetNvmapFD params{};
126 std::memcpy(&params, input.data(), input.size());
127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
128 128
129 nvmap_fd = params.nvmap_fd; 129 nvmap_fd = params.nvmap_fd;
130 return NvResult::Success; 130 return NvResult::Success;
131} 131}
132 132
133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) { 133NvResult nvhost_gpu::SetClientData(IoctlClientData& params) {
134 LOG_DEBUG(Service_NVDRV, "called"); 134 LOG_DEBUG(Service_NVDRV, "called");
135
136 IoctlClientData params{};
137 std::memcpy(&params, input.data(), input.size());
138 user_data = params.data; 135 user_data = params.data;
139 return NvResult::Success; 136 return NvResult::Success;
140} 137}
141 138
142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) { 139NvResult nvhost_gpu::GetClientData(IoctlClientData& params) {
143 LOG_DEBUG(Service_NVDRV, "called"); 140 LOG_DEBUG(Service_NVDRV, "called");
144
145 IoctlClientData params{};
146 std::memcpy(&params, input.data(), input.size());
147 params.data = user_data; 141 params.data = user_data;
148 std::memcpy(output.data(), &params, output.size());
149 return NvResult::Success; 142 return NvResult::Success;
150} 143}
151 144
152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) { 145NvResult nvhost_gpu::ZCullBind(IoctlZCullBind& params) {
153 std::memcpy(&zcull_params, input.data(), input.size()); 146 zcull_params = params;
154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 147 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
155 zcull_params.mode); 148 zcull_params.mode);
156
157 std::memcpy(output.data(), &zcull_params, output.size());
158 return NvResult::Success; 149 return NvResult::Success;
159} 150}
160 151
161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) { 152NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) {
162 IoctlSetErrorNotifier params{};
163 std::memcpy(&params, input.data(), input.size());
164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 153 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
165 params.size, params.mem); 154 params.size, params.mem);
166
167 std::memcpy(output.data(), &params, output.size());
168 return NvResult::Success; 155 return NvResult::Success;
169} 156}
170 157
171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) { 158NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) {
172 std::memcpy(&channel_priority, input.data(), input.size()); 159 channel_priority = params.priority;
173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 160 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
174
175 return NvResult::Success; 161 return NvResult::Success;
176} 162}
177 163
178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) { 164NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params) {
179 IoctlAllocGpfifoEx2 params{};
180 std::memcpy(&params, input.data(), input.size());
181 LOG_WARNING(Service_NVDRV, 165 LOG_WARNING(Service_NVDRV,
182 "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " 166 "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, "
183 "unk1={:X}, unk2={:X}, unk3={:X}", 167 "unk1={:X}, unk2={:X}, unk3={:X}",
@@ -193,18 +177,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> out
193 177
194 params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); 178 params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
195 179
196 std::memcpy(output.data(), &params, output.size());
197 return NvResult::Success; 180 return NvResult::Success;
198} 181}
199 182
200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) { 183NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
201 IoctlAllocObjCtx params{};
202 std::memcpy(&params, input.data(), input.size());
203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 184 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
204 params.flags); 185 params.flags);
205 186
206 params.obj_id = 0x0; 187 params.obj_id = 0x0;
207 std::memcpy(output.data(), &params, output.size());
208 return NvResult::Success; 188 return NvResult::Success;
209} 189}
210 190
@@ -248,8 +228,7 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementW
248 return result; 228 return result;
249} 229}
250 230
251NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, 231NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries) {
252 Tegra::CommandList&& entries) {
253 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 232 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
254 params.num_entries, params.flags.raw); 233 params.num_entries, params.flags.raw);
255 234
@@ -290,65 +269,55 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> o
290 269
291 flags.raw = 0; 270 flags.raw = 0;
292 271
293 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
294 return NvResult::Success; 272 return NvResult::Success;
295} 273}
296 274
297NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, 275NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
298 bool kickoff) { 276 std::span<Tegra::CommandListHeader> commands, bool kickoff) {
299 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 277 if (params.num_entries > commands.size()) {
300 UNIMPLEMENTED(); 278 UNIMPLEMENTED();
301 return NvResult::InvalidSize; 279 return NvResult::InvalidSize;
302 } 280 }
303 IoctlSubmitGpfifo params{};
304 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
305 Tegra::CommandList entries(params.num_entries);
306 281
282 Tegra::CommandList entries(params.num_entries);
307 if (kickoff) { 283 if (kickoff) {
308 system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(), 284 system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
309 params.num_entries * sizeof(Tegra::CommandListHeader)); 285 params.num_entries * sizeof(Tegra::CommandListHeader));
310 } else { 286 } else {
311 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], 287 std::memcpy(entries.command_lists.data(), commands.data(),
312 params.num_entries * sizeof(Tegra::CommandListHeader)); 288 params.num_entries * sizeof(Tegra::CommandListHeader));
313 } 289 }
314 290
315 return SubmitGPFIFOImpl(params, output, std::move(entries)); 291 return SubmitGPFIFOImpl(params, std::move(entries));
316} 292}
317 293
318NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 294NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
319 std::span<u8> output) { 295 std::span<const Tegra::CommandListHeader> commands) {
320 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 296 if (params.num_entries > commands.size()) {
321 UNIMPLEMENTED(); 297 UNIMPLEMENTED();
322 return NvResult::InvalidSize; 298 return NvResult::InvalidSize;
323 } 299 }
324 IoctlSubmitGpfifo params{}; 300
325 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
326 Tegra::CommandList entries(params.num_entries); 301 Tegra::CommandList entries(params.num_entries);
327 std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size()); 302 std::memcpy(entries.command_lists.data(), commands.data(),
328 return SubmitGPFIFOImpl(params, output, std::move(entries)); 303 params.num_entries * sizeof(Tegra::CommandListHeader));
304 return SubmitGPFIFOImpl(params, std::move(entries));
329} 305}
330 306
331NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) { 307NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) {
332 IoctlGetWaitbase params{};
333 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
334 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 308 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
335 309
336 params.value = 0; // Seems to be hard coded at 0 310 params.value = 0; // Seems to be hard coded at 0
337 std::memcpy(output.data(), &params, output.size());
338 return NvResult::Success; 311 return NvResult::Success;
339} 312}
340 313
341NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) { 314NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
342 IoctlChannelSetTimeout params{};
343 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
344 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 315 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
345 316
346 return NvResult::Success; 317 return NvResult::Success;
347} 318}
348 319
349NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) { 320NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
350 IoctlSetTimeslice params{};
351 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
352 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 321 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
353 322
354 channel_timeslice = params.timeslice; 323 channel_timeslice = params.timeslice;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 529c20526..88fd228ff 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -186,23 +186,24 @@ private:
186 u32_le channel_priority{}; 186 u32_le channel_priority{};
187 u32_le channel_timeslice{}; 187 u32_le channel_timeslice{};
188 188
189 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); 189 NvResult SetNVMAPfd(IoctlSetNvmapFD& params);
190 NvResult SetClientData(std::span<const u8> input, std::span<u8> output); 190 NvResult SetClientData(IoctlClientData& params);
191 NvResult GetClientData(std::span<const u8> input, std::span<u8> output); 191 NvResult GetClientData(IoctlClientData& params);
192 NvResult ZCullBind(std::span<const u8> input, std::span<u8> output); 192 NvResult ZCullBind(IoctlZCullBind& params);
193 NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output); 193 NvResult SetErrorNotifier(IoctlSetErrorNotifier& params);
194 NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output); 194 NvResult SetChannelPriority(IoctlChannelSetPriority& params);
195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output); 195 NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params);
196 NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output); 196 NvResult AllocateObjectContext(IoctlAllocObjCtx& params);
197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, 197
198 Tegra::CommandList&& entries); 198 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, 199 NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
200 bool kickoff = false); 200 std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 201 NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
202 std::span<u8> output); 202 std::span<const Tegra::CommandListHeader> commands);
203 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); 203
204 NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output); 204 NvResult GetWaitbase(IoctlGetWaitbase& params);
205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output); 205 NvResult ChannelSetTimeout(IoctlChannelSetTimeout& params);
206 NvResult ChannelSetTimeslice(IoctlSetTimeslice& params);
206 207
207 EventInterface& events_interface; 208 EventInterface& events_interface;
208 NvCore::Container& core; 209 NvCore::Container& core;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index a174442a6..f43914e1b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/core/container.h" 8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 10#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
11 12
@@ -25,18 +26,18 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
25 if (!host1x_file.fd_to_id.contains(fd)) { 26 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; 27 host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
27 } 28 }
28 return Submit(fd, input, output); 29 return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
29 } 30 }
30 case 0x2: 31 case 0x2:
31 return GetSyncpoint(input, output); 32 return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
32 case 0x3: 33 case 0x3:
33 return GetWaitbase(input, output); 34 return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output);
34 case 0x7: 35 case 0x7:
35 return SetSubmitTimeout(input, output); 36 return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output);
36 case 0x9: 37 case 0x9:
37 return MapBuffer(input, output); 38 return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output);
38 case 0xa: 39 case 0xa:
39 return UnmapBuffer(input, output); 40 return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output);
40 default: 41 default:
41 break; 42 break;
42 } 43 }
@@ -44,7 +45,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
44 case 'H': 45 case 'H':
45 switch (command.cmd) { 46 switch (command.cmd) {
46 case 0x1: 47 case 0x1:
47 return SetNVMAPfd(input); 48 return WrapFixed(this, &nvhost_nvdec::SetNVMAPfd, input, output);
48 default: 49 default:
49 break; 50 break;
50 } 51 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 61649aa4a..74c701b95 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -29,6 +29,9 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si
29 return 0; 29 return 0;
30 } 30 }
31 const size_t bytes_copied = count * sizeof(T); 31 const size_t bytes_copied = count * sizeof(T);
32 if (input.size() < offset + bytes_copied) {
33 return 0;
34 }
32 std::memcpy(dst.data(), input.data() + offset, bytes_copied); 35 std::memcpy(dst.data(), input.data() + offset, bytes_copied);
33 return bytes_copied; 36 return bytes_copied;
34} 37}
@@ -41,6 +44,9 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size
41 return 0; 44 return 0;
42 } 45 }
43 const size_t bytes_copied = src.size() * sizeof(T); 46 const size_t bytes_copied = src.size() * sizeof(T);
47 if (dst.size() < offset + bytes_copied) {
48 return 0;
49 }
44 std::memcpy(dst.data() + offset, src.data(), bytes_copied); 50 std::memcpy(dst.data() + offset, src.data(), bytes_copied);
45 return bytes_copied; 51 return bytes_copied;
46} 52}
@@ -63,18 +69,14 @@ nvhost_nvdec_common::~nvhost_nvdec_common() {
63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); 69 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
64} 70}
65 71
66NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) { 72NvResult nvhost_nvdec_common::SetNVMAPfd(IoctlSetNvmapFD& params) {
67 IoctlSetNvmapFD params{};
68 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
69 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 73 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
70 74
71 nvmap_fd = params.nvmap_fd; 75 nvmap_fd = params.nvmap_fd;
72 return NvResult::Success; 76 return NvResult::Success;
73} 77}
74 78
75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) { 79NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, DeviceFD fd) {
76 IoctlSubmit params{};
77 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
78 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 80 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
79 81
80 // Instantiate param buffers 82 // Instantiate param buffers
@@ -85,12 +87,12 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std
85 std::vector<u32> fence_thresholds(params.fence_count); 87 std::vector<u32> fence_thresholds(params.fence_count);
86 88
87 // Slice input into their respective buffers 89 // Slice input into their respective buffers
88 std::size_t offset = sizeof(IoctlSubmit); 90 std::size_t offset = 0;
89 offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset); 91 offset += SliceVectors(data, command_buffers, params.cmd_buffer_count, offset);
90 offset += SliceVectors(input, relocs, params.relocation_count, offset); 92 offset += SliceVectors(data, relocs, params.relocation_count, offset);
91 offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset); 93 offset += SliceVectors(data, reloc_shifts, params.relocation_count, offset);
92 offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset); 94 offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset);
93 offset += SliceVectors(input, fence_thresholds, params.fence_count, offset); 95 offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
94 96
95 auto& gpu = system.GPU(); 97 auto& gpu = system.GPU();
96 if (gpu.UseNvdec()) { 98 if (gpu.UseNvdec()) {
@@ -108,72 +110,51 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std
108 cmdlist.size() * sizeof(u32)); 110 cmdlist.size() * sizeof(u32));
109 gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); 111 gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
110 } 112 }
111 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
112 // Some games expect command_buffers to be written back 113 // Some games expect command_buffers to be written back
113 offset = sizeof(IoctlSubmit); 114 offset = 0;
114 offset += WriteVectors(output, command_buffers, offset); 115 offset += WriteVectors(data, command_buffers, offset);
115 offset += WriteVectors(output, relocs, offset); 116 offset += WriteVectors(data, relocs, offset);
116 offset += WriteVectors(output, reloc_shifts, offset); 117 offset += WriteVectors(data, reloc_shifts, offset);
117 offset += WriteVectors(output, syncpt_increments, offset); 118 offset += WriteVectors(data, syncpt_increments, offset);
118 offset += WriteVectors(output, fence_thresholds, offset); 119 offset += WriteVectors(data, fence_thresholds, offset);
119 120
120 return NvResult::Success; 121 return NvResult::Success;
121} 122}
122 123
123NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) { 124NvResult nvhost_nvdec_common::GetSyncpoint(IoctlGetSyncpoint& params) {
124 IoctlGetSyncpoint params{};
125 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
126 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 125 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
127
128 // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]};
129 params.value = channel_syncpoint; 126 params.value = channel_syncpoint;
130 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
131
132 return NvResult::Success; 127 return NvResult::Success;
133} 128}
134 129
135NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) { 130NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) {
136 IoctlGetWaitbase params{};
137 LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); 131 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
138 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
139 params.value = 0; // Seems to be hard coded at 0 132 params.value = 0; // Seems to be hard coded at 0
140 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
141 return NvResult::Success; 133 return NvResult::Success;
142} 134}
143 135
144NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) { 136NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) {
145 IoctlMapBuffer params{}; 137 const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
146 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 138 for (size_t i = 0; i < num_entries; i++) {
147 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 139 entries[i].map_address = nvmap.PinHandle(entries[i].map_handle);
148
149 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
150
151 for (auto& cmd_buffer : cmd_buffer_handles) {
152 cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle);
153 } 140 }
154 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
155 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
156 cmd_buffer_handles.size() * sizeof(MapBufferEntry));
157 141
158 return NvResult::Success; 142 return NvResult::Success;
159} 143}
160 144
161NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { 145NvResult nvhost_nvdec_common::UnmapBuffer(IoctlMapBuffer& params,
162 IoctlMapBuffer params{}; 146 std::span<MapBufferEntry> entries) {
163 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 147 const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
164 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 148 for (size_t i = 0; i < num_entries; i++) {
165 149 nvmap.UnpinHandle(entries[i].map_handle);
166 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); 150 entries[i] = {};
167 for (auto& cmd_buffer : cmd_buffer_handles) {
168 nvmap.UnpinHandle(cmd_buffer.map_handle);
169 } 151 }
170 152
171 std::memset(output.data(), 0, output.size()); 153 params = {};
172 return NvResult::Success; 154 return NvResult::Success;
173} 155}
174 156
175NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) { 157NvResult nvhost_nvdec_common::SetSubmitTimeout(u32 timeout) {
176 std::memcpy(&submit_timeout, input.data(), input.size());
177 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 158 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
178 return NvResult::Success; 159 return NvResult::Success;
179} 160}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 9bb573bfe..7ce748e18 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -107,13 +107,13 @@ protected:
107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
108 108
109 /// Ioctl command implementations 109 /// Ioctl command implementations
110 NvResult SetNVMAPfd(std::span<const u8> input); 110 NvResult SetNVMAPfd(IoctlSetNvmapFD&);
111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output); 111 NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd);
112 NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output); 112 NvResult GetSyncpoint(IoctlGetSyncpoint& params);
113 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); 113 NvResult GetWaitbase(IoctlGetWaitbase& params);
114 NvResult MapBuffer(std::span<const u8> input, std::span<u8> output); 114 NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
115 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); 115 NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
116 NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output); 116 NvResult SetSubmitTimeout(u32 timeout);
117 117
118 Kernel::KEvent* QueryEvent(u32 event_id) override; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119 119
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index a05c8cdae..9e6b86458 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
8#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" 9#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
9 10
10namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
@@ -18,7 +19,7 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
18 case 'H': 19 case 'H':
19 switch (command.cmd) { 20 switch (command.cmd) {
20 case 0x1: 21 case 0x1:
21 return SetNVMAPfd(input, output); 22 return WrapFixed(this, &nvhost_nvjpg::SetNVMAPfd, input, output);
22 default: 23 default:
23 break; 24 break;
24 } 25 }
@@ -46,9 +47,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
46void nvhost_nvjpg::OnOpen(DeviceFD fd) {} 47void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
47void nvhost_nvjpg::OnClose(DeviceFD fd) {} 48void nvhost_nvjpg::OnClose(DeviceFD fd) {}
48 49
49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { 50NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {
50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), input.size());
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 51 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
53 52
54 nvmap_fd = params.nvmap_fd; 53 nvmap_fd = params.nvmap_fd;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 5623e0d47..790c97f6a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -33,7 +33,7 @@ private:
33 33
34 s32_le nvmap_fd{}; 34 s32_le nvmap_fd{};
35 35
36 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); 36 NvResult SetNVMAPfd(IoctlSetNvmapFD& params);
37}; 37};
38 38
39} // namespace Service::Nvidia::Devices 39} // 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 c0b8684c3..87f8d7c22 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -5,6 +5,7 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/service/nvdrv/core/container.h" 7#include "core/hle/service/nvdrv/core/container.h"
8#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
8#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 9#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
9#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
10 11
@@ -25,16 +26,16 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
25 if (!host1x_file.fd_to_id.contains(fd)) { 26 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; 27 host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
27 } 28 }
28 return Submit(fd, input, output); 29 return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
29 } 30 }
30 case 0x2: 31 case 0x2:
31 return GetSyncpoint(input, output); 32 return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
32 case 0x3: 33 case 0x3:
33 return GetWaitbase(input, output); 34 return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output);
34 case 0x9: 35 case 0x9:
35 return MapBuffer(input, output); 36 return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output);
36 case 0xa: 37 case 0xa:
37 return UnmapBuffer(input, output); 38 return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output);
38 default: 39 default:
39 break; 40 break;
40 } 41 }
@@ -42,7 +43,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
42 case 'H': 43 case 'H':
43 switch (command.cmd) { 44 switch (command.cmd) {
44 case 0x1: 45 case 0x1:
45 return SetNVMAPfd(input); 46 return WrapFixed(this, &nvhost_vic::SetNVMAPfd, input, output);
46 default: 47 default:
47 break; 48 break;
48 } 49 }
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 968eaa175..71b2e62ec 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
14#include "core/hle/service/nvdrv/core/container.h" 14#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvdrv/core/nvmap.h" 15#include "core/hle/service/nvdrv/core/nvmap.h"
16#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
16#include "core/hle/service/nvdrv/devices/nvmap.h" 17#include "core/hle/service/nvdrv/devices/nvmap.h"
17#include "core/memory.h" 18#include "core/memory.h"
18 19
@@ -31,17 +32,17 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 case 0x1: 32 case 0x1:
32 switch (command.cmd) { 33 switch (command.cmd) {
33 case 0x1: 34 case 0x1:
34 return IocCreate(input, output); 35 return WrapFixed(this, &nvmap::IocCreate, input, output);
35 case 0x3: 36 case 0x3:
36 return IocFromId(input, output); 37 return WrapFixed(this, &nvmap::IocFromId, input, output);
37 case 0x4: 38 case 0x4:
38 return IocAlloc(input, output); 39 return WrapFixed(this, &nvmap::IocAlloc, input, output);
39 case 0x5: 40 case 0x5:
40 return IocFree(input, output); 41 return WrapFixed(this, &nvmap::IocFree, input, output);
41 case 0x9: 42 case 0x9:
42 return IocParam(input, output); 43 return WrapFixed(this, &nvmap::IocParam, input, output);
43 case 0xe: 44 case 0xe:
44 return IocGetId(input, output); 45 return WrapFixed(this, &nvmap::IocGetId, input, output);
45 default: 46 default:
46 break; 47 break;
47 } 48 }
@@ -69,9 +70,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st
69void nvmap::OnOpen(DeviceFD fd) {} 70void nvmap::OnOpen(DeviceFD fd) {}
70void nvmap::OnClose(DeviceFD fd) {} 71void nvmap::OnClose(DeviceFD fd) {}
71 72
72NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) { 73NvResult nvmap::IocCreate(IocCreateParams& params) {
73 IocCreateParams params;
74 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); 74 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
76 75
77 std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; 76 std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
@@ -85,13 +84,10 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {
85 params.handle = handle_description->id; 84 params.handle = handle_description->id;
86 LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); 85 LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
87 86
88 std::memcpy(output.data(), &params, sizeof(params));
89 return NvResult::Success; 87 return NvResult::Success;
90} 88}
91 89
92NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { 90NvResult nvmap::IocAlloc(IocAllocParams& params) {
93 IocAllocParams params;
94 std::memcpy(&params, input.data(), sizeof(params));
95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); 91 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
96 92
97 if (!params.handle) { 93 if (!params.handle) {
@@ -133,14 +129,10 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
133 handle_description->size, 129 handle_description->size,
134 Kernel::KMemoryPermission::None, true, false) 130 Kernel::KMemoryPermission::None, true, false)
135 .IsSuccess()); 131 .IsSuccess());
136 std::memcpy(output.data(), &params, sizeof(params));
137 return result; 132 return result;
138} 133}
139 134
140NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) { 135NvResult nvmap::IocGetId(IocGetIdParams& params) {
141 IocGetIdParams params;
142 std::memcpy(&params, input.data(), sizeof(params));
143
144 LOG_DEBUG(Service_NVDRV, "called"); 136 LOG_DEBUG(Service_NVDRV, "called");
145 137
146 // See the comment in FromId for extra info on this function 138 // See the comment in FromId for extra info on this function
@@ -157,14 +149,10 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {
157 } 149 }
158 150
159 params.id = handle_description->id; 151 params.id = handle_description->id;
160 std::memcpy(output.data(), &params, sizeof(params));
161 return NvResult::Success; 152 return NvResult::Success;
162} 153}
163 154
164NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) { 155NvResult nvmap::IocFromId(IocFromIdParams& params) {
165 IocFromIdParams params;
166 std::memcpy(&params, input.data(), sizeof(params));
167
168 LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); 156 LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
169 157
170 // Handles and IDs are always the same value in nvmap however IDs can be used globally given the 158 // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
@@ -188,16 +176,12 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {
188 return result; 176 return result;
189 } 177 }
190 params.handle = handle_description->id; 178 params.handle = handle_description->id;
191 std::memcpy(output.data(), &params, sizeof(params));
192 return NvResult::Success; 179 return NvResult::Success;
193} 180}
194 181
195NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) { 182NvResult nvmap::IocParam(IocParamParams& params) {
196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 183 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
197 184
198 IocParamParams params;
199 std::memcpy(&params, input.data(), sizeof(params));
200
201 LOG_DEBUG(Service_NVDRV, "called type={}", params.param); 185 LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
202 186
203 if (!params.handle) { 187 if (!params.handle) {
@@ -237,14 +221,10 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) {
237 return NvResult::BadValue; 221 return NvResult::BadValue;
238 } 222 }
239 223
240 std::memcpy(output.data(), &params, sizeof(params));
241 return NvResult::Success; 224 return NvResult::Success;
242} 225}
243 226
244NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { 227NvResult nvmap::IocFree(IocFreeParams& params) {
245 IocFreeParams params;
246 std::memcpy(&params, input.data(), sizeof(params));
247
248 LOG_DEBUG(Service_NVDRV, "called"); 228 LOG_DEBUG(Service_NVDRV, "called");
249 229
250 if (!params.handle) { 230 if (!params.handle) {
@@ -267,7 +247,6 @@ NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
267 // This is possible when there's internal dups or other duplicates. 247 // This is possible when there's internal dups or other duplicates.
268 } 248 }
269 249
270 std::memcpy(output.data(), &params, sizeof(params));
271 return NvResult::Success; 250 return NvResult::Success;
272} 251}
273 252
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 4c0cc71cd..049c11028 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -99,12 +99,12 @@ public:
99 }; 99 };
100 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 100 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
101 101
102 NvResult IocCreate(std::span<const u8> input, std::span<u8> output); 102 NvResult IocCreate(IocCreateParams& params);
103 NvResult IocAlloc(std::span<const u8> input, std::span<u8> output); 103 NvResult IocAlloc(IocAllocParams& params);
104 NvResult IocGetId(std::span<const u8> input, std::span<u8> output); 104 NvResult IocGetId(IocGetIdParams& params);
105 NvResult IocFromId(std::span<const u8> input, std::span<u8> output); 105 NvResult IocFromId(IocFromIdParams& params);
106 NvResult IocParam(std::span<const u8> input, std::span<u8> output); 106 NvResult IocParam(IocParamParams& params);
107 NvResult IocFree(std::span<const u8> input, std::span<u8> output); 107 NvResult IocFree(IocFreeParams& params);
108 108
109private: 109private:
110 /// Id to use for the next handle that is created. 110 /// Id to use for the next handle that is created.
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h
index 3da8cc3aa..7fd808f54 100644
--- a/src/core/hle/service/nvnflinger/buffer_item.h
+++ b/src/core/hle/service/nvnflinger/buffer_item.h
@@ -15,7 +15,7 @@
15 15
16namespace Service::android { 16namespace Service::android {
17 17
18struct GraphicBuffer; 18class GraphicBuffer;
19 19
20class BufferItem final { 20class BufferItem final {
21public: 21public:
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index 51291539d..d91886bed 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -5,7 +5,6 @@
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/hle/service/nvnflinger/buffer_item.h" 8#include "core/hle/service/nvnflinger/buffer_item.h"
10#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" 9#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvnflinger/buffer_queue_core.h" 10#include "core/hle/service/nvnflinger/buffer_queue_core.h"
@@ -14,9 +13,8 @@
14 13
15namespace Service::android { 14namespace Service::android {
16 15
17BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 16BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
18 Service::Nvidia::NvCore::NvMap& nvmap_) 17 : core{std::move(core_)}, slots{core->slots} {}
19 : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
20 18
21BufferQueueConsumer::~BufferQueueConsumer() = default; 19BufferQueueConsumer::~BufferQueueConsumer() = default;
22 20
@@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
136 134
137 slots[slot].buffer_state = BufferState::Free; 135 slots[slot].buffer_state = BufferState::Free;
138 136
139 nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
140
141 listener = core->connected_producer_listener; 137 listener = core->connected_producer_listener;
142 138
143 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); 139 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
175 return Status::NoError; 171 return Status::NoError;
176} 172}
177 173
174Status BufferQueueConsumer::Disconnect() {
175 LOG_DEBUG(Service_Nvnflinger, "called");
176
177 std::scoped_lock lock{core->mutex};
178
179 if (core->consumer_listener == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
181 return Status::BadValue;
182 }
183
184 core->is_abandoned = true;
185 core->consumer_listener = nullptr;
186 core->queue.clear();
187 core->FreeAllBuffersLocked();
188 core->SignalDequeueCondition();
189
190 return Status::NoError;
191}
192
178Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { 193Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
179 if (out_slot_mask == nullptr) { 194 if (out_slot_mask == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); 195 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
index 50ed0bb5f..0a61e8dbd 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
@@ -13,10 +13,6 @@
13#include "core/hle/service/nvnflinger/buffer_queue_defs.h" 13#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvnflinger/status.h" 14#include "core/hle/service/nvnflinger/status.h"
15 15
16namespace Service::Nvidia::NvCore {
17class NvMap;
18} // namespace Service::Nvidia::NvCore
19
20namespace Service::android { 16namespace Service::android {
21 17
22class BufferItem; 18class BufferItem;
@@ -25,19 +21,18 @@ class IConsumerListener;
25 21
26class BufferQueueConsumer final { 22class BufferQueueConsumer final {
27public: 23public:
28 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
29 Service::Nvidia::NvCore::NvMap& nvmap_);
30 ~BufferQueueConsumer(); 25 ~BufferQueueConsumer();
31 26
32 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
33 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); 28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
34 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); 29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status Disconnect();
35 Status GetReleasedBuffers(u64* out_slot_mask); 31 Status GetReleasedBuffers(u64* out_slot_mask);
36 32
37private: 33private:
38 std::shared_ptr<BufferQueueCore> core; 34 std::shared_ptr<BufferQueueCore> core;
39 BufferQueueDefs::SlotsType& slots; 35 BufferQueueDefs::SlotsType& slots;
40 Service::Nvidia::NvCore::NvMap& nvmap;
41}; 36};
42 37
43} // namespace Service::android 38} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
index ed66f6f5b..4ed5e5978 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
@@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default;
14 14
15BufferQueueCore::~BufferQueueCore() = default; 15BufferQueueCore::~BufferQueueCore() = default;
16 16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() { 17void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_possible.store(true); 18 dequeue_possible.store(true);
27 dequeue_condition.notify_all(); 19 dequeue_condition.notify_all();
28} 20}
29 21
30bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { 22bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
31 if (is_shutting_down) {
32 return false;
33 }
34
35 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); 23 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
36 dequeue_possible.store(false); 24 dequeue_possible.store(false);
37 25
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h
index 9164f08a0..e513d183b 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h
@@ -34,8 +34,6 @@ public:
34 BufferQueueCore(); 34 BufferQueueCore();
35 ~BufferQueueCore(); 35 ~BufferQueueCore();
36 36
37 void NotifyShutdown();
38
39private: 37private:
40 void SignalDequeueCondition(); 38 void SignalDequeueCondition();
41 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); 39 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -74,7 +72,6 @@ private:
74 u32 transform_hint{}; 72 u32 transform_hint{};
75 bool is_allocating{}; 73 bool is_allocating{};
76 mutable std::condition_variable_any is_allocating_condition; 74 mutable std::condition_variable_any is_allocating_condition;
77 bool is_shutting_down{};
78}; 75};
79 76
80} // namespace Service::android 77} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index 6e7a49658..5d8762d25 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -13,7 +13,6 @@
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hle_ipc.h" 14#include "core/hle/service/hle_ipc.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvdrv/core/nvmap.h"
17#include "core/hle/service/nvnflinger/buffer_queue_core.h" 16#include "core/hle/service/nvnflinger/buffer_queue_core.h"
18#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 17#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
19#include "core/hle/service/nvnflinger/consumer_listener.h" 18#include "core/hle/service/nvnflinger/consumer_listener.h"
@@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
533 item.is_droppable = core->dequeue_buffer_cannot_block || async; 532 item.is_droppable = core->dequeue_buffer_cannot_block || async;
534 item.swap_interval = swap_interval; 533 item.swap_interval = swap_interval;
535 534
536 nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
537
538 sticky_transform = sticky_transform_; 535 sticky_transform = sticky_transform_;
539 536
540 if (core->queue.empty()) { 537 if (core->queue.empty()) {
@@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
744 return Status::NoError; 741 return Status::NoError;
745 } 742 }
746 743
747 // HACK: We are not Android. Remove handle for items in queue, and clear queue.
748 // Allows synchronous destruction of nvmap handles.
749 for (auto& item : core->queue) {
750 nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
751 }
752 core->queue.clear();
753
754 switch (api) { 744 switch (api) {
755 case NativeWindowApi::Egl: 745 case NativeWindowApi::Egl:
756 case NativeWindowApi::Cpu: 746 case NativeWindowApi::Cpu:
757 case NativeWindowApi::Media: 747 case NativeWindowApi::Media:
758 case NativeWindowApi::Camera: 748 case NativeWindowApi::Camera:
759 if (core->connected_api == api) { 749 if (core->connected_api == api) {
750 core->queue.clear();
760 core->FreeAllBuffersLocked(); 751 core->FreeAllBuffersLocked();
761 core->connected_producer_listener = nullptr; 752 core->connected_producer_listener = nullptr;
762 core->connected_api = NativeWindowApi::NoConnectedApi; 753 core->connected_api = NativeWindowApi::NoConnectedApi;
@@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
785} 776}
786 777
787Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, 778Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
788 const std::shared_ptr<GraphicBuffer>& buffer) { 779 const std::shared_ptr<NvGraphicBuffer>& buffer) {
789 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); 780 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
790 781
791 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { 782 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
796 787
797 slots[slot] = {}; 788 slots[slot] = {};
798 slots[slot].fence = Fence::NoFence(); 789 slots[slot].fence = Fence::NoFence();
799 slots[slot].graphic_buffer = buffer; 790 slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
800 slots[slot].frame_number = 0; 791 slots[slot].frame_number = 0;
801 792
802 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for 793 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
839 } 830 }
840 case TransactionId::SetPreallocatedBuffer: { 831 case TransactionId::SetPreallocatedBuffer: {
841 const auto slot = parcel_in.Read<s32>(); 832 const auto slot = parcel_in.Read<s32>();
842 const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); 833 const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
843 834
844 status = SetPreallocatedBuffer(slot, buffer); 835 status = SetPreallocatedBuffer(slot, buffer);
845 break; 836 break;
@@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
867 858
868 status = RequestBuffer(slot, &buf); 859 status = RequestBuffer(slot, &buf);
869 860
870 parcel_out.WriteFlattenedObject(buf); 861 parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
871 break; 862 break;
872 } 863 }
873 case TransactionId::QueueBuffer: { 864 case TransactionId::QueueBuffer: {
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index d4201c104..64c17d56c 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -38,6 +38,7 @@ namespace Service::android {
38 38
39class BufferQueueCore; 39class BufferQueueCore;
40class IProducerListener; 40class IProducerListener;
41struct NvGraphicBuffer;
41 42
42class BufferQueueProducer final : public IBinder { 43class BufferQueueProducer final : public IBinder {
43public: 44public:
@@ -65,7 +66,7 @@ public:
65 bool producer_controlled_by_app, QueueBufferOutput* output); 66 bool producer_controlled_by_app, QueueBufferOutput* output);
66 67
67 Status Disconnect(NativeWindowApi api); 68 Status Disconnect(NativeWindowApi api);
68 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); 69 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
69 70
70private: 71private:
71 BufferQueueProducer(const BufferQueueProducer&) = delete; 72 BufferQueueProducer(const BufferQueueProducer&) = delete;
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index d8c9dec3b..d25bca049 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -13,7 +13,7 @@
13 13
14namespace Service::android { 14namespace Service::android {
15 15
16struct GraphicBuffer; 16class GraphicBuffer;
17 17
18enum class BufferState : u32 { 18enum class BufferState : u32 {
19 Free = 0, 19 Free = 0,
diff --git a/src/core/hle/service/nvnflinger/buffer_transform_flags.h b/src/core/hle/service/nvnflinger/buffer_transform_flags.h
index 67aa5dad6..ffe579718 100644
--- a/src/core/hle/service/nvnflinger/buffer_transform_flags.h
+++ b/src/core/hle/service/nvnflinger/buffer_transform_flags.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_funcs.h"
6#include "common/common_types.h" 7#include "common/common_types.h"
7 8
8namespace Service::android { 9namespace Service::android {
@@ -21,5 +22,6 @@ enum class BufferTransformFlags : u32 {
21 /// Rotate source image 270 degrees clockwise 22 /// Rotate source image 270 degrees clockwise
22 Rotate270 = 0x07, 23 Rotate270 = 0x07,
23}; 24};
25DECLARE_ENUM_FLAG_OPERATORS(BufferTransformFlags);
24 26
25} // namespace Service::android 27} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp
index 4dcda8dac..1059e72bf 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvnflinger/consumer_base.cpp
@@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app); 27 consumer->Connect(shared_from_this(), controlled_by_app);
28} 28}
29 29
30void ConsumerBase::Abandon() {
31 LOG_DEBUG(Service_Nvnflinger, "called");
32
33 std::scoped_lock lock{mutex};
34
35 if (!is_abandoned) {
36 this->AbandonLocked();
37 is_abandoned = true;
38 }
39}
40
41void ConsumerBase::AbandonLocked() {
42 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
43 this->FreeBufferLocked(i);
44 }
45 // disconnect from the BufferQueue
46 consumer->Disconnect();
47 consumer = nullptr;
48}
49
30void ConsumerBase::FreeBufferLocked(s32 slot_index) { 50void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); 51 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);
32 52
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h
index 264829414..ea3e9e97a 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.h
+++ b/src/core/hle/service/nvnflinger/consumer_base.h
@@ -24,6 +24,7 @@ class BufferQueueConsumer;
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { 24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public: 25public:
26 void Connect(bool controlled_by_app); 26 void Connect(bool controlled_by_app);
27 void Abandon();
27 28
28protected: 29protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); 30 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@@ -34,6 +35,7 @@ protected:
34 void OnBuffersReleased() override; 35 void OnBuffersReleased() override;
35 void OnSidebandStreamChanged() override; 36 void OnSidebandStreamChanged() override;
36 37
38 void AbandonLocked();
37 void FreeBufferLocked(s32 slot_index); 39 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); 40 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); 41 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 2e29bc848..d7db24f42 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -71,24 +71,17 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
71 R_SUCCEED(); 71 R_SUCCEED();
72} 72}
73 73
74template <typename T>
75std::span<u8> SerializeIoc(T& params) {
76 return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T));
77}
78
79Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { 74Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
80 // Create a handle. 75 // Create a handle.
81 Nvidia::Devices::nvmap::IocCreateParams create_in_params{ 76 Nvidia::Devices::nvmap::IocCreateParams create_params{
82 .size = size, 77 .size = size,
83 .handle = 0, 78 .handle = 0,
84 }; 79 };
85 Nvidia::Devices::nvmap::IocCreateParams create_out_params{}; 80 R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success,
86 R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) ==
87 Nvidia::NvResult::Success,
88 VI::ResultOperationFailed); 81 VI::ResultOperationFailed);
89 82
90 // Assign the output handle. 83 // Assign the output handle.
91 *out_nv_map_handle = create_out_params.handle; 84 *out_nv_map_handle = create_params.handle;
92 85
93 // We succeeded. 86 // We succeeded.
94 R_SUCCEED(); 87 R_SUCCEED();
@@ -96,13 +89,10 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap,
96 89
97Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { 90Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
98 // Free the handle. 91 // Free the handle.
99 Nvidia::Devices::nvmap::IocFreeParams free_in_params{ 92 Nvidia::Devices::nvmap::IocFreeParams free_params{
100 .handle = handle, 93 .handle = handle,
101 }; 94 };
102 Nvidia::Devices::nvmap::IocFreeParams free_out_params{}; 95 R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
103 R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) ==
104 Nvidia::NvResult::Success,
105 VI::ResultOperationFailed);
106 96
107 // We succeeded. 97 // We succeeded.
108 R_SUCCEED(); 98 R_SUCCEED();
@@ -111,7 +101,7 @@ Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
111Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, 101Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
112 u32 size) { 102 u32 size) {
113 // Assign the allocated memory to the handle. 103 // Assign the allocated memory to the handle.
114 Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{ 104 Nvidia::Devices::nvmap::IocAllocParams alloc_params{
115 .handle = handle, 105 .handle = handle,
116 .heap_mask = 0, 106 .heap_mask = 0,
117 .flags = {}, 107 .flags = {},
@@ -119,10 +109,7 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce
119 .kind = 0, 109 .kind = 0,
120 .address = GetInteger(buffer), 110 .address = GetInteger(buffer),
121 }; 111 };
122 Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{}; 112 R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
123 R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) ==
124 Nvidia::NvResult::Success,
125 VI::ResultOperationFailed);
126 113
127 // We succeeded. 114 // We succeeded.
128 R_SUCCEED(); 115 R_SUCCEED();
@@ -179,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
179}(); 166}();
180 167
181void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { 168void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
182 auto buffer = std::make_shared<android::GraphicBuffer>(); 169 auto buffer = std::make_shared<android::NvGraphicBuffer>();
183 buffer->width = SharedBufferWidth; 170 buffer->width = SharedBufferWidth;
184 buffer->height = SharedBufferHeight; 171 buffer->height = SharedBufferHeight;
185 buffer->stride = SharedBufferBlockLinearStride; 172 buffer->stride = SharedBufferBlockLinearStride;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index bebb45eae..0745434c5 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
47 vsync_signal.Wait(); 47 vsync_signal.Wait();
48 48
49 const auto lock_guard = Lock(); 49 const auto lock_guard = Lock();
50 Compose(); 50
51 if (!is_abandoned) {
52 Compose();
53 }
51 } 54 }
52} 55}
53 56
@@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() {
98 } 101 }
99 102
100 ShutdownLayers(); 103 ShutdownLayers();
101 vsync_thread = {};
102 104
103 if (nvdrv) { 105 if (nvdrv) {
104 nvdrv->Close(disp_fd); 106 nvdrv->Close(disp_fd);
@@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() {
106} 108}
107 109
108void Nvnflinger::ShutdownLayers() { 110void Nvnflinger::ShutdownLayers() {
109 const auto lock_guard = Lock(); 111 // Abandon consumers.
110 for (auto& display : displays) { 112 {
111 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 113 const auto lock_guard = Lock();
112 display.GetLayer(layer).Core().NotifyShutdown(); 114 for (auto& display : displays) {
115 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
116 display.GetLayer(layer).GetConsumer().Abandon();
117 }
113 } 118 }
119
120 is_abandoned = true;
114 } 121 }
122
123 // Join the vsync thread, if it exists.
124 vsync_thread = {};
115} 125}
116 126
117void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 127void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 959d8b46b..f5d73acdb 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -140,6 +140,8 @@ private:
140 140
141 s32 swap_interval = 1; 141 s32 swap_interval = 1;
142 142
143 bool is_abandoned = false;
144
143 /// Event that handles screen composition. 145 /// Event that handles screen composition.
144 std::shared_ptr<Core::Timing::EventType> multi_composition_event; 146 std::shared_ptr<Core::Timing::EventType> multi_composition_event;
145 std::shared_ptr<Core::Timing::EventType> single_composition_event; 147 std::shared_ptr<Core::Timing::EventType> single_composition_event;
diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h
index 7af166c40..3fa0fe15b 100644
--- a/src/core/hle/service/nvnflinger/status.h
+++ b/src/core/hle/service/nvnflinger/status.h
@@ -19,7 +19,7 @@ enum class Status : s32 {
19 Busy = -16, 19 Busy = -16,
20 NoInit = -19, 20 NoInit = -19,
21 BadValue = -22, 21 BadValue = -22,
22 InvalidOperation = -37, 22 InvalidOperation = -38,
23 BufferNeedsReallocation = 1, 23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2, 24 ReleaseAllBuffers = 2,
25}; 25};
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
new file mode 100644
index 000000000..ce70946ec
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/nvdrv/core/nvmap.h"
5#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
6
7namespace Service::android {
8
9static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
10 if (buffer) {
11 return *buffer;
12 } else {
13 return {};
14 }
15}
16
17GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
18 : NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
19
20GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
21 std::shared_ptr<NvGraphicBuffer> buffer)
22 : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
23 if (this->BufferId() > 0) {
24 m_nvmap->DuplicateHandle(this->BufferId(), true);
25 }
26}
27
28GraphicBuffer::~GraphicBuffer() {
29 if (m_nvmap != nullptr && this->BufferId() > 0) {
30 m_nvmap->FreeHandle(this->BufferId(), true);
31 }
32}
33
34} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
index 3eac5cedd..da430aa75 100644
--- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
@@ -6,16 +6,22 @@
6 6
7#pragma once 7#pragma once
8 8
9#include <memory>
10
9#include "common/common_funcs.h" 11#include "common/common_funcs.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "core/hle/service/nvnflinger/pixel_format.h" 13#include "core/hle/service/nvnflinger/pixel_format.h"
12 14
15namespace Service::Nvidia::NvCore {
16class NvMap;
17} // namespace Service::Nvidia::NvCore
18
13namespace Service::android { 19namespace Service::android {
14 20
15struct GraphicBuffer final { 21struct NvGraphicBuffer {
16 constexpr GraphicBuffer() = default; 22 constexpr NvGraphicBuffer() = default;
17 23
18 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) 24 constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
19 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, 25 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
20 usage{static_cast<s32>(usage_)} {} 26 usage{static_cast<s32>(usage_)} {}
21 27
@@ -93,6 +99,17 @@ struct GraphicBuffer final {
93 u32 offset{}; 99 u32 offset{};
94 INSERT_PADDING_WORDS(60); 100 INSERT_PADDING_WORDS(60);
95}; 101};
96static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); 102static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
103
104class GraphicBuffer final : public NvGraphicBuffer {
105public:
106 explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
107 explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
108 std::shared_ptr<NvGraphicBuffer> buffer);
109 ~GraphicBuffer();
110
111private:
112 Service::Nvidia::NvCore::NvMap* m_nvmap{};
113};
97 114
98} // namespace Service::android 115} // namespace Service::android
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index ec3af80af..f5edfdc8b 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -19,19 +19,8 @@
19 19
20namespace Service::Set { 20namespace Service::Set {
21 21
22namespace { 22Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
23constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; 23 GetFirmwareVersionType type) {
24
25enum class GetFirmwareVersionType {
26 Version1,
27 Version2,
28};
29
30void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
31 GetFirmwareVersionType type) {
32 ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
33 "FirmwareVersion output buffer must be 0x100 bytes in size!");
34
35 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; 24 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809;
36 auto& fsc = system.GetFileSystemController(); 25 auto& fsc = system.GetFileSystemController();
37 26
@@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
52 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); 41 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId));
53 } 42 }
54 43
55 const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { 44 const auto early_exit_failure = [](std::string_view desc, Result code) {
56 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", 45 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
57 desc); 46 desc);
58 IPC::ResponseBuilder rb{ctx, 2}; 47 return code;
59 rb.Push(code);
60 }; 48 };
61 49
62 const auto ver_file = romfs->GetFile("file"); 50 const auto ver_file = romfs->GetFile("file");
63 if (ver_file == nullptr) { 51 if (ver_file == nullptr) {
64 early_exit_failure("The system version archive didn't contain the file 'file'.", 52 return early_exit_failure("The system version archive didn't contain the file 'file'.",
65 FileSys::ERROR_INVALID_ARGUMENT); 53 FileSys::ERROR_INVALID_ARGUMENT);
66 return;
67 } 54 }
68 55
69 auto data = ver_file->ReadAllBytes(); 56 auto data = ver_file->ReadAllBytes();
70 if (data.size() != 0x100) { 57 if (data.size() != sizeof(FirmwareVersionFormat)) {
71 early_exit_failure("The system version file 'file' was not the correct size.", 58 return early_exit_failure("The system version file 'file' was not the correct size.",
72 FileSys::ERROR_OUT_OF_BOUNDS); 59 FileSys::ERROR_OUT_OF_BOUNDS);
73 return;
74 } 60 }
75 61
62 std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
63
76 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will 64 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
77 // zero out the REVISION_MINOR field. 65 // zero out the REVISION_MINOR field.
78 if (type == GetFirmwareVersionType::Version1) { 66 if (type == GetFirmwareVersionType::Version1) {
79 data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; 67 out_firmware.revision_minor = 0;
80 } 68 }
81 69
82 ctx.WriteBuffer(data); 70 return ResultSuccess;
83
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(ResultSuccess);
86} 71}
87} // Anonymous namespace
88 72
89void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { 73void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
90 IPC::RequestParser rp{ctx}; 74 IPC::RequestParser rp{ctx};
@@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
98 82
99void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { 83void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_SET, "called"); 84 LOG_DEBUG(Service_SET, "called");
101 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); 85
86 FirmwareVersionFormat firmware_data{};
87 const auto result =
88 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1);
89
90 if (result.IsSuccess()) {
91 ctx.WriteBuffer(firmware_data);
92 }
93
94 IPC::ResponseBuilder rb{ctx, 2};
95 rb.Push(result);
102} 96}
103 97
104void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { 98void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) {
105 LOG_DEBUG(Service_SET, "called"); 99 LOG_DEBUG(Service_SET, "called");
106 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); 100
101 FirmwareVersionFormat firmware_data{};
102 const auto result =
103 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2);
104
105 if (result.IsSuccess()) {
106 ctx.WriteBuffer(firmware_data);
107 }
108
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(result);
107} 111}
108 112
109void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { 113void SET_SYS::GetAccountSettings(HLERequestContext& ctx) {
@@ -431,8 +435,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
431void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { 435void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
432 u8 battery_percentage_flag{1}; 436 u8 battery_percentage_flag{1};
433 437
434 LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", 438 LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag);
435 battery_percentage_flag);
436 439
437 IPC::ResponseBuilder rb{ctx, 3}; 440 IPC::ResponseBuilder rb{ctx, 3};
438 rb.Push(ResultSuccess); 441 rb.Push(ResultSuccess);
@@ -492,6 +495,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
492 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); 495 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0);
493} 496}
494 497
498void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
499 LOG_DEBUG(Service_SET, "(STUBBED) called");
500
501 const HomeMenuScheme default_color = {
502 .main = 0xFF323232,
503 .back = 0xFF323232,
504 .sub = 0xFFFFFFFF,
505 .bezel = 0xFFFFFFFF,
506 .extra = 0xFF000000,
507 };
508
509 IPC::ResponseBuilder rb{ctx, 7};
510 rb.Push(ResultSuccess);
511 rb.PushRaw(default_color);
512}
513
514void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
515 LOG_WARNING(Service_SET, "(STUBBED) called");
516
517 IPC::ResponseBuilder rb{ctx, 3};
518 rb.Push(ResultSuccess);
519 rb.Push(0);
520}
495void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { 521void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
496 LOG_WARNING(Service_SET, "(STUBBED) called"); 522 LOG_WARNING(Service_SET, "(STUBBED) called");
497 523
@@ -674,7 +700,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
674 {171, nullptr, "SetChineseTraditionalInputMethod"}, 700 {171, nullptr, "SetChineseTraditionalInputMethod"},
675 {172, nullptr, "GetPtmCycleCountReliability"}, 701 {172, nullptr, "GetPtmCycleCountReliability"},
676 {173, nullptr, "SetPtmCycleCountReliability"}, 702 {173, nullptr, "SetPtmCycleCountReliability"},
677 {174, nullptr, "GetHomeMenuScheme"}, 703 {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"},
678 {175, nullptr, "GetThemeSettings"}, 704 {175, nullptr, "GetThemeSettings"},
679 {176, nullptr, "SetThemeSettings"}, 705 {176, nullptr, "SetThemeSettings"},
680 {177, nullptr, "GetThemeKey"}, 706 {177, nullptr, "GetThemeKey"},
@@ -685,7 +711,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
685 {182, nullptr, "SetT"}, 711 {182, nullptr, "SetT"},
686 {183, nullptr, "GetPlatformRegion"}, 712 {183, nullptr, "GetPlatformRegion"},
687 {184, nullptr, "SetPlatformRegion"}, 713 {184, nullptr, "SetPlatformRegion"},
688 {185, nullptr, "GetHomeMenuSchemeModel"}, 714 {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
689 {186, nullptr, "GetMemoryUsageRateFlag"}, 715 {186, nullptr, "GetMemoryUsageRateFlag"},
690 {187, nullptr, "GetTouchScreenMode"}, 716 {187, nullptr, "GetTouchScreenMode"},
691 {188, nullptr, "SetTouchScreenMode"}, 717 {188, nullptr, "SetTouchScreenMode"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index c7dba2a9e..5f770fd32 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/uuid.h" 6#include "common/uuid.h"
7#include "core/hle/result.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8#include "core/hle/service/time/clock_types.h" 9#include "core/hle/service/time/clock_types.h"
9 10
@@ -12,6 +13,29 @@ class System;
12} 13}
13 14
14namespace Service::Set { 15namespace Service::Set {
16enum class LanguageCode : u64;
17enum class GetFirmwareVersionType {
18 Version1,
19 Version2,
20};
21
22struct FirmwareVersionFormat {
23 u8 major;
24 u8 minor;
25 u8 micro;
26 INSERT_PADDING_BYTES(1);
27 u8 revision_major;
28 u8 revision_minor;
29 INSERT_PADDING_BYTES(2);
30 std::array<char, 0x20> platform;
31 std::array<u8, 0x40> version_hash;
32 std::array<char, 0x18> display_version;
33 std::array<char, 0x80> display_title;
34};
35static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
36
37Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
38 GetFirmwareVersionType type);
15 39
16class SET_SYS final : public ServiceFramework<SET_SYS> { 40class SET_SYS final : public ServiceFramework<SET_SYS> {
17public: 41public:
@@ -269,6 +293,16 @@ private:
269 }; 293 };
270 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); 294 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
271 295
296 /// This is nn::settings::system::HomeMenuScheme
297 struct HomeMenuScheme {
298 u32 main;
299 u32 back;
300 u32 sub;
301 u32 bezel;
302 u32 extra;
303 };
304 static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
305
272 void SetLanguageCode(HLERequestContext& ctx); 306 void SetLanguageCode(HLERequestContext& ctx);
273 void GetFirmwareVersion(HLERequestContext& ctx); 307 void GetFirmwareVersion(HLERequestContext& ctx);
274 void GetFirmwareVersion2(HLERequestContext& ctx); 308 void GetFirmwareVersion2(HLERequestContext& ctx);
@@ -305,6 +339,8 @@ private:
305 void GetKeyboardLayout(HLERequestContext& ctx); 339 void GetKeyboardLayout(HLERequestContext& ctx);
306 void GetChineseTraditionalInputMethod(HLERequestContext& ctx); 340 void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
307 void GetFieldTestingFlag(HLERequestContext& ctx); 341 void GetFieldTestingFlag(HLERequestContext& ctx);
342 void GetHomeMenuScheme(HLERequestContext& ctx);
343 void GetHomeMenuSchemeModel(HLERequestContext& ctx);
308 344
309 AccountSettings account_settings{ 345 AccountSettings account_settings{
310 .flags = {}, 346 .flags = {},
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 85849d5f3..dd652ca42 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -39,6 +39,18 @@ bool IsConnectionBased(Type type) {
39 } 39 }
40} 40}
41 41
42template <typename T>
43T GetValue(std::span<const u8> buffer) {
44 T t{};
45 std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size()));
46 return t;
47}
48
49template <typename T>
50void PutValue(std::span<u8> buffer, const T& t) {
51 std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size()));
52}
53
42} // Anonymous namespace 54} // Anonymous namespace
43 55
44void BSD::PollWork::Execute(BSD* bsd) { 56void BSD::PollWork::Execute(BSD* bsd) {
@@ -316,22 +328,12 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
316 const s32 fd = rp.Pop<s32>(); 328 const s32 fd = rp.Pop<s32>();
317 const u32 level = rp.Pop<u32>(); 329 const u32 level = rp.Pop<u32>();
318 const OptName optname = static_cast<OptName>(rp.Pop<u32>()); 330 const OptName optname = static_cast<OptName>(rp.Pop<u32>());
319 331 const auto optval = ctx.ReadBuffer();
320 const auto buffer = ctx.ReadBuffer();
321 const u8* optval = buffer.empty() ? nullptr : buffer.data();
322 size_t optlen = buffer.size();
323
324 std::array<u64, 2> values;
325 if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
326 std::memcpy(values.data(), buffer.data(), sizeof(values));
327 optlen = sizeof(values);
328 optval = reinterpret_cast<const u8*>(values.data());
329 }
330 332
331 LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, 333 LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
332 static_cast<u32>(optname), optlen); 334 static_cast<u32>(optname), optval.size());
333 335
334 BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); 336 BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
335} 337}
336 338
337void BSD::Shutdown(HLERequestContext& ctx) { 339void BSD::Shutdown(HLERequestContext& ctx) {
@@ -521,18 +523,19 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
521 523
522std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, 524std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
523 s32 nfds, s32 timeout) { 525 s32 nfds, s32 timeout) {
524 if (write_buffer.size() < nfds * sizeof(PollFD)) { 526 if (nfds <= 0) {
525 return {-1, Errno::INVAL};
526 }
527
528 if (nfds == 0) {
529 // When no entries are provided, -1 is returned with errno zero 527 // When no entries are provided, -1 is returned with errno zero
530 return {-1, Errno::SUCCESS}; 528 return {-1, Errno::SUCCESS};
531 } 529 }
530 if (read_buffer.size() < nfds * sizeof(PollFD)) {
531 return {-1, Errno::INVAL};
532 }
533 if (write_buffer.size() < nfds * sizeof(PollFD)) {
534 return {-1, Errno::INVAL};
535 }
532 536
533 const size_t length = std::min(read_buffer.size(), write_buffer.size());
534 std::vector<PollFD> fds(nfds); 537 std::vector<PollFD> fds(nfds);
535 std::memcpy(fds.data(), read_buffer.data(), length); 538 std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
536 539
537 if (timeout >= 0) { 540 if (timeout >= 0) {
538 const s64 seconds = timeout / 1000; 541 const s64 seconds = timeout / 1000;
@@ -580,7 +583,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
580 for (size_t i = 0; i < num; ++i) { 583 for (size_t i = 0; i < num; ++i) {
581 fds[i].revents = Translate(host_pollfds[i].revents); 584 fds[i].revents = Translate(host_pollfds[i].revents);
582 } 585 }
583 std::memcpy(write_buffer.data(), fds.data(), length); 586 std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
584 587
585 return Translate(result); 588 return Translate(result);
586} 589}
@@ -608,8 +611,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
608 new_descriptor.is_connection_based = descriptor.is_connection_based; 611 new_descriptor.is_connection_based = descriptor.is_connection_based;
609 612
610 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); 613 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
611 const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); 614 PutValue(write_buffer, guest_addr_in);
612 std::memcpy(write_buffer.data(), &guest_addr_in, length);
613 615
614 return {new_fd, Errno::SUCCESS}; 616 return {new_fd, Errno::SUCCESS};
615} 617}
@@ -619,8 +621,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
619 return Errno::BADF; 621 return Errno::BADF;
620 } 622 }
621 ASSERT(addr.size() == sizeof(SockAddrIn)); 623 ASSERT(addr.size() == sizeof(SockAddrIn));
622 SockAddrIn addr_in; 624 auto addr_in = GetValue<SockAddrIn>(addr);
623 std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
624 625
625 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); 626 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
626} 627}
@@ -631,8 +632,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
631 } 632 }
632 633
633 UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); 634 UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
634 SockAddrIn addr_in; 635 auto addr_in = GetValue<SockAddrIn>(addr);
635 std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
636 636
637 return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); 637 return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
638} 638}
@@ -650,7 +650,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
650 650
651 ASSERT(write_buffer.size() >= sizeof(guest_addrin)); 651 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
652 write_buffer.resize(sizeof(guest_addrin)); 652 write_buffer.resize(sizeof(guest_addrin));
653 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 653 PutValue(write_buffer, guest_addrin);
654 return Translate(bsd_errno); 654 return Translate(bsd_errno);
655} 655}
656 656
@@ -667,7 +667,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
667 667
668 ASSERT(write_buffer.size() >= sizeof(guest_addrin)); 668 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
669 write_buffer.resize(sizeof(guest_addrin)); 669 write_buffer.resize(sizeof(guest_addrin));
670 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 670 PutValue(write_buffer, guest_addrin);
671 return Translate(bsd_errno); 671 return Translate(bsd_errno);
672} 672}
673 673
@@ -725,7 +725,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
725 optval.size() == sizeof(Errno), { return Errno::INVAL; }, 725 optval.size() == sizeof(Errno), { return Errno::INVAL; },
726 "Incorrect getsockopt option size"); 726 "Incorrect getsockopt option size");
727 optval.resize(sizeof(Errno)); 727 optval.resize(sizeof(Errno));
728 memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); 728 PutValue(optval, translated_pending_err);
729 } 729 }
730 return Translate(getsockopt_err); 730 return Translate(getsockopt_err);
731 } 731 }
@@ -735,7 +735,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
735 } 735 }
736} 736}
737 737
738Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { 738Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
739 if (!IsFileDescriptorValid(fd)) { 739 if (!IsFileDescriptorValid(fd)) {
740 return Errno::BADF; 740 return Errno::BADF;
741 } 741 }
@@ -748,17 +748,15 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
748 Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); 748 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
749 749
750 if (optname == OptName::LINGER) { 750 if (optname == OptName::LINGER) {
751 ASSERT(optlen == sizeof(Linger)); 751 ASSERT(optval.size() == sizeof(Linger));
752 Linger linger; 752 auto linger = GetValue<Linger>(optval);
753 std::memcpy(&linger, optval, sizeof(linger));
754 ASSERT(linger.onoff == 0 || linger.onoff == 1); 753 ASSERT(linger.onoff == 0 || linger.onoff == 1);
755 754
756 return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); 755 return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
757 } 756 }
758 757
759 ASSERT(optlen == sizeof(u32)); 758 ASSERT(optval.size() == sizeof(u32));
760 u32 value; 759 auto value = GetValue<u32>(optval);
761 std::memcpy(&value, optval, sizeof(value));
762 760
763 switch (optname) { 761 switch (optname) {
764 case OptName::REUSEADDR: 762 case OptName::REUSEADDR:
@@ -862,7 +860,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
862 } else { 860 } else {
863 ASSERT(addr.size() == sizeof(SockAddrIn)); 861 ASSERT(addr.size() == sizeof(SockAddrIn));
864 const SockAddrIn result = Translate(addr_in); 862 const SockAddrIn result = Translate(addr_in);
865 std::memcpy(addr.data(), &result, sizeof(result)); 863 PutValue(addr, result);
866 } 864 }
867 } 865 }
868 866
@@ -886,8 +884,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes
886 Network::SockAddrIn* p_addr_in = nullptr; 884 Network::SockAddrIn* p_addr_in = nullptr;
887 if (!addr.empty()) { 885 if (!addr.empty()) {
888 ASSERT(addr.size() == sizeof(SockAddrIn)); 886 ASSERT(addr.size() == sizeof(SockAddrIn));
889 SockAddrIn guest_addr_in; 887 auto guest_addr_in = GetValue<SockAddrIn>(addr);
890 std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
891 addr_in = Translate(guest_addr_in); 888 addr_in = Translate(guest_addr_in);
892 p_addr_in = &addr_in; 889 p_addr_in = &addr_in;
893 } 890 }
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 161f22b9b..4f69d382c 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -163,7 +163,7 @@ private:
163 Errno ListenImpl(s32 fd, s32 backlog); 163 Errno ListenImpl(s32 fd, s32 backlog);
164 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); 164 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
165 Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); 165 Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
166 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); 166 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
167 Errno ShutdownImpl(s32 fd, s32 how); 167 Errno ShutdownImpl(s32 fd, s32 how);
168 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); 168 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
169 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, 169 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index 9fc01ea90..7149fffeb 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -11,6 +11,11 @@
11#include "core/hle/service/time/errors.h" 11#include "core/hle/service/time/errors.h"
12#include "core/hle/service/time/time_zone_types.h" 12#include "core/hle/service/time/time_zone_types.h"
13 13
14// Defined by WinBase.h on Windows
15#ifdef GetCurrentTime
16#undef GetCurrentTime
17#endif
18
14namespace Service::Time::Clock { 19namespace Service::Time::Clock {
15 20
16enum class TimeType : u8 { 21enum class TimeType : u8 {
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index f0b5eff8a..d30f49877 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont
35 return { 35 return {
36 buffer_queue_core, 36 buffer_queue_core,
37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), 37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; 38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
39} 39}
40 40
41Display::Display(u64 id, std::string name_, 41Display::Display(u64 id, std::string name_,
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index fa5273402..a3431772a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -1,8 +1,10 @@
1// SPDX-FileCopyrightText: 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// SPDX-FileCopyrightText: 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 3// SPDX-License-Identifier: GPL-2.0-or-later
3 4
4#include <algorithm> 5#include <algorithm>
5#include <cstring> 6#include <cstring>
7#include <mutex>
6#include <span> 8#include <span>
7 9
8#include "common/assert.h" 10#include "common/assert.h"
@@ -10,6 +12,7 @@
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "common/logging/log.h" 13#include "common/logging/log.h"
12#include "common/page_table.h" 14#include "common/page_table.h"
15#include "common/scope_exit.h"
13#include "common/settings.h" 16#include "common/settings.h"
14#include "common/swap.h" 17#include "common/swap.h"
15#include "core/core.h" 18#include "core/core.h"
@@ -41,7 +44,7 @@ struct Memory::Impl {
41 explicit Impl(Core::System& system_) : system{system_} {} 44 explicit Impl(Core::System& system_) : system{system_} {}
42 45
43 void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { 46 void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
44 current_page_table = &process.GetPageTable().PageTableImpl(); 47 current_page_table = &process.GetPageTable().GetImpl();
45 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); 48 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
46 49
47 const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth(); 50 const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth();
@@ -195,7 +198,7 @@ struct Memory::Impl {
195 198
196 bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, 199 bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
197 auto on_memory, auto on_rasterizer, auto increment) { 200 auto on_memory, auto on_rasterizer, auto increment) {
198 const auto& page_table = system.ApplicationProcess()->GetPageTable().PageTableImpl(); 201 const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl();
199 std::size_t remaining_size = size; 202 std::size_t remaining_size = size;
200 std::size_t page_index = addr >> YUZU_PAGEBITS; 203 std::size_t page_index = addr >> YUZU_PAGEBITS;
201 std::size_t page_offset = addr & YUZU_PAGEMASK; 204 std::size_t page_offset = addr & YUZU_PAGEMASK;
@@ -318,7 +321,7 @@ struct Memory::Impl {
318 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, 321 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
319 u8* const host_ptr) { 322 u8* const host_ptr) {
320 if constexpr (!UNSAFE) { 323 if constexpr (!UNSAFE) {
321 system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); 324 HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount);
322 } 325 }
323 std::memcpy(host_ptr, src_buffer, copy_amount); 326 std::memcpy(host_ptr, src_buffer, copy_amount);
324 }, 327 },
@@ -351,7 +354,7 @@ struct Memory::Impl {
351 }, 354 },
352 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, 355 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
353 u8* const host_ptr) { 356 u8* const host_ptr) {
354 system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); 357 HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount);
355 std::memset(host_ptr, 0, copy_amount); 358 std::memset(host_ptr, 0, copy_amount);
356 }, 359 },
357 [](const std::size_t copy_amount) {}); 360 [](const std::size_t copy_amount) {});
@@ -420,7 +423,7 @@ struct Memory::Impl {
420 const std::size_t block_size) { 423 const std::size_t block_size) {
421 // dc cvac: Store to point of coherency 424 // dc cvac: Store to point of coherency
422 // CPU flush -> GPU invalidate 425 // CPU flush -> GPU invalidate
423 system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); 426 HandleRasterizerWrite(GetInteger(current_vaddr), block_size);
424 }; 427 };
425 return PerformCacheOperation(dest_addr, size, on_rasterizer); 428 return PerformCacheOperation(dest_addr, size, on_rasterizer);
426 } 429 }
@@ -430,7 +433,7 @@ struct Memory::Impl {
430 const std::size_t block_size) { 433 const std::size_t block_size) {
431 // dc civac: Store to point of coherency, and invalidate from cache 434 // dc civac: Store to point of coherency, and invalidate from cache
432 // CPU flush -> GPU invalidate 435 // CPU flush -> GPU invalidate
433 system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); 436 HandleRasterizerWrite(GetInteger(current_vaddr), block_size);
434 }; 437 };
435 return PerformCacheOperation(dest_addr, size, on_rasterizer); 438 return PerformCacheOperation(dest_addr, size, on_rasterizer);
436 } 439 }
@@ -767,7 +770,18 @@ struct Memory::Impl {
767 } 770 }
768 771
769 void HandleRasterizerWrite(VAddr address, size_t size) { 772 void HandleRasterizerWrite(VAddr address, size_t size) {
770 const size_t core = system.GetCurrentHostThreadID(); 773 constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
774 const size_t core = std::min(system.GetCurrentHostThreadID(),
775 sys_core); // any other calls threads go to syscore.
776 // Guard on sys_core;
777 if (core == sys_core) [[unlikely]] {
778 sys_core_guard.lock();
779 }
780 SCOPE_EXIT({
781 if (core == sys_core) [[unlikely]] {
782 sys_core_guard.unlock();
783 }
784 });
771 auto& current_area = rasterizer_write_areas[core]; 785 auto& current_area = rasterizer_write_areas[core];
772 VAddr subaddress = address >> YUZU_PAGEBITS; 786 VAddr subaddress = address >> YUZU_PAGEBITS;
773 bool do_collection = current_area.last_address == subaddress; 787 bool do_collection = current_area.last_address == subaddress;
@@ -799,6 +813,7 @@ struct Memory::Impl {
799 rasterizer_read_areas{}; 813 rasterizer_read_areas{};
800 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; 814 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
801 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; 815 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
816 std::mutex sys_core_guard;
802}; 817};
803 818
804Memory::Memory(Core::System& system_) : system{system_} { 819Memory::Memory(Core::System& system_) : system{system_} {
@@ -826,7 +841,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress b
826 841
827bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { 842bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
828 const Kernel::KProcess& process = *system.ApplicationProcess(); 843 const Kernel::KProcess& process = *system.ApplicationProcess();
829 const auto& page_table = process.GetPageTable().PageTableImpl(); 844 const auto& page_table = process.GetPageTable().GetImpl();
830 const size_t page = vaddr >> YUZU_PAGEBITS; 845 const size_t page = vaddr >> YUZU_PAGEBITS;
831 if (page >= page_table.pointers.size()) { 846 if (page >= page_table.pointers.size()) {
832 return false; 847 return false;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 53a89cc8f..db30ba598 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -10,7 +10,8 @@
10#include "core/hle/kernel/k_page_table.h" 10#include "core/hle/kernel/k_page_table.h"
11#include "core/hle/kernel/k_process.h" 11#include "core/hle/kernel/k_process.h"
12#include "core/hle/service/hid/controllers/npad.h" 12#include "core/hle/service/hid/controllers/npad.h"
13#include "core/hle/service/hid/hid.h" 13#include "core/hle/service/hid/hid_server.h"
14#include "core/hle/service/hid/resource_manager.h"
14#include "core/hle/service/sm/sm.h" 15#include "core/hle/service/sm/sm.h"
15#include "core/memory.h" 16#include "core/memory.h"
16#include "core/memory/cheat_engine.h" 17#include "core/memory/cheat_engine.h"
@@ -54,23 +55,20 @@ void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size)
54} 55}
55 56
56u64 StandardVmCallbacks::HidKeysDown() { 57u64 StandardVmCallbacks::HidKeysDown() {
57 const auto hid = system.ServiceManager().GetService<Service::HID::Hid>("hid"); 58 const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
58 if (hid == nullptr) { 59 if (hid == nullptr) {
59 LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!"); 60 LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
60 return 0; 61 return 0;
61 } 62 }
62 63
63 const auto applet_resource = hid->GetAppletResource(); 64 const auto applet_resource = hid->GetResourceManager();
64 if (applet_resource == nullptr) { 65 if (applet_resource == nullptr) {
65 LOG_WARNING(CheatEngine, 66 LOG_WARNING(CheatEngine,
66 "Attempted to read input state, but applet resource is not initialized!"); 67 "Attempted to read input state, but applet resource is not initialized!");
67 return 0; 68 return 0;
68 } 69 }
69 70
70 const auto press_state = 71 const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
71 applet_resource
72 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
73 .GetAndResetPressState();
74 return static_cast<u64>(press_state & HID::NpadButton::All); 72 return static_cast<u64>(press_state & HID::NpadButton::All);
75} 73}
76 74
diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt
new file mode 100644
index 000000000..22e9337c4
--- /dev/null
+++ b/src/frontend_common/CMakeLists.txt
@@ -0,0 +1,10 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4add_library(frontend_common STATIC
5 config.cpp
6 config.h
7)
8
9create_target_directory_groups(frontend_common)
10target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers)
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
new file mode 100644
index 000000000..7474cb0f9
--- /dev/null
+++ b/src/frontend_common/config.cpp
@@ -0,0 +1,1008 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include "common/fs/fs.h"
7#include "common/fs/path_util.h"
8#include "common/settings.h"
9#include "common/settings_common.h"
10#include "common/settings_enums.h"
11#include "config.h"
12#include "core/core.h"
13#include "core/hle/service/acc/profile_manager.h"
14#include "core/hle/service/hid/controllers/npad.h"
15#include "network/network.h"
16
17#include <boost/algorithm/string/replace.hpp>
18
19#include "common/string_util.h"
20
21namespace FS = Common::FS;
22
23Config::Config(const ConfigType config_type)
24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {}
25
26void Config::Initialize(const std::string& config_name) {
27 const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
28 const auto config_file = fmt::format("{}.ini", config_name);
29
30 switch (type) {
31 case ConfigType::GlobalConfig:
32 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
33 void(FS::CreateParentDir(config_loc));
34 SetUpIni();
35 Reload();
36 break;
37 case ConfigType::PerGameConfig:
38 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
39 void(FS::CreateParentDir(config_loc));
40 SetUpIni();
41 Reload();
42 break;
43 case ConfigType::InputProfile:
44 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
45 void(FS::CreateParentDir(config_loc));
46 SetUpIni();
47 break;
48 }
49}
50
51void Config::Initialize(const std::optional<std::string> config_path) {
52 const std::filesystem::path default_sdl_config_path =
53 FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
54 config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path));
55 void(FS::CreateParentDir(config_loc));
56 SetUpIni();
57 Reload();
58}
59
60void Config::WriteToIni() const {
61 FILE* fp = nullptr;
62#ifdef _WIN32
63 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb");
64#else
65 fp = fopen(config_loc.c_str(), "wb");
66#endif
67
68 if (fp == nullptr) {
69 LOG_ERROR(Frontend, "Config file could not be saved!");
70 return;
71 }
72
73 CSimpleIniA::FileWriter writer(fp);
74 const SI_Error rc = config->Save(writer, false);
75 if (rc < 0) {
76 LOG_ERROR(Frontend, "Config file could not be saved!");
77 }
78 fclose(fp);
79}
80
81void Config::SetUpIni() {
82 config = std::make_unique<CSimpleIniA>();
83 config->SetUnicode(true);
84 config->SetSpaces(false);
85
86 FILE* fp = nullptr;
87#ifdef _WIN32
88 _wfopen_s(&fp, Common::UTF8ToUTF16W(config_loc).data(), L"rb, ccs=UTF-8");
89 if (fp == nullptr) {
90 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb, ccs=UTF-8");
91 }
92#else
93 fp = fopen(config_loc.c_str(), "rb");
94 if (fp == nullptr) {
95 fp = fopen(config_loc.c_str(), "wb");
96 }
97#endif
98
99 if (fp == nullptr) {
100 LOG_ERROR(Frontend, "Config file could not be loaded!");
101 return;
102 }
103
104 if (SI_Error rc = config->LoadFile(fp); rc < 0) {
105 LOG_ERROR(Frontend, "Config file could not be loaded!");
106 }
107 fclose(fp);
108}
109
110bool Config::IsCustomConfig() const {
111 return type == ConfigType::PerGameConfig;
112}
113
114void Config::ReadPlayerValues(const std::size_t player_index) {
115 std::string player_prefix;
116 if (type != ConfigType::InputProfile) {
117 player_prefix.append("player_").append(ToString(player_index)).append("_");
118 }
119
120 auto& player = Settings::values.players.GetValue()[player_index];
121 if (IsCustomConfig()) {
122 const auto profile_name =
123 ReadStringSetting(std::string(player_prefix).append("profile_name"));
124 if (profile_name.empty()) {
125 // Use the global input config
126 player = Settings::values.players.GetValue(true)[player_index];
127 return;
128 }
129 player.profile_name = profile_name;
130 }
131
132 if (player_prefix.empty() && Settings::IsConfiguringGlobal()) {
133 const auto controller = static_cast<Settings::ControllerType>(
134 ReadIntegerSetting(std::string(player_prefix).append("type"),
135 static_cast<u8>(Settings::ControllerType::ProController)));
136
137 if (controller == Settings::ControllerType::LeftJoycon ||
138 controller == Settings::ControllerType::RightJoycon) {
139 player.controller_type = controller;
140 }
141 } else {
142 std::string connected_key = player_prefix;
143 player.connected = ReadBooleanSetting(connected_key.append("connected"),
144 std::make_optional(player_index == 0));
145
146 player.controller_type = static_cast<Settings::ControllerType>(
147 ReadIntegerSetting(std::string(player_prefix).append("type"),
148 static_cast<u8>(Settings::ControllerType::ProController)));
149
150 player.vibration_enabled = ReadBooleanSetting(
151 std::string(player_prefix).append("vibration_enabled"), std::make_optional(true));
152
153 player.vibration_strength = static_cast<int>(
154 ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100));
155
156 player.body_color_left = static_cast<u32>(ReadIntegerSetting(
157 std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE));
158 player.body_color_right = static_cast<u32>(ReadIntegerSetting(
159 std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED));
160 player.button_color_left = static_cast<u32>(
161 ReadIntegerSetting(std::string(player_prefix).append("button_color_left"),
162 Settings::JOYCON_BUTTONS_NEON_BLUE));
163 player.button_color_right = static_cast<u32>(
164 ReadIntegerSetting(std::string(player_prefix).append("button_color_right"),
165 Settings::JOYCON_BUTTONS_NEON_RED));
166 }
167}
168
169void Config::ReadTouchscreenValues() {
170 Settings::values.touchscreen.enabled =
171 ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true));
172 Settings::values.touchscreen.rotation_angle =
173 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
174 Settings::values.touchscreen.diameter_x =
175 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15));
176 Settings::values.touchscreen.diameter_y =
177 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
178}
179
180void Config::ReadAudioValues() {
181 BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
182
183 ReadCategory(Settings::Category::Audio);
184 ReadCategory(Settings::Category::UiAudio);
185
186 EndGroup();
187}
188
189void Config::ReadControlValues() {
190 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
191
192 ReadCategory(Settings::Category::Controls);
193
194 Settings::values.players.SetGlobal(!IsCustomConfig());
195 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
196 ReadPlayerValues(p);
197 }
198
199 // Disable docked mode if handheld is selected
200 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
201 if (controller_type == Settings::ControllerType::Handheld) {
202 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
203 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
204 }
205
206 if (IsCustomConfig()) {
207 EndGroup();
208 return;
209 }
210 ReadTouchscreenValues();
211 ReadMotionTouchValues();
212
213 EndGroup();
214}
215
216void Config::ReadMotionTouchValues() {
217 int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps"));
218
219 if (num_touch_from_button_maps > 0) {
220 for (int i = 0; i < num_touch_from_button_maps; ++i) {
221 SetArrayIndex(i);
222
223 Settings::TouchFromButtonMap map;
224 map.name = ReadStringSetting(std::string("name"), std::string("default"));
225
226 const int num_touch_maps = BeginArray(std::string("entries"));
227 map.buttons.reserve(num_touch_maps);
228 for (int j = 0; j < num_touch_maps; j++) {
229 SetArrayIndex(j);
230 std::string touch_mapping = ReadStringSetting(std::string("bind"));
231 map.buttons.emplace_back(std::move(touch_mapping));
232 }
233 EndArray(); // entries
234 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
235 }
236 } else {
237 Settings::values.touch_from_button_maps.emplace_back(
238 Settings::TouchFromButtonMap{"default", {}});
239 num_touch_from_button_maps = 1;
240 }
241 EndArray(); // touch_from_button_maps
242
243 Settings::values.touch_from_button_map_index = std::clamp(
244 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
245}
246
247void Config::ReadCoreValues() {
248 BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
249
250 ReadCategory(Settings::Category::Core);
251
252 EndGroup();
253}
254
255void Config::ReadDataStorageValues() {
256 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
257
258 FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory")));
259 FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory")));
260 FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory")));
261 FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory")));
262 FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory")));
263
264 ReadCategory(Settings::Category::DataStorage);
265
266 EndGroup();
267}
268
269void Config::ReadDebuggingValues() {
270 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
271
272 // Intentionally not using the QT default setting as this is intended to be changed in the ini
273 Settings::values.record_frame_times =
274 ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
275
276 ReadCategory(Settings::Category::Debugging);
277 ReadCategory(Settings::Category::DebuggingGraphics);
278
279 EndGroup();
280}
281
282void Config::ReadServiceValues() {
283 BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
284
285 ReadCategory(Settings::Category::Services);
286
287 EndGroup();
288}
289
290void Config::ReadDisabledAddOnValues() {
291 // Custom config section
292 BeginGroup(std::string("DisabledAddOns"));
293
294 const int size = BeginArray(std::string(""));
295 for (int i = 0; i < size; ++i) {
296 SetArrayIndex(i);
297 const auto title_id = ReadUnsignedIntegerSetting(std::string("title_id"), 0);
298 std::vector<std::string> out;
299 const int d_size = BeginArray("disabled");
300 for (int j = 0; j < d_size; ++j) {
301 SetArrayIndex(j);
302 out.push_back(ReadStringSetting(std::string("d"), std::string("")));
303 }
304 EndArray(); // d
305 Settings::values.disabled_addons.insert_or_assign(title_id, out);
306 }
307 EndArray(); // Base disabled addons array - Has no base key
308
309 EndGroup();
310}
311
312void Config::ReadMiscellaneousValues() {
313 BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
314
315 ReadCategory(Settings::Category::Miscellaneous);
316
317 EndGroup();
318}
319
320void Config::ReadCpuValues() {
321 BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
322
323 ReadCategory(Settings::Category::Cpu);
324 ReadCategory(Settings::Category::CpuDebug);
325 ReadCategory(Settings::Category::CpuUnsafe);
326
327 EndGroup();
328}
329
330void Config::ReadRendererValues() {
331 BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
332
333 ReadCategory(Settings::Category::Renderer);
334 ReadCategory(Settings::Category::RendererAdvanced);
335 ReadCategory(Settings::Category::RendererDebug);
336
337 EndGroup();
338}
339
340void Config::ReadScreenshotValues() {
341 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
342
343 ReadCategory(Settings::Category::Screenshots);
344 FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir,
345 ReadStringSetting(std::string("screenshot_path")));
346
347 EndGroup();
348}
349
350void Config::ReadSystemValues() {
351 BeginGroup(Settings::TranslateCategory(Settings::Category::System));
352
353 ReadCategory(Settings::Category::System);
354 ReadCategory(Settings::Category::SystemAudio);
355
356 EndGroup();
357}
358
359void Config::ReadWebServiceValues() {
360 BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
361
362 ReadCategory(Settings::Category::WebService);
363
364 EndGroup();
365}
366
367void Config::ReadNetworkValues() {
368 BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
369
370 ReadCategory(Settings::Category::Network);
371
372 EndGroup();
373}
374
375void Config::ReadValues() {
376 if (global) {
377 ReadDataStorageValues();
378 ReadDebuggingValues();
379 ReadDisabledAddOnValues();
380 ReadNetworkValues();
381 ReadServiceValues();
382 ReadWebServiceValues();
383 ReadMiscellaneousValues();
384 }
385 ReadControlValues();
386 ReadCoreValues();
387 ReadCpuValues();
388 ReadRendererValues();
389 ReadAudioValues();
390 ReadSystemValues();
391}
392
393void Config::SavePlayerValues(const std::size_t player_index) {
394 std::string player_prefix;
395 if (type != ConfigType::InputProfile) {
396 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
397 }
398
399 const auto& player = Settings::values.players.GetValue()[player_index];
400 if (IsCustomConfig()) {
401 if (player.profile_name.empty()) {
402 // No custom profile selected
403 return;
404 }
405 WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
406 std::make_optional(std::string("")));
407 }
408
409 WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
410 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
411
412 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
413 WriteSetting(std::string(player_prefix).append("connected"), player.connected,
414 std::make_optional(player_index == 0));
415 WriteSetting(std::string(player_prefix).append("vibration_enabled"),
416 player.vibration_enabled, std::make_optional(true));
417 WriteSetting(std::string(player_prefix).append("vibration_strength"),
418 player.vibration_strength, std::make_optional(100));
419 WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left,
420 std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
421 WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right,
422 std::make_optional(Settings::JOYCON_BODY_NEON_RED));
423 WriteSetting(std::string(player_prefix).append("button_color_left"),
424 player.button_color_left,
425 std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
426 WriteSetting(std::string(player_prefix).append("button_color_right"),
427 player.button_color_right,
428 std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
429 }
430}
431
432void Config::SaveTouchscreenValues() {
433 const auto& touchscreen = Settings::values.touchscreen;
434
435 WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true));
436
437 WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
438 std::make_optional(static_cast<u32>(0)));
439 WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
440 std::make_optional(static_cast<u32>(15)));
441 WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
442 std::make_optional(static_cast<u32>(15)));
443}
444
445void Config::SaveMotionTouchValues() {
446 BeginArray(std::string("touch_from_button_maps"));
447 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
448 SetArrayIndex(static_cast<int>(p));
449 WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
450 std::make_optional(std::string("default")));
451
452 BeginArray(std::string("entries"));
453 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
454 ++q) {
455 SetArrayIndex(static_cast<int>(q));
456 WriteSetting(std::string("bind"),
457 Settings::values.touch_from_button_maps[p].buttons[q]);
458 }
459 EndArray(); // entries
460 }
461 EndArray(); // touch_from_button_maps
462}
463
464void Config::SaveValues() {
465 if (global) {
466 SaveDataStorageValues();
467 SaveDebuggingValues();
468 SaveDisabledAddOnValues();
469 SaveNetworkValues();
470 SaveWebServiceValues();
471 SaveMiscellaneousValues();
472 }
473 SaveControlValues();
474 SaveCoreValues();
475 SaveCpuValues();
476 SaveRendererValues();
477 SaveAudioValues();
478 SaveSystemValues();
479
480 WriteToIni();
481}
482
483void Config::SaveAudioValues() {
484 BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
485
486 WriteCategory(Settings::Category::Audio);
487 WriteCategory(Settings::Category::UiAudio);
488
489 EndGroup();
490}
491
492void Config::SaveControlValues() {
493 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
494
495 WriteCategory(Settings::Category::Controls);
496
497 Settings::values.players.SetGlobal(!IsCustomConfig());
498 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
499 SavePlayerValues(p);
500 }
501 if (IsCustomConfig()) {
502 EndGroup();
503 return;
504 }
505 SaveTouchscreenValues();
506 SaveMotionTouchValues();
507
508 EndGroup();
509}
510
511void Config::SaveCoreValues() {
512 BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
513
514 WriteCategory(Settings::Category::Core);
515
516 EndGroup();
517}
518
519void Config::SaveDataStorageValues() {
520 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
521
522 WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
523 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
524 WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
525 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
526 WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
527 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
528 WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
529 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
530 WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
531 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
532
533 WriteCategory(Settings::Category::DataStorage);
534
535 EndGroup();
536}
537
538void Config::SaveDebuggingValues() {
539 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
540
541 // Intentionally not using the QT default setting as this is intended to be changed in the ini
542 WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
543
544 WriteCategory(Settings::Category::Debugging);
545 WriteCategory(Settings::Category::DebuggingGraphics);
546
547 EndGroup();
548}
549
550void Config::SaveNetworkValues() {
551 BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
552
553 WriteCategory(Settings::Category::Network);
554
555 EndGroup();
556}
557
558void Config::SaveDisabledAddOnValues() {
559 // Custom config section
560 BeginGroup(std::string("DisabledAddOns"));
561
562 int i = 0;
563 BeginArray(std::string(""));
564 for (const auto& elem : Settings::values.disabled_addons) {
565 SetArrayIndex(i);
566 WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0)));
567 BeginArray(std::string("disabled"));
568 for (std::size_t j = 0; j < elem.second.size(); ++j) {
569 SetArrayIndex(static_cast<int>(j));
570 WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
571 }
572 EndArray(); // disabled
573 ++i;
574 }
575 EndArray(); // Base disabled addons array - Has no base key
576
577 EndGroup();
578}
579
580void Config::SaveMiscellaneousValues() {
581 BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
582
583 WriteCategory(Settings::Category::Miscellaneous);
584
585 EndGroup();
586}
587
588void Config::SaveCpuValues() {
589 BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
590
591 WriteCategory(Settings::Category::Cpu);
592 WriteCategory(Settings::Category::CpuDebug);
593 WriteCategory(Settings::Category::CpuUnsafe);
594
595 EndGroup();
596}
597
598void Config::SaveRendererValues() {
599 BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
600
601 WriteCategory(Settings::Category::Renderer);
602 WriteCategory(Settings::Category::RendererAdvanced);
603 WriteCategory(Settings::Category::RendererDebug);
604
605 EndGroup();
606}
607
608void Config::SaveScreenshotValues() {
609 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
610
611 WriteSetting(std::string("screenshot_path"),
612 FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
613 WriteCategory(Settings::Category::Screenshots);
614
615 EndGroup();
616}
617
618void Config::SaveSystemValues() {
619 BeginGroup(Settings::TranslateCategory(Settings::Category::System));
620
621 WriteCategory(Settings::Category::System);
622 WriteCategory(Settings::Category::SystemAudio);
623
624 EndGroup();
625}
626
627void Config::SaveWebServiceValues() {
628 BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
629
630 WriteCategory(Settings::Category::WebService);
631
632 EndGroup();
633}
634
635bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
636 std::string full_key = GetFullKey(key, false);
637 if (!default_value.has_value()) {
638 return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false);
639 }
640
641 if (config->GetBoolValue(GetSection().c_str(),
642 std::string(full_key).append("\\default").c_str(), false)) {
643 return static_cast<bool>(default_value.value());
644 } else {
645 return config->GetBoolValue(GetSection().c_str(), full_key.c_str(),
646 static_cast<bool>(default_value.value()));
647 }
648}
649
650s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) {
651 std::string full_key = GetFullKey(key, false);
652 if (!default_value.has_value()) {
653 try {
654 return std::stoll(
655 std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
656 } catch (...) {
657 return 0;
658 }
659 }
660
661 s64 result = 0;
662 if (config->GetBoolValue(GetSection().c_str(),
663 std::string(full_key).append("\\default").c_str(), true)) {
664 result = default_value.value();
665 } else {
666 try {
667 result = std::stoll(std::string(config->GetValue(
668 GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
669 } catch (...) {
670 result = default_value.value();
671 }
672 }
673 return result;
674}
675
676u64 Config::ReadUnsignedIntegerSetting(const std::string& key,
677 const std::optional<u64> default_value) {
678 std::string full_key = GetFullKey(key, false);
679 if (!default_value.has_value()) {
680 try {
681 return std::stoull(
682 std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
683 } catch (...) {
684 return 0;
685 }
686 }
687
688 u64 result = 0;
689 if (config->GetBoolValue(GetSection().c_str(),
690 std::string(full_key).append("\\default").c_str(), true)) {
691 result = default_value.value();
692 } else {
693 try {
694 result = std::stoull(std::string(config->GetValue(
695 GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
696 } catch (...) {
697 result = default_value.value();
698 }
699 }
700 return result;
701}
702
703double Config::ReadDoubleSetting(const std::string& key,
704 const std::optional<double> default_value) {
705 std::string full_key = GetFullKey(key, false);
706 if (!default_value.has_value()) {
707 return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0);
708 }
709
710 double result;
711 if (config->GetBoolValue(GetSection().c_str(),
712 std::string(full_key).append("\\default").c_str(), true)) {
713 result = default_value.value();
714 } else {
715 result =
716 config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value());
717 }
718 return result;
719}
720
721std::string Config::ReadStringSetting(const std::string& key,
722 const std::optional<std::string> default_value) {
723 std::string result;
724 std::string full_key = GetFullKey(key, false);
725 if (!default_value.has_value()) {
726 result = config->GetValue(GetSection().c_str(), full_key.c_str(), "");
727 boost::replace_all(result, "\"", "");
728 return result;
729 }
730
731 if (config->GetBoolValue(GetSection().c_str(),
732 std::string(full_key).append("\\default").c_str(), true)) {
733 result = default_value.value();
734 } else {
735 result =
736 config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str());
737 }
738 boost::replace_all(result, "\"", "");
739 boost::replace_all(result, "//", "/");
740 return result;
741}
742
743bool Config::Exists(const std::string& section, const std::string& key) const {
744 const std::string value = config->GetValue(section.c_str(), key.c_str(), "");
745 return !value.empty();
746}
747
748template <typename Type>
749void Config::WriteSetting(const std::string& key, const Type& value,
750 const std::optional<Type>& default_value,
751 const std::optional<bool>& use_global) {
752 std::string full_key = GetFullKey(key, false);
753
754 std::string saved_value;
755 std::string string_default;
756 if constexpr (std::is_same_v<Type, std::string>) {
757 saved_value.append(AdjustOutputString(value));
758 if (default_value.has_value()) {
759 string_default.append(AdjustOutputString(default_value.value()));
760 }
761 } else {
762 saved_value.append(AdjustOutputString(ToString(value)));
763 if (default_value.has_value()) {
764 string_default.append(ToString(default_value.value()));
765 }
766 }
767
768 if (default_value.has_value() && use_global.has_value()) {
769 if (!global) {
770 WriteSettingInternal(std::string(full_key).append("\\global"),
771 ToString(use_global.value()));
772 }
773 if (global || use_global.value() == false) {
774 WriteSettingInternal(std::string(full_key).append("\\default"),
775 ToString(string_default == saved_value));
776 WriteSettingInternal(full_key, saved_value);
777 }
778 } else if (default_value.has_value() && !use_global.has_value()) {
779 WriteSettingInternal(std::string(full_key).append("\\default"),
780 ToString(string_default == saved_value));
781 WriteSettingInternal(full_key, saved_value);
782 } else {
783 WriteSettingInternal(full_key, saved_value);
784 }
785}
786
787void Config::WriteSettingInternal(const std::string& key, const std::string& value) {
788 config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
789}
790
791void Config::Reload() {
792 ReadValues();
793 // To apply default value changes
794 SaveValues();
795}
796
797void Config::Save() {
798 SaveValues();
799}
800
801void Config::ClearControlPlayerValues() const {
802 // If key is an empty string, all keys in the current group() are removed.
803 const char* section = Settings::TranslateCategory(Settings::Category::Controls);
804 CSimpleIniA::TNamesDepend keys;
805 config->GetAllKeys(section, keys);
806 for (const auto& key : keys) {
807 if (std::string(config->GetValue(section, key.pItem)).empty()) {
808 config->Delete(section, key.pItem);
809 }
810 }
811}
812
813const std::string& Config::GetConfigFilePath() const {
814 return config_loc;
815}
816
817void Config::ReadCategory(const Settings::Category category) {
818 const auto& settings = FindRelevantList(category);
819 std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); });
820}
821
822void Config::WriteCategory(const Settings::Category category) {
823 const auto& settings = FindRelevantList(category);
824 std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); });
825}
826
827void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
828 if (!setting->Save() || (!setting->Switchable() && !global)) {
829 return;
830 }
831
832 const std::string key = AdjustKey(setting->GetLabel());
833 const std::string default_value(setting->DefaultToString());
834
835 bool use_global = true;
836 if (setting->Switchable() && !global) {
837 use_global =
838 ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true));
839 setting->SetGlobal(use_global);
840 }
841
842 if (global || !use_global) {
843 const bool is_default =
844 ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true));
845 if (!is_default) {
846 const std::string setting_string = ReadStringSetting(key, default_value);
847 setting->LoadString(setting_string);
848 } else {
849 // Empty string resets the Setting to default
850 setting->LoadString("");
851 }
852 }
853}
854
855void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
856 if (!setting->Save()) {
857 return;
858 }
859
860 std::string key = AdjustKey(setting->GetLabel());
861 if (setting->Switchable()) {
862 if (!global) {
863 WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
864 }
865 if (global || !setting->UsingGlobal()) {
866 WriteSetting(std::string(key).append("\\default"),
867 setting->ToString() == setting->DefaultToString());
868 WriteSetting(key, setting->ToString());
869 }
870 } else if (global) {
871 WriteSetting(std::string(key).append("\\default"),
872 setting->ToString() == setting->DefaultToString());
873 WriteSetting(key, setting->ToString());
874 }
875}
876
877void Config::BeginGroup(const std::string& group) {
878 // You can't begin a group while reading/writing from a config array
879 ASSERT(array_stack.empty());
880
881 key_stack.push_back(AdjustKey(group));
882}
883
884void Config::EndGroup() {
885 // You can't end a group if you haven't started one yet
886 ASSERT(!key_stack.empty());
887
888 // You can't end a group when reading/writing from a config array
889 ASSERT(array_stack.empty());
890
891 key_stack.pop_back();
892}
893
894std::string Config::GetSection() {
895 if (key_stack.empty()) {
896 return std::string{""};
897 }
898
899 return key_stack.front();
900}
901
902std::string Config::GetGroup() const {
903 if (key_stack.size() <= 1) {
904 return std::string{""};
905 }
906
907 std::string key;
908 for (size_t i = 1; i < key_stack.size(); ++i) {
909 key.append(key_stack[i]).append("\\");
910 }
911 return key;
912}
913
914std::string Config::AdjustKey(const std::string& key) {
915 std::string adjusted_key(key);
916 boost::replace_all(adjusted_key, "/", "\\");
917 boost::replace_all(adjusted_key, " ", "%20");
918 return adjusted_key;
919}
920
921std::string Config::AdjustOutputString(const std::string& string) {
922 std::string adjusted_string(string);
923 boost::replace_all(adjusted_string, "\\", "/");
924
925 // Windows requires that two forward slashes are used at the start of a path for unmapped
926 // network drives so we have to watch for that here
927 if (string.substr(0, 2) == "//") {
928 boost::replace_all(adjusted_string, "//", "/");
929 adjusted_string.insert(0, "/");
930 } else {
931 boost::replace_all(adjusted_string, "//", "/");
932 }
933
934 // Needed for backwards compatibility with QSettings deserialization
935 for (const auto& special_character : special_characters) {
936 if (adjusted_string.find(special_character) != std::string::npos) {
937 adjusted_string.insert(0, "\"");
938 adjusted_string.append("\"");
939 break;
940 }
941 }
942 return adjusted_string;
943}
944
945std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) {
946 if (array_stack.empty()) {
947 return std::string(GetGroup()).append(AdjustKey(key));
948 }
949
950 std::string array_key;
951 for (size_t i = 0; i < array_stack.size(); ++i) {
952 if (!array_stack[i].name.empty()) {
953 array_key.append(array_stack[i].name).append("\\");
954 }
955
956 if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) {
957 array_key.append(ToString(array_stack[i].index)).append("\\");
958 }
959 }
960 std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key));
961 return final_key;
962}
963
964int Config::BeginArray(const std::string& array) {
965 array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0});
966 const int size = config->GetLongValue(GetSection().c_str(),
967 GetFullKey(std::string("size"), true).c_str(), 0);
968 array_stack.back().size = size;
969 return size;
970}
971
972void Config::EndArray() {
973 // You can't end a config array before starting one
974 ASSERT(!array_stack.empty());
975
976 // Set the array size to 0 if the array is ended without changing the index
977 int size = 0;
978 if (array_stack.back().index != 0) {
979 size = array_stack.back().size;
980 }
981
982 // Write out the size to config
983 if (key_stack.size() == 1 && array_stack.back().name.empty()) {
984 // Edge-case where the first array created doesn't have a name
985 config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str());
986 } else {
987 const auto key = GetFullKey(std::string("size"), true);
988 config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str());
989 }
990
991 array_stack.pop_back();
992}
993
994void Config::SetArrayIndex(const int index) {
995 // You can't set the array index if you haven't started one yet
996 ASSERT(!array_stack.empty());
997
998 const int array_index = index + 1;
999
1000 // You can't exceed the known max size of the array by more than 1
1001 ASSERT(array_stack.front().size + 1 >= array_index);
1002
1003 // Change the config array size to the current index since you may want
1004 // to reduce the number of elements that you read back from the config
1005 // in the future.
1006 array_stack.back().size = array_index;
1007 array_stack.back().index = array_index;
1008}
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
new file mode 100644
index 000000000..b3812af17
--- /dev/null
+++ b/src/frontend_common/config.h
@@ -0,0 +1,211 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <string>
8#include "common/settings.h"
9
10#define SI_NO_CONVERSION
11#include <SimpleIni.h>
12#include <boost/algorithm/string/replace.hpp>
13
14// Workaround for conflicting definition in libloaderapi.h caused by SimpleIni
15#undef LoadString
16#undef CreateFile
17#undef DeleteFile
18#undef CopyFile
19#undef CreateDirectory
20#undef MoveFile
21
22namespace Core {
23class System;
24}
25
26class Config {
27public:
28 enum class ConfigType {
29 GlobalConfig,
30 PerGameConfig,
31 InputProfile,
32 };
33
34 virtual ~Config() = default;
35
36 void ClearControlPlayerValues() const;
37
38 [[nodiscard]] const std::string& GetConfigFilePath() const;
39
40 [[nodiscard]] bool Exists(const std::string& section, const std::string& key) const;
41
42protected:
43 explicit Config(ConfigType config_type = ConfigType::GlobalConfig);
44
45 void Initialize(const std::string& config_name = "config");
46 void Initialize(std::optional<std::string> config_path);
47
48 void WriteToIni() const;
49
50 void SetUpIni();
51 [[nodiscard]] bool IsCustomConfig() const;
52
53 void Reload();
54 void Save();
55
56 /**
57 * Derived config classes must implement this so they can reload all platform-specific
58 * values and global ones.
59 */
60 virtual void ReloadAllValues() = 0;
61
62 /**
63 * Derived config classes must implement this so they can save all platform-specific
64 * and global values.
65 */
66 virtual void SaveAllValues() = 0;
67
68 void ReadValues();
69 void ReadPlayerValues(std::size_t player_index);
70
71 void ReadTouchscreenValues();
72 void ReadMotionTouchValues();
73
74 // Read functions bases off the respective config section names.
75 void ReadAudioValues();
76 void ReadControlValues();
77 void ReadCoreValues();
78 void ReadDataStorageValues();
79 void ReadDebuggingValues();
80 void ReadServiceValues();
81 void ReadDisabledAddOnValues();
82 void ReadMiscellaneousValues();
83 void ReadCpuValues();
84 void ReadRendererValues();
85 void ReadScreenshotValues();
86 void ReadSystemValues();
87 void ReadWebServiceValues();
88 void ReadNetworkValues();
89
90 // Read platform specific sections
91 virtual void ReadHidbusValues() = 0;
92 virtual void ReadDebugControlValues() = 0;
93 virtual void ReadPathValues() = 0;
94 virtual void ReadShortcutValues() = 0;
95 virtual void ReadUIValues() = 0;
96 virtual void ReadUIGamelistValues() = 0;
97 virtual void ReadUILayoutValues() = 0;
98 virtual void ReadMultiplayerValues() = 0;
99
100 void SaveValues();
101 void SavePlayerValues(std::size_t player_index);
102 void SaveTouchscreenValues();
103 void SaveMotionTouchValues();
104
105 // Save functions based off the respective config section names.
106 void SaveAudioValues();
107 void SaveControlValues();
108 void SaveCoreValues();
109 void SaveDataStorageValues();
110 void SaveDebuggingValues();
111 void SaveNetworkValues();
112 void SaveDisabledAddOnValues();
113 void SaveMiscellaneousValues();
114 void SaveCpuValues();
115 void SaveRendererValues();
116 void SaveScreenshotValues();
117 void SaveSystemValues();
118 void SaveWebServiceValues();
119
120 // Save platform specific sections
121 virtual void SaveHidbusValues() = 0;
122 virtual void SaveDebugControlValues() = 0;
123 virtual void SavePathValues() = 0;
124 virtual void SaveShortcutValues() = 0;
125 virtual void SaveUIValues() = 0;
126 virtual void SaveUIGamelistValues() = 0;
127 virtual void SaveUILayoutValues() = 0;
128 virtual void SaveMultiplayerValues() = 0;
129
130 virtual std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) = 0;
131
132 /**
133 * Reads a setting from the qt_config.
134 *
135 * @param key The setting's identifier
136 * @param default_value The value to use when the setting is not already present in the config
137 */
138 bool ReadBooleanSetting(const std::string& key,
139 std::optional<bool> default_value = std::nullopt);
140 s64 ReadIntegerSetting(const std::string& key, std::optional<s64> default_value = std::nullopt);
141 u64 ReadUnsignedIntegerSetting(const std::string& key,
142 std::optional<u64> default_value = std::nullopt);
143 double ReadDoubleSetting(const std::string& key,
144 std::optional<double> default_value = std::nullopt);
145 std::string ReadStringSetting(const std::string& key,
146 std::optional<std::string> default_value = std::nullopt);
147
148 /**
149 * Writes a setting to the qt_config.
150 *
151 * @param key The setting's idetentifier
152 * @param value Value of the setting
153 * @param default_value Default of the setting if not present in config
154 * @param use_global Specifies if the custom or global config should be in use, for custom
155 * configs
156 */
157 template <typename Type = int>
158 void WriteSetting(const std::string& key, const Type& value,
159 const std::optional<Type>& default_value = std::nullopt,
160 const std::optional<bool>& use_global = std::nullopt);
161 void WriteSettingInternal(const std::string& key, const std::string& value);
162
163 void ReadCategory(Settings::Category category);
164 void WriteCategory(Settings::Category category);
165 void ReadSettingGeneric(Settings::BasicSetting* setting);
166 void WriteSettingGeneric(const Settings::BasicSetting* setting);
167
168 template <typename T>
169 [[nodiscard]] std::string ToString(const T& value_) {
170 if constexpr (std::is_same_v<T, std::string>) {
171 return value_;
172 } else if constexpr (std::is_same_v<T, std::optional<u32>>) {
173 return value_.has_value() ? std::to_string(*value_) : "none";
174 } else if constexpr (std::is_same_v<T, bool>) {
175 return value_ ? "true" : "false";
176 } else if constexpr (std::is_same_v<T, u64>) {
177 return std::to_string(static_cast<u64>(value_));
178 } else {
179 return std::to_string(static_cast<s64>(value_));
180 }
181 }
182
183 void BeginGroup(const std::string& group);
184 void EndGroup();
185 std::string GetSection();
186 [[nodiscard]] std::string GetGroup() const;
187 static std::string AdjustKey(const std::string& key);
188 static std::string AdjustOutputString(const std::string& string);
189 std::string GetFullKey(const std::string& key, bool skipArrayIndex);
190 int BeginArray(const std::string& array);
191 void EndArray();
192 void SetArrayIndex(int index);
193
194 const ConfigType type;
195 std::unique_ptr<CSimpleIniA> config;
196 std::string config_loc;
197 const bool global;
198
199private:
200 inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*',
201 '|', ';', '\'', '\"', ',', '<', '.',
202 '>', '?', '`', '~', '='};
203
204 struct ConfigArray {
205 std::string name;
206 int size;
207 int index;
208 };
209 std::vector<ConfigArray> array_stack;
210 std::vector<std::string> key_stack;
211};
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 3ad34884d..1ff296af5 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -415,7 +415,7 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p
415 // This list is missing ZL/ZR since those are not considered buttons. 415 // This list is missing ZL/ZR since those are not considered buttons.
416 // We will add those afterwards 416 // We will add those afterwards
417 // This list also excludes any button that can't be really mapped 417 // This list also excludes any button that can't be really mapped
418 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> 418 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 14>
419 switch_to_gcadapter_button = { 419 switch_to_gcadapter_button = {
420 std::pair{Settings::NativeButton::A, PadButton::ButtonA}, 420 std::pair{Settings::NativeButton::A, PadButton::ButtonA},
421 {Settings::NativeButton::B, PadButton::ButtonB}, 421 {Settings::NativeButton::B, PadButton::ButtonB},
@@ -426,8 +426,10 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p
426 {Settings::NativeButton::DUp, PadButton::ButtonUp}, 426 {Settings::NativeButton::DUp, PadButton::ButtonUp},
427 {Settings::NativeButton::DRight, PadButton::ButtonRight}, 427 {Settings::NativeButton::DRight, PadButton::ButtonRight},
428 {Settings::NativeButton::DDown, PadButton::ButtonDown}, 428 {Settings::NativeButton::DDown, PadButton::ButtonDown},
429 {Settings::NativeButton::SL, PadButton::TriggerL}, 429 {Settings::NativeButton::SLLeft, PadButton::TriggerL},
430 {Settings::NativeButton::SR, PadButton::TriggerR}, 430 {Settings::NativeButton::SRLeft, PadButton::TriggerR},
431 {Settings::NativeButton::SLRight, PadButton::TriggerL},
432 {Settings::NativeButton::SRRight, PadButton::TriggerR},
431 {Settings::NativeButton::R, PadButton::TriggerZ}, 433 {Settings::NativeButton::R, PadButton::TriggerZ},
432 }; 434 };
433 if (!params.Has("port")) { 435 if (!params.Has("port")) {
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 0aca5a3a3..72d2951f3 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -680,8 +680,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par
680 Common::ParamPackage sr_button_params = button_params; 680 Common::ParamPackage sr_button_params = button_params;
681 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); 681 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
682 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); 682 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
683 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); 683 mapping.insert_or_assign(Settings::NativeButton::SLLeft, std::move(sl_button_params));
684 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); 684 mapping.insert_or_assign(Settings::NativeButton::SRLeft, std::move(sr_button_params));
685 } 685 }
686 686
687 // Map SL and SR buttons for right joycons 687 // Map SL and SR buttons for right joycons
@@ -693,8 +693,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par
693 Common::ParamPackage sr_button_params = button_params; 693 Common::ParamPackage sr_button_params = button_params;
694 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); 694 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
695 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); 695 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
696 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); 696 mapping.insert_or_assign(Settings::NativeButton::SLRight, std::move(sl_button_params));
697 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); 697 mapping.insert_or_assign(Settings::NativeButton::SRRight, std::move(sr_button_params));
698 } 698 }
699 699
700 return mapping; 700 return mapping;
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 66e3ae9af..78f458afe 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -828,16 +828,18 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
828ButtonBindings SDLDriver::GetDefaultButtonBinding( 828ButtonBindings SDLDriver::GetDefaultButtonBinding(
829 const std::shared_ptr<SDLJoystick>& joystick) const { 829 const std::shared_ptr<SDLJoystick>& joystick) const {
830 // Default SL/SR mapping for other controllers 830 // Default SL/SR mapping for other controllers
831 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; 831 auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
832 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; 832 auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
833 auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
834 auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
833 835
834 if (joystick->IsJoyconLeft()) { 836 if (joystick->IsJoyconLeft()) {
835 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; 837 sll_button = SDL_CONTROLLER_BUTTON_PADDLE2;
836 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; 838 srl_button = SDL_CONTROLLER_BUTTON_PADDLE4;
837 } 839 }
838 if (joystick->IsJoyconRight()) { 840 if (joystick->IsJoyconRight()) {
839 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; 841 slr_button = SDL_CONTROLLER_BUTTON_PADDLE3;
840 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; 842 srr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
841 } 843 }
842 844
843 return { 845 return {
@@ -855,8 +857,10 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding(
855 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, 857 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
856 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, 858 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
857 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, 859 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
858 {Settings::NativeButton::SL, sl_button}, 860 {Settings::NativeButton::SLLeft, sll_button},
859 {Settings::NativeButton::SR, sr_button}, 861 {Settings::NativeButton::SRLeft, srl_button},
862 {Settings::NativeButton::SLRight, slr_button},
863 {Settings::NativeButton::SRRight, srr_button},
860 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, 864 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
861 {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, 865 {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
862 }; 866 };
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index fcba4e3c6..08e49a0da 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -24,7 +24,7 @@ namespace InputCommon {
24class SDLJoystick; 24class SDLJoystick;
25 25
26using ButtonBindings = 26using ButtonBindings =
27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>; 27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 20>;
28using ZButtonBindings = 28using ZButtonBindings =
29 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 29 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
30 30
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index 77db60e92..60821b31a 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -396,7 +396,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
396 396
397ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { 397ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) {
398 // This list excludes any button that can't be really mapped 398 // This list excludes any button that can't be really mapped
399 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 20> 399 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 22>
400 switch_to_dsu_button = { 400 switch_to_dsu_button = {
401 std::pair{Settings::NativeButton::A, PadButton::Circle}, 401 std::pair{Settings::NativeButton::A, PadButton::Circle},
402 {Settings::NativeButton::B, PadButton::Cross}, 402 {Settings::NativeButton::B, PadButton::Cross},
@@ -412,8 +412,10 @@ ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& p
412 {Settings::NativeButton::R, PadButton::R1}, 412 {Settings::NativeButton::R, PadButton::R1},
413 {Settings::NativeButton::ZL, PadButton::L2}, 413 {Settings::NativeButton::ZL, PadButton::L2},
414 {Settings::NativeButton::ZR, PadButton::R2}, 414 {Settings::NativeButton::ZR, PadButton::R2},
415 {Settings::NativeButton::SL, PadButton::L2}, 415 {Settings::NativeButton::SLLeft, PadButton::L2},
416 {Settings::NativeButton::SR, PadButton::R2}, 416 {Settings::NativeButton::SRLeft, PadButton::R2},
417 {Settings::NativeButton::SLRight, PadButton::L2},
418 {Settings::NativeButton::SRRight, PadButton::R2},
417 {Settings::NativeButton::LStick, PadButton::L3}, 419 {Settings::NativeButton::LStick, PadButton::L3},
418 {Settings::NativeButton::RStick, PadButton::R3}, 420 {Settings::NativeButton::RStick, PadButton::R3},
419 {Settings::NativeButton::Home, PadButton::Home}, 421 {Settings::NativeButton::Home, PadButton::Home},
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 83b763447..19db17c6d 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -231,6 +231,7 @@ add_library(shader_recompiler STATIC
231 ir_opt/rescaling_pass.cpp 231 ir_opt/rescaling_pass.cpp
232 ir_opt/ssa_rewrite_pass.cpp 232 ir_opt/ssa_rewrite_pass.cpp
233 ir_opt/texture_pass.cpp 233 ir_opt/texture_pass.cpp
234 ir_opt/vendor_workaround_pass.cpp
234 ir_opt/verification_pass.cpp 235 ir_opt/verification_pass.cpp
235 object_pool.h 236 object_pool.h
236 precompiled_headers.h 237 precompiled_headers.h
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index d0e308124..64e7bad75 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -559,12 +559,12 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
559 const IR::Value& offset, const IR::Value& lod_clamp) { 559 const IR::Value& offset, const IR::Value& lod_clamp) {
560 const auto info{inst.Flags<IR::TextureInstInfo>()}; 560 const auto info{inst.Flags<IR::TextureInstInfo>()};
561 ScopedRegister dpdx, dpdy, coords; 561 ScopedRegister dpdx, dpdy, coords;
562 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; 562 const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
563 if (multi_component) { 563 if (multi_component) {
564 // Allocate this early to avoid aliasing other registers 564 // Allocate this early to avoid aliasing other registers
565 dpdx = ScopedRegister{ctx.reg_alloc}; 565 dpdx = ScopedRegister{ctx.reg_alloc};
566 dpdy = ScopedRegister{ctx.reg_alloc}; 566 dpdy = ScopedRegister{ctx.reg_alloc};
567 if (info.num_derivates >= 3) { 567 if (info.num_derivatives >= 3) {
568 coords = ScopedRegister{ctx.reg_alloc}; 568 coords = ScopedRegister{ctx.reg_alloc};
569 } 569 }
570 } 570 }
@@ -584,7 +584,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
584 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec, 584 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
585 dpdy.reg, derivatives_vec); 585 dpdy.reg, derivatives_vec);
586 Register final_coord; 586 Register final_coord;
587 if (info.num_derivates >= 3) { 587 if (info.num_derivatives >= 3) {
588 ctx.Add("MOV.F {}.z,{}.x;" 588 ctx.Add("MOV.F {}.z,{}.x;"
589 "MOV.F {}.z,{}.y;", 589 "MOV.F {}.z,{}.y;",
590 dpdx.reg, coord_vec, dpdy.reg, coord_vec); 590 dpdx.reg, coord_vec, dpdy.reg, coord_vec);
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index d9872ecc2..6e940bd5a 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -548,15 +548,15 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
548 if (sparse_inst) { 548 if (sparse_inst) {
549 throw NotImplementedException("EmitImageGradient Sparse"); 549 throw NotImplementedException("EmitImageGradient Sparse");
550 } 550 }
551 if (!offset.IsEmpty() && info.num_derivates <= 2) { 551 if (!offset.IsEmpty() && info.num_derivatives <= 2) {
552 throw NotImplementedException("EmitImageGradient offset"); 552 throw NotImplementedException("EmitImageGradient offset");
553 } 553 }
554 const auto texture{Texture(ctx, info, index)}; 554 const auto texture{Texture(ctx, info, index)};
555 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; 555 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
556 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; 556 const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
557 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)}; 557 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)};
558 if (multi_component) { 558 if (multi_component) {
559 if (info.num_derivates >= 3) { 559 if (info.num_derivatives >= 3) {
560 const auto offset_vec{ctx.var_alloc.Consume(offset)}; 560 const auto offset_vec{ctx.var_alloc.Consume(offset)};
561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, 561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture,
562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); 562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 34592a01f..0031fa5fb 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -407,7 +407,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
407 } 407 }
408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); 408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
409 } 409 }
410 if (info.stores[IR::Attribute::ViewportIndex]) { 410 if (info.stores[IR::Attribute::ViewportIndex] && profile.support_multi_viewport) {
411 ctx.AddCapability(spv::Capability::MultiViewport); 411 ctx.AddCapability(spv::Capability::MultiViewport);
412 } 412 }
413 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { 413 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 1d77426e0..e5a78a914 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -84,6 +84,10 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
84 } 84 }
85 return std::nullopt; 85 return std::nullopt;
86 case IR::Attribute::ViewportIndex: 86 case IR::Attribute::ViewportIndex:
87 if (!ctx.profile.support_multi_viewport) {
88 LOG_WARNING(Shader, "Ignoring viewport index store on non-supporting driver");
89 return std::nullopt;
90 }
87 if (ctx.profile.support_viewport_index_layer_non_geometry || 91 if (ctx.profile.support_viewport_index_layer_non_geometry ||
88 ctx.stage == Shader::Stage::Geometry) { 92 ctx.stage == Shader::Stage::Geometry) {
89 return OutAttr{ctx.viewport_index, ctx.U32[1]}; 93 return OutAttr{ctx.viewport_index, ctx.U32[1]};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 8decdf399..22ceca19c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -67,22 +67,22 @@ public:
67 } 67 }
68 } 68 }
69 69
70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates, 70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
71 Id offset, Id lod_clamp) { 71 u32 num_derivatives, Id offset, Id lod_clamp) {
72 if (!Sirit::ValidId(derivates)) { 72 if (!Sirit::ValidId(derivatives)) {
73 throw LogicError("Derivates must be present"); 73 throw LogicError("Derivatives must be present");
74 } 74 }
75 boost::container::static_vector<Id, 3> deriv_x_accum; 75 boost::container::static_vector<Id, 3> deriv_x_accum;
76 boost::container::static_vector<Id, 3> deriv_y_accum; 76 boost::container::static_vector<Id, 3> deriv_y_accum;
77 for (u32 i = 0; i < num_derivates; ++i) { 77 for (u32 i = 0; i < num_derivatives; ++i) {
78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2)); 78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2));
79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1)); 79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2 + 1));
80 } 80 }
81 const Id derivates_X{ctx.OpCompositeConstruct( 81 const Id derivatives_X{ctx.OpCompositeConstruct(
82 ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; 82 ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
83 const Id derivates_Y{ctx.OpCompositeConstruct( 83 const Id derivatives_Y{ctx.OpCompositeConstruct(
84 ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; 84 ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
85 Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y); 85 Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
86 if (Sirit::ValidId(offset)) { 86 if (Sirit::ValidId(offset)) {
87 Add(spv::ImageOperandsMask::Offset, offset); 87 Add(spv::ImageOperandsMask::Offset, offset);
88 } 88 }
@@ -91,26 +91,26 @@ public:
91 } 91 }
92 } 92 }
93 93
94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, 94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
95 Id offset, Id lod_clamp) { 95 Id offset, Id lod_clamp) {
96 if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { 96 if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
97 throw LogicError("Derivates must be present"); 97 throw LogicError("Derivatives must be present");
98 } 98 }
99 boost::container::static_vector<Id, 3> deriv_1_accum{ 99 boost::container::static_vector<Id, 3> deriv_1_accum{
100 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), 100 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 0),
101 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), 101 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 2),
102 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), 102 ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 0),
103 }; 103 };
104 boost::container::static_vector<Id, 3> deriv_2_accum{ 104 boost::container::static_vector<Id, 3> deriv_2_accum{
105 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), 105 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 1),
106 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), 106 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 3),
107 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), 107 ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 1),
108 }; 108 };
109 const Id derivates_id1{ctx.OpCompositeConstruct( 109 const Id derivatives_id1{ctx.OpCompositeConstruct(
110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; 110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})};
111 const Id derivates_id2{ctx.OpCompositeConstruct( 111 const Id derivatives_id2{ctx.OpCompositeConstruct(
112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; 112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
113 Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); 113 Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
114 if (Sirit::ValidId(offset)) { 114 if (Sirit::ValidId(offset)) {
115 Add(spv::ImageOperandsMask::Offset, offset); 115 Add(spv::ImageOperandsMask::Offset, offset);
116 } 116 }
@@ -548,12 +548,12 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
548} 548}
549 549
550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
551 Id derivates, Id offset, Id lod_clamp) { 551 Id derivatives, Id offset, Id lod_clamp) {
552 const auto info{inst->Flags<IR::TextureInstInfo>()}; 552 const auto info{inst->Flags<IR::TextureInstInfo>()};
553 const auto operands = 553 const auto operands =
554 info.num_derivates == 3 554 info.num_derivatives == 3
555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) 555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp)
556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, 556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset,
557 lod_clamp); 557 lod_clamp);
558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], 559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index a440b557d..7d34575c8 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
543 const IR::Value& skip_mips); 543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivates, Id offset, Id lod_clamp); 546 Id derivatives, Id offset, Id lod_clamp);
547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); 548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); 549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index b7caa4246..49171c470 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1864,11 +1864,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture
1864 return Inst(op, Flags{info}, handle, coords); 1864 return Inst(op, Flags{info}, handle, coords);
1865} 1865}
1866 1866
1867Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivates, 1867Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives,
1868 const Value& offset, const F32& lod_clamp, TextureInstInfo info) { 1868 const Value& offset, const F32& lod_clamp, TextureInstInfo info) {
1869 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient 1869 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient
1870 : Opcode::BindlessImageGradient}; 1870 : Opcode::BindlessImageGradient};
1871 return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); 1871 return Inst(op, Flags{info}, handle, coords, derivatives, offset, lod_clamp);
1872} 1872}
1873 1873
1874Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { 1874Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index f3c81dbe1..6c30897f4 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -335,7 +335,7 @@ public:
335 [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, 335 [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
336 const U32& lod, const U32& multisampling, TextureInstInfo info); 336 const U32& lod, const U32& multisampling, TextureInstInfo info);
337 [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, 337 [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
338 const Value& derivates, const Value& offset, 338 const Value& derivatives, const Value& offset,
339 const F32& lod_clamp, TextureInstInfo info); 339 const F32& lod_clamp, TextureInstInfo info);
340 [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); 340 [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
341 void ImageWrite(const Value& handle, const Value& coords, const Value& color, 341 void ImageWrite(const Value& handle, const Value& coords, const Value& color,
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h
index 1e9e8c8f5..c20c2401f 100644
--- a/src/shader_recompiler/frontend/ir/modifiers.h
+++ b/src/shader_recompiler/frontend/ir/modifiers.h
@@ -40,7 +40,7 @@ union TextureInstInfo {
40 BitField<21, 1, u32> has_lod_clamp; 40 BitField<21, 1, u32> has_lod_clamp;
41 BitField<22, 1, u32> relaxed_precision; 41 BitField<22, 1, u32> relaxed_precision;
42 BitField<23, 2, u32> gather_component; 42 BitField<23, 2, u32> gather_component;
43 BitField<25, 2, u32> num_derivates; 43 BitField<25, 2, u32> num_derivatives;
44 BitField<27, 3, ImageFormat> image_format; 44 BitField<27, 3, ImageFormat> image_format;
45 BitField<30, 1, u32> ndv_is_active; 45 BitField<30, 1, u32> ndv_is_active;
46}; 46};
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
index dd34507bc..4ce3dd0cd 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
@@ -59,7 +59,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
59 BitField<51, 3, IR::Pred> sparse_pred; 59 BitField<51, 3, IR::Pred> sparse_pred;
60 BitField<0, 8, IR::Reg> dest_reg; 60 BitField<0, 8, IR::Reg> dest_reg;
61 BitField<8, 8, IR::Reg> coord_reg; 61 BitField<8, 8, IR::Reg> coord_reg;
62 BitField<20, 8, IR::Reg> derivate_reg; 62 BitField<20, 8, IR::Reg> derivative_reg;
63 BitField<28, 3, TextureType> type; 63 BitField<28, 3, TextureType> type;
64 BitField<31, 4, u64> mask; 64 BitField<31, 4, u64> mask;
65 BitField<36, 13, u64> cbuf_offset; 65 BitField<36, 13, u64> cbuf_offset;
@@ -71,7 +71,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
71 } 71 }
72 72
73 IR::Value coords; 73 IR::Value coords;
74 u32 num_derivates{}; 74 u32 num_derivatives{};
75 IR::Reg base_reg{txd.coord_reg}; 75 IR::Reg base_reg{txd.coord_reg};
76 IR::Reg last_reg; 76 IR::Reg last_reg;
77 IR::Value handle; 77 IR::Value handle;
@@ -90,42 +90,42 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
90 switch (txd.type) { 90 switch (txd.type) {
91 case TextureType::_1D: { 91 case TextureType::_1D: {
92 coords = v.F(base_reg); 92 coords = v.F(base_reg);
93 num_derivates = 1; 93 num_derivatives = 1;
94 last_reg = base_reg + 1; 94 last_reg = base_reg + 1;
95 break; 95 break;
96 } 96 }
97 case TextureType::ARRAY_1D: { 97 case TextureType::ARRAY_1D: {
98 last_reg = base_reg + 1; 98 last_reg = base_reg + 1;
99 coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); 99 coords = v.ir.CompositeConstruct(v.F(base_reg), read_array());
100 num_derivates = 1; 100 num_derivatives = 1;
101 break; 101 break;
102 } 102 }
103 case TextureType::_2D: { 103 case TextureType::_2D: {
104 last_reg = base_reg + 2; 104 last_reg = base_reg + 2;
105 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); 105 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1));
106 num_derivates = 2; 106 num_derivatives = 2;
107 break; 107 break;
108 } 108 }
109 case TextureType::ARRAY_2D: { 109 case TextureType::ARRAY_2D: {
110 last_reg = base_reg + 2; 110 last_reg = base_reg + 2;
111 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); 111 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array());
112 num_derivates = 2; 112 num_derivatives = 2;
113 break; 113 break;
114 } 114 }
115 default: 115 default:
116 throw NotImplementedException("Invalid texture type"); 116 throw NotImplementedException("Invalid texture type");
117 } 117 }
118 118
119 const IR::Reg derivate_reg{txd.derivate_reg}; 119 const IR::Reg derivative_reg{txd.derivative_reg};
120 IR::Value derivates; 120 IR::Value derivatives;
121 switch (num_derivates) { 121 switch (num_derivatives) {
122 case 1: { 122 case 1: {
123 derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1)); 123 derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1));
124 break; 124 break;
125 } 125 }
126 case 2: { 126 case 2: {
127 derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1), 127 derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1),
128 v.F(derivate_reg + 2), v.F(derivate_reg + 3)); 128 v.F(derivative_reg + 2), v.F(derivative_reg + 3));
129 break; 129 break;
130 } 130 }
131 default: 131 default:
@@ -150,9 +150,10 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
150 150
151 IR::TextureInstInfo info{}; 151 IR::TextureInstInfo info{};
152 info.type.Assign(GetType(txd.type)); 152 info.type.Assign(GetType(txd.type));
153 info.num_derivates.Assign(num_derivates); 153 info.num_derivatives.Assign(num_derivatives);
154 info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); 154 info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0);
155 const IR::Value sample{v.ir.ImageGradient(handle, coords, derivates, offset, lod_clamp, info)}; 155 const IR::Value sample{
156 v.ir.ImageGradient(handle, coords, derivatives, offset, lod_clamp, info)};
156 157
157 IR::Reg dest_reg{txd.dest_reg}; 158 IR::Reg dest_reg{txd.dest_reg};
158 for (size_t element = 0; element < 4; ++element) { 159 for (size_t element = 0; element < 4; ++element) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 47183cae1..321ea625b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -310,6 +310,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
310 } 310 }
311 Optimization::CollectShaderInfoPass(env, program); 311 Optimization::CollectShaderInfoPass(env, program);
312 Optimization::LayerPass(program, host_info); 312 Optimization::LayerPass(program, host_info);
313 Optimization::VendorWorkaroundPass(program);
313 314
314 CollectInterpolationInfo(env, program); 315 CollectInterpolationInfo(env, program);
315 AddNVNStorageBuffers(program); 316 AddNVNStorageBuffers(program);
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index f46e55122..ec12c843a 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -428,7 +428,7 @@ void FoldFPAdd32(IR::Inst& inst) {
428 } 428 }
429} 429}
430 430
431bool FoldDerivateYFromCorrection(IR::Inst& inst) { 431bool FoldDerivativeYFromCorrection(IR::Inst& inst) {
432 const IR::Value lhs_value{inst.Arg(0)}; 432 const IR::Value lhs_value{inst.Arg(0)};
433 const IR::Value rhs_value{inst.Arg(1)}; 433 const IR::Value rhs_value{inst.Arg(1)};
434 IR::Inst* const lhs_op{lhs_value.InstRecursive()}; 434 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
@@ -464,7 +464,7 @@ void FoldFPMul32(IR::Inst& inst) {
464 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) { 464 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) {
465 return; 465 return;
466 } 466 }
467 if (FoldDerivateYFromCorrection(inst)) { 467 if (FoldDerivativeYFromCorrection(inst)) {
468 return; 468 return;
469 } 469 }
470 IR::Inst* const lhs_op{lhs_value.InstRecursive()}; 470 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
@@ -699,7 +699,7 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
699 } 699 }
700} 700}
701 701
702bool FindGradient3DDerivates(std::array<IR::Value, 3>& results, IR::Value coord) { 702bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coord) {
703 if (coord.IsImmediate()) { 703 if (coord.IsImmediate()) {
704 return false; 704 return false;
705 } 705 }
@@ -834,7 +834,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
834 IR::Inst* const inst2 = coords.InstRecursive(); 834 IR::Inst* const inst2 = coords.InstRecursive();
835 std::array<std::array<IR::Value, 3>, 3> results_matrix; 835 std::array<std::array<IR::Value, 3>, 3> results_matrix;
836 for (size_t i = 0; i < 3; i++) { 836 for (size_t i = 0; i < 3; i++) {
837 if (!FindGradient3DDerivates(results_matrix[i], inst2->Arg(i).Resolve())) { 837 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
838 return; 838 return;
839 } 839 }
840 } 840 }
@@ -852,7 +852,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], 852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
853 results_matrix[1][1], results_matrix[1][2]); 853 results_matrix[1][1], results_matrix[1][2]);
854 IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]); 854 IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]);
855 info.num_derivates.Assign(3); 855 info.num_derivatives.Assign(3);
856 IR::Value new_gradient_instruction = 856 IR::Value new_gradient_instruction =
857 ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info); 857 ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info);
858 IR::Inst* const new_inst = new_gradient_instruction.InstRecursive(); 858 IR::Inst* const new_inst = new_gradient_instruction.InstRecursive();
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 7082fc5f2..1e637cb23 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -26,6 +26,7 @@ void SsaRewritePass(IR::Program& program);
26void PositionPass(Environment& env, IR::Program& program); 26void PositionPass(Environment& env, IR::Program& program);
27void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); 27void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
28void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); 28void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
29void VendorWorkaroundPass(IR::Program& program);
29void VerificationPass(const IR::Program& program); 30void VerificationPass(const IR::Program& program);
30 31
31// Dual Vertex 32// Dual Vertex
diff --git a/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp
new file mode 100644
index 000000000..08c658cb8
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "shader_recompiler/frontend/ir/basic_block.h"
5#include "shader_recompiler/frontend/ir/ir_emitter.h"
6#include "shader_recompiler/frontend/ir/value.h"
7#include "shader_recompiler/ir_opt/passes.h"
8
9namespace Shader::Optimization {
10
11namespace {
12void AddingByteSwapsWorkaround(IR::Block& block, IR::Inst& inst) {
13 /*
14 * Workaround for an NVIDIA bug seen in Super Mario RPG
15 *
16 * We are looking for this pattern:
17 * %lhs_bfe = BitFieldUExtract %factor_a, #0, #16
18 * %lhs_mul = IMul32 %lhs_bfe, %factor_b // potentially optional?
19 * %lhs_shl = ShiftLeftLogical32 %lhs_mul, #16
20 * %rhs_bfe = BitFieldUExtract %factor_a, #16, #16
21 * %result = IAdd32 %lhs_shl, %rhs_bfe
22 *
23 * And replacing the IAdd32 with a BitwiseOr32
24 * %result = BitwiseOr32 %lhs_shl, %rhs_bfe
25 *
26 */
27 IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
28 IR::Inst* const rhs_bfe{inst.Arg(1).TryInstRecursive()};
29 if (!lhs_shl || !rhs_bfe) {
30 return;
31 }
32 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
33 lhs_shl->Arg(1) != IR::Value{16U}) {
34 return;
35 }
36 if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract || rhs_bfe->Arg(1) != IR::Value{16U} ||
37 rhs_bfe->Arg(2) != IR::Value{16U}) {
38 return;
39 }
40 IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
41 if (!lhs_mul) {
42 return;
43 }
44 const bool lhs_mul_optional{lhs_mul->GetOpcode() == IR::Opcode::BitFieldUExtract};
45 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 &&
46 lhs_mul->GetOpcode() != IR::Opcode::BitFieldUExtract) {
47 return;
48 }
49 IR::Inst* const lhs_bfe{lhs_mul_optional ? lhs_mul : lhs_mul->Arg(0).TryInstRecursive()};
50 if (!lhs_bfe) {
51 return;
52 }
53 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
54 return;
55 }
56 if (lhs_bfe->Arg(1) != IR::Value{0U} || lhs_bfe->Arg(2) != IR::Value{16U}) {
57 return;
58 }
59 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
60 inst.ReplaceUsesWith(ir.BitwiseOr(IR::U32{inst.Arg(0)}, IR::U32{inst.Arg(1)}));
61}
62
63} // Anonymous namespace
64
65void VendorWorkaroundPass(IR::Program& program) {
66 for (IR::Block* const block : program.post_order_blocks) {
67 for (IR::Inst& inst : block->Instructions()) {
68 switch (inst.GetOpcode()) {
69 case IR::Opcode::IAdd32:
70 AddingByteSwapsWorkaround(*block, inst);
71 break;
72 default:
73 break;
74 }
75 }
76 }
77}
78
79} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index e62ba8a20..66901a965 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -43,6 +43,7 @@ struct Profile {
43 bool support_gl_sparse_textures{}; 43 bool support_gl_sparse_textures{};
44 bool support_gl_derivative_control{}; 44 bool support_gl_derivative_control{};
45 bool support_scaled_attributes{}; 45 bool support_scaled_attributes{};
46 bool support_multi_viewport{};
46 47
47 bool warp_size_potentially_larger_than_guest{}; 48 bool warp_size_potentially_larger_than_guest{};
48 49
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index cf9266d54..c22c7631c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -4,7 +4,7 @@
4add_subdirectory(host_shaders) 4add_subdirectory(host_shaders)
5 5
6if(LIBVA_FOUND) 6if(LIBVA_FOUND)
7 set_source_files_properties(host1x/codecs/codec.cpp 7 set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp
8 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) 8 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
9 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) 9 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
10endif() 10endif()
@@ -15,6 +15,7 @@ add_library(video_core STATIC
15 buffer_cache/buffer_cache.cpp 15 buffer_cache/buffer_cache.cpp
16 buffer_cache/buffer_cache.h 16 buffer_cache/buffer_cache.h
17 buffer_cache/memory_tracker_base.h 17 buffer_cache/memory_tracker_base.h
18 buffer_cache/usage_tracker.h
18 buffer_cache/word_manager.h 19 buffer_cache/word_manager.h
19 cache_types.h 20 cache_types.h
20 cdma_pusher.cpp 21 cdma_pusher.cpp
@@ -66,6 +67,8 @@ add_library(video_core STATIC
66 host1x/codecs/vp9.cpp 67 host1x/codecs/vp9.cpp
67 host1x/codecs/vp9.h 68 host1x/codecs/vp9.h
68 host1x/codecs/vp9_types.h 69 host1x/codecs/vp9_types.h
70 host1x/ffmpeg/ffmpeg.cpp
71 host1x/ffmpeg/ffmpeg.h
69 host1x/control.cpp 72 host1x/control.cpp
70 host1x/control.h 73 host1x/control.h
71 host1x/host1x.cpp 74 host1x/host1x.cpp
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 2648970b6..6d1fc3887 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -67,6 +67,7 @@ void BufferCache<P>::TickFrame() {
67 if (!channel_state) { 67 if (!channel_state) {
68 return; 68 return;
69 } 69 }
70 runtime.TickFrame(slot_buffers);
70 71
71 // Calculate hits and shots and move hit bits to the right 72 // Calculate hits and shots and move hit bits to the right
72 const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), 73 const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(),
@@ -230,7 +231,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
230 for (const IntervalType& add_interval : tmp_intervals) { 231 for (const IntervalType& add_interval : tmp_intervals) {
231 common_ranges.add(add_interval); 232 common_ranges.add(add_interval);
232 } 233 }
233 runtime.CopyBuffer(dest_buffer, src_buffer, copies); 234 const auto& copy = copies[0];
235 src_buffer.MarkUsage(copy.src_offset, copy.size);
236 dest_buffer.MarkUsage(copy.dst_offset, copy.size);
237 runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
234 if (has_new_downloads) { 238 if (has_new_downloads) {
235 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); 239 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
236 } 240 }
@@ -258,9 +262,10 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
258 common_ranges.subtract(subtract_interval); 262 common_ranges.subtract(subtract_interval);
259 263
260 const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); 264 const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
261 auto& dest_buffer = slot_buffers[buffer]; 265 Buffer& dest_buffer = slot_buffers[buffer];
262 const u32 offset = dest_buffer.Offset(*cpu_dst_address); 266 const u32 offset = dest_buffer.Offset(*cpu_dst_address);
263 runtime.ClearBuffer(dest_buffer, offset, size, value); 267 runtime.ClearBuffer(dest_buffer, offset, size, value);
268 dest_buffer.MarkUsage(offset, size);
264 return true; 269 return true;
265} 270}
266 271
@@ -603,6 +608,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
603 VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset); 608 VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
604 const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; 609 const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
605 async_downloads += std::make_pair(base_interval, 1); 610 async_downloads += std::make_pair(base_interval, 1);
611 buffer.MarkUsage(copy.src_offset, copy.size);
606 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); 612 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
607 normalized_copies.push_back(second_copy); 613 normalized_copies.push_back(second_copy);
608 } 614 }
@@ -621,8 +627,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
621 // Have in mind the staging buffer offset for the copy 627 // Have in mind the staging buffer offset for the copy
622 copy.dst_offset += download_staging.offset; 628 copy.dst_offset += download_staging.offset;
623 const std::array copies{copy}; 629 const std::array copies{copy};
624 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, 630 Buffer& buffer = slot_buffers[buffer_id];
625 false); 631 buffer.MarkUsage(copy.src_offset, copy.size);
632 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
626 } 633 }
627 runtime.PostCopyBarrier(); 634 runtime.PostCopyBarrier();
628 runtime.Finish(); 635 runtime.Finish();
@@ -742,7 +749,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
742 {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; 749 {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
743 std::memcpy(upload_staging.mapped_span.data(), 750 std::memcpy(upload_staging.mapped_span.data(),
744 draw_state.inline_index_draw_indexes.data(), size); 751 draw_state.inline_index_draw_indexes.data(), size);
745 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 752 runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
746 } else { 753 } else {
747 buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); 754 buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
748 } 755 }
@@ -754,6 +761,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
754 offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); 761 offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
755 runtime.BindIndexBuffer(buffer, new_offset, size); 762 runtime.BindIndexBuffer(buffer, new_offset, size);
756 } else { 763 } else {
764 buffer.MarkUsage(offset, size);
757 runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, 765 runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
758 draw_state.index_buffer.first, draw_state.index_buffer.count, 766 draw_state.index_buffer.first, draw_state.index_buffer.count,
759 buffer, offset, size); 767 buffer, offset, size);
@@ -790,6 +798,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
790 798
791 const u32 stride = maxwell3d->regs.vertex_streams[index].stride; 799 const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
792 const u32 offset = buffer.Offset(binding.cpu_addr); 800 const u32 offset = buffer.Offset(binding.cpu_addr);
801 buffer.MarkUsage(offset, binding.size);
793 802
794 host_bindings.buffers.push_back(&buffer); 803 host_bindings.buffers.push_back(&buffer);
795 host_bindings.offsets.push_back(offset); 804 host_bindings.offsets.push_back(offset);
@@ -895,6 +904,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
895 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 904 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
896 channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; 905 channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
897 } 906 }
907 buffer.MarkUsage(offset, size);
898 if constexpr (NEEDS_BIND_UNIFORM_INDEX) { 908 if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
899 runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); 909 runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size);
900 } else { 910 } else {
@@ -913,6 +923,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
913 SynchronizeBuffer(buffer, binding.cpu_addr, size); 923 SynchronizeBuffer(buffer, binding.cpu_addr, size);
914 924
915 const u32 offset = buffer.Offset(binding.cpu_addr); 925 const u32 offset = buffer.Offset(binding.cpu_addr);
926 buffer.MarkUsage(offset, size);
916 const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; 927 const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
917 928
918 if (is_written) { 929 if (is_written) {
@@ -943,6 +954,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
943 954
944 const u32 offset = buffer.Offset(binding.cpu_addr); 955 const u32 offset = buffer.Offset(binding.cpu_addr);
945 const PixelFormat format = binding.format; 956 const PixelFormat format = binding.format;
957 buffer.MarkUsage(offset, size);
946 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { 958 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
947 if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { 959 if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) {
948 runtime.BindImageBuffer(buffer, offset, size, format); 960 runtime.BindImageBuffer(buffer, offset, size, format);
@@ -975,9 +987,10 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
975 MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); 987 MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
976 988
977 const u32 offset = buffer.Offset(binding.cpu_addr); 989 const u32 offset = buffer.Offset(binding.cpu_addr);
990 buffer.MarkUsage(offset, size);
978 host_bindings.buffers.push_back(&buffer); 991 host_bindings.buffers.push_back(&buffer);
979 host_bindings.offsets.push_back(offset); 992 host_bindings.offsets.push_back(offset);
980 host_bindings.sizes.push_back(binding.size); 993 host_bindings.sizes.push_back(size);
981 } 994 }
982 if (host_bindings.buffers.size() > 0) { 995 if (host_bindings.buffers.size() > 0) {
983 runtime.BindTransformFeedbackBuffers(host_bindings); 996 runtime.BindTransformFeedbackBuffers(host_bindings);
@@ -1001,6 +1014,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
1001 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1014 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1002 1015
1003 const u32 offset = buffer.Offset(binding.cpu_addr); 1016 const u32 offset = buffer.Offset(binding.cpu_addr);
1017 buffer.MarkUsage(offset, size);
1004 if constexpr (NEEDS_BIND_UNIFORM_INDEX) { 1018 if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
1005 runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); 1019 runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size);
1006 ++binding_index; 1020 ++binding_index;
@@ -1021,6 +1035,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
1021 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1035 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1022 1036
1023 const u32 offset = buffer.Offset(binding.cpu_addr); 1037 const u32 offset = buffer.Offset(binding.cpu_addr);
1038 buffer.MarkUsage(offset, size);
1024 const bool is_written = 1039 const bool is_written =
1025 ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; 1040 ((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
1026 1041
@@ -1053,6 +1068,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
1053 1068
1054 const u32 offset = buffer.Offset(binding.cpu_addr); 1069 const u32 offset = buffer.Offset(binding.cpu_addr);
1055 const PixelFormat format = binding.format; 1070 const PixelFormat format = binding.format;
1071 buffer.MarkUsage(offset, size);
1056 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { 1072 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
1057 if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { 1073 if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) {
1058 runtime.BindImageBuffer(buffer, offset, size, format); 1074 runtime.BindImageBuffer(buffer, offset, size, format);
@@ -1172,10 +1188,11 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1172 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { 1188 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
1173 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); 1189 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
1174 } 1190 }
1191 const BufferId buffer_id = FindBuffer(*cpu_addr, size);
1175 channel_state->vertex_buffers[index] = Binding{ 1192 channel_state->vertex_buffers[index] = Binding{
1176 .cpu_addr = *cpu_addr, 1193 .cpu_addr = *cpu_addr,
1177 .size = size, 1194 .size = size,
1178 .buffer_id = FindBuffer(*cpu_addr, size), 1195 .buffer_id = buffer_id,
1179 }; 1196 };
1180} 1197}
1181 1198
@@ -1192,11 +1209,6 @@ void BufferCache<P>::UpdateDrawIndirect() {
1192 .size = static_cast<u32>(size), 1209 .size = static_cast<u32>(size),
1193 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), 1210 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1194 }; 1211 };
1195 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
1196 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
1197 IntervalType interval{cpu_addr_start, cpu_addr_end};
1198 ClearDownload(interval);
1199 common_ranges.subtract(interval);
1200 }; 1212 };
1201 if (current_draw_indirect->include_count) { 1213 if (current_draw_indirect->include_count) {
1202 update(current_draw_indirect->count_start_address, sizeof(u32), 1214 update(current_draw_indirect->count_start_address, sizeof(u32),
@@ -1406,7 +1418,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
1406 .dst_offset = dst_base_offset, 1418 .dst_offset = dst_base_offset,
1407 .size = overlap.SizeBytes(), 1419 .size = overlap.SizeBytes(),
1408 }); 1420 });
1409 runtime.CopyBuffer(new_buffer, overlap, copies); 1421 new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size);
1422 runtime.CopyBuffer(new_buffer, overlap, copies, true);
1410 DeleteBuffer(overlap_id, true); 1423 DeleteBuffer(overlap_id, true);
1411} 1424}
1412 1425
@@ -1419,7 +1432,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1419 const u32 size = static_cast<u32>(overlap.end - overlap.begin); 1432 const u32 size = static_cast<u32>(overlap.end - overlap.begin);
1420 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); 1433 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
1421 auto& new_buffer = slot_buffers[new_buffer_id]; 1434 auto& new_buffer = slot_buffers[new_buffer_id];
1422 runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); 1435 const size_t size_bytes = new_buffer.SizeBytes();
1436 runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
1437 new_buffer.MarkUsage(0, size_bytes);
1423 for (const BufferId overlap_id : overlap.ids) { 1438 for (const BufferId overlap_id : overlap.ids) {
1424 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); 1439 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
1425 } 1440 }
@@ -1472,11 +1487,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
1472 1487
1473template <class P> 1488template <class P>
1474bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { 1489bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
1475 return SynchronizeBufferImpl(buffer, cpu_addr, size);
1476}
1477
1478template <class P>
1479bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
1480 boost::container::small_vector<BufferCopy, 4> copies; 1490 boost::container::small_vector<BufferCopy, 4> copies;
1481 u64 total_size_bytes = 0; 1491 u64 total_size_bytes = 0;
1482 u64 largest_copy = 0; 1492 u64 largest_copy = 0;
@@ -1499,51 +1509,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
1499} 1509}
1500 1510
1501template <class P> 1511template <class P>
1502bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) {
1503 boost::container::small_vector<BufferCopy, 4> copies;
1504 u64 total_size_bytes = 0;
1505 u64 largest_copy = 0;
1506 IntervalSet found_sets{};
1507 auto make_copies = [&] {
1508 for (auto& interval : found_sets) {
1509 const std::size_t sub_size = interval.upper() - interval.lower();
1510 const VAddr cpu_addr_ = interval.lower();
1511 copies.push_back(BufferCopy{
1512 .src_offset = total_size_bytes,
1513 .dst_offset = cpu_addr_ - buffer.CpuAddr(),
1514 .size = sub_size,
1515 });
1516 total_size_bytes += sub_size;
1517 largest_copy = std::max<u64>(largest_copy, sub_size);
1518 }
1519 const std::span<BufferCopy> copies_span(copies.data(), copies.size());
1520 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
1521 };
1522 memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
1523 const VAddr base_adr = cpu_addr_out;
1524 const VAddr end_adr = base_adr + range_size;
1525 const IntervalType add_interval{base_adr, end_adr};
1526 found_sets.add(add_interval);
1527 });
1528 if (found_sets.empty()) {
1529 return true;
1530 }
1531 const IntervalType search_interval{cpu_addr, cpu_addr + size};
1532 auto it = common_ranges.lower_bound(search_interval);
1533 auto it_end = common_ranges.upper_bound(search_interval);
1534 if (it == common_ranges.end()) {
1535 make_copies();
1536 return false;
1537 }
1538 while (it != it_end) {
1539 found_sets.subtract(*it);
1540 it++;
1541 }
1542 make_copies();
1543 return false;
1544}
1545
1546template <class P>
1547void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 1512void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
1548 std::span<BufferCopy> copies) { 1513 std::span<BufferCopy> copies) {
1549 if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { 1514 if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
@@ -1591,7 +1556,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
1591 // Apply the staging offset 1556 // Apply the staging offset
1592 copy.src_offset += upload_staging.offset; 1557 copy.src_offset += upload_staging.offset;
1593 } 1558 }
1594 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 1559 const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
1560 runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
1595 } 1561 }
1596} 1562}
1597 1563
@@ -1633,7 +1599,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_
1633 }}; 1599 }};
1634 u8* const src_pointer = upload_staging.mapped_span.data(); 1600 u8* const src_pointer = upload_staging.mapped_span.data();
1635 std::memcpy(src_pointer, inlined_buffer.data(), copy_size); 1601 std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
1636 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 1602 const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
1603 runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
1637 } else { 1604 } else {
1638 buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); 1605 buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
1639 } 1606 }
@@ -1686,8 +1653,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1686 for (BufferCopy& copy : copies) { 1653 for (BufferCopy& copy : copies) {
1687 // Modify copies to have the staging offset in mind 1654 // Modify copies to have the staging offset in mind
1688 copy.dst_offset += download_staging.offset; 1655 copy.dst_offset += download_staging.offset;
1656 buffer.MarkUsage(copy.src_offset, copy.size);
1689 } 1657 }
1690 runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); 1658 runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true);
1691 runtime.Finish(); 1659 runtime.Finish();
1692 for (const BufferCopy& copy : copies) { 1660 for (const BufferCopy& copy : copies) {
1693 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; 1661 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index eed267361..d6d696d8c 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -529,10 +529,6 @@ private:
529 529
530 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); 530 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
531 531
532 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
533
534 bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size);
535
536 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 532 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
537 std::span<BufferCopy> copies); 533 std::span<BufferCopy> copies);
538 534
diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h
new file mode 100644
index 000000000..ab05fe415
--- /dev/null
+++ b/src/video_core/buffer_cache/usage_tracker.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/alignment.h"
7#include "common/common_types.h"
8
9namespace VideoCommon {
10
11class UsageTracker {
12 static constexpr size_t BYTES_PER_BIT_SHIFT = 6;
13 static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT;
14 static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT;
15
16public:
17 explicit UsageTracker(size_t size) {
18 const size_t num_pages = (size >> PAGE_SHIFT) + 1;
19 pages.resize(num_pages, 0ULL);
20 }
21
22 void Reset() noexcept {
23 std::ranges::fill(pages, 0ULL);
24 }
25
26 void Track(u64 offset, u64 size) noexcept {
27 const size_t page = offset >> PAGE_SHIFT;
28 const size_t page_end = (offset + size) >> PAGE_SHIFT;
29 TrackPage(page, offset, size);
30 if (page == page_end) {
31 return;
32 }
33 for (size_t i = page + 1; i < page_end; i++) {
34 pages[i] = ~u64{0};
35 }
36 const size_t offset_end = offset + size;
37 const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
38 TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
39 }
40
41 [[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept {
42 const size_t page = offset >> PAGE_SHIFT;
43 const size_t page_end = (offset + size) >> PAGE_SHIFT;
44 if (IsPageUsed(page, offset, size)) {
45 return true;
46 }
47 for (size_t i = page + 1; i < page_end; i++) {
48 if (pages[i] != 0) {
49 return true;
50 }
51 }
52 const size_t offset_end = offset + size;
53 const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
54 return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
55 }
56
57private:
58 void TrackPage(u64 page, u64 offset, u64 size) noexcept {
59 const size_t offset_in_page = offset % PAGE_BYTES;
60 const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
61 const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
62 const size_t mask = ~u64{0} >> (64 - num_bits);
63 pages[page] |= (~u64{0} & mask) << first_bit;
64 }
65
66 bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept {
67 const size_t offset_in_page = offset % PAGE_BYTES;
68 const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
69 const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
70 const size_t mask = ~u64{0} >> (64 - num_bits);
71 const size_t mask2 = (~u64{0} & mask) << first_bit;
72 return (pages[page] & mask2) != 0;
73 }
74
75private:
76 std::vector<u64> pages;
77};
78
79} // namespace VideoCommon
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 02e161270..91f10aec2 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -72,7 +72,7 @@ void Fermi2D::Blit() {
72 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); 72 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
73 73
74 const auto& args = regs.pixels_from_memory; 74 const auto& args = regs.pixels_from_memory;
75 constexpr s64 null_derivate = 1ULL << 32; 75 constexpr s64 null_derivative = 1ULL << 32;
76 Surface src = regs.src; 76 Surface src = regs.src;
77 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); 77 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
78 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && 78 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
@@ -89,7 +89,7 @@ void Fermi2D::Blit() {
89 .operation = regs.operation, 89 .operation = regs.operation,
90 .filter = args.sample_mode.filter, 90 .filter = args.sample_mode.filter,
91 .must_accelerate = 91 .must_accelerate =
92 args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu, 92 args.du_dx != null_derivative || args.dv_dy != null_derivative || delegate_to_gpu,
93 .dst_x0 = args.dst_x0, 93 .dst_x0 = args.dst_x0,
94 .dst_y0 = args.dst_y0, 94 .dst_y0 = args.dst_y0,
95 .dst_x1 = args.dst_x0 + args.dst_width, 95 .dst_x1 = args.dst_x0 + args.dst_width,
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 32d767d85..592c28ba3 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -268,7 +268,7 @@ size_t Maxwell3D::EstimateIndexBufferSize() {
268 std::numeric_limits<u32>::max()}; 268 std::numeric_limits<u32>::max()};
269 const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); 269 const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
270 const size_t log2_byte_size = Common::Log2Ceil64(byte_size); 270 const size_t log2_byte_size = Common::Log2Ceil64(byte_size);
271 const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; 271 const size_t cap{GetMaxCurrentVertices() * 4 * byte_size};
272 const size_t lower_cap = 272 const size_t lower_cap =
273 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); 273 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap);
274 return std::min<size_t>( 274 return std::min<size_t>(
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index c0e6471fe..805a89900 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -86,10 +86,7 @@ public:
86 uncommitted_operations.emplace_back(std::move(func)); 86 uncommitted_operations.emplace_back(std::move(func));
87 } 87 }
88 pending_operations.emplace_back(std::move(uncommitted_operations)); 88 pending_operations.emplace_back(std::move(uncommitted_operations));
89 { 89 QueueFence(new_fence);
90 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
91 QueueFence(new_fence);
92 }
93 if (!delay_fence) { 90 if (!delay_fence) {
94 func(); 91 func();
95 } 92 }
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index dbcf508e5..1030db681 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -1,11 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5#include <fstream>
6#include <vector>
7#include "common/assert.h" 4#include "common/assert.h"
8#include "common/scope_exit.h"
9#include "common/settings.h" 5#include "common/settings.h"
10#include "video_core/host1x/codecs/codec.h" 6#include "video_core/host1x/codecs/codec.h"
11#include "video_core/host1x/codecs/h264.h" 7#include "video_core/host1x/codecs/h264.h"
@@ -14,242 +10,17 @@
14#include "video_core/host1x/host1x.h" 10#include "video_core/host1x/host1x.h"
15#include "video_core/memory_manager.h" 11#include "video_core/memory_manager.h"
16 12
17extern "C" {
18#include <libavfilter/buffersink.h>
19#include <libavfilter/buffersrc.h>
20#include <libavutil/opt.h>
21#ifdef LIBVA_FOUND
22// for querying VAAPI driver information
23#include <libavutil/hwcontext_vaapi.h>
24#endif
25}
26
27namespace Tegra { 13namespace Tegra {
28namespace {
29constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
30constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
31constexpr std::array PREFERRED_GPU_DECODERS = {
32 AV_HWDEVICE_TYPE_CUDA,
33#ifdef _WIN32
34 AV_HWDEVICE_TYPE_D3D11VA,
35 AV_HWDEVICE_TYPE_DXVA2,
36#elif defined(__unix__)
37 AV_HWDEVICE_TYPE_VAAPI,
38 AV_HWDEVICE_TYPE_VDPAU,
39#endif
40 // last resort for Linux Flatpak (w/ NVIDIA)
41 AV_HWDEVICE_TYPE_VULKAN,
42};
43
44void AVPacketDeleter(AVPacket* ptr) {
45 av_packet_free(&ptr);
46}
47
48using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
49
50AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
51 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
52 if (*p == av_codec_ctx->pix_fmt) {
53 return av_codec_ctx->pix_fmt;
54 }
55 }
56 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
57 av_buffer_unref(&av_codec_ctx->hw_device_ctx);
58 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
59 return PREFERRED_CPU_FMT;
60}
61
62// List all the currently available hwcontext in ffmpeg
63std::vector<AVHWDeviceType> ListSupportedContexts() {
64 std::vector<AVHWDeviceType> contexts{};
65 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
66 do {
67 current_device_type = av_hwdevice_iterate_types(current_device_type);
68 contexts.push_back(current_device_type);
69 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
70 return contexts;
71}
72
73} // namespace
74
75void AVFrameDeleter(AVFrame* ptr) {
76 av_frame_free(&ptr);
77}
78 14
79Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) 15Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
80 : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), 16 : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
81 vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), 17 vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
82 vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} 18 vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
83 19
84Codec::~Codec() { 20Codec::~Codec() = default;
85 if (!initialized) {
86 return;
87 }
88 // Free libav memory
89 avcodec_free_context(&av_codec_ctx);
90 av_buffer_unref(&av_gpu_decoder);
91
92 if (filters_initialized) {
93 avfilter_graph_free(&av_filter_graph);
94 }
95}
96
97bool Codec::CreateGpuAvDevice() {
98 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
99 static const auto supported_contexts = ListSupportedContexts();
100 for (const auto& type : PREFERRED_GPU_DECODERS) {
101 if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
102 [&type](const auto& context) { return context == type; })) {
103 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
104 continue;
105 }
106 // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
107 av_buffer_unref(&av_gpu_decoder);
108 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
109 if (hwdevice_res < 0) {
110 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
111 av_hwdevice_get_type_name(type), hwdevice_res);
112 continue;
113 }
114#ifdef LIBVA_FOUND
115 if (type == AV_HWDEVICE_TYPE_VAAPI) {
116 // we need to determine if this is an impersonated VAAPI driver
117 AVHWDeviceContext* hwctx =
118 static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
119 AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
120 const char* vendor_name = vaQueryVendorString(vactx->display);
121 if (strstr(vendor_name, "VDPAU backend")) {
122 // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
123 LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
124 continue;
125 } else {
126 // according to some user testing, certain vaapi driver (Intel?) could be buggy
127 // so let's log the driver name which may help the developers/supporters
128 LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
129 }
130 }
131#endif
132 for (int i = 0;; i++) {
133 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
134 if (!config) {
135 LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
136 av_codec->name, av_hwdevice_get_type_name(type));
137 break;
138 }
139 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
140 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
141 av_codec_ctx->pix_fmt = config->pix_fmt;
142 return true;
143 }
144 }
145 }
146 return false;
147}
148
149void Codec::InitializeAvCodecContext() {
150 av_codec_ctx = avcodec_alloc_context3(av_codec);
151 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
152 av_codec_ctx->thread_count = 0;
153 av_codec_ctx->thread_type &= ~FF_THREAD_FRAME;
154}
155
156void Codec::InitializeGpuDecoder() {
157 if (!CreateGpuAvDevice()) {
158 av_buffer_unref(&av_gpu_decoder);
159 return;
160 }
161 auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
162 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
163 av_codec_ctx->hw_device_ctx = hw_device_ctx;
164 av_codec_ctx->get_format = GetGpuFormat;
165}
166
167void Codec::InitializeAvFilters(AVFrame* frame) {
168 const AVFilter* buffer_src = avfilter_get_by_name("buffer");
169 const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
170 AVFilterInOut* inputs = avfilter_inout_alloc();
171 AVFilterInOut* outputs = avfilter_inout_alloc();
172 SCOPE_EXIT({
173 avfilter_inout_free(&inputs);
174 avfilter_inout_free(&outputs);
175 });
176
177 // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
178 // so just use 1/1 to make buffer filter happy
179 std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width,
180 frame->height, frame->format);
181
182 av_filter_graph = avfilter_graph_alloc();
183 int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(),
184 nullptr, av_filter_graph);
185 if (ret < 0) {
186 LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret);
187 return;
188 }
189
190 ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr,
191 av_filter_graph);
192 if (ret < 0) {
193 LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret);
194 return;
195 }
196
197 inputs->name = av_strdup("out");
198 inputs->filter_ctx = av_filter_sink_ctx;
199 inputs->pad_idx = 0;
200 inputs->next = nullptr;
201
202 outputs->name = av_strdup("in");
203 outputs->filter_ctx = av_filter_src_ctx;
204 outputs->pad_idx = 0;
205 outputs->next = nullptr;
206
207 const char* description = "yadif=1:-1:0";
208 ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr);
209 if (ret < 0) {
210 LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret);
211 return;
212 }
213
214 ret = avfilter_graph_config(av_filter_graph, nullptr);
215 if (ret < 0) {
216 LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret);
217 return;
218 }
219
220 filters_initialized = true;
221}
222 21
223void Codec::Initialize() { 22void Codec::Initialize() {
224 const AVCodecID codec = [&] { 23 initialized = decode_api.Initialize(current_codec);
225 switch (current_codec) {
226 case Host1x::NvdecCommon::VideoCodec::H264:
227 return AV_CODEC_ID_H264;
228 case Host1x::NvdecCommon::VideoCodec::VP8:
229 return AV_CODEC_ID_VP8;
230 case Host1x::NvdecCommon::VideoCodec::VP9:
231 return AV_CODEC_ID_VP9;
232 default:
233 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
234 return AV_CODEC_ID_NONE;
235 }
236 }();
237 av_codec = avcodec_find_decoder(codec);
238
239 InitializeAvCodecContext();
240 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
241 InitializeGpuDecoder();
242 }
243 if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
244 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
245 avcodec_free_context(&av_codec_ctx);
246 av_buffer_unref(&av_gpu_decoder);
247 return;
248 }
249 if (!av_codec_ctx->hw_device_ctx) {
250 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
251 }
252 initialized = true;
253} 24}
254 25
255void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { 26void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
@@ -264,14 +35,18 @@ void Codec::Decode() {
264 if (is_first_frame) { 35 if (is_first_frame) {
265 Initialize(); 36 Initialize();
266 } 37 }
38
267 if (!initialized) { 39 if (!initialized) {
268 return; 40 return;
269 } 41 }
42
43 // Assemble bitstream.
270 bool vp9_hidden_frame = false; 44 bool vp9_hidden_frame = false;
271 const auto& frame_data = [&]() { 45 size_t configuration_size = 0;
46 const auto packet_data = [&]() {
272 switch (current_codec) { 47 switch (current_codec) {
273 case Tegra::Host1x::NvdecCommon::VideoCodec::H264: 48 case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
274 return h264_decoder->ComposeFrame(state, is_first_frame); 49 return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
275 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: 50 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
276 return vp8_decoder->ComposeFrame(state); 51 return vp8_decoder->ComposeFrame(state);
277 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: 52 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
@@ -283,89 +58,35 @@ void Codec::Decode() {
283 return std::span<const u8>{}; 58 return std::span<const u8>{};
284 } 59 }
285 }(); 60 }();
286 AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; 61
287 if (!packet) { 62 // Send assembled bitstream to decoder.
288 LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); 63 if (!decode_api.SendPacket(packet_data, configuration_size)) {
289 return;
290 }
291 packet->data = const_cast<u8*>(frame_data.data());
292 packet->size = static_cast<s32>(frame_data.size());
293 if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
294 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
295 return; 64 return;
296 } 65 }
297 // Only receive/store visible frames 66
67 // Only receive/store visible frames.
298 if (vp9_hidden_frame) { 68 if (vp9_hidden_frame) {
299 return; 69 return;
300 } 70 }
301 AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
302 AVFramePtr final_frame{nullptr, AVFrameDeleter};
303 ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
304 if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
305 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
306 return;
307 }
308 if (initial_frame->width == 0 || initial_frame->height == 0) {
309 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
310 return;
311 }
312 bool is_interlaced = initial_frame->interlaced_frame != 0;
313 if (av_codec_ctx->hw_device_ctx) {
314 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
315 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
316 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
317 // because Intel drivers crash unless using AV_PIX_FMT_NV12
318 final_frame->format = PREFERRED_GPU_FMT;
319 const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
320 ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
321 } else {
322 final_frame = std::move(initial_frame);
323 }
324 if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
325 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
326 return;
327 }
328 if (!is_interlaced) {
329 av_frames.push(std::move(final_frame));
330 } else {
331 if (!filters_initialized) {
332 InitializeAvFilters(final_frame.get());
333 }
334 if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(),
335 AV_BUFFERSRC_FLAG_KEEP_REF);
336 ret) {
337 LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret);
338 return;
339 }
340 while (true) {
341 auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
342 71
343 int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get()); 72 // Receive output frames from decoder.
73 decode_api.ReceiveFrames(frames);
344 74
345 if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) 75 while (frames.size() > 10) {
346 break; 76 LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
347 if (ret < 0) { 77 frames.pop();
348 LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret);
349 return;
350 }
351
352 av_frames.push(std::move(filter_frame));
353 }
354 }
355 while (av_frames.size() > 10) {
356 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
357 av_frames.pop();
358 } 78 }
359} 79}
360 80
361AVFramePtr Codec::GetCurrentFrame() { 81std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
362 // Sometimes VIC will request more frames than have been decoded. 82 // Sometimes VIC will request more frames than have been decoded.
363 // in this case, return a nullptr and don't overwrite previous frame data 83 // in this case, return a blank frame and don't overwrite previous data.
364 if (av_frames.empty()) { 84 if (frames.empty()) {
365 return AVFramePtr{nullptr, AVFrameDeleter}; 85 return {};
366 } 86 }
367 AVFramePtr frame = std::move(av_frames.front()); 87
368 av_frames.pop(); 88 auto frame = std::move(frames.front());
89 frames.pop();
369 return frame; 90 return frame;
370} 91}
371 92
diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h
index 06fe00a4b..f700ae129 100644
--- a/src/video_core/host1x/codecs/codec.h
+++ b/src/video_core/host1x/codecs/codec.h
@@ -4,28 +4,15 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <optional>
7#include <string_view> 8#include <string_view>
8#include <queue> 9#include <queue>
9#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/host1x/ffmpeg/ffmpeg.h"
10#include "video_core/host1x/nvdec_common.h" 12#include "video_core/host1x/nvdec_common.h"
11 13
12extern "C" {
13#if defined(__GNUC__) || defined(__clang__)
14#pragma GCC diagnostic push
15#pragma GCC diagnostic ignored "-Wconversion"
16#endif
17#include <libavcodec/avcodec.h>
18#include <libavfilter/avfilter.h>
19#if defined(__GNUC__) || defined(__clang__)
20#pragma GCC diagnostic pop
21#endif
22}
23
24namespace Tegra { 14namespace Tegra {
25 15
26void AVFrameDeleter(AVFrame* ptr);
27using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
28
29namespace Decoder { 16namespace Decoder {
30class H264; 17class H264;
31class VP8; 18class VP8;
@@ -51,7 +38,7 @@ public:
51 void Decode(); 38 void Decode();
52 39
53 /// Returns next decoded frame 40 /// Returns next decoded frame
54 [[nodiscard]] AVFramePtr GetCurrentFrame(); 41 [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
55 42
56 /// Returns the value of current_codec 43 /// Returns the value of current_codec
57 [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; 44 [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
@@ -60,25 +47,9 @@ public:
60 [[nodiscard]] std::string_view GetCurrentCodecName() const; 47 [[nodiscard]] std::string_view GetCurrentCodecName() const;
61 48
62private: 49private:
63 void InitializeAvCodecContext();
64
65 void InitializeAvFilters(AVFrame* frame);
66
67 void InitializeGpuDecoder();
68
69 bool CreateGpuAvDevice();
70
71 bool initialized{}; 50 bool initialized{};
72 bool filters_initialized{};
73 Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; 51 Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
74 52 FFmpeg::DecodeApi decode_api;
75 const AVCodec* av_codec{nullptr};
76 AVCodecContext* av_codec_ctx{nullptr};
77 AVBufferRef* av_gpu_decoder{nullptr};
78
79 AVFilterContext* av_filter_src_ctx{nullptr};
80 AVFilterContext* av_filter_sink_ctx{nullptr};
81 AVFilterGraph* av_filter_graph{nullptr};
82 53
83 Host1x::Host1x& host1x; 54 Host1x::Host1x& host1x;
84 const Host1x::NvdecCommon::NvdecRegisters& state; 55 const Host1x::NvdecCommon::NvdecRegisters& state;
@@ -86,7 +57,7 @@ private:
86 std::unique_ptr<Decoder::VP8> vp8_decoder; 57 std::unique_ptr<Decoder::VP8> vp8_decoder;
87 std::unique_ptr<Decoder::VP9> vp9_decoder; 58 std::unique_ptr<Decoder::VP9> vp9_decoder;
88 59
89 std::queue<AVFramePtr> av_frames{}; 60 std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
90}; 61};
91 62
92} // namespace Tegra 63} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index ece79b1e2..309a7f1d5 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
30H264::~H264() = default; 30H264::~H264() = default;
31 31
32std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, 32std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
33 bool is_first_frame) { 33 size_t* out_configuration_size, bool is_first_frame) {
34 H264DecoderContext context; 34 H264DecoderContext context;
35 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, 35 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
36 sizeof(H264DecoderContext)); 36 sizeof(H264DecoderContext));
@@ -39,6 +39,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
39 if (!is_first_frame && frame_number != 0) { 39 if (!is_first_frame && frame_number != 0) {
40 frame.resize_destructive(context.stream_len); 40 frame.resize_destructive(context.stream_len);
41 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); 41 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
42 *out_configuration_size = 0;
42 return frame; 43 return frame;
43 } 44 }
44 45
@@ -157,6 +158,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
157 frame.resize(encoded_header.size() + context.stream_len); 158 frame.resize(encoded_header.size() + context.stream_len);
158 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); 159 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
159 160
161 *out_configuration_size = encoded_header.size();
160 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, 162 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
161 frame.data() + encoded_header.size(), context.stream_len); 163 frame.data() + encoded_header.size(), context.stream_len);
162 164
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h
index d6b556322..1deaf4632 100644
--- a/src/video_core/host1x/codecs/h264.h
+++ b/src/video_core/host1x/codecs/h264.h
@@ -67,6 +67,7 @@ public:
67 67
68 /// Compose the H264 frame for FFmpeg decoding 68 /// Compose the H264 frame for FFmpeg decoding
69 [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, 69 [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
70 size_t* out_configuration_size,
70 bool is_first_frame = false); 71 bool is_first_frame = false);
71 72
72private: 73private:
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
new file mode 100644
index 000000000..dcd07e6d2
--- /dev/null
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
@@ -0,0 +1,419 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "common/logging/log.h"
6#include "common/scope_exit.h"
7#include "common/settings.h"
8#include "video_core/host1x/ffmpeg/ffmpeg.h"
9
10extern "C" {
11#ifdef LIBVA_FOUND
12// for querying VAAPI driver information
13#include <libavutil/hwcontext_vaapi.h>
14#endif
15}
16
17namespace FFmpeg {
18
19namespace {
20
21constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
22constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
23constexpr std::array PreferredGpuDecoders = {
24 AV_HWDEVICE_TYPE_CUDA,
25#ifdef _WIN32
26 AV_HWDEVICE_TYPE_D3D11VA,
27 AV_HWDEVICE_TYPE_DXVA2,
28#elif defined(__unix__)
29 AV_HWDEVICE_TYPE_VAAPI,
30 AV_HWDEVICE_TYPE_VDPAU,
31#endif
32 // last resort for Linux Flatpak (w/ NVIDIA)
33 AV_HWDEVICE_TYPE_VULKAN,
34};
35
36AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
37 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
38 if (*p == codec_context->pix_fmt) {
39 return codec_context->pix_fmt;
40 }
41 }
42
43 LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU");
44 av_buffer_unref(&codec_context->hw_device_ctx);
45
46 codec_context->pix_fmt = PreferredCpuFormat;
47 return codec_context->pix_fmt;
48}
49
50std::string AVError(int errnum) {
51 char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
52 av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
53 return errbuf;
54}
55
56} // namespace
57
58Packet::Packet(std::span<const u8> data) {
59 m_packet = av_packet_alloc();
60 m_packet->data = const_cast<u8*>(data.data());
61 m_packet->size = static_cast<s32>(data.size());
62}
63
64Packet::~Packet() {
65 av_packet_free(&m_packet);
66}
67
68Frame::Frame() {
69 m_frame = av_frame_alloc();
70}
71
72Frame::~Frame() {
73 av_frame_free(&m_frame);
74}
75
76Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
77 const AVCodecID av_codec = [&] {
78 switch (codec) {
79 case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
80 return AV_CODEC_ID_H264;
81 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
82 return AV_CODEC_ID_VP8;
83 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
84 return AV_CODEC_ID_VP9;
85 default:
86 UNIMPLEMENTED_MSG("Unknown codec {}", codec);
87 return AV_CODEC_ID_NONE;
88 }
89 }();
90
91 m_codec = avcodec_find_decoder(av_codec);
92}
93
94bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
95 for (int i = 0;; i++) {
96 const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
97 if (!config) {
98 LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name,
99 av_hwdevice_get_type_name(type));
100 break;
101 }
102 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 &&
103 config->device_type == type) {
104 LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
105 *out_pix_fmt = config->pix_fmt;
106 return true;
107 }
108 }
109
110 return false;
111}
112
113std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
114 std::vector<AVHWDeviceType> types;
115 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
116
117 while (true) {
118 current_device_type = av_hwdevice_iterate_types(current_device_type);
119 if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
120 return types;
121 }
122
123 types.push_back(current_device_type);
124 }
125}
126
127HardwareContext::~HardwareContext() {
128 av_buffer_unref(&m_gpu_decoder);
129}
130
131bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
132 const Decoder& decoder) {
133 const auto supported_types = GetSupportedDeviceTypes();
134 for (const auto type : PreferredGpuDecoders) {
135 AVPixelFormat hw_pix_fmt;
136
137 if (std::ranges::find(supported_types, type) == supported_types.end()) {
138 LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
139 continue;
140 }
141
142 if (!this->InitializeWithType(type)) {
143 continue;
144 }
145
146 if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
147 decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
148 return true;
149 }
150 }
151
152 return false;
153}
154
155bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
156 av_buffer_unref(&m_gpu_decoder);
157
158 if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0);
159 ret < 0) {
160 LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type),
161 AVError(ret));
162 return false;
163 }
164
165#ifdef LIBVA_FOUND
166 if (type == AV_HWDEVICE_TYPE_VAAPI) {
167 // We need to determine if this is an impersonated VAAPI driver.
168 auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
169 auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
170 const char* vendor_name = vaQueryVendorString(vactx->display);
171 if (strstr(vendor_name, "VDPAU backend")) {
172 // VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
173 LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
174 return false;
175 } else {
176 // According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
177 // Log the driver name just in case.
178 LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
179 }
180 }
181#endif
182
183 return true;
184}
185
186DecoderContext::DecoderContext(const Decoder& decoder) {
187 m_codec_context = avcodec_alloc_context3(decoder.GetCodec());
188 av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
189 m_codec_context->thread_count = 0;
190 m_codec_context->thread_type &= ~FF_THREAD_FRAME;
191}
192
193DecoderContext::~DecoderContext() {
194 av_buffer_unref(&m_codec_context->hw_device_ctx);
195 avcodec_free_context(&m_codec_context);
196}
197
198void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context,
199 AVPixelFormat hw_pix_fmt) {
200 m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
201 m_codec_context->get_format = GetGpuFormat;
202 m_codec_context->pix_fmt = hw_pix_fmt;
203}
204
205bool DecoderContext::OpenContext(const Decoder& decoder) {
206 if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
207 LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
208 return false;
209 }
210
211 if (!m_codec_context->hw_device_ctx) {
212 LOG_INFO(HW_GPU, "Using FFmpeg software decoding");
213 }
214
215 return true;
216}
217
218bool DecoderContext::SendPacket(const Packet& packet) {
219 if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
220 LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
221 return false;
222 }
223
224 return true;
225}
226
227std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
228 auto dst_frame = std::make_unique<Frame>();
229
230 const auto ReceiveImpl = [&](AVFrame* frame) {
231 if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
232 LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
233 return false;
234 }
235
236 *out_is_interlaced = frame->interlaced_frame != 0;
237 return true;
238 };
239
240 if (m_codec_context->hw_device_ctx) {
241 // If we have a hardware context, make a separate frame here to receive the
242 // hardware result before sending it to the output.
243 Frame intermediate_frame;
244
245 if (!ReceiveImpl(intermediate_frame.GetFrame())) {
246 return {};
247 }
248
249 dst_frame->SetFormat(PreferredGpuFormat);
250 if (const int ret =
251 av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
252 ret < 0) {
253 LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
254 return {};
255 }
256 } else {
257 // Otherwise, decode the frame as normal.
258 if (!ReceiveImpl(dst_frame->GetFrame())) {
259 return {};
260 }
261 }
262
263 return dst_frame;
264}
265
266DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
267 const AVFilter* buffer_src = avfilter_get_by_name("buffer");
268 const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
269 AVFilterInOut* inputs = avfilter_inout_alloc();
270 AVFilterInOut* outputs = avfilter_inout_alloc();
271 SCOPE_EXIT({
272 avfilter_inout_free(&inputs);
273 avfilter_inout_free(&outputs);
274 });
275
276 // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
277 // so just use 1/1 to make buffer filter happy
278 std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
279 frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));
280
281 m_filter_graph = avfilter_graph_alloc();
282 int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
283 nullptr, m_filter_graph);
284 if (ret < 0) {
285 LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
286 return;
287 }
288
289 ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
290 m_filter_graph);
291 if (ret < 0) {
292 LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
293 return;
294 }
295
296 inputs->name = av_strdup("out");
297 inputs->filter_ctx = m_sink_context;
298 inputs->pad_idx = 0;
299 inputs->next = nullptr;
300
301 outputs->name = av_strdup("in");
302 outputs->filter_ctx = m_source_context;
303 outputs->pad_idx = 0;
304 outputs->next = nullptr;
305
306 const char* description = "yadif=1:-1:0";
307 ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
308 if (ret < 0) {
309 LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
310 return;
311 }
312
313 ret = avfilter_graph_config(m_filter_graph, nullptr);
314 if (ret < 0) {
315 LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
316 return;
317 }
318
319 m_initialized = true;
320}
321
322bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
323 if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
324 AV_BUFFERSRC_FLAG_KEEP_REF);
325 ret < 0) {
326 LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
327 return false;
328 }
329
330 return true;
331}
332
333std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
334 auto dst_frame = std::make_unique<Frame>();
335 const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());
336
337 if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
338 return {};
339 }
340
341 if (ret < 0) {
342 LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
343 return {};
344 }
345
346 return dst_frame;
347}
348
349DeinterlaceFilter::~DeinterlaceFilter() {
350 avfilter_graph_free(&m_filter_graph);
351}
352
353void DecodeApi::Reset() {
354 m_deinterlace_filter.reset();
355 m_hardware_context.reset();
356 m_decoder_context.reset();
357 m_decoder.reset();
358}
359
360bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
361 this->Reset();
362 m_decoder.emplace(codec);
363 m_decoder_context.emplace(*m_decoder);
364
365 // Enable GPU decoding if requested.
366 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
367 m_hardware_context.emplace();
368 m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
369 }
370
371 // Open the decoder context.
372 if (!m_decoder_context->OpenContext(*m_decoder)) {
373 this->Reset();
374 return false;
375 }
376
377 return true;
378}
379
380bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) {
381 FFmpeg::Packet packet(packet_data);
382 return m_decoder_context->SendPacket(packet);
383}
384
385void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
386 // Receive raw frame from decoder.
387 bool is_interlaced;
388 auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
389 if (!frame) {
390 return;
391 }
392
393 if (!is_interlaced) {
394 // If the frame is not interlaced, we can pend it now.
395 frame_queue.push(std::move(frame));
396 } else {
397 // Create the deinterlacer if needed.
398 if (!m_deinterlace_filter) {
399 m_deinterlace_filter.emplace(*frame);
400 }
401
402 // Add the frame we just received.
403 if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
404 return;
405 }
406
407 // Pend output fields.
408 while (true) {
409 auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
410 if (!filter_frame) {
411 break;
412 }
413
414 frame_queue.push(std::move(filter_frame));
415 }
416 }
417}
418
419} // namespace FFmpeg
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h
new file mode 100644
index 000000000..1de0bbd83
--- /dev/null
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.h
@@ -0,0 +1,213 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <optional>
8#include <span>
9#include <vector>
10#include <queue>
11
12#include "common/common_funcs.h"
13#include "common/common_types.h"
14#include "video_core/host1x/nvdec_common.h"
15
16extern "C" {
17#if defined(__GNUC__) || defined(__clang__)
18#pragma GCC diagnostic push
19#pragma GCC diagnostic ignored "-Wconversion"
20#endif
21
22#include <libavcodec/avcodec.h>
23#include <libavfilter/avfilter.h>
24#include <libavfilter/buffersink.h>
25#include <libavfilter/buffersrc.h>
26#include <libavutil/avutil.h>
27#include <libavutil/opt.h>
28
29#if defined(__GNUC__) || defined(__clang__)
30#pragma GCC diagnostic pop
31#endif
32}
33
34namespace FFmpeg {
35
36class Packet;
37class Frame;
38class Decoder;
39class HardwareContext;
40class DecoderContext;
41class DeinterlaceFilter;
42
43// Wraps an AVPacket, a container for compressed bitstream data.
44class Packet {
45public:
46 YUZU_NON_COPYABLE(Packet);
47 YUZU_NON_MOVEABLE(Packet);
48
49 explicit Packet(std::span<const u8> data);
50 ~Packet();
51
52 AVPacket* GetPacket() const {
53 return m_packet;
54 }
55
56private:
57 AVPacket* m_packet{};
58};
59
60// Wraps an AVFrame, a container for audio and video stream data.
61class Frame {
62public:
63 YUZU_NON_COPYABLE(Frame);
64 YUZU_NON_MOVEABLE(Frame);
65
66 explicit Frame();
67 ~Frame();
68
69 int GetWidth() const {
70 return m_frame->width;
71 }
72
73 int GetHeight() const {
74 return m_frame->height;
75 }
76
77 AVPixelFormat GetPixelFormat() const {
78 return static_cast<AVPixelFormat>(m_frame->format);
79 }
80
81 int GetStride(int plane) const {
82 return m_frame->linesize[plane];
83 }
84
85 int* GetStrides() const {
86 return m_frame->linesize;
87 }
88
89 u8* GetData(int plane) const {
90 return m_frame->data[plane];
91 }
92
93 u8** GetPlanes() const {
94 return m_frame->data;
95 }
96
97 void SetFormat(int format) {
98 m_frame->format = format;
99 }
100
101 AVFrame* GetFrame() const {
102 return m_frame;
103 }
104
105private:
106 AVFrame* m_frame{};
107};
108
109// Wraps an AVCodec, a type containing information about a codec.
110class Decoder {
111public:
112 YUZU_NON_COPYABLE(Decoder);
113 YUZU_NON_MOVEABLE(Decoder);
114
115 explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec);
116 ~Decoder() = default;
117
118 bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const;
119
120 const AVCodec* GetCodec() const {
121 return m_codec;
122 }
123
124private:
125 const AVCodec* m_codec{};
126};
127
128// Wraps AVBufferRef for an accelerated decoder.
129class HardwareContext {
130public:
131 YUZU_NON_COPYABLE(HardwareContext);
132 YUZU_NON_MOVEABLE(HardwareContext);
133
134 static std::vector<AVHWDeviceType> GetSupportedDeviceTypes();
135
136 explicit HardwareContext() = default;
137 ~HardwareContext();
138
139 bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder);
140
141 AVBufferRef* GetBufferRef() const {
142 return m_gpu_decoder;
143 }
144
145private:
146 bool InitializeWithType(AVHWDeviceType type);
147
148 AVBufferRef* m_gpu_decoder{};
149};
150
151// Wraps an AVCodecContext.
152class DecoderContext {
153public:
154 YUZU_NON_COPYABLE(DecoderContext);
155 YUZU_NON_MOVEABLE(DecoderContext);
156
157 explicit DecoderContext(const Decoder& decoder);
158 ~DecoderContext();
159
160 void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
161 bool OpenContext(const Decoder& decoder);
162 bool SendPacket(const Packet& packet);
163 std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);
164
165 AVCodecContext* GetCodecContext() const {
166 return m_codec_context;
167 }
168
169private:
170 AVCodecContext* m_codec_context{};
171};
172
173// Wraps an AVFilterGraph.
174class DeinterlaceFilter {
175public:
176 YUZU_NON_COPYABLE(DeinterlaceFilter);
177 YUZU_NON_MOVEABLE(DeinterlaceFilter);
178
179 explicit DeinterlaceFilter(const Frame& frame);
180 ~DeinterlaceFilter();
181
182 bool AddSourceFrame(const Frame& frame);
183 std::unique_ptr<Frame> DrainSinkFrame();
184
185private:
186 AVFilterGraph* m_filter_graph{};
187 AVFilterContext* m_source_context{};
188 AVFilterContext* m_sink_context{};
189 bool m_initialized{};
190};
191
192class DecodeApi {
193public:
194 YUZU_NON_COPYABLE(DecodeApi);
195 YUZU_NON_MOVEABLE(DecodeApi);
196
197 DecodeApi() = default;
198 ~DecodeApi() = default;
199
200 bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
201 void Reset();
202
203 bool SendPacket(std::span<const u8> packet_data, size_t configuration_size);
204 void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);
205
206private:
207 std::optional<FFmpeg::Decoder> m_decoder;
208 std::optional<FFmpeg::DecoderContext> m_decoder_context;
209 std::optional<FFmpeg::HardwareContext> m_hardware_context;
210 std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
211};
212
213} // namespace FFmpeg
diff --git a/src/video_core/host1x/nvdec.cpp b/src/video_core/host1x/nvdec.cpp
index a4bd5b79f..b8f5866d3 100644
--- a/src/video_core/host1x/nvdec.cpp
+++ b/src/video_core/host1x/nvdec.cpp
@@ -28,7 +28,7 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) {
28 } 28 }
29} 29}
30 30
31AVFramePtr Nvdec::GetFrame() { 31std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() {
32 return codec->GetCurrentFrame(); 32 return codec->GetCurrentFrame();
33} 33}
34 34
diff --git a/src/video_core/host1x/nvdec.h b/src/video_core/host1x/nvdec.h
index 3949d5181..ddddb8d28 100644
--- a/src/video_core/host1x/nvdec.h
+++ b/src/video_core/host1x/nvdec.h
@@ -23,7 +23,7 @@ public:
23 void ProcessMethod(u32 method, u32 argument); 23 void ProcessMethod(u32 method, u32 argument);
24 24
25 /// Return most recently decoded frame 25 /// Return most recently decoded frame
26 [[nodiscard]] AVFramePtr GetFrame(); 26 [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame();
27 27
28private: 28private:
29 /// Invoke codec to decode a frame 29 /// Invoke codec to decode a frame
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp
index 10d7ef884..2a5eba415 100644
--- a/src/video_core/host1x/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -82,27 +82,26 @@ void Vic::Execute() {
82 return; 82 return;
83 } 83 }
84 const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)}; 84 const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)};
85 const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); 85 auto frame = nvdec_processor->GetFrame();
86 const auto* frame = frame_ptr.get();
87 if (!frame) { 86 if (!frame) {
88 return; 87 return;
89 } 88 }
90 const u64 surface_width = config.surface_width_minus1 + 1; 89 const u64 surface_width = config.surface_width_minus1 + 1;
91 const u64 surface_height = config.surface_height_minus1 + 1; 90 const u64 surface_height = config.surface_height_minus1 + 1;
92 if (static_cast<u64>(frame->width) != surface_width || 91 if (static_cast<u64>(frame->GetWidth()) != surface_width ||
93 static_cast<u64>(frame->height) != surface_height) { 92 static_cast<u64>(frame->GetHeight()) != surface_height) {
94 // TODO: Properly support multiple video streams with differing frame dimensions 93 // TODO: Properly support multiple video streams with differing frame dimensions
95 LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}", 94 LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}",
96 frame->width, frame->height, surface_width, surface_height); 95 frame->GetWidth(), frame->GetHeight(), surface_width, surface_height);
97 } 96 }
98 switch (config.pixel_format) { 97 switch (config.pixel_format) {
99 case VideoPixelFormat::RGBA8: 98 case VideoPixelFormat::RGBA8:
100 case VideoPixelFormat::BGRA8: 99 case VideoPixelFormat::BGRA8:
101 case VideoPixelFormat::RGBX8: 100 case VideoPixelFormat::RGBX8:
102 WriteRGBFrame(frame, config); 101 WriteRGBFrame(std::move(frame), config);
103 break; 102 break;
104 case VideoPixelFormat::YUV420: 103 case VideoPixelFormat::YUV420:
105 WriteYUVFrame(frame, config); 104 WriteYUVFrame(std::move(frame), config);
106 break; 105 break;
107 default: 106 default:
108 UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value()); 107 UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value());
@@ -110,10 +109,14 @@ void Vic::Execute() {
110 } 109 }
111} 110}
112 111
113void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { 112void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
114 LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); 113 LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
115 114
116 if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) { 115 const auto frame_width = frame->GetWidth();
116 const auto frame_height = frame->GetHeight();
117 const auto frame_format = frame->GetPixelFormat();
118
119 if (!scaler_ctx || frame_width != scaler_width || frame_height != scaler_height) {
117 const AVPixelFormat target_format = [pixel_format = config.pixel_format]() { 120 const AVPixelFormat target_format = [pixel_format = config.pixel_format]() {
118 switch (pixel_format) { 121 switch (pixel_format) {
119 case VideoPixelFormat::RGBA8: 122 case VideoPixelFormat::RGBA8:
@@ -129,27 +132,26 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
129 132
130 sws_freeContext(scaler_ctx); 133 sws_freeContext(scaler_ctx);
131 // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format 134 // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format
132 scaler_ctx = sws_getContext(frame->width, frame->height, 135 scaler_ctx = sws_getContext(frame_width, frame_height, frame_format, frame_width,
133 static_cast<AVPixelFormat>(frame->format), frame->width, 136 frame_height, target_format, 0, nullptr, nullptr, nullptr);
134 frame->height, target_format, 0, nullptr, nullptr, nullptr); 137 scaler_width = frame_width;
135 scaler_width = frame->width; 138 scaler_height = frame_height;
136 scaler_height = frame->height;
137 converted_frame_buffer.reset(); 139 converted_frame_buffer.reset();
138 } 140 }
139 if (!converted_frame_buffer) { 141 if (!converted_frame_buffer) {
140 const size_t frame_size = frame->width * frame->height * 4; 142 const size_t frame_size = frame_width * frame_height * 4;
141 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free}; 143 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free};
142 } 144 }
143 const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0}; 145 const std::array<int, 4> converted_stride{frame_width * 4, frame_height * 4, 0, 0};
144 u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; 146 u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
145 sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr, 147 sws_scale(scaler_ctx, frame->GetPlanes(), frame->GetStrides(), 0, frame_height,
146 converted_stride.data()); 148 &converted_frame_buf_addr, converted_stride.data());
147 149
148 // Use the minimum of surface/frame dimensions to avoid buffer overflow. 150 // Use the minimum of surface/frame dimensions to avoid buffer overflow.
149 const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1; 151 const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1;
150 const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1; 152 const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1;
151 const u32 width = std::min(surface_width, static_cast<u32>(frame->width)); 153 const u32 width = std::min(surface_width, static_cast<u32>(frame_width));
152 const u32 height = std::min(surface_height, static_cast<u32>(frame->height)); 154 const u32 height = std::min(surface_height, static_cast<u32>(frame_height));
153 const u32 blk_kind = static_cast<u32>(config.block_linear_kind); 155 const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
154 if (blk_kind != 0) { 156 if (blk_kind != 0) {
155 // swizzle pitch linear to block linear 157 // swizzle pitch linear to block linear
@@ -169,23 +171,23 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
169 } 171 }
170} 172}
171 173
172void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { 174void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
173 LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); 175 LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
174 176
175 const std::size_t surface_width = config.surface_width_minus1 + 1; 177 const std::size_t surface_width = config.surface_width_minus1 + 1;
176 const std::size_t surface_height = config.surface_height_minus1 + 1; 178 const std::size_t surface_height = config.surface_height_minus1 + 1;
177 const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; 179 const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
178 // Use the minimum of surface/frame dimensions to avoid buffer overflow. 180 // Use the minimum of surface/frame dimensions to avoid buffer overflow.
179 const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); 181 const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->GetWidth()));
180 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); 182 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->GetHeight()));
181 183
182 const auto stride = static_cast<size_t>(frame->linesize[0]); 184 const auto stride = static_cast<size_t>(frame->GetStride(0));
183 185
184 luma_buffer.resize_destructive(aligned_width * surface_height); 186 luma_buffer.resize_destructive(aligned_width * surface_height);
185 chroma_buffer.resize_destructive(aligned_width * surface_height / 2); 187 chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
186 188
187 // Populate luma buffer 189 // Populate luma buffer
188 const u8* luma_src = frame->data[0]; 190 const u8* luma_src = frame->GetData(0);
189 for (std::size_t y = 0; y < frame_height; ++y) { 191 for (std::size_t y = 0; y < frame_height; ++y) {
190 const std::size_t src = y * stride; 192 const std::size_t src = y * stride;
191 const std::size_t dst = y * aligned_width; 193 const std::size_t dst = y * aligned_width;
@@ -196,16 +198,16 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
196 198
197 // Chroma 199 // Chroma
198 const std::size_t half_height = frame_height / 2; 200 const std::size_t half_height = frame_height / 2;
199 const auto half_stride = static_cast<size_t>(frame->linesize[1]); 201 const auto half_stride = static_cast<size_t>(frame->GetStride(1));
200 202
201 switch (frame->format) { 203 switch (frame->GetPixelFormat()) {
202 case AV_PIX_FMT_YUV420P: { 204 case AV_PIX_FMT_YUV420P: {
203 // Frame from FFmpeg software 205 // Frame from FFmpeg software
204 // Populate chroma buffer from both channels with interleaving. 206 // Populate chroma buffer from both channels with interleaving.
205 const std::size_t half_width = frame_width / 2; 207 const std::size_t half_width = frame_width / 2;
206 u8* chroma_buffer_data = chroma_buffer.data(); 208 u8* chroma_buffer_data = chroma_buffer.data();
207 const u8* chroma_b_src = frame->data[1]; 209 const u8* chroma_b_src = frame->GetData(1);
208 const u8* chroma_r_src = frame->data[2]; 210 const u8* chroma_r_src = frame->GetData(2);
209 for (std::size_t y = 0; y < half_height; ++y) { 211 for (std::size_t y = 0; y < half_height; ++y) {
210 const std::size_t src = y * half_stride; 212 const std::size_t src = y * half_stride;
211 const std::size_t dst = y * aligned_width; 213 const std::size_t dst = y * aligned_width;
@@ -219,7 +221,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
219 case AV_PIX_FMT_NV12: { 221 case AV_PIX_FMT_NV12: {
220 // Frame from VA-API hardware 222 // Frame from VA-API hardware
221 // This is already interleaved so just copy 223 // This is already interleaved so just copy
222 const u8* chroma_src = frame->data[1]; 224 const u8* chroma_src = frame->GetData(1);
223 for (std::size_t y = 0; y < half_height; ++y) { 225 for (std::size_t y = 0; y < half_height; ++y) {
224 const std::size_t src = y * stride; 226 const std::size_t src = y * stride;
225 const std::size_t dst = y * aligned_width; 227 const std::size_t dst = y * aligned_width;
diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h
index 3d9753047..6c868f062 100644
--- a/src/video_core/host1x/vic.h
+++ b/src/video_core/host1x/vic.h
@@ -39,9 +39,9 @@ public:
39private: 39private:
40 void Execute(); 40 void Execute();
41 41
42 void WriteRGBFrame(const AVFrame* frame, const VicConfig& config); 42 void WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
43 43
44 void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); 44 void WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
45 45
46 Host1x& host1x; 46 Host1x& host1x;
47 std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor; 47 std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h
index 78b42b518..efa9adf7a 100644
--- a/src/video_core/query_cache/query_cache.h
+++ b/src/video_core/query_cache/query_cache.h
@@ -266,7 +266,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
266 return; 266 return;
267 } 267 }
268 if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] { 268 if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] {
269 UNREACHABLE(); 269 ASSERT(false);
270 return; 270 return;
271 } 271 }
272 query_base->value += streamer->GetAmmendValue(); 272 query_base->value += streamer->GetAmmendValue();
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index 65cd5aa06..4f1d5b548 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/alignment.h" 4#include "common/alignment.h"
5#include "core/memory.h" 5#include "core/memory.h"
6#include "video_core/control/channel_state.h"
6#include "video_core/host1x/host1x.h" 7#include "video_core/host1x/host1x.h"
7#include "video_core/memory_manager.h" 8#include "video_core/memory_manager.h"
8#include "video_core/renderer_null/null_rasterizer.h" 9#include "video_core/renderer_null/null_rasterizer.h"
@@ -99,8 +100,14 @@ bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config,
99} 100}
100void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, 101void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
101 const VideoCore::DiskResourceLoadCallback& callback) {} 102 const VideoCore::DiskResourceLoadCallback& callback) {}
102void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {} 103void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {
103void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {} 104 CreateChannel(channel);
104void RasterizerNull::ReleaseChannel(s32 channel_id) {} 105}
106void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {
107 BindToChannel(channel.bind_id);
108}
109void RasterizerNull::ReleaseChannel(s32 channel_id) {
110 EraseChannel(channel_id);
111}
105 112
106} // namespace Null 113} // namespace Null
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 38d553d3c..dfd696de6 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -178,13 +178,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
178} 178}
179 179
180void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, 180void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
181 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 181 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
182 bool) {
182 CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); 183 CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier);
183} 184}
184 185
185void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 186void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
186 std::span<const VideoCommon::BufferCopy> copies) { 187 std::span<const VideoCommon::BufferCopy> copies, bool) {
187 CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); 188 CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true);
188} 189}
189 190
190void BufferCacheRuntime::PreCopyBarrier() { 191void BufferCacheRuntime::PreCopyBarrier() {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index e8dbbd3a2..000f29a82 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -30,6 +30,8 @@ public:
30 30
31 void MakeResident(GLenum access) noexcept; 31 void MakeResident(GLenum access) noexcept;
32 32
33 void MarkUsage(u64 offset, u64 size) {}
34
33 [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); 35 [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
34 36
35 [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { 37 [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
@@ -66,22 +68,29 @@ public:
66 68
67 [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); 69 [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size);
68 70
71 bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) {
72 return false;
73 }
74
69 void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, 75 void CopyBuffer(GLuint dst_buffer, GLuint src_buffer,
70 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 76 std::span<const VideoCommon::BufferCopy> copies, bool barrier);
71 77
72 void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, 78 void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
73 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 79 std::span<const VideoCommon::BufferCopy> copies, bool barrier);
74 80
75 void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, 81 void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
76 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 82 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
83 bool can_reorder_upload = false);
77 84
78 void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 85 void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
79 std::span<const VideoCommon::BufferCopy> copies); 86 std::span<const VideoCommon::BufferCopy> copies, bool);
80 87
81 void PreCopyBarrier(); 88 void PreCopyBarrier();
82 void PostCopyBarrier(); 89 void PostCopyBarrier();
83 void Finish(); 90 void Finish();
84 91
92 void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
93
85 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); 94 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
86 95
87 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); 96 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 44a771d65..af0a453ee 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -559,7 +559,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
559} 559}
560 560
561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { 561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
562 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS); 562 const GLenum buffer_mode =
563 num_xfb_buffers_active == 1 ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS;
564 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), buffer_mode);
563} 565}
564 566
565void GraphicsPipeline::GenerateTransformFeedbackState() { 567void GraphicsPipeline::GenerateTransformFeedbackState() {
@@ -567,12 +569,14 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
567 // when this is required. 569 // when this is required.
568 GLint* cursor{xfb_attribs.data()}; 570 GLint* cursor{xfb_attribs.data()};
569 571
572 num_xfb_buffers_active = 0;
570 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { 573 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
571 const auto& layout = key.xfb_state.layouts[feedback]; 574 const auto& layout = key.xfb_state.layouts[feedback];
572 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); 575 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
573 if (layout.varying_count == 0) { 576 if (layout.varying_count == 0) {
574 continue; 577 continue;
575 } 578 }
579 num_xfb_buffers_active++;
576 580
577 const auto& locations = key.xfb_state.varyings[feedback]; 581 const auto& locations = key.xfb_state.varyings[feedback];
578 std::optional<u32> current_index; 582 std::optional<u32> current_index;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 74fc9cc3d..2f70c1ae9 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -154,6 +154,7 @@ private:
154 154
155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3; 155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
156 GLsizei num_xfb_attribs{}; 156 GLsizei num_xfb_attribs{};
157 u32 num_xfb_buffers_active{};
157 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; 158 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
158 159
159 std::mutex built_mutex; 160 std::mutex built_mutex;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 27e2de1bf..9995b6dd4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -555,7 +555,7 @@ void RasterizerOpenGL::OnCacheInvalidation(VAddr addr, u64 size) {
555 } 555 }
556 { 556 {
557 std::scoped_lock lock{buffer_cache.mutex}; 557 std::scoped_lock lock{buffer_cache.mutex};
558 buffer_cache.CachedWriteMemory(addr, size); 558 buffer_cache.WriteMemory(addr, size);
559 } 559 }
560 shader_cache.InvalidateRegion(addr, size); 560 shader_cache.InvalidateRegion(addr, size);
561} 561}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 7e7a80740..c4c30d807 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -132,16 +132,12 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
132 const bool use_accelerated = 132 const bool use_accelerated =
133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
134 const bool is_srgb = use_accelerated && screen_info.is_srgb; 134 const bool is_srgb = use_accelerated && screen_info.is_srgb;
135 RenderScreenshot(*framebuffer, use_accelerated);
135 136
136 { 137 Frame* frame = present_manager.GetRenderFrame();
137 std::scoped_lock lock{rasterizer.LockCaches()}; 138 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
138 RenderScreenshot(*framebuffer, use_accelerated); 139 scheduler.Flush(*frame->render_ready);
139 140 present_manager.Present(frame);
140 Frame* frame = present_manager.GetRenderFrame();
141 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
142 scheduler.Flush(*frame->render_ready);
143 present_manager.Present(frame);
144 }
145 141
146 gpu.RendererFrameEndNotify(); 142 gpu.RendererFrameEndNotify();
147 rasterizer.TickFrame(); 143 rasterizer.TickFrame();
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 52fc142d1..66483a900 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -137,6 +137,56 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin
137 137
138BlitScreen::~BlitScreen() = default; 138BlitScreen::~BlitScreen() = default;
139 139
140static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer,
141 const ScreenInfo& screen_info) {
142 f32 left, top, right, bottom;
143
144 if (!framebuffer.crop_rect.IsEmpty()) {
145 // If crop rectangle is not empty, apply properties from rectangle.
146 left = static_cast<f32>(framebuffer.crop_rect.left);
147 top = static_cast<f32>(framebuffer.crop_rect.top);
148 right = static_cast<f32>(framebuffer.crop_rect.right);
149 bottom = static_cast<f32>(framebuffer.crop_rect.bottom);
150 } else {
151 // Otherwise, fall back to framebuffer dimensions.
152 left = 0;
153 top = 0;
154 right = static_cast<f32>(framebuffer.width);
155 bottom = static_cast<f32>(framebuffer.height);
156 }
157
158 // Apply transformation flags.
159 auto framebuffer_transform_flags = framebuffer.transform_flags;
160
161 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) {
162 // Switch left and right.
163 std::swap(left, right);
164 }
165 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) {
166 // Switch top and bottom.
167 std::swap(top, bottom);
168 }
169
170 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH;
171 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV;
172 if (True(framebuffer_transform_flags)) {
173 UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}",
174 static_cast<u32>(framebuffer_transform_flags));
175 }
176
177 // Get the screen properties.
178 const f32 screen_width = static_cast<f32>(screen_info.width);
179 const f32 screen_height = static_cast<f32>(screen_info.height);
180
181 // Normalize coordinate space.
182 left /= screen_width;
183 top /= screen_height;
184 right /= screen_width;
185 bottom /= screen_height;
186
187 return Common::Rectangle<f32>(left, top, right, bottom);
188}
189
140void BlitScreen::Recreate() { 190void BlitScreen::Recreate() {
141 present_manager.WaitPresent(); 191 present_manager.WaitPresent();
142 scheduler.Finish(); 192 scheduler.Finish();
@@ -354,17 +404,10 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
354 source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); 404 source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view);
355 } 405 }
356 if (fsr) { 406 if (fsr) {
357 auto crop_rect = framebuffer.crop_rect; 407 const auto crop_rect = NormalizeCrop(framebuffer, screen_info);
358 if (crop_rect.GetWidth() == 0) { 408 const VkExtent2D fsr_input_size{
359 crop_rect.right = framebuffer.width; 409 .width = Settings::values.resolution_info.ScaleUp(screen_info.width),
360 } 410 .height = Settings::values.resolution_info.ScaleUp(screen_info.height),
361 if (crop_rect.GetHeight() == 0) {
362 crop_rect.bottom = framebuffer.height;
363 }
364 crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
365 VkExtent2D fsr_input_size{
366 .width = Settings::values.resolution_info.ScaleUp(framebuffer.width),
367 .height = Settings::values.resolution_info.ScaleUp(framebuffer.height),
368 }; 411 };
369 VkImageView fsr_image_view = 412 VkImageView fsr_image_view =
370 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); 413 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
@@ -1397,61 +1440,37 @@ void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayou
1397 1440
1398void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, 1441void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
1399 const Layout::FramebufferLayout layout) const { 1442 const Layout::FramebufferLayout layout) const {
1400 const auto& framebuffer_transform_flags = framebuffer.transform_flags; 1443 f32 left, top, right, bottom;
1401 const auto& framebuffer_crop_rect = framebuffer.crop_rect;
1402
1403 static constexpr Common::Rectangle<f32> texcoords{0.f, 0.f, 1.f, 1.f};
1404 auto left = texcoords.left;
1405 auto right = texcoords.right;
1406
1407 switch (framebuffer_transform_flags) {
1408 case Service::android::BufferTransformFlags::Unset:
1409 break;
1410 case Service::android::BufferTransformFlags::FlipV:
1411 // Flip the framebuffer vertically
1412 left = texcoords.right;
1413 right = texcoords.left;
1414 break;
1415 default:
1416 UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}",
1417 static_cast<u32>(framebuffer_transform_flags));
1418 break;
1419 }
1420 1444
1421 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); 1445 if (fsr) {
1422 1446 // FSR has already applied the crop, so we just want to render the image
1423 f32 left_start{}; 1447 // it has produced.
1424 if (framebuffer_crop_rect.Top() > 0) { 1448 left = 0;
1425 left_start = static_cast<f32>(framebuffer_crop_rect.Top()) / 1449 top = 0;
1426 static_cast<f32>(framebuffer_crop_rect.Bottom()); 1450 right = 1;
1427 } 1451 bottom = 1;
1428 f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); 1452 } else {
1429 f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); 1453 // Get the normalized crop rectangle.
1430 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 1454 const auto crop = NormalizeCrop(framebuffer, screen_info);
1431 // (e.g. handheld mode) on a 1920x1080 framebuffer. 1455
1432 if (!fsr) { 1456 // Apply the crop.
1433 if (framebuffer_crop_rect.GetWidth() > 0) { 1457 left = crop.left;
1434 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / 1458 top = crop.top;
1435 static_cast<f32>(screen_info.width); 1459 right = crop.right;
1436 } 1460 bottom = crop.bottom;
1437 if (framebuffer_crop_rect.GetHeight() > 0) {
1438 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
1439 static_cast<f32>(screen_info.height);
1440 }
1441 } 1461 }
1442 1462
1463 // Map the coordinates to the screen.
1443 const auto& screen = layout.screen; 1464 const auto& screen = layout.screen;
1444 const auto x = static_cast<f32>(screen.left); 1465 const auto x = static_cast<f32>(screen.left);
1445 const auto y = static_cast<f32>(screen.top); 1466 const auto y = static_cast<f32>(screen.top);
1446 const auto w = static_cast<f32>(screen.GetWidth()); 1467 const auto w = static_cast<f32>(screen.GetWidth());
1447 const auto h = static_cast<f32>(screen.GetHeight()); 1468 const auto h = static_cast<f32>(screen.GetHeight());
1448 data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left_start + left * scale_v); 1469
1449 data.vertices[1] = 1470 data.vertices[0] = ScreenRectVertex(x, y, left, top);
1450 ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left_start + left * scale_v); 1471 data.vertices[1] = ScreenRectVertex(x + w, y, right, top);
1451 data.vertices[2] = 1472 data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
1452 ScreenRectVertex(x, y + h, texcoords.top * scale_u, left_start + right * scale_v); 1473 data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
1453 data.vertices[3] =
1454 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v);
1455} 1474}
1456 1475
1457void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { 1476void BlitScreen::CreateSMAA(VkExtent2D smaa_size) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 976c3f6a6..5958f52f7 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
79} // Anonymous namespace 79} // Anonymous namespace
80 80
81Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) 81Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params) {} 82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
83 83
84Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, 84Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
85 VAddr cpu_addr_, u64 size_bytes_) 85 VAddr cpu_addr_, u64 size_bytes_)
86 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), 86 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
87 device{&runtime.device}, buffer{ 87 device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())},
88 CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} { 88 tracker{SizeBytes()} {
89 if (runtime.device.HasDebuggingToolAttached()) { 89 if (runtime.device.HasDebuggingToolAttached()) {
90 buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); 90 buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
91 } 91 }
@@ -359,12 +359,31 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
359 return static_cast<u32>(device.GetStorageBufferAlignment()); 359 return static_cast<u32>(device.GetStorageBufferAlignment());
360} 360}
361 361
362void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
363 for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
364 it->ResetUsageTracking();
365 }
366}
367
362void BufferCacheRuntime::Finish() { 368void BufferCacheRuntime::Finish() {
363 scheduler.Finish(); 369 scheduler.Finish();
364} 370}
365 371
372bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer,
373 std::span<const VideoCommon::BufferCopy> copies) {
374 if (Settings::values.disable_buffer_reorder) {
375 return false;
376 }
377 const bool can_use_upload_cmdbuf =
378 std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) {
379 return !buffer.IsRegionUsed(copy.dst_offset, copy.size);
380 });
381 return can_use_upload_cmdbuf;
382}
383
366void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, 384void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
367 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 385 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
386 bool can_reorder_upload) {
368 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { 387 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
369 return; 388 return;
370 } 389 }
@@ -380,9 +399,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
380 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, 399 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
381 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, 400 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
382 }; 401 };
402
383 // Measuring a popular game, this number never exceeds the specified size once data is warmed up 403 // Measuring a popular game, this number never exceeds the specified size once data is warmed up
384 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); 404 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
385 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); 405 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
406 if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) {
407 scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies](
408 vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) {
409 upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies);
410 });
411 return;
412 }
413
386 scheduler.RequestOutsideRenderPassOperationContext(); 414 scheduler.RequestOutsideRenderPassOperationContext();
387 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { 415 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) {
388 if (barrier) { 416 if (barrier) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 833dfac45..0b3fbd6d0 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -5,6 +5,7 @@
5 5
6#include "video_core/buffer_cache/buffer_cache_base.h" 6#include "video_core/buffer_cache/buffer_cache_base.h"
7#include "video_core/buffer_cache/memory_tracker_base.h" 7#include "video_core/buffer_cache/memory_tracker_base.h"
8#include "video_core/buffer_cache/usage_tracker.h"
8#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_vulkan/vk_compute_pass.h" 10#include "video_core/renderer_vulkan/vk_compute_pass.h"
10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -34,6 +35,18 @@ public:
34 return *buffer; 35 return *buffer;
35 } 36 }
36 37
38 [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
39 return tracker.IsUsed(offset, size);
40 }
41
42 void MarkUsage(u64 offset, u64 size) noexcept {
43 tracker.Track(offset, size);
44 }
45
46 void ResetUsageTracking() noexcept {
47 tracker.Reset();
48 }
49
37 operator VkBuffer() const noexcept { 50 operator VkBuffer() const noexcept {
38 return *buffer; 51 return *buffer;
39 } 52 }
@@ -49,6 +62,7 @@ private:
49 const Device* device{}; 62 const Device* device{};
50 vk::Buffer buffer; 63 vk::Buffer buffer;
51 std::vector<BufferView> views; 64 std::vector<BufferView> views;
65 VideoCommon::UsageTracker tracker;
52}; 66};
53 67
54class QuadArrayIndexBuffer; 68class QuadArrayIndexBuffer;
@@ -67,6 +81,8 @@ public:
67 ComputePassDescriptorQueue& compute_pass_descriptor_queue, 81 ComputePassDescriptorQueue& compute_pass_descriptor_queue,
68 DescriptorPool& descriptor_pool); 82 DescriptorPool& descriptor_pool);
69 83
84 void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
85
70 void Finish(); 86 void Finish();
71 87
72 u64 GetDeviceLocalMemory() const; 88 u64 GetDeviceLocalMemory() const;
@@ -81,12 +97,15 @@ public:
81 97
82 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); 98 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
83 99
100 bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies);
101
84 void FreeDeferredStagingBuffer(StagingBufferRef& ref); 102 void FreeDeferredStagingBuffer(StagingBufferRef& ref);
85 103
86 void PreCopyBarrier(); 104 void PreCopyBarrier();
87 105
88 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, 106 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
89 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 107 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
108 bool can_reorder_upload = false);
90 109
91 void PostCopyBarrier(); 110 void PostCopyBarrier();
92 111
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
index ce8f3f3c2..f7a05fbc0 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ b/src/video_core/renderer_vulkan/vk_fsr.cpp
@@ -34,7 +34,7 @@ FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image
34} 34}
35 35
36VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, 36VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
37 VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect) { 37 VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) {
38 38
39 UpdateDescriptorSet(image_index, image_view); 39 UpdateDescriptorSet(image_index, image_view);
40 40
@@ -61,15 +61,21 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView imag
61 61
62 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); 62 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline);
63 63
64 const f32 input_image_width = static_cast<f32>(input_image_extent.width);
65 const f32 input_image_height = static_cast<f32>(input_image_extent.height);
66 const f32 output_image_width = static_cast<f32>(output_size.width);
67 const f32 output_image_height = static_cast<f32>(output_size.height);
68 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
69 const f32 viewport_x = crop_rect.left * input_image_width;
70 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
71 const f32 viewport_y = crop_rect.top * input_image_height;
72
64 std::array<u32, 4 * 4> push_constants; 73 std::array<u32, 4 * 4> push_constants;
65 FsrEasuConOffset( 74 FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4,
66 push_constants.data() + 0, push_constants.data() + 4, push_constants.data() + 8, 75 push_constants.data() + 8, push_constants.data() + 12,
67 push_constants.data() + 12, 76
68 77 viewport_width, viewport_height, input_image_width, input_image_height,
69 static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()), 78 output_image_width, output_image_height, viewport_x, viewport_y);
70 static_cast<f32>(input_image_extent.width), static_cast<f32>(input_image_extent.height),
71 static_cast<f32>(output_size.width), static_cast<f32>(output_size.height),
72 static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
73 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); 79 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
74 80
75 { 81 {
diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h
index 8bb9fc23a..3505c1416 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.h
+++ b/src/video_core/renderer_vulkan/vk_fsr.h
@@ -17,7 +17,7 @@ public:
17 explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, 17 explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
18 VkExtent2D output_size); 18 VkExtent2D output_size);
19 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, 19 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
20 VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect); 20 VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect);
21 21
22private: 22private:
23 void CreateDescriptorPool(); 23 void CreateDescriptorPool();
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index 6b288b994..ac8b6e838 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) {
100 Refresh(); 100 Refresh();
101} 101}
102 102
103VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 103VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
104 VkSemaphore wait_semaphore, u64 host_tick) { 104 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
105 u64 host_tick) {
105 if (semaphore) { 106 if (semaphore) {
106 return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); 107 return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore,
108 host_tick);
107 } else { 109 } else {
108 return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); 110 return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick);
109 } 111 }
110} 112}
111 113
@@ -115,6 +117,7 @@ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
115}; 117};
116 118
117VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, 119VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
120 vk::CommandBuffer& upload_cmdbuf,
118 VkSemaphore signal_semaphore, 121 VkSemaphore signal_semaphore,
119 VkSemaphore wait_semaphore, u64 host_tick) { 122 VkSemaphore wait_semaphore, u64 host_tick) {
120 const VkSemaphore timeline_semaphore = *semaphore; 123 const VkSemaphore timeline_semaphore = *semaphore;
@@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
123 const std::array signal_values{host_tick, u64(0)}; 126 const std::array signal_values{host_tick, u64(0)};
124 const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; 127 const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
125 128
129 const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
130
126 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; 131 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
127 const VkTimelineSemaphoreSubmitInfo timeline_si{ 132 const VkTimelineSemaphoreSubmitInfo timeline_si{
128 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, 133 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
@@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
138 .waitSemaphoreCount = num_wait_semaphores, 143 .waitSemaphoreCount = num_wait_semaphores,
139 .pWaitSemaphores = &wait_semaphore, 144 .pWaitSemaphores = &wait_semaphore,
140 .pWaitDstStageMask = wait_stage_masks.data(), 145 .pWaitDstStageMask = wait_stage_masks.data(),
141 .commandBufferCount = 1, 146 .commandBufferCount = static_cast<u32>(cmdbuffers.size()),
142 .pCommandBuffers = cmdbuf.address(), 147 .pCommandBuffers = cmdbuffers.data(),
143 .signalSemaphoreCount = num_signal_semaphores, 148 .signalSemaphoreCount = num_signal_semaphores,
144 .pSignalSemaphores = signal_semaphores.data(), 149 .pSignalSemaphores = signal_semaphores.data(),
145 }; 150 };
@@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
147 return device.GetGraphicsQueue().Submit(submit_info); 152 return device.GetGraphicsQueue().Submit(submit_info);
148} 153}
149 154
150VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 155VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf,
151 VkSemaphore wait_semaphore, u64 host_tick) { 156 vk::CommandBuffer& upload_cmdbuf,
157 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
158 u64 host_tick) {
152 const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; 159 const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
153 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; 160 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
154 161
162 const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
163
155 const VkSubmitInfo submit_info{ 164 const VkSubmitInfo submit_info{
156 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 165 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
157 .pNext = nullptr, 166 .pNext = nullptr,
158 .waitSemaphoreCount = num_wait_semaphores, 167 .waitSemaphoreCount = num_wait_semaphores,
159 .pWaitSemaphores = &wait_semaphore, 168 .pWaitSemaphores = &wait_semaphore,
160 .pWaitDstStageMask = wait_stage_masks.data(), 169 .pWaitDstStageMask = wait_stage_masks.data(),
161 .commandBufferCount = 1, 170 .commandBufferCount = static_cast<u32>(cmdbuffers.size()),
162 .pCommandBuffers = cmdbuf.address(), 171 .pCommandBuffers = cmdbuffers.data(),
163 .signalSemaphoreCount = num_signal_semaphores, 172 .signalSemaphoreCount = num_signal_semaphores,
164 .pSignalSemaphores = &signal_semaphore, 173 .pSignalSemaphores = &signal_semaphore,
165 }; 174 };
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 3f599d7bd..7dfb93ffb 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -52,14 +52,16 @@ public:
52 void Wait(u64 tick); 52 void Wait(u64 tick);
53 53
54 /// Submits the device graphics queue, updating the tick as necessary 54 /// Submits the device graphics queue, updating the tick as necessary
55 VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 55 VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
56 VkSemaphore wait_semaphore, u64 host_tick); 56 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick);
57 57
58private: 58private:
59 VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 59 VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
60 VkSemaphore wait_semaphore, u64 host_tick); 60 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
61 VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 61 u64 host_tick);
62 VkSemaphore wait_semaphore, u64 host_tick); 62 VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
63 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
64 u64 host_tick);
63 65
64 void WaitThread(std::stop_token token); 66 void WaitThread(std::stop_token token);
65 67
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 0d604eee3..2a13b2a72 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -263,6 +263,22 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
263 info.y_negate = key.state.y_negate != 0; 263 info.y_negate = key.state.y_negate != 0;
264 return info; 264 return info;
265} 265}
266
267size_t GetTotalPipelineWorkers() {
268 const size_t max_core_threads =
269 std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
270#ifdef ANDROID
271 // Leave at least a few cores free in android
272 constexpr size_t free_cores = 3ULL;
273 if (max_core_threads <= free_cores) {
274 return 1ULL;
275 }
276 return max_core_threads - free_cores;
277#else
278 return max_core_threads;
279#endif
280}
281
266} // Anonymous namespace 282} // Anonymous namespace
267 283
268size_t ComputePipelineCacheKey::Hash() const noexcept { 284size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -294,11 +310,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
294 texture_cache{texture_cache_}, shader_notify{shader_notify_}, 310 texture_cache{texture_cache_}, shader_notify{shader_notify_},
295 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, 311 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
296 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, 312 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
297#ifdef ANDROID 313 workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(),
298 workers(1, "VkPipelineBuilder"), 314 "VkPipelineBuilder"),
299#else
300 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
301#endif
302 serialization_thread(1, "VkPipelineSerialization") { 315 serialization_thread(1, "VkPipelineSerialization") {
303 const auto& float_control{device.FloatControlProperties()}; 316 const auto& float_control{device.FloatControlProperties()};
304 const VkDriverId driver_id{device.GetDriverID()}; 317 const VkDriverId driver_id{device.GetDriverID()};
@@ -338,6 +351,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
338 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 351 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
339 .support_native_ndc = device.IsExtDepthClipControlSupported(), 352 .support_native_ndc = device.IsExtDepthClipControlSupported(),
340 .support_scaled_attributes = !device.MustEmulateScaledFormats(), 353 .support_scaled_attributes = !device.MustEmulateScaledFormats(),
354 .support_multi_viewport = device.SupportsMultiViewport(),
341 355
342 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), 356 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
343 357
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 66c03bf17..078777cdd 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -211,6 +211,13 @@ public:
211 return; 211 return;
212 } 212 }
213 PauseCounter(); 213 PauseCounter();
214 const auto driver_id = device.GetDriverID();
215 if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
216 driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
217 pending_sync.clear();
218 sync_values_stash.clear();
219 return;
220 }
214 sync_values_stash.clear(); 221 sync_values_stash.clear();
215 sync_values_stash.emplace_back(); 222 sync_values_stash.emplace_back();
216 std::vector<HostSyncValues>* sync_values = &sync_values_stash.back(); 223 std::vector<HostSyncValues>* sync_values = &sync_values_stash.back();
@@ -1378,6 +1385,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
1378 return true; 1385 return true;
1379 } 1386 }
1380 1387
1388 auto driver_id = impl->device.GetDriverID();
1389 if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
1390 driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
1391 return true;
1392 }
1393
1381 for (size_t i = 0; i < 2; i++) { 1394 for (size_t i = 0; i < 2; i++) {
1382 is_null[i] = !is_in_ac[i] && check_value(objects[i]->address); 1395 is_null[i] = !is_in_ac[i] && check_value(objects[i]->address);
1383 } 1396 }
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 059b7cb40..e0ab1eaac 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -82,7 +82,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
82 } 82 }
83 83
84 if (y_negate) { 84 if (y_negate) {
85 y += height; 85 y += conv(static_cast<f32>(regs.surface_clip.height));
86 height = -height; 86 height = -height;
87 } 87 }
88 88
@@ -199,7 +199,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
199 if (!pipeline) { 199 if (!pipeline) {
200 return; 200 return;
201 } 201 }
202 std::scoped_lock lock{LockCaches()}; 202 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
203 // update engine as channel may be different. 203 // update engine as channel may be different.
204 pipeline->SetEngine(maxwell3d, gpu_memory); 204 pipeline->SetEngine(maxwell3d, gpu_memory);
205 pipeline->Configure(is_indexed); 205 pipeline->Configure(is_indexed);
@@ -621,7 +621,7 @@ void RasterizerVulkan::OnCacheInvalidation(VAddr addr, u64 size) {
621 } 621 }
622 { 622 {
623 std::scoped_lock lock{buffer_cache.mutex}; 623 std::scoped_lock lock{buffer_cache.mutex};
624 buffer_cache.CachedWriteMemory(addr, size); 624 buffer_cache.WriteMemory(addr, size);
625 } 625 }
626 pipeline_cache.InvalidateRegion(addr, size); 626 pipeline_cache.InvalidateRegion(addr, size);
627} 627}
@@ -710,7 +710,6 @@ void RasterizerVulkan::TiledCacheBarrier() {
710} 710}
711 711
712void RasterizerVulkan::FlushCommands() { 712void RasterizerVulkan::FlushCommands() {
713 std::scoped_lock lock{LockCaches()};
714 if (draw_counter == 0) { 713 if (draw_counter == 0) {
715 return; 714 return;
716 } 715 }
@@ -808,7 +807,6 @@ void RasterizerVulkan::FlushWork() {
808 if ((++draw_counter & 7) != 7) { 807 if ((++draw_counter & 7) != 7) {
809 return; 808 return;
810 } 809 }
811 std::scoped_lock lock{LockCaches()};
812 if (draw_counter < DRAWS_TO_DISPATCH) { 810 if (draw_counter < DRAWS_TO_DISPATCH) {
813 // Send recorded tasks to the worker thread 811 // Send recorded tasks to the worker thread
814 scheduler.DispatchWork(); 812 scheduler.DispatchWork();
@@ -923,9 +921,13 @@ void RasterizerVulkan::UpdateDynamicStates() {
923} 921}
924 922
925void RasterizerVulkan::HandleTransformFeedback() { 923void RasterizerVulkan::HandleTransformFeedback() {
924 static std::once_flag warn_unsupported;
925
926 const auto& regs = maxwell3d->regs; 926 const auto& regs = maxwell3d->regs;
927 if (!device.IsExtTransformFeedbackSupported()) { 927 if (!device.IsExtTransformFeedbackSupported()) {
928 LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); 928 std::call_once(warn_unsupported, [&] {
929 LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported");
930 });
929 return; 931 return;
930 } 932 }
931 query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, 933 query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount,
@@ -1503,7 +1505,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
1503void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { 1505void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {
1504 CreateChannel(channel); 1506 CreateChannel(channel);
1505 { 1507 {
1506 std::scoped_lock lock{LockCaches()}; 1508 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1507 texture_cache.CreateChannel(channel); 1509 texture_cache.CreateChannel(channel);
1508 buffer_cache.CreateChannel(channel); 1510 buffer_cache.CreateChannel(channel);
1509 } 1511 }
@@ -1516,7 +1518,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
1516 const s32 channel_id = channel.bind_id; 1518 const s32 channel_id = channel.bind_id;
1517 BindToChannel(channel_id); 1519 BindToChannel(channel_id);
1518 { 1520 {
1519 std::scoped_lock lock{LockCaches()}; 1521 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1520 texture_cache.BindToChannel(channel_id); 1522 texture_cache.BindToChannel(channel_id);
1521 buffer_cache.BindToChannel(channel_id); 1523 buffer_cache.BindToChannel(channel_id);
1522 } 1524 }
@@ -1529,7 +1531,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
1529void RasterizerVulkan::ReleaseChannel(s32 channel_id) { 1531void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
1530 EraseChannel(channel_id); 1532 EraseChannel(channel_id);
1531 { 1533 {
1532 std::scoped_lock lock{LockCaches()}; 1534 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1533 texture_cache.EraseChannel(channel_id); 1535 texture_cache.EraseChannel(channel_id);
1534 buffer_cache.EraseChannel(channel_id); 1536 buffer_cache.EraseChannel(channel_id);
1535 } 1537 }
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index ce3dfbaab..ad069556c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -133,10 +133,6 @@ public:
133 133
134 void ReleaseChannel(s32 channel_id) override; 134 void ReleaseChannel(s32 channel_id) override;
135 135
136 std::scoped_lock<std::recursive_mutex, std::recursive_mutex> LockCaches() {
137 return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex};
138 }
139
140private: 136private:
141 static constexpr size_t MAX_TEXTURES = 192; 137 static constexpr size_t MAX_TEXTURES = 192;
142 static constexpr size_t MAX_IMAGES = 48; 138 static constexpr size_t MAX_IMAGES = 48;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 3be7837f4..146923db4 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -22,11 +22,12 @@ namespace Vulkan {
22 22
23MICROPROFILE_DECLARE(Vulkan_WaitForWorker); 23MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
24 24
25void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { 25void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
26 vk::CommandBuffer upload_cmdbuf) {
26 auto command = first; 27 auto command = first;
27 while (command != nullptr) { 28 while (command != nullptr) {
28 auto next = command->GetNext(); 29 auto next = command->GetNext();
29 command->Execute(cmdbuf); 30 command->Execute(cmdbuf, upload_cmdbuf);
30 command->~Command(); 31 command->~Command();
31 command = next; 32 command = next;
32 } 33 }
@@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
180 // Perform the work, tracking whether the chunk was a submission 181 // Perform the work, tracking whether the chunk was a submission
181 // before executing. 182 // before executing.
182 const bool has_submit = work->HasSubmit(); 183 const bool has_submit = work->HasSubmit();
183 work->ExecuteAll(current_cmdbuf); 184 work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf);
184 185
185 // If the chunk was a submission, reallocate the command buffer. 186 // If the chunk was a submission, reallocate the command buffer.
186 if (has_submit) { 187 if (has_submit) {
@@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() {
205 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 206 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
206 .pInheritanceInfo = nullptr, 207 .pInheritanceInfo = nullptr,
207 }); 208 });
209 current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
210 current_upload_cmdbuf.Begin({
211 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
212 .pNext = nullptr,
213 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
214 .pInheritanceInfo = nullptr,
215 });
208} 216}
209 217
210u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 218u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
@@ -212,7 +220,17 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
212 InvalidateState(); 220 InvalidateState();
213 221
214 const u64 signal_value = master_semaphore->NextTick(); 222 const u64 signal_value = master_semaphore->NextTick();
215 Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { 223 RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value,
224 this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) {
225 static constexpr VkMemoryBarrier WRITE_BARRIER{
226 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
227 .pNext = nullptr,
228 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
229 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
230 };
231 upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
232 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
233 upload_cmdbuf.End();
216 cmdbuf.End(); 234 cmdbuf.End();
217 235
218 if (on_submit) { 236 if (on_submit) {
@@ -221,7 +239,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
221 239
222 std::scoped_lock lock{submit_mutex}; 240 std::scoped_lock lock{submit_mutex};
223 switch (const VkResult result = master_semaphore->SubmitQueue( 241 switch (const VkResult result = master_semaphore->SubmitQueue(
224 cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { 242 cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
225 case VK_SUCCESS: 243 case VK_SUCCESS:
226 break; 244 break;
227 case VK_ERROR_DEVICE_LOST: 245 case VK_ERROR_DEVICE_LOST:
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index da03803aa..f8d8ca80a 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -80,7 +80,8 @@ public:
80 80
81 /// Send work to a separate thread. 81 /// Send work to a separate thread.
82 template <typename T> 82 template <typename T>
83 void Record(T&& command) { 83 requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer>
84 void RecordWithUploadBuffer(T&& command) {
84 if (chunk->Record(command)) { 85 if (chunk->Record(command)) {
85 return; 86 return;
86 } 87 }
@@ -88,6 +89,15 @@ public:
88 (void)chunk->Record(command); 89 (void)chunk->Record(command);
89 } 90 }
90 91
92 template <typename T>
93 requires std::is_invocable_v<T, vk::CommandBuffer>
94 void Record(T&& c) {
95 this->RecordWithUploadBuffer(
96 [command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) {
97 command(cmdbuf);
98 });
99 }
100
91 /// Returns the current command buffer tick. 101 /// Returns the current command buffer tick.
92 [[nodiscard]] u64 CurrentTick() const noexcept { 102 [[nodiscard]] u64 CurrentTick() const noexcept {
93 return master_semaphore->CurrentTick(); 103 return master_semaphore->CurrentTick();
@@ -119,7 +129,7 @@ private:
119 public: 129 public:
120 virtual ~Command() = default; 130 virtual ~Command() = default;
121 131
122 virtual void Execute(vk::CommandBuffer cmdbuf) const = 0; 132 virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0;
123 133
124 Command* GetNext() const { 134 Command* GetNext() const {
125 return next; 135 return next;
@@ -142,8 +152,8 @@ private:
142 TypedCommand(TypedCommand&&) = delete; 152 TypedCommand(TypedCommand&&) = delete;
143 TypedCommand& operator=(TypedCommand&&) = delete; 153 TypedCommand& operator=(TypedCommand&&) = delete;
144 154
145 void Execute(vk::CommandBuffer cmdbuf) const override { 155 void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override {
146 command(cmdbuf); 156 command(cmdbuf, upload_cmdbuf);
147 } 157 }
148 158
149 private: 159 private:
@@ -152,7 +162,7 @@ private:
152 162
153 class CommandChunk final { 163 class CommandChunk final {
154 public: 164 public:
155 void ExecuteAll(vk::CommandBuffer cmdbuf); 165 void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf);
156 166
157 template <typename T> 167 template <typename T>
158 bool Record(T& command) { 168 bool Record(T& command) {
@@ -228,6 +238,7 @@ private:
228 VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr; 238 VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr;
229 239
230 vk::CommandBuffer current_cmdbuf; 240 vk::CommandBuffer current_cmdbuf;
241 vk::CommandBuffer current_upload_cmdbuf;
231 242
232 std::unique_ptr<CommandChunk> chunk; 243 std::unique_ptr<CommandChunk> chunk;
233 std::function<void()> on_submit; 244 std::function<void()> on_submit;
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp
index 5efd7d66e..70644ea82 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.cpp
+++ b/src/video_core/renderer_vulkan/vk_smaa.cpp
@@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) {
672 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, 672 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
673 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); 673 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
674 674
675 scheduler.Record([&](vk::CommandBuffer& cmdbuf) { 675 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
676 for (auto& images : m_dynamic_images) { 676 for (auto& images : m_dynamic_images) {
677 for (size_t i = 0; i < MaxDynamicImage; i++) { 677 for (size_t i = 0; i < MaxDynamicImage; i++) {
678 ClearColorImage(cmdbuf, *images.images[i]); 678 ClearColorImage(cmdbuf, *images.images[i]);
@@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_
707 UpdateDescriptorSets(source_image_view, image_index); 707 UpdateDescriptorSets(source_image_view, image_index);
708 708
709 scheduler.RequestOutsideRenderPassOperationContext(); 709 scheduler.RequestOutsideRenderPassOperationContext();
710 scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { 710 scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
711 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); 711 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
712 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); 712 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
713 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, 713 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,
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 d3deb9072..f63a20327 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -36,6 +36,10 @@ public:
36 StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); 36 StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
37 void FreeDeferred(StagingBufferRef& ref); 37 void FreeDeferred(StagingBufferRef& ref);
38 38
39 [[nodiscard]] VkBuffer StreamBuf() const noexcept {
40 return *stream_buffer;
41 }
42
39 void TickFrame(); 43 void TickFrame();
40 44
41private: 45private:
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index de34f6d49..5dbec2e62 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1785,8 +1785,22 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
1785 : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, 1785 : VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
1786 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} 1786 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
1787 1787
1788ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params) 1788ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
1789 : VideoCommon::ImageViewBase{params} {} 1789 : VideoCommon::ImageViewBase{params}, device{&runtime.device} {
1790 if (device->HasNullDescriptor()) {
1791 return;
1792 }
1793
1794 // Handle fallback for devices without nullDescriptor
1795 ImageInfo info{};
1796 info.format = PixelFormat::A8B8G8R8_UNORM;
1797
1798 null_image = MakeImage(*device, runtime.memory_allocator, info, {});
1799 image_handle = *null_image;
1800 for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) {
1801 image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT);
1802 }
1803}
1790 1804
1791ImageView::~ImageView() = default; 1805ImageView::~ImageView() = default;
1792 1806
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 7a0807709..edf5d7635 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -267,6 +267,7 @@ private:
267 vk::ImageView depth_view; 267 vk::ImageView depth_view;
268 vk::ImageView stencil_view; 268 vk::ImageView stencil_view;
269 vk::ImageView color_view; 269 vk::ImageView color_view;
270 vk::Image null_image;
270 VkImage image_handle = VK_NULL_HANDLE; 271 VkImage image_handle = VK_NULL_HANDLE;
271 VkImageView render_target = VK_NULL_HANDLE; 272 VkImageView render_target = VK_NULL_HANDLE;
272 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; 273 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 9df6a2903..3ffa2a661 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -138,6 +138,10 @@ public:
138 return Iterator(this, SlotId{SlotId::INVALID_INDEX}); 138 return Iterator(this, SlotId{SlotId::INVALID_INDEX});
139 } 139 }
140 140
141 [[nodiscard]] size_t size() const noexcept {
142 return values_capacity - free_list.size();
143 }
144
141private: 145private:
142 struct NonTrivialDummy { 146 struct NonTrivialDummy {
143 NonTrivialDummy() noexcept {} 147 NonTrivialDummy() noexcept {}
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index e518756d2..fde36a49c 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -635,6 +635,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
635 has_broken_cube_compatibility = true; 635 has_broken_cube_compatibility = true;
636 } 636 }
637 } 637 }
638 if (is_qualcomm) {
639 const u32 version = (properties.properties.driverVersion << 3) >> 3;
640 if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
641 has_broken_parallel_compiling = true;
642 }
643 }
638 if (extensions.sampler_filter_minmax && is_amd) { 644 if (extensions.sampler_filter_minmax && is_amd) {
639 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. 645 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
640 if (!features.shader_float16_int8.shaderFloat16) { 646 if (!features.shader_float16_int8.shaderFloat16) {
@@ -863,7 +869,8 @@ bool Device::ShouldBoostClocks() const {
863 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || 869 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA ||
864 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; 870 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP;
865 871
866 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; 872 const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) ||
873 (vendor_id == 0x1002 && device_id == 0x1435);
867 874
868 const bool is_debugging = this->HasDebuggingToolAttached(); 875 const bool is_debugging = this->HasDebuggingToolAttached();
869 876
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index b213ed7dd..4f3846345 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -102,6 +102,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
102 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ 102 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
103 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ 103 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
104 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ 104 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
105 EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \
105 EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ 106 EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
106 EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ 107 EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
107 EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ 108 EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
@@ -599,6 +600,11 @@ public:
599 return has_broken_cube_compatibility; 600 return has_broken_cube_compatibility;
600 } 601 }
601 602
603 /// Returns true if parallel shader compiling has issues with the current driver.
604 bool HasBrokenParallelShaderCompiling() const {
605 return has_broken_parallel_compiling;
606 }
607
602 /// Returns the vendor name reported from Vulkan. 608 /// Returns the vendor name reported from Vulkan.
603 std::string_view GetVendorName() const { 609 std::string_view GetVendorName() const {
604 return properties.driver.driverName; 610 return properties.driver.driverName;
@@ -663,6 +669,10 @@ public:
663 return supports_conditional_barriers; 669 return supports_conditional_barriers;
664 } 670 }
665 671
672 bool SupportsMultiViewport() const {
673 return features2.features.multiViewport;
674 }
675
666 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id, 676 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
667 u32 driver_version) { 677 u32 driver_version) {
668 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { 678 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
@@ -794,6 +804,7 @@ private:
794 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. 804 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
795 bool has_broken_compute{}; ///< Compute shaders can cause crashes 805 bool has_broken_compute{}; ///< Compute shaders can cause crashes
796 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit 806 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
807 bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling.
797 bool has_renderdoc{}; ///< Has RenderDoc attached 808 bool has_renderdoc{}; ///< Has RenderDoc attached
798 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 809 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
799 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 810 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 0487cd3b6..a0c70797f 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -1101,6 +1101,10 @@ public:
1101 return &handle; 1101 return &handle;
1102 } 1102 }
1103 1103
1104 VkCommandBuffer operator*() const noexcept {
1105 return handle;
1106 }
1107
1104 void Begin(const VkCommandBufferBeginInfo& begin_info) const { 1108 void Begin(const VkCommandBufferBeginInfo& begin_info) const {
1105 Check(dld->vkBeginCommandBuffer(handle, &begin_info)); 1109 Check(dld->vkBeginCommandBuffer(handle, &begin_info));
1106 } 1110 }
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 33e1fb663..90278052a 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -38,8 +38,6 @@ add_executable(yuzu
38 compatdb.ui 38 compatdb.ui
39 compatibility_list.cpp 39 compatibility_list.cpp
40 compatibility_list.h 40 compatibility_list.h
41 configuration/config.cpp
42 configuration/config.h
43 configuration/configuration_shared.cpp 41 configuration/configuration_shared.cpp
44 configuration/configuration_shared.h 42 configuration/configuration_shared.h
45 configuration/configure.ui 43 configuration/configure.ui
@@ -147,6 +145,8 @@ add_executable(yuzu
147 configuration/shared_translation.h 145 configuration/shared_translation.h
148 configuration/shared_widget.cpp 146 configuration/shared_widget.cpp
149 configuration/shared_widget.h 147 configuration/shared_widget.h
148 configuration/qt_config.cpp
149 configuration/qt_config.h
150 debugger/console.cpp 150 debugger/console.cpp
151 debugger/console.h 151 debugger/console.h
152 debugger/controller.cpp 152 debugger/controller.cpp
@@ -252,6 +252,7 @@ file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
252if (ENABLE_QT_TRANSLATION) 252if (ENABLE_QT_TRANSLATION)
253 set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") 253 set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
254 option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) 254 option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
255 option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF)
255 256
256 # Update source TS file if enabled 257 # Update source TS file if enabled
257 if (GENERATE_QT_TRANSLATION) 258 if (GENERATE_QT_TRANSLATION)
@@ -259,19 +260,51 @@ if (ENABLE_QT_TRANSLATION)
259 # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals 260 # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals
260 # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm 261 # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm
261 set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") 262 set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
262 qt_create_translation(QM_FILES 263 if (WORKAROUND_BROKEN_LUPDATE)
263 ${SRCS} 264 add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts
264 ${UIS} 265 COMMAND lupdate
265 ${YUZU_QT_LANGUAGES}/en.ts 266 -source-language en_US
266 OPTIONS 267 -target-language en_US
267 -source-language en_US 268 ${SRCS}
268 -target-language en_US 269 ${UIS}
269 ) 270 -ts ${YUZU_QT_LANGUAGES}/en.ts
271 DEPENDS
272 ${SRCS}
273 ${UIS}
274 WORKING_DIRECTORY
275 ${CMAKE_CURRENT_SOURCE_DIR}
276 )
277 else()
278 qt_create_translation(QM_FILES
279 ${SRCS}
280 ${UIS}
281 ${YUZU_QT_LANGUAGES}/en.ts
282 OPTIONS
283 -source-language en_US
284 -target-language en_US
285 )
286 endif()
270 287
271 # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts 288 # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts
272 set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) 289 set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts)
273 set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") 290 set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals")
274 qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) 291 if (WORKAROUND_BROKEN_LUPDATE)
292 add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE}
293 COMMAND lupdate
294 -source-language en_US
295 -target-language en_US
296 ${SRCS}
297 ${UIS}
298 -ts ${GENERATED_PLURALS_FILE}
299 DEPENDS
300 ${SRCS}
301 ${UIS}
302 WORKING_DIRECTORY
303 ${CMAKE_CURRENT_SOURCE_DIR}
304 )
305 else()
306 qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
307 endif()
275 308
276 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) 309 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE})
277 endif() 310 endif()
@@ -344,7 +377,7 @@ endif()
344 377
345create_target_directory_groups(yuzu) 378create_target_directory_groups(yuzu)
346 379
347target_link_libraries(yuzu PRIVATE common core input_common network video_core) 380target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core)
348target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) 381target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets)
349target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 382target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
350 383
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 515cb7ce6..9e5319716 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -13,7 +13,6 @@
13#include "core/hid/hid_core.h" 13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
15#include "core/hle/service/hid/controllers/npad.h" 15#include "core/hle/service/hid/controllers/npad.h"
16#include "core/hle/service/hid/hid.h"
17#include "core/hle/service/sm/sm.h" 16#include "core/hle/service/sm/sm.h"
18#include "ui_qt_controller.h" 17#include "ui_qt_controller.h"
19#include "yuzu/applets/qt_controller.h" 18#include "yuzu/applets/qt_controller.h"
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
deleted file mode 100644
index baa3e55f3..000000000
--- a/src/yuzu/configuration/config.cpp
+++ /dev/null
@@ -1,1306 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <QKeySequence>
7#include <QSettings>
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/settings.h"
11#include "common/settings_common.h"
12#include "common/settings_enums.h"
13#include "core/core.h"
14#include "core/hle/service/acc/profile_manager.h"
15#include "core/hle/service/hid/controllers/npad.h"
16#include "input_common/main.h"
17#include "network/network.h"
18#include "yuzu/configuration/config.h"
19
20namespace FS = Common::FS;
21
22Config::Config(const std::string& config_name, ConfigType config_type)
23 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
24 Initialize(config_name);
25}
26
27Config::~Config() {
28 if (global) {
29 Save();
30 }
31}
32
33const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
34 Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
35 Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
36 Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
37 Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
38};
39
40const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
41 Qt::Key_7,
42 Qt::Key_8,
43};
44
45const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
46 {
47 Qt::Key_W,
48 Qt::Key_S,
49 Qt::Key_A,
50 Qt::Key_D,
51 },
52 {
53 Qt::Key_I,
54 Qt::Key_K,
55 Qt::Key_J,
56 Qt::Key_L,
57 },
58}};
59
60const std::array<int, 2> Config::default_stick_mod = {
61 Qt::Key_Shift,
62 0,
63};
64
65const std::array<int, 2> Config::default_ringcon_analogs{{
66 Qt::Key_A,
67 Qt::Key_D,
68}};
69
70const std::map<Settings::AntiAliasing, QString> Config::anti_aliasing_texts_map = {
71 {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
72 {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
73 {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
74};
75
76const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_map = {
77 {Settings::ScalingFilter::NearestNeighbor,
78 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
79 {Settings::ScalingFilter::Bilinear,
80 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
81 {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
82 {Settings::ScalingFilter::Gaussian,
83 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
84 {Settings::ScalingFilter::ScaleForce,
85 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
86 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
87};
88
89const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
90 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
91 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
92};
93
94const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
95 {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
96 {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
97 {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
98};
99
100const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = {
101 {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
102 {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
103 {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
104};
105
106const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
107 {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
108 {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
109 {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
110};
111
112// This shouldn't have anything except static initializers (no functions). So
113// QKeySequence(...).toString() is NOT ALLOWED HERE.
114// This must be in alphabetical order according to action name as it must have the same order as
115// UISetting::values.shortcuts, which is alphabetically ordered.
116// clang-format off
117const std::array<UISettings::Shortcut, 23> Config::default_hotkeys{{
118 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}},
119 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
120 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
121 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
122 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
123 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
124 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
125 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
126 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
127 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
128 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
129 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
130 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
131 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}},
132 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}},
133 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
134 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
135 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
136 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
137 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
138 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
139 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}},
140 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
141}};
142// clang-format on
143
144void Config::Initialize(const std::string& config_name) {
145 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
146 const auto config_file = fmt::format("{}.ini", config_name);
147
148 switch (type) {
149 case ConfigType::GlobalConfig:
150 qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
151 void(FS::CreateParentDir(qt_config_loc));
152 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
153 QSettings::IniFormat);
154 Reload();
155 break;
156 case ConfigType::PerGameConfig:
157 qt_config_loc =
158 FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
159 void(FS::CreateParentDir(qt_config_loc));
160 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
161 QSettings::IniFormat);
162 Reload();
163 break;
164 case ConfigType::InputProfile:
165 qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
166 void(FS::CreateParentDir(qt_config_loc));
167 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
168 QSettings::IniFormat);
169 break;
170 }
171}
172
173bool Config::IsCustomConfig() {
174 return type == ConfigType::PerGameConfig;
175}
176
177void Config::ReadPlayerValue(std::size_t player_index) {
178 const QString player_prefix = [this, player_index] {
179 if (type == ConfigType::InputProfile) {
180 return QString{};
181 } else {
182 return QStringLiteral("player_%1_").arg(player_index);
183 }
184 }();
185
186 auto& player = Settings::values.players.GetValue()[player_index];
187 if (IsCustomConfig()) {
188 const auto profile_name =
189 qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{})
190 .toString()
191 .toStdString();
192 if (profile_name.empty()) {
193 // Use the global input config
194 player = Settings::values.players.GetValue(true)[player_index];
195 return;
196 }
197 player.profile_name = profile_name;
198 }
199
200 if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) {
201 const auto controller = static_cast<Settings::ControllerType>(
202 qt_config
203 ->value(QStringLiteral("%1type").arg(player_prefix),
204 static_cast<u8>(Settings::ControllerType::ProController))
205 .toUInt());
206
207 if (controller == Settings::ControllerType::LeftJoycon ||
208 controller == Settings::ControllerType::RightJoycon) {
209 player.controller_type = controller;
210 }
211 } else {
212 player.connected =
213 ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
214 .toBool();
215
216 player.controller_type = static_cast<Settings::ControllerType>(
217 qt_config
218 ->value(QStringLiteral("%1type").arg(player_prefix),
219 static_cast<u8>(Settings::ControllerType::ProController))
220 .toUInt());
221
222 player.vibration_enabled =
223 qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
224 .toBool();
225
226 player.vibration_strength =
227 qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
228 .toInt();
229
230 player.body_color_left = qt_config
231 ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
232 Settings::JOYCON_BODY_NEON_BLUE)
233 .toUInt();
234 player.body_color_right =
235 qt_config
236 ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
237 Settings::JOYCON_BODY_NEON_RED)
238 .toUInt();
239 player.button_color_left =
240 qt_config
241 ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
242 Settings::JOYCON_BUTTONS_NEON_BLUE)
243 .toUInt();
244 player.button_color_right =
245 qt_config
246 ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
247 Settings::JOYCON_BUTTONS_NEON_RED)
248 .toUInt();
249 }
250
251 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
252 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
253 auto& player_buttons = player.buttons[i];
254
255 player_buttons = qt_config
256 ->value(QStringLiteral("%1").arg(player_prefix) +
257 QString::fromUtf8(Settings::NativeButton::mapping[i]),
258 QString::fromStdString(default_param))
259 .toString()
260 .toStdString();
261 if (player_buttons.empty()) {
262 player_buttons = default_param;
263 }
264 }
265
266 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
267 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
268 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
269 default_analogs[i][3], default_stick_mod[i], 0.5f);
270 auto& player_analogs = player.analogs[i];
271
272 player_analogs = qt_config
273 ->value(QStringLiteral("%1").arg(player_prefix) +
274 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
275 QString::fromStdString(default_param))
276 .toString()
277 .toStdString();
278 if (player_analogs.empty()) {
279 player_analogs = default_param;
280 }
281 }
282
283 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
284 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
285 auto& player_motions = player.motions[i];
286
287 player_motions = qt_config
288 ->value(QStringLiteral("%1").arg(player_prefix) +
289 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
290 QString::fromStdString(default_param))
291 .toString()
292 .toStdString();
293 if (player_motions.empty()) {
294 player_motions = default_param;
295 }
296 }
297}
298
299void Config::ReadDebugValues() {
300 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
301 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
302 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
303
304 debug_pad_buttons = qt_config
305 ->value(QStringLiteral("debug_pad_") +
306 QString::fromUtf8(Settings::NativeButton::mapping[i]),
307 QString::fromStdString(default_param))
308 .toString()
309 .toStdString();
310 if (debug_pad_buttons.empty()) {
311 debug_pad_buttons = default_param;
312 }
313 }
314
315 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
316 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
317 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
318 default_analogs[i][3], default_stick_mod[i], 0.5f);
319 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
320
321 debug_pad_analogs = qt_config
322 ->value(QStringLiteral("debug_pad_") +
323 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
324 QString::fromStdString(default_param))
325 .toString()
326 .toStdString();
327 if (debug_pad_analogs.empty()) {
328 debug_pad_analogs = default_param;
329 }
330 }
331}
332
333void Config::ReadTouchscreenValues() {
334 Settings::values.touchscreen.enabled =
335 ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool();
336
337 Settings::values.touchscreen.rotation_angle =
338 ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt();
339 Settings::values.touchscreen.diameter_x =
340 ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt();
341 Settings::values.touchscreen.diameter_y =
342 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
343}
344
345void Config::ReadHidbusValues() {
346 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
347 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
348 auto& ringcon_analogs = Settings::values.ringcon_analogs;
349
350 ringcon_analogs =
351 qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
352 .toString()
353 .toStdString();
354 if (ringcon_analogs.empty()) {
355 ringcon_analogs = default_param;
356 }
357}
358
359void Config::ReadAudioValues() {
360 qt_config->beginGroup(QStringLiteral("Audio"));
361
362 ReadCategory(Settings::Category::Audio);
363
364 qt_config->endGroup();
365}
366
367void Config::ReadControlValues() {
368 qt_config->beginGroup(QStringLiteral("Controls"));
369
370 ReadCategory(Settings::Category::Controls);
371
372 Settings::values.players.SetGlobal(!IsCustomConfig());
373 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
374 ReadPlayerValue(p);
375 }
376
377 // Disable docked mode if handheld is selected
378 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
379 if (controller_type == Settings::ControllerType::Handheld) {
380 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
381 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
382 }
383
384 if (IsCustomConfig()) {
385 qt_config->endGroup();
386 return;
387 }
388 ReadDebugValues();
389 ReadTouchscreenValues();
390 ReadMotionTouchValues();
391 ReadHidbusValues();
392
393 qt_config->endGroup();
394}
395
396void Config::ReadMotionTouchValues() {
397 int num_touch_from_button_maps =
398 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
399
400 if (num_touch_from_button_maps > 0) {
401 const auto append_touch_from_button_map = [this] {
402 Settings::TouchFromButtonMap map;
403 map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default"))
404 .toString()
405 .toStdString();
406 const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries"));
407 map.buttons.reserve(num_touch_maps);
408 for (int i = 0; i < num_touch_maps; i++) {
409 qt_config->setArrayIndex(i);
410 std::string touch_mapping =
411 ReadSetting(QStringLiteral("bind")).toString().toStdString();
412 map.buttons.emplace_back(std::move(touch_mapping));
413 }
414 qt_config->endArray(); // entries
415 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
416 };
417
418 for (int i = 0; i < num_touch_from_button_maps; ++i) {
419 qt_config->setArrayIndex(i);
420 append_touch_from_button_map();
421 }
422 } else {
423 Settings::values.touch_from_button_maps.emplace_back(
424 Settings::TouchFromButtonMap{"default", {}});
425 num_touch_from_button_maps = 1;
426 }
427 qt_config->endArray();
428
429 Settings::values.touch_from_button_map_index = std::clamp(
430 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
431}
432
433void Config::ReadCoreValues() {
434 qt_config->beginGroup(QStringLiteral("Core"));
435
436 ReadCategory(Settings::Category::Core);
437
438 qt_config->endGroup();
439}
440
441void Config::ReadDataStorageValues() {
442 qt_config->beginGroup(QStringLiteral("Data Storage"));
443
444 FS::SetYuzuPath(
445 FS::YuzuPath::NANDDir,
446 qt_config
447 ->value(QStringLiteral("nand_directory"),
448 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)))
449 .toString()
450 .toStdString());
451 FS::SetYuzuPath(
452 FS::YuzuPath::SDMCDir,
453 qt_config
454 ->value(QStringLiteral("sdmc_directory"),
455 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)))
456 .toString()
457 .toStdString());
458 FS::SetYuzuPath(
459 FS::YuzuPath::LoadDir,
460 qt_config
461 ->value(QStringLiteral("load_directory"),
462 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)))
463 .toString()
464 .toStdString());
465 FS::SetYuzuPath(
466 FS::YuzuPath::DumpDir,
467 qt_config
468 ->value(QStringLiteral("dump_directory"),
469 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
470 .toString()
471 .toStdString());
472 FS::SetYuzuPath(FS::YuzuPath::TASDir,
473 qt_config
474 ->value(QStringLiteral("tas_directory"),
475 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)))
476 .toString()
477 .toStdString());
478
479 ReadCategory(Settings::Category::DataStorage);
480
481 qt_config->endGroup();
482}
483
484void Config::ReadDebuggingValues() {
485 qt_config->beginGroup(QStringLiteral("Debugging"));
486
487 // Intentionally not using the QT default setting as this is intended to be changed in the ini
488 Settings::values.record_frame_times =
489 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
490
491 ReadCategory(Settings::Category::Debugging);
492 ReadCategory(Settings::Category::DebuggingGraphics);
493
494 qt_config->endGroup();
495}
496
497void Config::ReadServiceValues() {
498 qt_config->beginGroup(QStringLiteral("Services"));
499
500 ReadCategory(Settings::Category::Services);
501
502 qt_config->endGroup();
503}
504
505void Config::ReadDisabledAddOnValues() {
506 const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns"));
507
508 for (int i = 0; i < size; ++i) {
509 qt_config->setArrayIndex(i);
510 const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong();
511 std::vector<std::string> out;
512 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
513 for (int j = 0; j < d_size; ++j) {
514 qt_config->setArrayIndex(j);
515 out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
516 }
517 qt_config->endArray();
518 Settings::values.disabled_addons.insert_or_assign(title_id, out);
519 }
520
521 qt_config->endArray();
522}
523
524void Config::ReadMiscellaneousValues() {
525 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
526
527 ReadCategory(Settings::Category::Miscellaneous);
528
529 qt_config->endGroup();
530}
531
532void Config::ReadPathValues() {
533 qt_config->beginGroup(QStringLiteral("Paths"));
534
535 UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
536 UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
537 UISettings::values.game_dir_deprecated =
538 ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
539 UISettings::values.game_dir_deprecated_deepscan =
540 ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool();
541 const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs"));
542 for (int i = 0; i < gamedirs_size; ++i) {
543 qt_config->setArrayIndex(i);
544 UISettings::GameDir game_dir;
545 game_dir.path = ReadSetting(QStringLiteral("path")).toString();
546 game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool();
547 game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool();
548 UISettings::values.game_dirs.append(game_dir);
549 }
550 qt_config->endArray();
551 // create NAND and SD card directories if empty, these are not removable through the UI,
552 // also carries over old game list settings if present
553 if (UISettings::values.game_dirs.isEmpty()) {
554 UISettings::GameDir game_dir;
555 game_dir.path = QStringLiteral("SDMC");
556 game_dir.expanded = true;
557 UISettings::values.game_dirs.append(game_dir);
558 game_dir.path = QStringLiteral("UserNAND");
559 UISettings::values.game_dirs.append(game_dir);
560 game_dir.path = QStringLiteral("SysNAND");
561 UISettings::values.game_dirs.append(game_dir);
562 if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) {
563 game_dir.path = UISettings::values.game_dir_deprecated;
564 game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
565 UISettings::values.game_dirs.append(game_dir);
566 }
567 }
568 UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
569 UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
570
571 qt_config->endGroup();
572}
573
574void Config::ReadCpuValues() {
575 qt_config->beginGroup(QStringLiteral("Cpu"));
576
577 ReadCategory(Settings::Category::Cpu);
578 ReadCategory(Settings::Category::CpuDebug);
579 ReadCategory(Settings::Category::CpuUnsafe);
580
581 qt_config->endGroup();
582}
583
584void Config::ReadRendererValues() {
585 qt_config->beginGroup(QStringLiteral("Renderer"));
586
587 ReadCategory(Settings::Category::Renderer);
588 ReadCategory(Settings::Category::RendererAdvanced);
589 ReadCategory(Settings::Category::RendererDebug);
590
591 qt_config->endGroup();
592}
593
594void Config::ReadScreenshotValues() {
595 qt_config->beginGroup(QStringLiteral("Screenshots"));
596
597 ReadCategory(Settings::Category::Screenshots);
598 FS::SetYuzuPath(
599 FS::YuzuPath::ScreenshotsDir,
600 qt_config
601 ->value(QStringLiteral("screenshot_path"),
602 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)))
603 .toString()
604 .toStdString());
605
606 qt_config->endGroup();
607}
608
609void Config::ReadShortcutValues() {
610 qt_config->beginGroup(QStringLiteral("Shortcuts"));
611
612 for (const auto& [name, group, shortcut] : default_hotkeys) {
613 qt_config->beginGroup(group);
614 qt_config->beginGroup(name);
615 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
616 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
617 // a file dialog in windowed mode
618 UISettings::values.shortcuts.push_back(
619 {name,
620 group,
621 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
622 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
623 .toString(),
624 shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
625 qt_config->endGroup();
626 qt_config->endGroup();
627 }
628
629 qt_config->endGroup();
630}
631
632void Config::ReadSystemValues() {
633 qt_config->beginGroup(QStringLiteral("System"));
634
635 ReadCategory(Settings::Category::System);
636 ReadCategory(Settings::Category::SystemAudio);
637
638 qt_config->endGroup();
639}
640
641void Config::ReadUIValues() {
642 qt_config->beginGroup(QStringLiteral("UI"));
643
644 UISettings::values.theme =
645 ReadSetting(
646 QStringLiteral("theme"),
647 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second))
648 .toString();
649
650 ReadUIGamelistValues();
651 ReadUILayoutValues();
652 ReadPathValues();
653 ReadScreenshotValues();
654 ReadShortcutValues();
655 ReadMultiplayerValues();
656
657 ReadCategory(Settings::Category::Ui);
658 ReadCategory(Settings::Category::UiGeneral);
659
660 qt_config->endGroup();
661}
662
663void Config::ReadUIGamelistValues() {
664 qt_config->beginGroup(QStringLiteral("UIGameList"));
665
666 ReadCategory(Settings::Category::UiGameList);
667
668 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
669 for (int i = 0; i < favorites_size; i++) {
670 qt_config->setArrayIndex(i);
671 UISettings::values.favorited_ids.append(
672 ReadSetting(QStringLiteral("program_id")).toULongLong());
673 }
674 qt_config->endArray();
675
676 qt_config->endGroup();
677}
678
679void Config::ReadUILayoutValues() {
680 qt_config->beginGroup(QStringLiteral("UILayout"));
681
682 UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray();
683 UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray();
684 UISettings::values.renderwindow_geometry =
685 ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray();
686 UISettings::values.gamelist_header_state =
687 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
688 UISettings::values.microprofile_geometry =
689 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
690
691 ReadCategory(Settings::Category::UiLayout);
692
693 qt_config->endGroup();
694}
695
696void Config::ReadWebServiceValues() {
697 qt_config->beginGroup(QStringLiteral("WebService"));
698
699 ReadCategory(Settings::Category::WebService);
700
701 qt_config->endGroup();
702}
703
704void Config::ReadMultiplayerValues() {
705 qt_config->beginGroup(QStringLiteral("Multiplayer"));
706
707 ReadCategory(Settings::Category::Multiplayer);
708
709 // Read ban list back
710 int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
711 UISettings::values.multiplayer_ban_list.first.resize(size);
712 for (int i = 0; i < size; ++i) {
713 qt_config->setArrayIndex(i);
714 UISettings::values.multiplayer_ban_list.first[i] =
715 ReadSetting(QStringLiteral("username")).toString().toStdString();
716 }
717 qt_config->endArray();
718 size = qt_config->beginReadArray(QStringLiteral("ip_ban_list"));
719 UISettings::values.multiplayer_ban_list.second.resize(size);
720 for (int i = 0; i < size; ++i) {
721 qt_config->setArrayIndex(i);
722 UISettings::values.multiplayer_ban_list.second[i] =
723 ReadSetting(QStringLiteral("ip")).toString().toStdString();
724 }
725 qt_config->endArray();
726
727 qt_config->endGroup();
728}
729
730void Config::ReadNetworkValues() {
731 qt_config->beginGroup(QString::fromStdString("Services"));
732
733 ReadCategory(Settings::Category::Network);
734
735 qt_config->endGroup();
736}
737
738void Config::ReadValues() {
739 if (global) {
740 ReadDataStorageValues();
741 ReadDebuggingValues();
742 ReadDisabledAddOnValues();
743 ReadNetworkValues();
744 ReadServiceValues();
745 ReadUIValues();
746 ReadWebServiceValues();
747 ReadMiscellaneousValues();
748 }
749 ReadControlValues();
750 ReadCoreValues();
751 ReadCpuValues();
752 ReadRendererValues();
753 ReadAudioValues();
754 ReadSystemValues();
755}
756
757void Config::SavePlayerValue(std::size_t player_index) {
758 const QString player_prefix = [this, player_index] {
759 if (type == ConfigType::InputProfile) {
760 return QString{};
761 } else {
762 return QStringLiteral("player_%1_").arg(player_index);
763 }
764 }();
765
766 const auto& player = Settings::values.players.GetValue()[player_index];
767 if (IsCustomConfig()) {
768 if (player.profile_name.empty()) {
769 // No custom profile selected
770 return;
771 }
772 WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix),
773 QString::fromStdString(player.profile_name), QString{});
774 }
775
776 WriteSetting(QStringLiteral("%1type").arg(player_prefix),
777 static_cast<u8>(player.controller_type),
778 static_cast<u8>(Settings::ControllerType::ProController));
779
780 if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) {
781 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected,
782 player_index == 0);
783 WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
784 player.vibration_enabled, true);
785 WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
786 player.vibration_strength, 100);
787 WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
788 Settings::JOYCON_BODY_NEON_BLUE);
789 WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
790 player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
791 WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
792 player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
793 WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
794 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
795 }
796
797 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
798 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
799 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
800 QString::fromStdString(Settings::NativeButton::mapping[i]),
801 QString::fromStdString(player.buttons[i]),
802 QString::fromStdString(default_param));
803 }
804 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
805 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
806 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
807 default_analogs[i][3], default_stick_mod[i], 0.5f);
808 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
809 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
810 QString::fromStdString(player.analogs[i]),
811 QString::fromStdString(default_param));
812 }
813 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
814 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
815 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
816 QString::fromStdString(Settings::NativeMotion::mapping[i]),
817 QString::fromStdString(player.motions[i]),
818 QString::fromStdString(default_param));
819 }
820}
821
822void Config::SaveDebugValues() {
823 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
824 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
825 WriteSetting(QStringLiteral("debug_pad_") +
826 QString::fromStdString(Settings::NativeButton::mapping[i]),
827 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
828 QString::fromStdString(default_param));
829 }
830 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
831 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
832 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
833 default_analogs[i][3], default_stick_mod[i], 0.5f);
834 WriteSetting(QStringLiteral("debug_pad_") +
835 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
836 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
837 QString::fromStdString(default_param));
838 }
839}
840
841void Config::SaveTouchscreenValues() {
842 const auto& touchscreen = Settings::values.touchscreen;
843
844 WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true);
845
846 WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0);
847 WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15);
848 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
849}
850
851void Config::SaveMotionTouchValues() {
852 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
853 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
854 qt_config->setArrayIndex(static_cast<int>(p));
855 WriteSetting(QStringLiteral("name"),
856 QString::fromStdString(Settings::values.touch_from_button_maps[p].name),
857 QStringLiteral("default"));
858 qt_config->beginWriteArray(QStringLiteral("entries"));
859 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
860 ++q) {
861 qt_config->setArrayIndex(static_cast<int>(q));
862 WriteSetting(
863 QStringLiteral("bind"),
864 QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q]));
865 }
866 qt_config->endArray();
867 }
868 qt_config->endArray();
869}
870
871void Config::SaveHidbusValues() {
872 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
873 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
874 WriteSetting(QStringLiteral("ring_controller"),
875 QString::fromStdString(Settings::values.ringcon_analogs),
876 QString::fromStdString(default_param));
877}
878
879void Config::SaveValues() {
880 if (global) {
881 SaveDataStorageValues();
882 SaveDebuggingValues();
883 SaveDisabledAddOnValues();
884 SaveNetworkValues();
885 SaveUIValues();
886 SaveWebServiceValues();
887 SaveMiscellaneousValues();
888 }
889 SaveControlValues();
890 SaveCoreValues();
891 SaveCpuValues();
892 SaveRendererValues();
893 SaveAudioValues();
894 SaveSystemValues();
895
896 qt_config->sync();
897}
898
899void Config::SaveAudioValues() {
900 qt_config->beginGroup(QStringLiteral("Audio"));
901
902 WriteCategory(Settings::Category::Audio);
903
904 qt_config->endGroup();
905}
906
907void Config::SaveControlValues() {
908 qt_config->beginGroup(QStringLiteral("Controls"));
909
910 WriteCategory(Settings::Category::Controls);
911
912 Settings::values.players.SetGlobal(!IsCustomConfig());
913 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
914 SavePlayerValue(p);
915 }
916 if (IsCustomConfig()) {
917 qt_config->endGroup();
918 return;
919 }
920 SaveDebugValues();
921 SaveTouchscreenValues();
922 SaveMotionTouchValues();
923 SaveHidbusValues();
924
925 qt_config->endGroup();
926}
927
928void Config::SaveCoreValues() {
929 qt_config->beginGroup(QStringLiteral("Core"));
930
931 WriteCategory(Settings::Category::Core);
932
933 qt_config->endGroup();
934}
935
936void Config::SaveDataStorageValues() {
937 qt_config->beginGroup(QStringLiteral("Data Storage"));
938
939 WriteSetting(QStringLiteral("nand_directory"),
940 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)),
941 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
942 WriteSetting(QStringLiteral("sdmc_directory"),
943 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)),
944 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
945 WriteSetting(QStringLiteral("load_directory"),
946 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)),
947 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
948 WriteSetting(QStringLiteral("dump_directory"),
949 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
950 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
951 WriteSetting(QStringLiteral("tas_directory"),
952 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
953 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
954
955 WriteCategory(Settings::Category::DataStorage);
956
957 qt_config->endGroup();
958}
959
960void Config::SaveDebuggingValues() {
961 qt_config->beginGroup(QStringLiteral("Debugging"));
962
963 // Intentionally not using the QT default setting as this is intended to be changed in the ini
964 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
965
966 WriteCategory(Settings::Category::Debugging);
967 WriteCategory(Settings::Category::DebuggingGraphics);
968
969 qt_config->endGroup();
970}
971
972void Config::SaveNetworkValues() {
973 qt_config->beginGroup(QStringLiteral("Services"));
974
975 WriteCategory(Settings::Category::Network);
976
977 qt_config->endGroup();
978}
979
980void Config::SaveDisabledAddOnValues() {
981 qt_config->beginWriteArray(QStringLiteral("DisabledAddOns"));
982
983 int i = 0;
984 for (const auto& elem : Settings::values.disabled_addons) {
985 qt_config->setArrayIndex(i);
986 WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0);
987 qt_config->beginWriteArray(QStringLiteral("disabled"));
988 for (std::size_t j = 0; j < elem.second.size(); ++j) {
989 qt_config->setArrayIndex(static_cast<int>(j));
990 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
991 }
992 qt_config->endArray();
993 ++i;
994 }
995
996 qt_config->endArray();
997}
998
999void Config::SaveMiscellaneousValues() {
1000 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
1001
1002 WriteCategory(Settings::Category::Miscellaneous);
1003
1004 qt_config->endGroup();
1005}
1006
1007void Config::SavePathValues() {
1008 qt_config->beginGroup(QStringLiteral("Paths"));
1009
1010 WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
1011 WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
1012 qt_config->beginWriteArray(QStringLiteral("gamedirs"));
1013 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
1014 qt_config->setArrayIndex(i);
1015 const auto& game_dir = UISettings::values.game_dirs[i];
1016 WriteSetting(QStringLiteral("path"), game_dir.path);
1017 WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false);
1018 WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true);
1019 }
1020 qt_config->endArray();
1021 WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
1022 WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
1023
1024 qt_config->endGroup();
1025}
1026
1027void Config::SaveCpuValues() {
1028 qt_config->beginGroup(QStringLiteral("Cpu"));
1029
1030 WriteCategory(Settings::Category::Cpu);
1031 WriteCategory(Settings::Category::CpuDebug);
1032 WriteCategory(Settings::Category::CpuUnsafe);
1033
1034 qt_config->endGroup();
1035}
1036
1037void Config::SaveRendererValues() {
1038 qt_config->beginGroup(QStringLiteral("Renderer"));
1039
1040 WriteCategory(Settings::Category::Renderer);
1041 WriteCategory(Settings::Category::RendererAdvanced);
1042 WriteCategory(Settings::Category::RendererDebug);
1043
1044 qt_config->endGroup();
1045}
1046
1047void Config::SaveScreenshotValues() {
1048 qt_config->beginGroup(QStringLiteral("Screenshots"));
1049
1050 WriteSetting(QStringLiteral("screenshot_path"),
1051 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
1052 WriteCategory(Settings::Category::Screenshots);
1053
1054 qt_config->endGroup();
1055}
1056
1057void Config::SaveShortcutValues() {
1058 qt_config->beginGroup(QStringLiteral("Shortcuts"));
1059
1060 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
1061 // However, their ordering must also be the same.
1062 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
1063 const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
1064 const auto& default_hotkey = default_hotkeys[i].shortcut;
1065
1066 qt_config->beginGroup(group);
1067 qt_config->beginGroup(name);
1068 WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq);
1069 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
1070 default_hotkey.controller_keyseq);
1071 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
1072 WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
1073 qt_config->endGroup();
1074 qt_config->endGroup();
1075 }
1076
1077 qt_config->endGroup();
1078}
1079
1080void Config::SaveSystemValues() {
1081 qt_config->beginGroup(QStringLiteral("System"));
1082
1083 WriteCategory(Settings::Category::System);
1084 WriteCategory(Settings::Category::SystemAudio);
1085
1086 qt_config->endGroup();
1087}
1088
1089void Config::SaveUIValues() {
1090 qt_config->beginGroup(QStringLiteral("UI"));
1091
1092 WriteCategory(Settings::Category::Ui);
1093 WriteCategory(Settings::Category::UiGeneral);
1094
1095 WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
1096 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second));
1097
1098 SaveUIGamelistValues();
1099 SaveUILayoutValues();
1100 SavePathValues();
1101 SaveScreenshotValues();
1102 SaveShortcutValues();
1103 SaveMultiplayerValues();
1104
1105 qt_config->endGroup();
1106}
1107
1108void Config::SaveUIGamelistValues() {
1109 qt_config->beginGroup(QStringLiteral("UIGameList"));
1110
1111 WriteCategory(Settings::Category::UiGameList);
1112
1113 qt_config->beginWriteArray(QStringLiteral("favorites"));
1114 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
1115 qt_config->setArrayIndex(i);
1116 WriteSetting(QStringLiteral("program_id"),
1117 QVariant::fromValue(UISettings::values.favorited_ids[i]));
1118 }
1119 qt_config->endArray();
1120
1121 qt_config->endGroup();
1122}
1123
1124void Config::SaveUILayoutValues() {
1125 qt_config->beginGroup(QStringLiteral("UILayout"));
1126
1127 WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry);
1128 WriteSetting(QStringLiteral("state"), UISettings::values.state);
1129 WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry);
1130 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
1131 WriteSetting(QStringLiteral("microProfileDialogGeometry"),
1132 UISettings::values.microprofile_geometry);
1133
1134 WriteCategory(Settings::Category::UiLayout);
1135
1136 qt_config->endGroup();
1137}
1138
1139void Config::SaveWebServiceValues() {
1140 qt_config->beginGroup(QStringLiteral("WebService"));
1141
1142 WriteCategory(Settings::Category::WebService);
1143
1144 qt_config->endGroup();
1145}
1146
1147void Config::SaveMultiplayerValues() {
1148 qt_config->beginGroup(QStringLiteral("Multiplayer"));
1149
1150 WriteCategory(Settings::Category::Multiplayer);
1151
1152 // Write ban list
1153 qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
1154 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
1155 qt_config->setArrayIndex(static_cast<int>(i));
1156 WriteSetting(QStringLiteral("username"),
1157 QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i]));
1158 }
1159 qt_config->endArray();
1160 qt_config->beginWriteArray(QStringLiteral("ip_ban_list"));
1161 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
1162 qt_config->setArrayIndex(static_cast<int>(i));
1163 WriteSetting(QStringLiteral("ip"),
1164 QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i]));
1165 }
1166 qt_config->endArray();
1167
1168 qt_config->endGroup();
1169}
1170
1171QVariant Config::ReadSetting(const QString& name) const {
1172 return qt_config->value(name);
1173}
1174
1175QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
1176 QVariant result;
1177 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
1178 result = default_value;
1179 } else {
1180 result = qt_config->value(name, default_value);
1181 }
1182 return result;
1183}
1184
1185void Config::WriteSetting(const QString& name, const QVariant& value) {
1186 qt_config->setValue(name, value);
1187}
1188
1189void Config::WriteSetting(const QString& name, const QVariant& value,
1190 const QVariant& default_value) {
1191 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1192 qt_config->setValue(name, value);
1193}
1194
1195void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
1196 bool use_global) {
1197 if (!global) {
1198 qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
1199 }
1200 if (global || !use_global) {
1201 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1202 qt_config->setValue(name, value);
1203 }
1204}
1205
1206void Config::Reload() {
1207 ReadValues();
1208 // To apply default value changes
1209 SaveValues();
1210}
1211
1212void Config::Save() {
1213 SaveValues();
1214}
1215
1216void Config::ReadControlPlayerValue(std::size_t player_index) {
1217 qt_config->beginGroup(QStringLiteral("Controls"));
1218 ReadPlayerValue(player_index);
1219 qt_config->endGroup();
1220}
1221
1222void Config::SaveControlPlayerValue(std::size_t player_index) {
1223 qt_config->beginGroup(QStringLiteral("Controls"));
1224 SavePlayerValue(player_index);
1225 qt_config->endGroup();
1226}
1227
1228void Config::ClearControlPlayerValues() {
1229 qt_config->beginGroup(QStringLiteral("Controls"));
1230 // If key is an empty string, all keys in the current group() are removed.
1231 qt_config->remove(QString{});
1232 qt_config->endGroup();
1233}
1234
1235const std::string& Config::GetConfigFilePath() const {
1236 return qt_config_loc;
1237}
1238
1239static auto FindRelevantList(Settings::Category category) {
1240 auto& map = Settings::values.linkage.by_category;
1241 if (map.contains(category)) {
1242 return Settings::values.linkage.by_category[category];
1243 }
1244 return UISettings::values.linkage.by_category[category];
1245}
1246
1247void Config::ReadCategory(Settings::Category category) {
1248 const auto& settings = FindRelevantList(category);
1249 std::for_each(settings.begin(), settings.end(),
1250 [&](const auto& setting) { ReadSettingGeneric(setting); });
1251}
1252
1253void Config::WriteCategory(Settings::Category category) {
1254 const auto& settings = FindRelevantList(category);
1255 std::for_each(settings.begin(), settings.end(),
1256 [&](const auto& setting) { WriteSettingGeneric(setting); });
1257}
1258
1259void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
1260 if (!setting->Save() || (!setting->Switchable() && !global)) {
1261 return;
1262 }
1263 const QString name = QString::fromStdString(setting->GetLabel());
1264 const auto default_value =
1265 QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString()));
1266
1267 bool use_global = true;
1268 if (setting->Switchable() && !global) {
1269 use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>();
1270 setting->SetGlobal(use_global);
1271 }
1272
1273 if (global || !use_global) {
1274 const bool is_default =
1275 qt_config->value(name + QStringLiteral("/default"), true).value<bool>();
1276 if (!is_default) {
1277 setting->LoadString(
1278 qt_config->value(name, default_value).value<QString>().toStdString());
1279 } else {
1280 // Empty string resets the Setting to default
1281 setting->LoadString("");
1282 }
1283 }
1284}
1285
1286void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const {
1287 if (!setting->Save()) {
1288 return;
1289 }
1290 const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString()));
1291 const QVariant default_value =
1292 QVariant::fromValue(QString::fromStdString(setting->DefaultToString()));
1293 const QString label = QString::fromStdString(setting->GetLabel());
1294 if (setting->Switchable()) {
1295 if (!global) {
1296 qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal());
1297 }
1298 if (global || !setting->UsingGlobal()) {
1299 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1300 qt_config->setValue(label, value);
1301 }
1302 } else if (global) {
1303 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1304 qt_config->setValue(label, value);
1305 }
1306}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
deleted file mode 100644
index 74ec4f771..000000000
--- a/src/yuzu/configuration/config.h
+++ /dev/null
@@ -1,179 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <string>
9#include <QMetaType>
10#include <QVariant>
11#include "common/settings.h"
12#include "common/settings_enums.h"
13#include "yuzu/uisettings.h"
14
15class QSettings;
16
17namespace Core {
18class System;
19}
20
21class Config {
22public:
23 enum class ConfigType {
24 GlobalConfig,
25 PerGameConfig,
26 InputProfile,
27 };
28
29 explicit Config(const std::string& config_name = "qt-config",
30 ConfigType config_type = ConfigType::GlobalConfig);
31 ~Config();
32
33 void Reload();
34 void Save();
35
36 void ReadControlPlayerValue(std::size_t player_index);
37 void SaveControlPlayerValue(std::size_t player_index);
38 void ClearControlPlayerValues();
39
40 const std::string& GetConfigFilePath() const;
41
42 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
43 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
44 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
45 static const std::array<int, 2> default_stick_mod;
46 static const std::array<int, 2> default_ringcon_analogs;
47 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
48 default_mouse_buttons;
49 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
50 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
51 static const std::array<UISettings::Shortcut, 23> default_hotkeys;
52
53 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
54 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
55 static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
56 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
57 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
58 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
59
60 static constexpr UISettings::Theme default_theme{
61#ifdef _WIN32
62 UISettings::Theme::DarkColorful
63#else
64 UISettings::Theme::DefaultColorful
65#endif
66 };
67
68private:
69 void Initialize(const std::string& config_name);
70 bool IsCustomConfig();
71
72 void ReadValues();
73 void ReadPlayerValue(std::size_t player_index);
74 void ReadDebugValues();
75 void ReadKeyboardValues();
76 void ReadMouseValues();
77 void ReadTouchscreenValues();
78 void ReadMotionTouchValues();
79 void ReadHidbusValues();
80 void ReadIrCameraValues();
81
82 // Read functions bases off the respective config section names.
83 void ReadAudioValues();
84 void ReadControlValues();
85 void ReadCoreValues();
86 void ReadDataStorageValues();
87 void ReadDebuggingValues();
88 void ReadServiceValues();
89 void ReadDisabledAddOnValues();
90 void ReadMiscellaneousValues();
91 void ReadPathValues();
92 void ReadCpuValues();
93 void ReadRendererValues();
94 void ReadScreenshotValues();
95 void ReadShortcutValues();
96 void ReadSystemValues();
97 void ReadUIValues();
98 void ReadUIGamelistValues();
99 void ReadUILayoutValues();
100 void ReadWebServiceValues();
101 void ReadMultiplayerValues();
102 void ReadNetworkValues();
103
104 void SaveValues();
105 void SavePlayerValue(std::size_t player_index);
106 void SaveDebugValues();
107 void SaveMouseValues();
108 void SaveTouchscreenValues();
109 void SaveMotionTouchValues();
110 void SaveHidbusValues();
111 void SaveIrCameraValues();
112
113 // Save functions based off the respective config section names.
114 void SaveAudioValues();
115 void SaveControlValues();
116 void SaveCoreValues();
117 void SaveDataStorageValues();
118 void SaveDebuggingValues();
119 void SaveNetworkValues();
120 void SaveDisabledAddOnValues();
121 void SaveMiscellaneousValues();
122 void SavePathValues();
123 void SaveCpuValues();
124 void SaveRendererValues();
125 void SaveScreenshotValues();
126 void SaveShortcutValues();
127 void SaveSystemValues();
128 void SaveUIValues();
129 void SaveUIGamelistValues();
130 void SaveUILayoutValues();
131 void SaveWebServiceValues();
132 void SaveMultiplayerValues();
133
134 /**
135 * Reads a setting from the qt_config.
136 *
137 * @param name The setting's identifier
138 * @param default_value The value to use when the setting is not already present in the config
139 */
140 QVariant ReadSetting(const QString& name) const;
141 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
142
143 /**
144 * Writes a setting to the qt_config.
145 *
146 * @param name The setting's idetentifier
147 * @param value Value of the setting
148 * @param default_value Default of the setting if not present in qt_config
149 * @param use_global Specifies if the custom or global config should be in use, for custom
150 * configs
151 */
152 void WriteSetting(const QString& name, const QVariant& value);
153 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
154 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
155 bool use_global);
156
157 void ReadCategory(Settings::Category category);
158 void WriteCategory(Settings::Category category);
159 void ReadSettingGeneric(Settings::BasicSetting* const setting);
160 void WriteSettingGeneric(Settings::BasicSetting* const setting) const;
161
162 const ConfigType type;
163 std::unique_ptr<QSettings> qt_config;
164 std::string qt_config_loc;
165 const bool global;
166};
167
168// These metatype declarations cannot be in common/settings.h because core is devoid of QT
169Q_DECLARE_METATYPE(Settings::CpuAccuracy);
170Q_DECLARE_METATYPE(Settings::GpuAccuracy);
171Q_DECLARE_METATYPE(Settings::FullscreenMode);
172Q_DECLARE_METATYPE(Settings::NvdecEmulation);
173Q_DECLARE_METATYPE(Settings::ResolutionSetup);
174Q_DECLARE_METATYPE(Settings::ScalingFilter);
175Q_DECLARE_METATYPE(Settings::AntiAliasing);
176Q_DECLARE_METATYPE(Settings::RendererBackend);
177Q_DECLARE_METATYPE(Settings::ShaderBackend);
178Q_DECLARE_METATYPE(Settings::AstcRecompression);
179Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 81dd51ad3..9b6ef47a7 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
38 38
39 std::map<u32, QWidget*> hold; 39 std::map<u32, QWidget*> hold;
40 40
41 auto push = [&](Settings::Category category) { 41 auto push_settings = [&](Settings::Category category) {
42 for (auto* setting : Settings::values.linkage.by_category[category]) { 42 for (auto* setting : Settings::values.linkage.by_category[category]) {
43 settings.push_back(setting); 43 settings.push_back(setting);
44 } 44 }
45 };
46
47 auto push_ui_settings = [&](Settings::Category category) {
45 for (auto* setting : UISettings::values.linkage.by_category[category]) { 48 for (auto* setting : UISettings::values.linkage.by_category[category]) {
46 settings.push_back(setting); 49 settings.push_back(setting);
47 } 50 }
48 }; 51 };
49 52
50 push(Settings::Category::Audio); 53 push_settings(Settings::Category::Audio);
51 push(Settings::Category::SystemAudio); 54 push_settings(Settings::Category::SystemAudio);
55 push_ui_settings(Settings::Category::UiAudio);
52 56
53 for (auto* setting : settings) { 57 for (auto* setting : settings) {
54 auto* widget = builder.BuildWidget(setting, apply_funcs); 58 auto* widget = builder.BuildWidget(setting, apply_funcs);
diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp
index d95e96696..3368f53f3 100644
--- a/src/yuzu/configuration/configure_camera.cpp
+++ b/src/yuzu/configuration/configure_camera.cpp
@@ -10,10 +10,10 @@
10#include <QStandardItemModel> 10#include <QStandardItemModel>
11#include <QTimer> 11#include <QTimer>
12 12
13#include "common/settings.h"
13#include "input_common/drivers/camera.h" 14#include "input_common/drivers/camera.h"
14#include "input_common/main.h" 15#include "input_common/main.h"
15#include "ui_configure_camera.h" 16#include "ui_configure_camera.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_camera.h" 17#include "yuzu/configuration/configure_camera.h"
18 18
19ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) 19ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h
index 9a90512b3..3d822da7b 100644
--- a/src/yuzu/configuration/configure_camera.h
+++ b/src/yuzu/configuration/configure_camera.h
@@ -1,4 +1,4 @@
1// Text : Copyright 2022 yuzu Emulator Project 1// Text : Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index ef421c754..1010038b7 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
51 ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); 51 ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
52 ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); 52 ui->enable_renderdoc_hotkey->setEnabled(runtime_lock);
53 ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); 53 ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue());
54 ui->disable_buffer_reorder->setEnabled(runtime_lock);
55 ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue());
54 ui->enable_graphics_debugging->setEnabled(runtime_lock); 56 ui->enable_graphics_debugging->setEnabled(runtime_lock);
55 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 57 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
56 ui->enable_shader_feedback->setEnabled(runtime_lock); 58 ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() {
96 Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); 98 Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
97 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 99 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
98 Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); 100 Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked();
101 Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked();
99 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); 102 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
100 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 103 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
101 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 104 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 76fe98924..22b51f39c 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -271,19 +271,6 @@
271 </widget> 271 </widget>
272 </item> 272 </item>
273 <item row="8" column="0"> 273 <item row="8" column="0">
274 <widget class="QCheckBox" name="disable_macro_hle">
275 <property name="enabled">
276 <bool>true</bool>
277 </property>
278 <property name="toolTip">
279 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
280 </property>
281 <property name="text">
282 <string>Disable Macro HLE</string>
283 </property>
284 </widget>
285 </item>
286 <item row="7" column="0">
287 <widget class="QCheckBox" name="dump_macros"> 274 <widget class="QCheckBox" name="dump_macros">
288 <property name="enabled"> 275 <property name="enabled">
289 <bool>true</bool> 276 <bool>true</bool>
@@ -306,17 +293,27 @@
306 </property> 293 </property>
307 </widget> 294 </widget>
308 </item> 295 </item>
309 <item row="2" column="0"> 296 <item row="6" column="0">
310 <widget class="QCheckBox" name="enable_shader_feedback"> 297 <widget class="QCheckBox" name="dump_shaders">
298 <property name="enabled">
299 <bool>true</bool>
300 </property>
311 <property name="toolTip"> 301 <property name="toolTip">
312 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> 302 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
313 </property> 303 </property>
314 <property name="text"> 304 <property name="text">
315 <string>Enable Shader Feedback</string> 305 <string>Dump Game Shaders</string>
316 </property> 306 </property>
317 </widget> 307 </widget>
318 </item> 308 </item>
319 <item row="6" column="0"> 309 <item row="1" column="0">
310 <widget class="QCheckBox" name="enable_renderdoc_hotkey">
311 <property name="text">
312 <string>Enable Renderdoc Hotkey</string>
313 </property>
314 </widget>
315 </item>
316 <item row="7" column="0">
320 <widget class="QCheckBox" name="disable_macro_jit"> 317 <widget class="QCheckBox" name="disable_macro_jit">
321 <property name="enabled"> 318 <property name="enabled">
322 <bool>true</bool> 319 <bool>true</bool>
@@ -330,20 +327,17 @@
330 </widget> 327 </widget>
331 </item> 328 </item>
332 <item row="9" column="0"> 329 <item row="9" column="0">
333 <spacer name="verticalSpacer_5"> 330 <widget class="QCheckBox" name="disable_macro_hle">
334 <property name="orientation"> 331 <property name="enabled">
335 <enum>Qt::Vertical</enum> 332 <bool>true</bool>
336 </property> 333 </property>
337 <property name="sizeType"> 334 <property name="toolTip">
338 <enum>QSizePolicy::Preferred</enum> 335 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
339 </property> 336 </property>
340 <property name="sizeHint" stdset="0"> 337 <property name="text">
341 <size> 338 <string>Disable Macro HLE</string>
342 <width>20</width>
343 <height>0</height>
344 </size>
345 </property> 339 </property>
346 </spacer> 340 </widget>
347 </item> 341 </item>
348 <item row="0" column="0"> 342 <item row="0" column="0">
349 <widget class="QCheckBox" name="enable_graphics_debugging"> 343 <widget class="QCheckBox" name="enable_graphics_debugging">
@@ -358,23 +352,39 @@
358 </property> 352 </property>
359 </widget> 353 </widget>
360 </item> 354 </item>
361 <item row="5" column="0"> 355 <item row="10" column="0">
362 <widget class="QCheckBox" name="dump_shaders"> 356 <spacer name="verticalSpacer_5">
363 <property name="enabled"> 357 <property name="orientation">
364 <bool>true</bool> 358 <enum>Qt::Vertical</enum>
359 </property>
360 <property name="sizeType">
361 <enum>QSizePolicy::Preferred</enum>
365 </property> 362 </property>
363 <property name="sizeHint" stdset="0">
364 <size>
365 <width>20</width>
366 <height>0</height>
367 </size>
368 </property>
369 </spacer>
370 </item>
371 <item row="2" column="0">
372 <widget class="QCheckBox" name="enable_shader_feedback">
366 <property name="toolTip"> 373 <property name="toolTip">
367 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> 374 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
368 </property> 375 </property>
369 <property name="text"> 376 <property name="text">
370 <string>Dump Game Shaders</string> 377 <string>Enable Shader Feedback</string>
371 </property> 378 </property>
372 </widget> 379 </widget>
373 </item> 380 </item>
374 <item row="1" column="0"> 381 <item row="5" column="0">
375 <widget class="QCheckBox" name="enable_renderdoc_hotkey"> 382 <widget class="QCheckBox" name="disable_buffer_reorder">
383 <property name="toolTip">
384 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
385 </property>
376 <property name="text"> 386 <property name="text">
377 <string>Enable Renderdoc Hotkey</string> 387 <string>Disable Buffer Reorder</string>
378 </property> 388 </property>
379 </widget> 389 </widget>
380 </item> 390 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 0ad95cc02..aab54a1cc 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,7 +8,6 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "ui_configure.h" 9#include "ui_configure.h"
10#include "vk_device_info.h" 10#include "vk_device_info.h"
11#include "yuzu/configuration/config.h"
12#include "yuzu/configuration/configure_audio.h" 11#include "yuzu/configuration/configure_audio.h"
13#include "yuzu/configuration/configure_cpu.h" 12#include "yuzu/configuration/configure_cpu.h"
14#include "yuzu/configuration/configure_debug_tab.h" 13#include "yuzu/configuration/configure_debug_tab.h"
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 68e21cd84..76fc33e49 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -9,10 +9,11 @@
9#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
11 11
12#include "frontend_common/config.h"
12#include "ui_configure_hotkeys.h" 13#include "ui_configure_hotkeys.h"
13#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configure_hotkeys.h" 14#include "yuzu/configuration/configure_hotkeys.h"
15#include "yuzu/hotkeys.h" 15#include "yuzu/hotkeys.h"
16#include "yuzu/uisettings.h"
16#include "yuzu/util/sequence_dialog/sequence_dialog.h" 17#include "yuzu/util/sequence_dialog/sequence_dialog.h"
17 18
18constexpr int name_column = 0; 19constexpr int name_column = 0;
@@ -62,18 +63,21 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
62 63
63void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { 64void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
64 for (const auto& group : registry.hotkey_groups) { 65 for (const auto& group : registry.hotkey_groups) {
66 QString parent_item_data = QString::fromStdString(group.first);
65 auto* parent_item = 67 auto* parent_item =
66 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); 68 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(parent_item_data)));
67 parent_item->setEditable(false); 69 parent_item->setEditable(false);
68 parent_item->setData(group.first); 70 parent_item->setData(parent_item_data);
69 for (const auto& hotkey : group.second) { 71 for (const auto& hotkey : group.second) {
70 auto* action = 72 QString hotkey_action_data = QString::fromStdString(hotkey.first);
71 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); 73 auto* action = new QStandardItem(
74 QCoreApplication::translate("Hotkeys", qPrintable(hotkey_action_data)));
72 auto* keyseq = 75 auto* keyseq =
73 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); 76 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
74 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); 77 auto* controller_keyseq =
78 new QStandardItem(QString::fromStdString(hotkey.second.controller_keyseq));
75 action->setEditable(false); 79 action->setEditable(false);
76 action->setData(hotkey.first); 80 action->setData(hotkey_action_data);
77 keyseq->setEditable(false); 81 keyseq->setEditable(false);
78 controller_keyseq->setEditable(false); 82 controller_keyseq->setEditable(false);
79 parent_item->appendRow({action, keyseq, controller_keyseq}); 83 parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -301,13 +305,13 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
301 const QStandardItem* controller_keyseq = 305 const QStandardItem* controller_keyseq =
302 parent->child(key_column_id, controller_column); 306 parent->child(key_column_id, controller_column);
303 for (auto& [group, sub_actions] : registry.hotkey_groups) { 307 for (auto& [group, sub_actions] : registry.hotkey_groups) {
304 if (group != parent->data()) 308 if (group != parent->data().toString().toStdString())
305 continue; 309 continue;
306 for (auto& [action_name, hotkey] : sub_actions) { 310 for (auto& [action_name, hotkey] : sub_actions) {
307 if (action_name != action->data()) 311 if (action_name != action->data().toString().toStdString())
308 continue; 312 continue;
309 hotkey.keyseq = QKeySequence(keyseq->text()); 313 hotkey.keyseq = QKeySequence(keyseq->text());
310 hotkey.controller_keyseq = controller_keyseq->text(); 314 hotkey.controller_keyseq = controller_keyseq->text().toStdString();
311 } 315 }
312 } 316 }
313 } 317 }
@@ -319,7 +323,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
319void ConfigureHotkeys::RestoreDefaults() { 323void ConfigureHotkeys::RestoreDefaults() {
320 for (int r = 0; r < model->rowCount(); ++r) { 324 for (int r = 0; r < model->rowCount(); ++r) {
321 const QStandardItem* parent = model->item(r, 0); 325 const QStandardItem* parent = model->item(r, 0);
322 const int hotkey_size = static_cast<int>(Config::default_hotkeys.size()); 326 const int hotkey_size = static_cast<int>(UISettings::default_hotkeys.size());
323 327
324 if (hotkey_size != parent->rowCount()) { 328 if (hotkey_size != parent->rowCount()) {
325 QMessageBox::warning(this, tr("Invalid hotkey settings"), 329 QMessageBox::warning(this, tr("Invalid hotkey settings"),
@@ -330,10 +334,11 @@ void ConfigureHotkeys::RestoreDefaults() {
330 for (int r2 = 0; r2 < parent->rowCount(); ++r2) { 334 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
331 model->item(r, 0) 335 model->item(r, 0)
332 ->child(r2, hotkey_column) 336 ->child(r2, hotkey_column)
333 ->setText(Config::default_hotkeys[r2].shortcut.keyseq); 337 ->setText(QString::fromStdString(UISettings::default_hotkeys[r2].shortcut.keyseq));
334 model->item(r, 0) 338 model->item(r, 0)
335 ->child(r2, controller_column) 339 ->child(r2, controller_column)
336 ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); 340 ->setText(QString::fromStdString(
341 UISettings::default_hotkeys[r2].shortcut.controller_keyseq));
337 } 342 }
338 } 343 }
339} 344}
@@ -379,7 +384,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
379 384
380void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { 385void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
381 const QString& default_key_sequence = 386 const QString& default_key_sequence =
382 Config::default_hotkeys[index.row()].shortcut.controller_keyseq; 387 QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.controller_keyseq);
383 const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); 388 const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence);
384 389
385 if (key_sequence_used && default_key_sequence != model->data(index).toString()) { 390 if (key_sequence_used && default_key_sequence != model->data(index).toString()) {
@@ -393,7 +398,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
393 398
394void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { 399void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
395 const QKeySequence& default_key_sequence = QKeySequence::fromString( 400 const QKeySequence& default_key_sequence = QKeySequence::fromString(
396 Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); 401 QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.keyseq),
402 QKeySequence::NativeText);
397 const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); 403 const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
398 404
399 if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { 405 if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) {
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 3dcad2701..02e23cce6 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -152,7 +152,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
152 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, 152 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
153 [this](bool is_handheld) { UpdateDockedState(is_handheld); }); 153 [this](bool is_handheld) { UpdateDockedState(is_handheld); });
154 154
155 advanced = new ConfigureInputAdvanced(this); 155 advanced = new ConfigureInputAdvanced(hid_core, this);
156 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); 156 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
157 ui->tabAdvanced->layout()->addWidget(advanced); 157 ui->tabAdvanced->layout()->addWidget(advanced);
158 158
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 136cd3a0a..beb503dae 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 3cfd5d439..441cea3f6 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -4,11 +4,13 @@
4#include <QColorDialog> 4#include <QColorDialog>
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hid/emulated_controller.h"
8#include "core/hid/hid_core.h"
7#include "ui_configure_input_advanced.h" 9#include "ui_configure_input_advanced.h"
8#include "yuzu/configuration/configure_input_advanced.h" 10#include "yuzu/configuration/configure_input_advanced.h"
9 11
10ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) 12ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent)
11 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) { 13 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()), hid_core{hid_core_} {
12 ui->setupUi(this); 14 ui->setupUi(this);
13 15
14 controllers_color_buttons = {{ 16 controllers_color_buttons = {{
@@ -123,6 +125,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
123 player.button_color_left = colors[1]; 125 player.button_color_left = colors[1];
124 player.body_color_right = colors[2]; 126 player.body_color_right = colors[2];
125 player.button_color_right = colors[3]; 127 player.button_color_right = colors[3];
128
129 hid_core.GetEmulatedControllerByIndex(player_idx)->ReloadColorsFromSettings();
126 } 130 }
127 131
128 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); 132 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index fc1230284..41f822c4a 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -14,11 +14,15 @@ namespace Ui {
14class ConfigureInputAdvanced; 14class ConfigureInputAdvanced;
15} 15}
16 16
17namespace Core::HID {
18class HIDCore;
19} // namespace Core::HID
20
17class ConfigureInputAdvanced : public QWidget { 21class ConfigureInputAdvanced : public QWidget {
18 Q_OBJECT 22 Q_OBJECT
19 23
20public: 24public:
21 explicit ConfigureInputAdvanced(QWidget* parent = nullptr); 25 explicit ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr);
22 ~ConfigureInputAdvanced() override; 26 ~ConfigureInputAdvanced() override;
23 27
24 void ApplyConfiguration(); 28 void ApplyConfiguration();
@@ -44,4 +48,6 @@ private:
44 48
45 std::array<std::array<QColor, 4>, 8> controllers_colors; 49 std::array<std::array<QColor, 4>, 8> controllers_colors;
46 std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons; 50 std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
51
52 Core::HID::HIDCore& hid_core;
47}; 53};
diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp
index 78e65d468..8d9f65a05 100644
--- a/src/yuzu/configuration/configure_input_per_game.cpp
+++ b/src/yuzu/configuration/configure_input_per_game.cpp
@@ -5,12 +5,12 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "frontend_common/config.h"
8#include "ui_configure_input_per_game.h" 9#include "ui_configure_input_per_game.h"
9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_input_per_game.h" 10#include "yuzu/configuration/configure_input_per_game.h"
11#include "yuzu/configuration/input_profiles.h" 11#include "yuzu/configuration/input_profiles.h"
12 12
13ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, 13ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, QtConfig* config_,
14 QWidget* parent) 14 QWidget* parent)
15 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()), 15 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()),
16 profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} { 16 profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} {
@@ -110,6 +110,6 @@ void ConfigureInputPerGame::SaveConfiguration() {
110 // Clear all controls from the config in case the user reverted back to globals 110 // Clear all controls from the config in case the user reverted back to globals
111 config->ClearControlPlayerValues(); 111 config->ClearControlPlayerValues();
112 for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { 112 for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
113 config->SaveControlPlayerValue(index); 113 config->SaveQtControlPlayerValues(index);
114 } 114 }
115} 115}
diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h
index 660faf574..4420e856c 100644
--- a/src/yuzu/configuration/configure_input_per_game.h
+++ b/src/yuzu/configuration/configure_input_per_game.h
@@ -9,6 +9,7 @@
9 9
10#include "ui_configure_input_per_game.h" 10#include "ui_configure_input_per_game.h"
11#include "yuzu/configuration/input_profiles.h" 11#include "yuzu/configuration/input_profiles.h"
12#include "yuzu/configuration/qt_config.h"
12 13
13class QComboBox; 14class QComboBox;
14 15
@@ -22,7 +23,7 @@ class ConfigureInputPerGame : public QWidget {
22 Q_OBJECT 23 Q_OBJECT
23 24
24public: 25public:
25 explicit ConfigureInputPerGame(Core::System& system_, Config* config_, 26 explicit ConfigureInputPerGame(Core::System& system_, QtConfig* config_,
26 QWidget* parent = nullptr); 27 QWidget* parent = nullptr);
27 28
28 /// Load and Save configurations to settings file. 29 /// Load and Save configurations to settings file.
@@ -41,5 +42,5 @@ private:
41 std::array<QComboBox*, 8> profile_comboboxes; 42 std::array<QComboBox*, 8> profile_comboboxes;
42 43
43 Core::System& system; 44 Core::System& system;
44 Config* config; 45 QtConfig* config;
45}; 46};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 576f5b571..0f7b3714e 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -12,15 +12,16 @@
12#include <QTimer> 12#include <QTimer>
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/param_package.h" 14#include "common/param_package.h"
15#include "configuration/qt_config.h"
15#include "core/hid/emulated_controller.h" 16#include "core/hid/emulated_controller.h"
16#include "core/hid/hid_core.h" 17#include "core/hid/hid_core.h"
17#include "core/hid/hid_types.h" 18#include "core/hid/hid_types.h"
19#include "frontend_common/config.h"
18#include "input_common/drivers/keyboard.h" 20#include "input_common/drivers/keyboard.h"
19#include "input_common/drivers/mouse.h" 21#include "input_common/drivers/mouse.h"
20#include "input_common/main.h" 22#include "input_common/main.h"
21#include "ui_configure_input_player.h" 23#include "ui_configure_input_player.h"
22#include "yuzu/bootmanager.h" 24#include "yuzu/bootmanager.h"
23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 25#include "yuzu/configuration/configure_input_player.h"
25#include "yuzu/configuration/configure_input_player_widget.h" 26#include "yuzu/configuration/configure_input_player_widget.h"
26#include "yuzu/configuration/configure_mouse_panning.h" 27#include "yuzu/configuration/configure_mouse_panning.h"
@@ -322,11 +323,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
322 setFocusPolicy(Qt::ClickFocus); 323 setFocusPolicy(Qt::ClickFocus);
323 324
324 button_map = { 325 button_map = {
325 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, 326 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
326 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, 327 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
327 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, 328 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
328 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, 329 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
329 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, 330 ui->buttonSLLeft, ui->buttonSRLeft, ui->buttonHome, ui->buttonScreenshot,
331 ui->buttonSLRight, ui->buttonSRRight,
330 }; 332 };
331 333
332 analog_map_buttons = {{ 334 analog_map_buttons = {{
@@ -1181,10 +1183,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1181 1183
1182 // List of all the widgets that will be hidden by any of the following layouts that need 1184 // List of all the widgets that will be hidden by any of the following layouts that need
1183 // "unhidden" after the controller type changes 1185 // "unhidden" after the controller type changes
1184 const std::array<QWidget*, 11> layout_show = { 1186 const std::array<QWidget*, 14> layout_show = {
1185 ui->buttonShoulderButtonsSLSR, 1187 ui->buttonShoulderButtonsSLSRLeft,
1188 ui->buttonShoulderButtonsSLSRRight,
1186 ui->horizontalSpacerShoulderButtonsWidget, 1189 ui->horizontalSpacerShoulderButtonsWidget,
1187 ui->horizontalSpacerShoulderButtonsWidget2, 1190 ui->horizontalSpacerShoulderButtonsWidget2,
1191 ui->horizontalSpacerShoulderButtonsWidget3,
1192 ui->horizontalSpacerShoulderButtonsWidget4,
1188 ui->buttonShoulderButtonsLeft, 1193 ui->buttonShoulderButtonsLeft,
1189 ui->buttonMiscButtonsMinusScreenshot, 1194 ui->buttonMiscButtonsMinusScreenshot,
1190 ui->bottomLeft, 1195 ui->bottomLeft,
@@ -1202,16 +1207,19 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1202 std::vector<QWidget*> layout_hidden; 1207 std::vector<QWidget*> layout_hidden;
1203 switch (layout) { 1208 switch (layout) {
1204 case Core::HID::NpadStyleIndex::ProController: 1209 case Core::HID::NpadStyleIndex::ProController:
1205 case Core::HID::NpadStyleIndex::JoyconDual:
1206 case Core::HID::NpadStyleIndex::Handheld: 1210 case Core::HID::NpadStyleIndex::Handheld:
1207 layout_hidden = { 1211 layout_hidden = {
1208 ui->buttonShoulderButtonsSLSR, 1212 ui->buttonShoulderButtonsSLSRLeft,
1213 ui->buttonShoulderButtonsSLSRRight,
1209 ui->horizontalSpacerShoulderButtonsWidget2, 1214 ui->horizontalSpacerShoulderButtonsWidget2,
1215 ui->horizontalSpacerShoulderButtonsWidget4,
1210 }; 1216 };
1211 break; 1217 break;
1212 case Core::HID::NpadStyleIndex::JoyconLeft: 1218 case Core::HID::NpadStyleIndex::JoyconLeft:
1213 layout_hidden = { 1219 layout_hidden = {
1220 ui->buttonShoulderButtonsSLSRRight,
1214 ui->horizontalSpacerShoulderButtonsWidget2, 1221 ui->horizontalSpacerShoulderButtonsWidget2,
1222 ui->horizontalSpacerShoulderButtonsWidget3,
1215 ui->buttonShoulderButtonsRight, 1223 ui->buttonShoulderButtonsRight,
1216 ui->buttonMiscButtonsPlusHome, 1224 ui->buttonMiscButtonsPlusHome,
1217 ui->bottomRight, 1225 ui->bottomRight,
@@ -1219,16 +1227,17 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1219 break; 1227 break;
1220 case Core::HID::NpadStyleIndex::JoyconRight: 1228 case Core::HID::NpadStyleIndex::JoyconRight:
1221 layout_hidden = { 1229 layout_hidden = {
1222 ui->horizontalSpacerShoulderButtonsWidget, 1230 ui->buttonShoulderButtonsSLSRLeft, ui->horizontalSpacerShoulderButtonsWidget,
1223 ui->buttonShoulderButtonsLeft, 1231 ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft,
1224 ui->buttonMiscButtonsMinusScreenshot, 1232 ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft,
1225 ui->bottomLeft,
1226 }; 1233 };
1227 break; 1234 break;
1228 case Core::HID::NpadStyleIndex::GameCube: 1235 case Core::HID::NpadStyleIndex::GameCube:
1229 layout_hidden = { 1236 layout_hidden = {
1230 ui->buttonShoulderButtonsSLSR, 1237 ui->buttonShoulderButtonsSLSRLeft,
1238 ui->buttonShoulderButtonsSLSRRight,
1231 ui->horizontalSpacerShoulderButtonsWidget2, 1239 ui->horizontalSpacerShoulderButtonsWidget2,
1240 ui->horizontalSpacerShoulderButtonsWidget4,
1232 ui->buttonMiscButtonsMinusGroup, 1241 ui->buttonMiscButtonsMinusGroup,
1233 ui->buttonMiscButtonsScreenshotGroup, 1242 ui->buttonMiscButtonsScreenshotGroup,
1234 }; 1243 };
@@ -1389,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1389 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 1398 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
1390 emulated_controller->SetButtonParam( 1399 emulated_controller->SetButtonParam(
1391 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( 1400 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1392 Config::default_buttons[button_id])}); 1401 QtConfig::default_buttons[button_id])});
1393 } 1402 }
1394 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 1403 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
1395 Common::ParamPackage analog_param{}; 1404 Common::ParamPackage analog_param{};
1396 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 1405 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
1397 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 1406 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
1398 Config::default_analogs[analog_id][sub_button_id])}; 1407 QtConfig::default_analogs[analog_id][sub_button_id])};
1399 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); 1408 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
1400 } 1409 }
1401 1410
1402 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( 1411 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
1403 Config::default_stick_mod[analog_id])); 1412 QtConfig::default_stick_mod[analog_id]));
1404 emulated_controller->SetStickParam(analog_id, analog_param); 1413 emulated_controller->SetStickParam(analog_id, analog_param);
1405 } 1414 }
1406 1415
1407 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 1416 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
1408 emulated_controller->SetMotionParam( 1417 emulated_controller->SetMotionParam(
1409 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( 1418 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1410 Config::default_motions[motion_id])}); 1419 QtConfig::default_motions[motion_id])});
1411 } 1420 }
1412 1421
1413 // If mouse is selected we want to override with mappings from the driver 1422 // If mouse is selected we want to override with mappings from the driver
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index d3255d2b4..fda09e925 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 611a79477..5518cccd1 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1208,6 +1208,159 @@
1208 <property name="spacing"> 1208 <property name="spacing">
1209 <number>3</number> 1209 <number>3</number>
1210 </property> 1210 </property>
1211 <item>
1212 <widget class="QWidget" name="buttonShoulderButtonsSLSRLeft" native="true">
1213 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRLeftVerticalLayout">
1214 <property name="spacing">
1215 <number>0</number>
1216 </property>
1217 <property name="leftMargin">
1218 <number>0</number>
1219 </property>
1220 <property name="topMargin">
1221 <number>0</number>
1222 </property>
1223 <property name="rightMargin">
1224 <number>0</number>
1225 </property>
1226 <property name="bottomMargin">
1227 <number>0</number>
1228 </property>
1229 <item alignment="Qt::AlignHCenter">
1230 <widget class="QGroupBox" name="buttonShoulderButtonsSLLeftGroup">
1231 <property name="title">
1232 <string>SL</string>
1233 </property>
1234 <property name="alignment">
1235 <set>Qt::AlignCenter</set>
1236 </property>
1237 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLLeftVerticalLayout">
1238 <property name="spacing">
1239 <number>3</number>
1240 </property>
1241 <property name="leftMargin">
1242 <number>3</number>
1243 </property>
1244 <property name="topMargin">
1245 <number>3</number>
1246 </property>
1247 <property name="rightMargin">
1248 <number>3</number>
1249 </property>
1250 <property name="bottomMargin">
1251 <number>3</number>
1252 </property>
1253 <item>
1254 <widget class="QPushButton" name="buttonSLLeft">
1255 <property name="minimumSize">
1256 <size>
1257 <width>68</width>
1258 <height>0</height>
1259 </size>
1260 </property>
1261 <property name="maximumSize">
1262 <size>
1263 <width>68</width>
1264 <height>16777215</height>
1265 </size>
1266 </property>
1267 <property name="styleSheet">
1268 <string notr="true">min-width: 68px;</string>
1269 </property>
1270 <property name="text">
1271 <string>SL</string>
1272 </property>
1273 </widget>
1274 </item>
1275 </layout>
1276 </widget>
1277 </item>
1278 <item alignment="Qt::AlignHCenter">
1279 <widget class="QGroupBox" name="buttonShoulderButtonsSRLeftGroup">
1280 <property name="title">
1281 <string>SR</string>
1282 </property>
1283 <property name="alignment">
1284 <set>Qt::AlignCenter</set>
1285 </property>
1286 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRLeftVerticalLayout">
1287 <property name="spacing">
1288 <number>3</number>
1289 </property>
1290 <property name="leftMargin">
1291 <number>3</number>
1292 </property>
1293 <property name="topMargin">
1294 <number>3</number>
1295 </property>
1296 <property name="rightMargin">
1297 <number>3</number>
1298 </property>
1299 <property name="bottomMargin">
1300 <number>3</number>
1301 </property>
1302 <item>
1303 <widget class="QPushButton" name="buttonSRLeft">
1304 <property name="minimumSize">
1305 <size>
1306 <width>68</width>
1307 <height>0</height>
1308 </size>
1309 </property>
1310 <property name="maximumSize">
1311 <size>
1312 <width>68</width>
1313 <height>16777215</height>
1314 </size>
1315 </property>
1316 <property name="styleSheet">
1317 <string notr="true">min-width: 68px;</string>
1318 </property>
1319 <property name="text">
1320 <string>SR</string>
1321 </property>
1322 </widget>
1323 </item>
1324 </layout>
1325 </widget>
1326 </item>
1327 </layout>
1328 </widget>
1329 </item>
1330 <item>
1331 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget4" native="true">
1332 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget4Layout">
1333 <property name="spacing">
1334 <number>0</number>
1335 </property>
1336 <property name="leftMargin">
1337 <number>0</number>
1338 </property>
1339 <property name="topMargin">
1340 <number>0</number>
1341 </property>
1342 <property name="rightMargin">
1343 <number>0</number>
1344 </property>
1345 <property name="bottomMargin">
1346 <number>0</number>
1347 </property>
1348 <item>
1349 <spacer name="horizontalSpacerShoulderButtons5">
1350 <property name="orientation">
1351 <enum>Qt::Horizontal</enum>
1352 </property>
1353 <property name="sizeHint" stdset="0">
1354 <size>
1355 <width>0</width>
1356 <height>20</height>
1357 </size>
1358 </property>
1359 </spacer>
1360 </item>
1361 </layout>
1362 </widget>
1363 </item>
1211 <item> 1364 <item>
1212 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true"> 1365 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
1213 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout"> 1366 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
@@ -1830,125 +1983,125 @@
1830 </layout> 1983 </layout>
1831 </widget> 1984 </widget>
1832 </item> 1985 </item>
1833 <item> 1986 <item>
1834 <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true"> 1987 <widget class="QWidget" name="buttonShoulderButtonsSLSRRight" native="true">
1835 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout"> 1988 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRRightVerticalLayout">
1836 <property name="spacing"> 1989 <property name="spacing">
1837 <number>0</number> 1990 <number>0</number>
1838 </property> 1991 </property>
1839 <property name="leftMargin"> 1992 <property name="leftMargin">
1840 <number>0</number> 1993 <number>0</number>
1841 </property> 1994 </property>
1842 <property name="topMargin"> 1995 <property name="topMargin">
1843 <number>0</number> 1996 <number>0</number>
1844 </property> 1997 </property>
1845 <property name="rightMargin"> 1998 <property name="rightMargin">
1846 <number>0</number> 1999 <number>0</number>
1847 </property> 2000 </property>
1848 <property name="bottomMargin"> 2001 <property name="bottomMargin">
1849 <number>0</number> 2002 <number>0</number>
1850 </property> 2003 </property>
1851 <item alignment="Qt::AlignHCenter"> 2004 <item alignment="Qt::AlignHCenter">
1852 <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup"> 2005 <widget class="QGroupBox" name="buttonShoulderButtonsSLRightGroup">
1853 <property name="title"> 2006 <property name="title">
1854 <string>SL</string> 2007 <string>SL</string>
1855 </property> 2008 </property>
1856 <property name="alignment"> 2009 <property name="alignment">
1857 <set>Qt::AlignCenter</set> 2010 <set>Qt::AlignCenter</set>
1858 </property> 2011 </property>
1859 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> 2012 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLRightVerticalLayout">
1860 <property name="spacing"> 2013 <property name="spacing">
1861 <number>3</number> 2014 <number>3</number>
1862 </property> 2015 </property>
1863 <property name="leftMargin"> 2016 <property name="leftMargin">
1864 <number>3</number> 2017 <number>3</number>
1865 </property> 2018 </property>
1866 <property name="topMargin"> 2019 <property name="topMargin">
1867 <number>3</number> 2020 <number>3</number>
1868 </property> 2021 </property>
1869 <property name="rightMargin"> 2022 <property name="rightMargin">
1870 <number>3</number> 2023 <number>3</number>
1871 </property> 2024 </property>
1872 <property name="bottomMargin"> 2025 <property name="bottomMargin">
1873 <number>3</number> 2026 <number>3</number>
1874 </property> 2027 </property>
1875 <item> 2028 <item>
1876 <widget class="QPushButton" name="buttonSL"> 2029 <widget class="QPushButton" name="buttonSLRight">
1877 <property name="minimumSize"> 2030 <property name="minimumSize">
1878 <size> 2031 <size>
1879 <width>68</width> 2032 <width>68</width>
1880 <height>0</height> 2033 <height>0</height>
1881 </size> 2034 </size>
1882 </property> 2035 </property>
1883 <property name="maximumSize"> 2036 <property name="maximumSize">
1884 <size> 2037 <size>
1885 <width>68</width> 2038 <width>68</width>
1886 <height>16777215</height> 2039 <height>16777215</height>
1887 </size> 2040 </size>
1888 </property> 2041 </property>
1889 <property name="styleSheet"> 2042 <property name="styleSheet">
1890 <string notr="true">min-width: 68px;</string> 2043 <string notr="true">min-width: 68px;</string>
1891 </property> 2044 </property>
1892 <property name="text"> 2045 <property name="text">
1893 <string>SL</string> 2046 <string>SL</string>
1894 </property> 2047 </property>
1895 </widget> 2048 </widget>
1896 </item> 2049 </item>
1897 </layout> 2050 </layout>
1898 </widget> 2051 </widget>
1899 </item> 2052 </item>
1900 <item alignment="Qt::AlignHCenter"> 2053 <item alignment="Qt::AlignHCenter">
1901 <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup"> 2054 <widget class="QGroupBox" name="buttonShoulderButtonsSRRightGroup">
1902 <property name="title"> 2055 <property name="title">
1903 <string>SR</string> 2056 <string>SR</string>
1904 </property> 2057 </property>
1905 <property name="alignment"> 2058 <property name="alignment">
1906 <set>Qt::AlignCenter</set> 2059 <set>Qt::AlignCenter</set>
1907 </property> 2060 </property>
1908 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> 2061 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRRightVerticalLayout">
1909 <property name="spacing"> 2062 <property name="spacing">
1910 <number>3</number> 2063 <number>3</number>
1911 </property> 2064 </property>
1912 <property name="leftMargin"> 2065 <property name="leftMargin">
1913 <number>3</number> 2066 <number>3</number>
1914 </property> 2067 </property>
1915 <property name="topMargin"> 2068 <property name="topMargin">
1916 <number>3</number> 2069 <number>3</number>
1917 </property> 2070 </property>
1918 <property name="rightMargin"> 2071 <property name="rightMargin">
1919 <number>3</number> 2072 <number>3</number>
1920 </property> 2073 </property>
1921 <property name="bottomMargin"> 2074 <property name="bottomMargin">
1922 <number>3</number> 2075 <number>3</number>
1923 </property> 2076 </property>
1924 <item> 2077 <item>
1925 <widget class="QPushButton" name="buttonSR"> 2078 <widget class="QPushButton" name="buttonSRRight">
1926 <property name="minimumSize"> 2079 <property name="minimumSize">
1927 <size> 2080 <size>
1928 <width>68</width> 2081 <width>68</width>
1929 <height>0</height> 2082 <height>0</height>
1930 </size> 2083 </size>
1931 </property> 2084 </property>
1932 <property name="maximumSize"> 2085 <property name="maximumSize">
1933 <size> 2086 <size>
1934 <width>68</width> 2087 <width>68</width>
1935 <height>16777215</height> 2088 <height>16777215</height>
1936 </size> 2089 </size>
1937 </property> 2090 </property>
1938 <property name="styleSheet"> 2091 <property name="styleSheet">
1939 <string notr="true">min-width: 68px;</string> 2092 <string notr="true">min-width: 68px;</string>
1940 </property> 2093 </property>
1941 <property name="text"> 2094 <property name="text">
1942 <string>SR</string> 2095 <string>SR</string>
1943 </property> 2096 </property>
1944 </widget> 2097 </widget>
1945 </item> 2098 </item>
2099 </layout>
2100 </widget>
2101 </item>
1946 </layout> 2102 </layout>
1947 </widget> 2103 </widget>
1948 </item> 2104 </item>
1949 </layout>
1950 </widget>
1951 </item>
1952 </layout> 2105 </layout>
1953 </item> 2106 </item>
1954 <item> 2107 <item>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index a188eef92..550cff9a0 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -297,8 +297,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
297 297
298 // Sideview SL and SR buttons 298 // Sideview SL and SR buttons
299 button_color = colors.slider_button; 299 button_color = colors.slider_button;
300 DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left); 300 DrawRoundButton(p, center + QPoint(59, 52), button_values[SRLeft], 5, 12, Direction::Left);
301 DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left); 301 DrawRoundButton(p, center + QPoint(59, -69), button_values[SLLeft], 5, 12, Direction::Left);
302 302
303 DrawLeftBody(p, center); 303 DrawLeftBody(p, center);
304 304
@@ -353,8 +353,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
353 // SR and SL buttons 353 // SR and SL buttons
354 p.setPen(colors.outline); 354 p.setPen(colors.outline);
355 button_color = colors.slider_button; 355 button_color = colors.slider_button;
356 DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4); 356 DrawRoundButton(p, center + QPoint(155, 52), button_values[SRLeft], 5.2f, 12, Direction::None,
357 DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4); 357 4);
358 DrawRoundButton(p, center + QPoint(155, -69), button_values[SLLeft], 5.2f, 12, Direction::None,
359 4);
358 360
359 // SR and SL text 361 // SR and SL text
360 p.setPen(colors.transparent); 362 p.setPen(colors.transparent);
@@ -428,8 +430,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
428 430
429 // Sideview SL and SR buttons 431 // Sideview SL and SR buttons
430 button_color = colors.slider_button; 432 button_color = colors.slider_button;
431 DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right); 433 DrawRoundButton(p, center + QPoint(-59, 52), button_values[SLRight], 5, 11,
432 DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right); 434 Direction::Right);
435 DrawRoundButton(p, center + QPoint(-59, -69), button_values[SRRight], 5, 11,
436 Direction::Right);
433 437
434 DrawRightBody(p, center); 438 DrawRightBody(p, center);
435 439
@@ -484,8 +488,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
484 // SR and SL buttons 488 // SR and SL buttons
485 p.setPen(colors.outline); 489 p.setPen(colors.outline);
486 button_color = colors.slider_button; 490 button_color = colors.slider_button;
487 DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f); 491 DrawRoundButton(p, center + QPoint(-155, 52), button_values[SLRight], 5, 12, Direction::None,
488 DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f); 492 4.0f);
493 DrawRoundButton(p, center + QPoint(-155, -69), button_values[SRRight], 5, 12, Direction::None,
494 4.0f);
489 495
490 // SR and SL text 496 // SR and SL text
491 p.setPen(colors.transparent); 497 p.setPen(colors.transparent);
@@ -557,6 +563,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
557 DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, 563 DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up,
558 1); 564 1);
559 565
566 // Left SR and SL sideview buttons
567 button_color = colors.slider_button;
568 DrawRoundButton(p, center + QPoint(-20, -62), button_values[SLLeft], 4, 11,
569 Direction::Left);
570 DrawRoundButton(p, center + QPoint(-20, 47), button_values[SRLeft], 4, 11, Direction::Left);
571
572 // Right SR and SL sideview buttons
573 button_color = colors.slider_button;
574 DrawRoundButton(p, center + QPoint(20, 47), button_values[SLRight], 4, 11,
575 Direction::Right);
576 DrawRoundButton(p, center + QPoint(20, -62), button_values[SRRight], 4, 11,
577 Direction::Right);
578
560 DrawDualBody(p, center); 579 DrawDualBody(p, center);
561 580
562 // Right trigger top view 581 // Right trigger top view
@@ -1792,16 +1811,6 @@ void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) {
1792 p.setBrush(colors.right); 1811 p.setBrush(colors.right);
1793 DrawPolygon(p, qright_joycon_topview); 1812 DrawPolygon(p, qright_joycon_topview);
1794 1813
1795 // Right SR and SL sideview buttons
1796 p.setPen(colors.outline);
1797 p.setBrush(colors.slider_button);
1798 DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1);
1799 DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1);
1800
1801 // Left SR and SL sideview buttons
1802 DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1);
1803 DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1);
1804
1805 // Right Sideview body 1814 // Right Sideview body
1806 p.setBrush(colors.slider); 1815 p.setBrush(colors.slider);
1807 DrawPolygon(p, qright_joycon_slider); 1816 DrawPolygon(p, qright_joycon_slider);
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index b91d6ad4a..b274a3321 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -25,8 +25,8 @@
25#include "core/file_sys/patch_manager.h" 25#include "core/file_sys/patch_manager.h"
26#include "core/file_sys/xts_archive.h" 26#include "core/file_sys/xts_archive.h"
27#include "core/loader/loader.h" 27#include "core/loader/loader.h"
28#include "frontend_common/config.h"
28#include "ui_configure_per_game.h" 29#include "ui_configure_per_game.h"
29#include "yuzu/configuration/config.h"
30#include "yuzu/configuration/configuration_shared.h" 30#include "yuzu/configuration/configuration_shared.h"
31#include "yuzu/configuration/configure_audio.h" 31#include "yuzu/configuration/configure_audio.h"
32#include "yuzu/configuration/configure_cpu.h" 32#include "yuzu/configuration/configure_cpu.h"
@@ -50,8 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
50 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); 50 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
51 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) 51 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
52 : fmt::format("{:016X}", title_id); 52 : fmt::format("{:016X}", title_id);
53 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); 53 game_config = std::make_unique<QtConfig>(config_file_name, Config::ConfigType::PerGameConfig);
54
55 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); 54 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
56 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this); 55 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
57 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this); 56 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
@@ -108,7 +107,7 @@ void ConfigurePerGame::ApplyConfiguration() {
108 system.ApplySettings(); 107 system.ApplySettings();
109 Settings::LogSettings(); 108 Settings::LogSettings();
110 109
111 game_config->Save(); 110 game_config->SaveAllValues();
112} 111}
113 112
114void ConfigurePerGame::changeEvent(QEvent* event) { 113void ConfigurePerGame::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index 1a727f32c..c8ee46c04 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
@@ -12,9 +12,10 @@
12 12
13#include "configuration/shared_widget.h" 13#include "configuration/shared_widget.h"
14#include "core/file_sys/vfs_types.h" 14#include "core/file_sys/vfs_types.h"
15#include "frontend_common/config.h"
15#include "vk_device_info.h" 16#include "vk_device_info.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configuration_shared.h" 17#include "yuzu/configuration/configuration_shared.h"
18#include "yuzu/configuration/qt_config.h"
18#include "yuzu/configuration/shared_translation.h" 19#include "yuzu/configuration/shared_translation.h"
19 20
20namespace Core { 21namespace Core {
@@ -72,7 +73,7 @@ private:
72 73
73 QGraphicsScene* scene; 74 QGraphicsScene* scene;
74 75
75 std::unique_ptr<Config> game_config; 76 std::unique_ptr<QtConfig> game_config;
76 77
77 Core::System& system; 78 Core::System& system;
78 std::unique_ptr<ConfigurationShared::Builder> builder; 79 std::unique_ptr<ConfigurationShared::Builder> builder;
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 674a75a62..140a7fe5d 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -19,7 +19,6 @@
19#include "core/file_sys/xts_archive.h" 19#include "core/file_sys/xts_archive.h"
20#include "core/loader/loader.h" 20#include "core/loader/loader.h"
21#include "ui_configure_per_game_addons.h" 21#include "ui_configure_per_game_addons.h"
22#include "yuzu/configuration/config.h"
23#include "yuzu/configuration/configure_input.h" 22#include "yuzu/configuration/configure_input.h"
24#include "yuzu/configuration/configure_per_game_addons.h" 23#include "yuzu/configuration/configure_per_game_addons.h"
25#include "yuzu/uisettings.h" 24#include "yuzu/uisettings.h"
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index a47089988..6d2219bf5 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -306,10 +306,10 @@ void ConfigureProfileManager::SetUserImage() {
306 return; 306 return;
307 } 307 }
308 308
309 // Some games crash when the profile image is too big. Resize any image bigger than 256x256 309 // Profile image must be 256x256
310 QImage image(image_path); 310 QImage image(image_path);
311 if (image.width() > 256 || image.height() > 256) { 311 if (image.width() != 256 || image.height() != 256) {
312 image = image.scaled(256, 256, Qt::KeepAspectRatio); 312 image = image.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
313 if (!image.save(image_path)) { 313 if (!image.save(image_path)) {
314 QMessageBox::warning(this, tr("Error resizing user image"), 314 QMessageBox::warning(this, tr("Error resizing user image"),
315 tr("Unable to resize image")); 315 tr("Unable to resize image"));
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index f83705544..9572ff43c 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -8,6 +8,7 @@
8#include <QTimer> 8#include <QTimer>
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "configuration/qt_config.h"
11#include "core/hid/emulated_controller.h" 12#include "core/hid/emulated_controller.h"
12#include "core/hid/hid_core.h" 13#include "core/hid/hid_core.h"
13#include "input_common/drivers/keyboard.h" 14#include "input_common/drivers/keyboard.h"
@@ -15,7 +16,6 @@
15#include "input_common/main.h" 16#include "input_common/main.h"
16#include "ui_configure_ringcon.h" 17#include "ui_configure_ringcon.h"
17#include "yuzu/bootmanager.h" 18#include "yuzu/bootmanager.h"
18#include "yuzu/configuration/config.h"
19#include "yuzu/configuration/configure_ringcon.h" 19#include "yuzu/configuration/configure_ringcon.h"
20 20
21const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM> 21const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
@@ -270,7 +270,7 @@ void ConfigureRingController::LoadConfiguration() {
270 270
271void ConfigureRingController::RestoreDefaults() { 271void ConfigureRingController::RestoreDefaults() {
272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( 272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
273 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); 273 0, 0, QtConfig::default_ringcon_analogs[0], QtConfig::default_ringcon_analogs[1], 0, 0.05f);
274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); 274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
275 UpdateUI(); 275 UpdateUI();
276} 276}
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
index b23c27906..6fd95e2b8 100644
--- a/src/yuzu/configuration/configure_ringcon.h
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 0c8e5c8b4..7cbf43775 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -16,7 +16,6 @@
16#include "core/core.h" 16#include "core/core.h"
17#include "core/hle/service/time/time_manager.h" 17#include "core/hle/service/time/time_manager.h"
18#include "ui_configure_system.h" 18#include "ui_configure_system.h"
19#include "yuzu/configuration/config.h"
20#include "yuzu/configuration/configuration_shared.h" 19#include "yuzu/configuration/configuration_shared.h"
21#include "yuzu/configuration/configure_system.h" 20#include "yuzu/configuration/configure_system.h"
22#include "yuzu/configuration/shared_widget.h" 21#include "yuzu/configuration/shared_widget.h"
diff --git a/src/yuzu/configuration/configure_tas.h b/src/yuzu/configuration/configure_tas.h
index 4a6b0ba4e..a91891906 100644
--- a/src/yuzu/configuration/configure_tas.h
+++ b/src/yuzu/configuration/configure_tas.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
index 5a03e48df..94df6d9d3 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
@@ -2,8 +2,8 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <memory> 4#include <memory>
5#include "common/settings.h"
5#include "ui_configure_touchscreen_advanced.h" 6#include "ui_configure_touchscreen_advanced.h"
6#include "yuzu/configuration/config.h"
7#include "yuzu/configuration/configure_touchscreen_advanced.h" 7#include "yuzu/configuration/configure_touchscreen_advanced.h"
8 8
9ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) 9ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h
index 034dc0d46..b6fdffdc8 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.h
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 82f3b6e78..dd43f0a0e 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -164,7 +164,7 @@ ConfigureUi::~ConfigureUi() = default;
164 164
165void ConfigureUi::ApplyConfiguration() { 165void ConfigureUi::ApplyConfiguration() {
166 UISettings::values.theme = 166 UISettings::values.theme =
167 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 167 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString();
168 UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); 168 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
169 UISettings::values.show_compat = ui->show_compat->isChecked(); 169 UISettings::values.show_compat = ui->show_compat->isChecked();
170 UISettings::values.show_size = ui->show_size->isChecked(); 170 UISettings::values.show_size = ui->show_size->isChecked();
@@ -191,9 +191,10 @@ void ConfigureUi::RequestGameListUpdate() {
191} 191}
192 192
193void ConfigureUi::SetConfiguration() { 193void ConfigureUi::SetConfiguration() {
194 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 194 ui->theme_combobox->setCurrentIndex(
195 ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
195 ui->language_combobox->setCurrentIndex( 196 ui->language_combobox->setCurrentIndex(
196 ui->language_combobox->findData(UISettings::values.language)); 197 ui->language_combobox->findData(QString::fromStdString(UISettings::values.language)));
197 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 198 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
198 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); 199 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
199 ui->show_size->setChecked(UISettings::values.show_size.GetValue()); 200 ui->show_size->setChecked(UISettings::values.show_size.GetValue());
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 41ef4250a..716efbccd 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -5,7 +5,7 @@
5 5
6#include "common/fs/fs.h" 6#include "common/fs/fs.h"
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "yuzu/configuration/config.h" 8#include "frontend_common/config.h"
9#include "yuzu/configuration/input_profiles.h" 9#include "yuzu/configuration/input_profiles.h"
10 10
11namespace FS = Common::FS; 11namespace FS = Common::FS;
@@ -44,7 +44,7 @@ InputProfiles::InputProfiles() {
44 if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { 44 if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
45 map_profiles.insert_or_assign( 45 map_profiles.insert_or_assign(
46 name_without_ext, 46 name_without_ext,
47 std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); 47 std::make_unique<QtConfig>(name_without_ext, Config::ConfigType::InputProfile));
48 } 48 }
49 49
50 return true; 50 return true;
@@ -85,7 +85,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p
85 } 85 }
86 86
87 map_profiles.insert_or_assign( 87 map_profiles.insert_or_assign(
88 profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); 88 profile_name, std::make_unique<QtConfig>(profile_name, Config::ConfigType::InputProfile));
89 89
90 return SaveProfile(profile_name, player_index); 90 return SaveProfile(profile_name, player_index);
91} 91}
@@ -113,7 +113,7 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla
113 return false; 113 return false;
114 } 114 }
115 115
116 map_profiles[profile_name]->ReadControlPlayerValue(player_index); 116 map_profiles[profile_name]->ReadQtControlPlayerValues(player_index);
117 return true; 117 return true;
118} 118}
119 119
@@ -122,7 +122,7 @@ bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t pla
122 return false; 122 return false;
123 } 123 }
124 124
125 map_profiles[profile_name]->SaveControlPlayerValue(player_index); 125 map_profiles[profile_name]->SaveQtControlPlayerValues(player_index);
126 return true; 126 return true;
127} 127}
128 128
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
index 2bf3e4250..023ec74a6 100644
--- a/src/yuzu/configuration/input_profiles.h
+++ b/src/yuzu/configuration/input_profiles.h
@@ -6,6 +6,8 @@
6#include <string> 6#include <string>
7#include <unordered_map> 7#include <unordered_map>
8 8
9#include "configuration/qt_config.h"
10
9namespace Core { 11namespace Core {
10class System; 12class System;
11} 13}
@@ -30,5 +32,5 @@ public:
30private: 32private:
31 bool ProfileExistsInMap(const std::string& profile_name) const; 33 bool ProfileExistsInMap(const std::string& profile_name) const;
32 34
33 std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; 35 std::unordered_map<std::string, std::unique_ptr<QtConfig>> map_profiles;
34}; 36};
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
new file mode 100644
index 000000000..5a8e69aa9
--- /dev/null
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -0,0 +1,549 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "input_common/main.h"
5#include "qt_config.h"
6#include "uisettings.h"
7
8const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = {
9 Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
10 Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
11 Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
12 Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
13 Qt::Key_Q, Qt::Key_E,
14};
15
16const std::array<int, Settings::NativeMotion::NumMotions> QtConfig::default_motions = {
17 Qt::Key_7,
18 Qt::Key_8,
19};
20
21const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{
22 {
23 Qt::Key_W,
24 Qt::Key_S,
25 Qt::Key_A,
26 Qt::Key_D,
27 },
28 {
29 Qt::Key_I,
30 Qt::Key_K,
31 Qt::Key_J,
32 Qt::Key_L,
33 },
34}};
35
36const std::array<int, 2> QtConfig::default_stick_mod = {
37 Qt::Key_Shift,
38 0,
39};
40
41const std::array<int, 2> QtConfig::default_ringcon_analogs{{
42 Qt::Key_A,
43 Qt::Key_D,
44}};
45
46QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type)
47 : Config(config_type) {
48 Initialize(config_name);
49 if (config_type != ConfigType::InputProfile) {
50 ReadQtValues();
51 SaveQtValues();
52 }
53}
54
55QtConfig::~QtConfig() {
56 if (global) {
57 QtConfig::SaveAllValues();
58 }
59}
60
61void QtConfig::ReloadAllValues() {
62 Reload();
63 ReadQtValues();
64 SaveQtValues();
65}
66
67void QtConfig::SaveAllValues() {
68 Save();
69 SaveQtValues();
70}
71
72void QtConfig::ReadQtValues() {
73 if (global) {
74 ReadUIValues();
75 }
76 ReadQtControlValues();
77}
78
79void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
80 std::string player_prefix;
81 if (type != ConfigType::InputProfile) {
82 player_prefix.append("player_").append(ToString(player_index)).append("_");
83 }
84
85 auto& player = Settings::values.players.GetValue()[player_index];
86 if (IsCustomConfig()) {
87 const auto profile_name =
88 ReadStringSetting(std::string(player_prefix).append("profile_name"));
89 if (profile_name.empty()) {
90 // Use the global input config
91 player = Settings::values.players.GetValue(true)[player_index];
92 return;
93 }
94 }
95
96 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
97 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
98 auto& player_buttons = player.buttons[i];
99
100 player_buttons = ReadStringSetting(
101 std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
102 if (player_buttons.empty()) {
103 player_buttons = default_param;
104 }
105 }
106
107 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
108 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
109 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
110 default_analogs[i][3], default_stick_mod[i], 0.5f);
111 auto& player_analogs = player.analogs[i];
112
113 player_analogs = ReadStringSetting(
114 std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
115 if (player_analogs.empty()) {
116 player_analogs = default_param;
117 }
118 }
119
120 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
121 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
122 auto& player_motions = player.motions[i];
123
124 player_motions = ReadStringSetting(
125 std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
126 if (player_motions.empty()) {
127 player_motions = default_param;
128 }
129 }
130}
131
132void QtConfig::ReadHidbusValues() {
133 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
134 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
135 auto& ringcon_analogs = Settings::values.ringcon_analogs;
136
137 ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
138 if (ringcon_analogs.empty()) {
139 ringcon_analogs = default_param;
140 }
141}
142
143void QtConfig::ReadDebugControlValues() {
144 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
145 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
146 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
147
148 debug_pad_buttons = ReadStringSetting(
149 std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
150 if (debug_pad_buttons.empty()) {
151 debug_pad_buttons = default_param;
152 }
153 }
154
155 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
156 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
157 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
158 default_analogs[i][3], default_stick_mod[i], 0.5f);
159 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
160
161 debug_pad_analogs = ReadStringSetting(
162 std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
163 if (debug_pad_analogs.empty()) {
164 debug_pad_analogs = default_param;
165 }
166 }
167}
168
169void QtConfig::ReadQtControlValues() {
170 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
171
172 Settings::values.players.SetGlobal(!IsCustomConfig());
173 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
174 ReadQtPlayerValues(p);
175 }
176 if (IsCustomConfig()) {
177 EndGroup();
178 return;
179 }
180 ReadDebugControlValues();
181 ReadHidbusValues();
182
183 EndGroup();
184}
185
186void QtConfig::ReadPathValues() {
187 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
188
189 UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
190 UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath"));
191 UISettings::values.game_dir_deprecated =
192 ReadStringSetting(std::string("gameListRootDir"), std::string("."));
193 UISettings::values.game_dir_deprecated_deepscan =
194 ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false));
195
196 const int gamedirs_size = BeginArray(std::string("gamedirs"));
197 for (int i = 0; i < gamedirs_size; ++i) {
198 SetArrayIndex(i);
199 UISettings::GameDir game_dir;
200 game_dir.path = ReadStringSetting(std::string("path"));
201 game_dir.deep_scan =
202 ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false));
203 game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true));
204 UISettings::values.game_dirs.append(game_dir);
205 }
206 EndArray();
207
208 // Create NAND and SD card directories if empty, these are not removable through the UI,
209 // also carries over old game list settings if present
210 if (UISettings::values.game_dirs.empty()) {
211 UISettings::GameDir game_dir;
212 game_dir.path = std::string("SDMC");
213 game_dir.expanded = true;
214 UISettings::values.game_dirs.append(game_dir);
215 game_dir.path = std::string("UserNAND");
216 UISettings::values.game_dirs.append(game_dir);
217 game_dir.path = std::string("SysNAND");
218 UISettings::values.game_dirs.append(game_dir);
219 if (UISettings::values.game_dir_deprecated != std::string(".")) {
220 game_dir.path = UISettings::values.game_dir_deprecated;
221 game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
222 UISettings::values.game_dirs.append(game_dir);
223 }
224 }
225 UISettings::values.recent_files =
226 QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
227 .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
228 UISettings::values.language = ReadStringSetting(std::string("language"), std::string(""));
229
230 EndGroup();
231}
232
233void QtConfig::ReadShortcutValues() {
234 BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
235
236 for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) {
237 BeginGroup(group);
238 BeginGroup(name);
239
240 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
241 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
242 // a file dialog in windowed mode
243 UISettings::values.shortcuts.push_back(
244 {name,
245 group,
246 {ReadStringSetting(std::string("KeySeq"), shortcut.keyseq),
247 ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq),
248 shortcut.context,
249 ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}});
250
251 EndGroup(); // name
252 EndGroup(); // group
253 }
254
255 EndGroup();
256}
257
258void QtConfig::ReadUIValues() {
259 BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
260
261 UISettings::values.theme = ReadStringSetting(
262 std::string("theme"),
263 std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second));
264
265 ReadUIGamelistValues();
266 ReadUILayoutValues();
267 ReadPathValues();
268 ReadScreenshotValues();
269 ReadShortcutValues();
270 ReadMultiplayerValues();
271
272 ReadCategory(Settings::Category::Ui);
273 ReadCategory(Settings::Category::UiGeneral);
274
275 EndGroup();
276}
277
278void QtConfig::ReadUIGamelistValues() {
279 BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
280
281 ReadCategory(Settings::Category::UiGameList);
282
283 const int favorites_size = BeginArray("favorites");
284 for (int i = 0; i < favorites_size; i++) {
285 SetArrayIndex(i);
286 UISettings::values.favorited_ids.append(
287 ReadUnsignedIntegerSetting(std::string("program_id")));
288 }
289 EndArray();
290
291 EndGroup();
292}
293
294void QtConfig::ReadUILayoutValues() {
295 BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
296
297 ReadCategory(Settings::Category::UiLayout);
298
299 EndGroup();
300}
301
302void QtConfig::ReadMultiplayerValues() {
303 BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer));
304
305 ReadCategory(Settings::Category::Multiplayer);
306
307 // Read ban list back
308 int size = BeginArray(std::string("username_ban_list"));
309 UISettings::values.multiplayer_ban_list.first.resize(size);
310 for (int i = 0; i < size; ++i) {
311 SetArrayIndex(i);
312 UISettings::values.multiplayer_ban_list.first[i] =
313 ReadStringSetting(std::string("username"), std::string(""));
314 }
315 EndArray();
316
317 size = BeginArray(std::string("ip_ban_list"));
318 UISettings::values.multiplayer_ban_list.second.resize(size);
319 for (int i = 0; i < size; ++i) {
320 UISettings::values.multiplayer_ban_list.second[i] =
321 ReadStringSetting("username", std::string(""));
322 }
323 EndArray();
324
325 EndGroup();
326}
327
328void QtConfig::SaveQtValues() {
329 if (global) {
330 SaveUIValues();
331 }
332 SaveQtControlValues();
333
334 WriteToIni();
335}
336
337void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
338 std::string player_prefix;
339 if (type != ConfigType::InputProfile) {
340 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
341 }
342
343 const auto& player = Settings::values.players.GetValue()[player_index];
344 if (IsCustomConfig() && player.profile_name.empty()) {
345 // No custom profile selected
346 return;
347 }
348
349 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
350 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
351 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
352 player.buttons[i], std::make_optional(default_param));
353 }
354 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
355 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
356 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
357 default_analogs[i][3], default_stick_mod[i], 0.5f);
358 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
359 player.analogs[i], std::make_optional(default_param));
360 }
361 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
362 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
363 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
364 player.motions[i], std::make_optional(default_param));
365 }
366}
367
368void QtConfig::SaveDebugControlValues() {
369 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
370 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
371 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
372 Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
373 }
374 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
375 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
376 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
377 default_analogs[i][3], default_stick_mod[i], 0.5f);
378 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
379 Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
380 }
381}
382
383void QtConfig::SaveHidbusValues() {
384 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
385 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
386 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
387 std::make_optional(default_param));
388}
389
390void QtConfig::SaveQtControlValues() {
391 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
392
393 Settings::values.players.SetGlobal(!IsCustomConfig());
394 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
395 SaveQtPlayerValues(p);
396 }
397 if (IsCustomConfig()) {
398 EndGroup();
399 return;
400 }
401 SaveDebugControlValues();
402 SaveHidbusValues();
403
404 EndGroup();
405}
406
407void QtConfig::SavePathValues() {
408 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
409
410 WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
411 WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path);
412 BeginArray(std::string("gamedirs"));
413 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
414 SetArrayIndex(i);
415 const auto& game_dir = UISettings::values.game_dirs[i];
416 WriteSetting(std::string("path"), game_dir.path);
417 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
418 WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
419 }
420 EndArray();
421
422 WriteSetting(std::string("recentFiles"),
423 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
424 WriteSetting(std::string("language"), UISettings::values.language);
425
426 EndGroup();
427}
428
429void QtConfig::SaveShortcutValues() {
430 BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
431
432 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
433 // However, their ordering must also be the same.
434 for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) {
435 const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
436 const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut;
437
438 BeginGroup(group);
439 BeginGroup(name);
440
441 WriteSetting(std::string("KeySeq"), shortcut.keyseq,
442 std::make_optional(default_hotkey.keyseq));
443 WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
444 std::make_optional(default_hotkey.controller_keyseq));
445 WriteSetting(std::string("Context"), shortcut.context,
446 std::make_optional(default_hotkey.context));
447 WriteSetting(std::string("Repeat"), shortcut.repeat,
448 std::make_optional(default_hotkey.repeat));
449
450 EndGroup(); // name
451 EndGroup(); // group
452 }
453
454 EndGroup();
455}
456
457void QtConfig::SaveUIValues() {
458 BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
459
460 WriteCategory(Settings::Category::Ui);
461 WriteCategory(Settings::Category::UiGeneral);
462
463 WriteSetting(std::string("theme"), UISettings::values.theme,
464 std::make_optional(std::string(
465 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
466
467 SaveUIGamelistValues();
468 SaveUILayoutValues();
469 SavePathValues();
470 SaveScreenshotValues();
471 SaveShortcutValues();
472 SaveMultiplayerValues();
473
474 EndGroup();
475}
476
477void QtConfig::SaveUIGamelistValues() {
478 BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
479
480 WriteCategory(Settings::Category::UiGameList);
481
482 BeginArray(std::string("favorites"));
483 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
484 SetArrayIndex(i);
485 WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
486 }
487 EndArray(); // favorites
488
489 EndGroup();
490}
491
492void QtConfig::SaveUILayoutValues() {
493 BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout));
494
495 WriteCategory(Settings::Category::UiLayout);
496
497 EndGroup();
498}
499
500void QtConfig::SaveMultiplayerValues() {
501 BeginGroup(std::string("Multiplayer"));
502
503 WriteCategory(Settings::Category::Multiplayer);
504
505 // Write ban list
506 BeginArray(std::string("username_ban_list"));
507 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
508 SetArrayIndex(static_cast<int>(i));
509 WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]);
510 }
511 EndArray(); // username_ban_list
512
513 BeginArray(std::string("ip_ban_list"));
514 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
515 SetArrayIndex(static_cast<int>(i));
516 WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
517 }
518 EndArray(); // ip_ban_list
519
520 EndGroup();
521}
522
523std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
524 auto& map = Settings::values.linkage.by_category;
525 if (map.contains(category)) {
526 return Settings::values.linkage.by_category[category];
527 }
528 return UISettings::values.linkage.by_category[category];
529}
530
531void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) {
532 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
533
534 ReadPlayerValues(player_index);
535 ReadQtPlayerValues(player_index);
536
537 EndGroup();
538}
539
540void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) {
541 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
542
543 SavePlayerValues(player_index);
544 SaveQtPlayerValues(player_index);
545
546 EndGroup();
547
548 WriteToIni();
549}
diff --git a/src/yuzu/configuration/qt_config.h b/src/yuzu/configuration/qt_config.h
new file mode 100644
index 000000000..dc2dceb4d
--- /dev/null
+++ b/src/yuzu/configuration/qt_config.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <QMetaType>
7
8#include "frontend_common/config.h"
9
10class QtConfig final : public Config {
11public:
12 explicit QtConfig(const std::string& config_name = "qt-config",
13 ConfigType config_type = ConfigType::GlobalConfig);
14 ~QtConfig() override;
15
16 void ReloadAllValues() override;
17 void SaveAllValues() override;
18
19 void ReadQtControlPlayerValues(std::size_t player_index);
20 void SaveQtControlPlayerValues(std::size_t player_index);
21
22protected:
23 void ReadQtValues();
24 void ReadQtPlayerValues(std::size_t player_index);
25 void ReadQtControlValues();
26 void ReadHidbusValues() override;
27 void ReadDebugControlValues() override;
28 void ReadPathValues() override;
29 void ReadShortcutValues() override;
30 void ReadUIValues() override;
31 void ReadUIGamelistValues() override;
32 void ReadUILayoutValues() override;
33 void ReadMultiplayerValues() override;
34
35 void SaveQtValues();
36 void SaveQtPlayerValues(std::size_t player_index);
37 void SaveQtControlValues();
38 void SaveHidbusValues() override;
39 void SaveDebugControlValues() override;
40 void SavePathValues() override;
41 void SaveShortcutValues() override;
42 void SaveUIValues() override;
43 void SaveUIGamelistValues() override;
44 void SaveUILayoutValues() override;
45 void SaveMultiplayerValues() override;
46
47 std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
48
49public:
50 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
51 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
52 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
53 static const std::array<int, 2> default_stick_mod;
54 static const std::array<int, 2> default_ringcon_analogs;
55};
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 3fe448f27..a7b5def32 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -1,17 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/time_zone.h"
5#include "yuzu/configuration/shared_translation.h" 4#include "yuzu/configuration/shared_translation.h"
6 5
7#include <map> 6#include <map>
8#include <memory> 7#include <memory>
9#include <tuple> 8#include <tuple>
10#include <utility> 9#include <utility>
10#include <QCoreApplication>
11#include <QWidget> 11#include <QWidget>
12#include "common/settings.h" 12#include "common/settings.h"
13#include "common/settings_enums.h" 13#include "common/settings_enums.h"
14#include "common/settings_setting.h" 14#include "common/settings_setting.h"
15#include "common/time_zone.h"
15#include "yuzu/uisettings.h" 16#include "yuzu/uisettings.h"
16 17
17namespace ConfigurationShared { 18namespace ConfigurationShared {
@@ -21,123 +22,135 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
21 const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; 22 const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
22 23
23#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ 24#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
24 translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}}) 25 translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
25 26
26 // A setting can be ignored by giving it a blank name 27 // A setting can be ignored by giving it a blank name
27 28
28 // Audio 29 // Audio
29 INSERT(Settings, sink_id, "Output Engine:", ""); 30 INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
30 INSERT(Settings, audio_output_device_id, "Output Device:", ""); 31 INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
31 INSERT(Settings, audio_input_device_id, "Input Device:", ""); 32 INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral());
32 INSERT(Settings, audio_muted, "Mute audio", ""); 33 INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral());
33 INSERT(Settings, volume, "Volume:", ""); 34 INSERT(Settings, volume, tr("Volume:"), QStringLiteral());
34 INSERT(Settings, dump_audio_commands, "", ""); 35 INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral());
35 INSERT(UISettings, mute_when_in_background, "Mute audio when in background", ""); 36 INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"),
37 QStringLiteral());
36 38
37 // Core 39 // Core
38 INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); 40 INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral());
39 INSERT(Settings, memory_layout_mode, "Memory Layout", ""); 41 INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral());
40 INSERT(Settings, use_speed_limit, "", ""); 42 INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
41 INSERT(Settings, speed_limit, "Limit Speed Percent", ""); 43 INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral());
42 44
43 // Cpu 45 // Cpu
44 INSERT(Settings, cpu_accuracy, "Accuracy:", ""); 46 INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral());
45 47
46 // Cpu Debug 48 // Cpu Debug
47 49
48 // Cpu Unsafe 50 // Cpu Unsafe
49 INSERT(Settings, cpuopt_unsafe_unfuse_fma,
50 "Unfuse FMA (improve performance on CPUs without FMA)",
51 "This option improves speed by reducing accuracy of fused-multiply-add instructions on "
52 "CPUs without native FMA support.");
53 INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE",
54 "This option improves the speed of some approximate floating-point functions by using "
55 "less accurate native approximations.");
56 INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)",
57 "This option improves the speed of 32 bits ASIMD floating-point functions by running "
58 "with incorrect rounding modes.");
59 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling",
60 "This option improves speed by removing NaN checking. Please note this also reduces "
61 "accuracy of certain floating-point instructions.");
62 INSERT( 51 INSERT(
63 Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", 52 Settings, cpuopt_unsafe_unfuse_fma,
64 "This option improves speed by eliminating a safety check before every memory read/write " 53 tr("Unfuse FMA (improve performance on CPUs without FMA)"),
65 "in guest. Disabling it may allow a game to read/write the emulator's memory."); 54 tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on "
66 INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor", 55 "CPUs without native FMA support."));
67 "This option improves speed by relying only on the semantics of cmpxchg to ensure " 56 INSERT(
57 Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"),
58 tr("This option improves the speed of some approximate floating-point functions by using "
59 "less accurate native approximations."));
60 INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr,
61 tr("Faster ASIMD instructions (32 bits only)"),
62 tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
63 "with incorrect rounding modes."));
64 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
65 tr("This option improves speed by removing NaN checking. Please note this also reduces "
66 "accuracy of certain floating-point instructions."));
67 INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
68 tr("This option improves speed by eliminating a safety check before every memory "
69 "read/write "
70 "in guest. Disabling it may allow a game to read/write the emulator's memory."));
71 INSERT(
72 Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
73 tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
68 "safety of exclusive access instructions. Please note this may result in deadlocks and " 74 "safety of exclusive access instructions. Please note this may result in deadlocks and "
69 "other race conditions."); 75 "other race conditions."));
70 76
71 // Renderer 77 // Renderer
72 INSERT(Settings, renderer_backend, "API:", ""); 78 INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral());
73 INSERT(Settings, vulkan_device, "Device:", ""); 79 INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral());
74 INSERT(Settings, shader_backend, "Shader Backend:", ""); 80 INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral());
75 INSERT(Settings, resolution_setup, "Resolution:", ""); 81 INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral());
76 INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); 82 INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
77 INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); 83 INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral());
78 INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); 84 INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral());
79 INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); 85 INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral());
80 INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); 86 INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral());
81 INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); 87 INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral());
82 INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); 88 INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
83 INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); 89 QStringLiteral());
84 INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); 90 INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral());
85 INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); 91 INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral());
86 INSERT(Settings, vsync_mode, "VSync Mode:", 92 INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral());
87 "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " 93 INSERT(
94 Settings, vsync_mode, tr("VSync Mode:"),
95 tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
88 "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " 96 "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from "
89 "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " 97 "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop "
90 "frames.\nImmediate (no synchronization) just presents whatever is available and can " 98 "frames.\nImmediate (no synchronization) just presents whatever is available and can "
91 "exhibit tearing."); 99 "exhibit tearing."));
92 INSERT(Settings, bg_red, "", ""); 100 INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral());
93 INSERT(Settings, bg_green, "", ""); 101 INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral());
94 INSERT(Settings, bg_blue, "", ""); 102 INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral());
95 103
96 // Renderer (Advanced Graphics) 104 // Renderer (Advanced Graphics)
97 INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); 105 INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
98 INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", 106 QStringLiteral());
99 "Runs work in the background while waiting for graphics commands to keep the GPU from " 107 INSERT(
100 "lowering its clock speed."); 108 Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
101 INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); 109 tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
102 INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); 110 "lowering its clock speed."));
103 INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", 111 INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral());
104 "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " 112 INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral());
105 "is experimental."); 113 INSERT(
106 INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", 114 Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
107 "Enables Fast GPU Time. This option will force most games to run at their highest " 115 tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
108 "native resolution."); 116 "is experimental."));
109 INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", 117 INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
110 "Enables GPU vendor-specific pipeline cache. This option can improve shader loading " 118 tr("Enables Fast GPU Time. This option will force most games to run at their highest "
111 "time significantly in cases where the Vulkan driver does not store pipeline cache " 119 "native resolution."));
112 "files internally."); 120 INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
113 INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", 121 tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
114 "Enable compute pipelines, required by some games.\nThis setting only exists for Intel " 122 "time significantly in cases where the Vulkan driver does not store pipeline cache "
123 "files internally."));
124 INSERT(
125 Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"),
126 tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
115 "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " 127 "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
116 "on all other drivers."); 128 "on all other drivers."));
117 INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", 129 INSERT(
118 "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " 130 Settings, use_reactive_flushing, tr("Enable Reactive Flushing"),
119 "syncing."); 131 tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
120 INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", 132 "syncing."));
121 "Run the game at normal speed during video playback, even when the framerate is " 133 INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"),
122 "unlocked."); 134 tr("Run the game at normal speed during video playback, even when the framerate is "
123 INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", 135 "unlocked."));
124 "Improves rendering of transparency effects in specific games."); 136 INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"),
137 tr("Improves rendering of transparency effects in specific games."));
125 138
126 // Renderer (Debug) 139 // Renderer (Debug)
127 140
128 // System 141 // System
129 INSERT(Settings, rng_seed, "RNG Seed", ""); 142 INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
130 INSERT(Settings, rng_seed_enabled, "", ""); 143 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
131 INSERT(Settings, device_name, "Device Name", ""); 144 INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
132 INSERT(Settings, custom_rtc, "Custom RTC", ""); 145 INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral());
133 INSERT(Settings, custom_rtc_enabled, "", ""); 146 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
134 INSERT(Settings, language_index, 147 INSERT(Settings, language_index, tr("Language:"),
135 "Language:", "Note: this can be overridden when region setting is auto-select"); 148 tr("Note: this can be overridden when region setting is auto-select"));
136 INSERT(Settings, region_index, "Region:", ""); 149 INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
137 INSERT(Settings, time_zone_index, "Time Zone:", ""); 150 INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral());
138 INSERT(Settings, sound_index, "Sound Output Mode:", ""); 151 INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
139 INSERT(Settings, use_docked_mode, "Console Mode:", ""); 152 INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral());
140 INSERT(Settings, current_user, "", ""); 153 INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());
141 154
142 // Controls 155 // Controls
143 156
@@ -154,12 +167,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
154 // Ui 167 // Ui
155 168
156 // Ui General 169 // Ui General
157 INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); 170 INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral());
158 INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); 171 INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
159 INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); 172 QStringLiteral());
160 INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); 173 INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
161 INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); 174 QStringLiteral());
162 INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); 175 INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral());
176 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
177 QStringLiteral());
163 178
164 // Ui Debugging 179 // Ui Debugging
165 180
@@ -179,140 +194,141 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
179 return parent->tr(text, context); 194 return parent->tr(text, context);
180 }; 195 };
181 196
182#define PAIR(ENUM, VALUE, TRANSLATION) \ 197#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
183 { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) }
184#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \
185 { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) }
186 198
187 // Intentionally skipping VSyncMode to let the UI fill that one out 199 // Intentionally skipping VSyncMode to let the UI fill that one out
188 200
189 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), 201 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
190 { 202 {
191 PAIR(AstcDecodeMode, Cpu, "CPU"), 203 PAIR(AstcDecodeMode, Cpu, tr("CPU")),
192 PAIR(AstcDecodeMode, Gpu, "GPU"), 204 PAIR(AstcDecodeMode, Gpu, tr("GPU")),
193 PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), 205 PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")),
194 }});
195 translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
196 {
197 PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"),
198 PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"),
199 PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"),
200 }}); 206 }});
207 translations->insert(
208 {Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
209 {
210 PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
211 PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
212 PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
213 }});
201 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), 214 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
202 { 215 {
203#ifdef HAS_OPENGL 216#ifdef HAS_OPENGL
204 PAIR(RendererBackend, OpenGL, "OpenGL"), 217 PAIR(RendererBackend, OpenGL, tr("OpenGL")),
205#endif 218#endif
206 PAIR(RendererBackend, Vulkan, "Vulkan"), 219 PAIR(RendererBackend, Vulkan, tr("Vulkan")),
207 PAIR(RendererBackend, Null, "Null"), 220 PAIR(RendererBackend, Null, tr("Null")),
208 }});
209 translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
210 {
211 PAIR(ShaderBackend, Glsl, "GLSL"),
212 PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"),
213 PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"),
214 }}); 221 }});
222 translations->insert(
223 {Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
224 {
225 PAIR(ShaderBackend, Glsl, tr("GLSL")),
226 PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
227 PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")),
228 }});
215 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), 229 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
216 { 230 {
217 PAIR(GpuAccuracy, Normal, "Normal"), 231 PAIR(GpuAccuracy, Normal, tr("Normal")),
218 PAIR(GpuAccuracy, High, "High"), 232 PAIR(GpuAccuracy, High, tr("High")),
219 PAIR(GpuAccuracy, Extreme, "Extreme"), 233 PAIR(GpuAccuracy, Extreme, tr("Extreme")),
220 }});
221 translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
222 {
223 PAIR(CpuAccuracy, Auto, "Auto"),
224 PAIR(CpuAccuracy, Accurate, "Accurate"),
225 PAIR(CpuAccuracy, Unsafe, "Unsafe"),
226 PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"),
227 }}); 234 }});
235 translations->insert(
236 {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
237 {
238 PAIR(CpuAccuracy, Auto, tr("Auto")),
239 PAIR(CpuAccuracy, Accurate, tr("Accurate")),
240 PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
241 PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
242 }});
228 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), 243 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
229 { 244 {
230 PAIR(FullscreenMode, Borderless, "Borderless Windowed"), 245 PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")),
231 PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), 246 PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")),
232 }}); 247 }});
233 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), 248 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
234 { 249 {
235 PAIR(NvdecEmulation, Off, "No Video Output"), 250 PAIR(NvdecEmulation, Off, tr("No Video Output")),
236 PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), 251 PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")),
237 PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), 252 PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")),
238 }});
239 translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
240 {
241 PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"),
242 PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"),
243 PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"),
244 PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"),
245 PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"),
246 PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"),
247 PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"),
248 PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"),
249 PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"),
250 PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"),
251 PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"),
252 }}); 253 }});
254 translations->insert(
255 {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
256 {
257 PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")),
258 PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")),
259 PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")),
260 PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")),
261 PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")),
262 PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")),
263 PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")),
264 PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")),
265 PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")),
266 PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")),
267 PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")),
268 }});
253 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), 269 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
254 { 270 {
255 PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), 271 PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")),
256 PAIR(ScalingFilter, Bilinear, "Bilinear"), 272 PAIR(ScalingFilter, Bilinear, tr("Bilinear")),
257 PAIR(ScalingFilter, Bicubic, "Bicubic"), 273 PAIR(ScalingFilter, Bicubic, tr("Bicubic")),
258 PAIR(ScalingFilter, Gaussian, "Gaussian"), 274 PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
259 PAIR(ScalingFilter, ScaleForce, "ScaleForce"), 275 PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
260 PAIR(ScalingFilter, Fsr, "AMD FidelityFXâ„¢ï¸ Super Resolution"), 276 PAIR(ScalingFilter, Fsr, tr("AMD FidelityFXâ„¢ï¸ Super Resolution")),
261 }}); 277 }});
262 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), 278 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
263 { 279 {
264 PAIR(AntiAliasing, None, "None"), 280 PAIR(AntiAliasing, None, tr("None")),
265 PAIR(AntiAliasing, Fxaa, "FXAA"), 281 PAIR(AntiAliasing, Fxaa, tr("FXAA")),
266 PAIR(AntiAliasing, Smaa, "SMAA"), 282 PAIR(AntiAliasing, Smaa, tr("SMAA")),
267 }}); 283 }});
268 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), 284 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
269 { 285 {
270 PAIR(AspectRatio, R16_9, "Default (16:9)"), 286 PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
271 PAIR(AspectRatio, R4_3, "Force 4:3"), 287 PAIR(AspectRatio, R4_3, tr("Force 4:3")),
272 PAIR(AspectRatio, R21_9, "Force 21:9"), 288 PAIR(AspectRatio, R21_9, tr("Force 21:9")),
273 PAIR(AspectRatio, R16_10, "Force 16:10"), 289 PAIR(AspectRatio, R16_10, tr("Force 16:10")),
274 PAIR(AspectRatio, Stretch, "Stretch to Window"), 290 PAIR(AspectRatio, Stretch, tr("Stretch to Window")),
275 }}); 291 }});
276 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), 292 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
277 { 293 {
278 PAIR(AnisotropyMode, Automatic, "Automatic"), 294 PAIR(AnisotropyMode, Automatic, tr("Automatic")),
279 PAIR(AnisotropyMode, Default, "Default"), 295 PAIR(AnisotropyMode, Default, tr("Default")),
280 PAIR(AnisotropyMode, X2, "2x"), 296 PAIR(AnisotropyMode, X2, tr("2x")),
281 PAIR(AnisotropyMode, X4, "4x"), 297 PAIR(AnisotropyMode, X4, tr("4x")),
282 PAIR(AnisotropyMode, X8, "8x"), 298 PAIR(AnisotropyMode, X8, tr("8x")),
283 PAIR(AnisotropyMode, X16, "16x"), 299 PAIR(AnisotropyMode, X16, tr("16x")),
284 }}); 300 }});
285 translations->insert( 301 translations->insert(
286 {Settings::EnumMetadata<Settings::Language>::Index(), 302 {Settings::EnumMetadata<Settings::Language>::Index(),
287 { 303 {
288 PAIR(Language, Japanese, "Japanese (日本語)"), 304 PAIR(Language, Japanese, tr("Japanese (日本語)")),
289 PAIR(Language, EnglishAmerican, "American English"), 305 PAIR(Language, EnglishAmerican, tr("American English")),
290 PAIR(Language, French, "French (français)"), 306 PAIR(Language, French, tr("French (français)")),
291 PAIR(Language, German, "German (Deutsch)"), 307 PAIR(Language, German, tr("German (Deutsch)")),
292 PAIR(Language, Italian, "Italian (italiano)"), 308 PAIR(Language, Italian, tr("Italian (italiano)")),
293 PAIR(Language, Spanish, "Spanish (español)"), 309 PAIR(Language, Spanish, tr("Spanish (español)")),
294 PAIR(Language, Chinese, "Chinese"), 310 PAIR(Language, Chinese, tr("Chinese")),
295 PAIR(Language, Korean, "Korean (한국어)"), 311 PAIR(Language, Korean, tr("Korean (한국어)")),
296 PAIR(Language, Dutch, "Dutch (Nederlands)"), 312 PAIR(Language, Dutch, tr("Dutch (Nederlands)")),
297 PAIR(Language, Portuguese, "Portuguese (português)"), 313 PAIR(Language, Portuguese, tr("Portuguese (português)")),
298 PAIR(Language, Russian, "Russian (РуÑÑкий)"), 314 PAIR(Language, Russian, tr("Russian (РуÑÑкий)")),
299 PAIR(Language, Taiwanese, "Taiwanese"), 315 PAIR(Language, Taiwanese, tr("Taiwanese")),
300 PAIR(Language, EnglishBritish, "British English"), 316 PAIR(Language, EnglishBritish, tr("British English")),
301 PAIR(Language, FrenchCanadian, "Canadian French"), 317 PAIR(Language, FrenchCanadian, tr("Canadian French")),
302 PAIR(Language, SpanishLatin, "Latin American Spanish"), 318 PAIR(Language, SpanishLatin, tr("Latin American Spanish")),
303 PAIR(Language, ChineseSimplified, "Simplified Chinese"), 319 PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
304 PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), 320 PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
305 PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), 321 PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
306 }}); 322 }});
307 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), 323 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
308 { 324 {
309 PAIR(Region, Japan, "Japan"), 325 PAIR(Region, Japan, tr("Japan")),
310 PAIR(Region, Usa, "USA"), 326 PAIR(Region, Usa, tr("USA")),
311 PAIR(Region, Europe, "Europe"), 327 PAIR(Region, Europe, tr("Europe")),
312 PAIR(Region, Australia, "Australia"), 328 PAIR(Region, Australia, tr("Australia")),
313 PAIR(Region, China, "China"), 329 PAIR(Region, China, tr("China")),
314 PAIR(Region, Korea, "Korea"), 330 PAIR(Region, Korea, tr("Korea")),
315 PAIR(Region, Taiwan, "Taiwan"), 331 PAIR(Region, Taiwan, tr("Taiwan")),
316 }}); 332 }});
317 translations->insert( 333 translations->insert(
318 {Settings::EnumMetadata<Settings::TimeZone>::Index(), 334 {Settings::EnumMetadata<Settings::TimeZone>::Index(),
@@ -324,72 +340,74 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
324 {static_cast<u32>(Settings::TimeZone::Default), 340 {static_cast<u32>(Settings::TimeZone::Default),
325 tr("Default (%1)", "Default time zone") 341 tr("Default (%1)", "Default time zone")
326 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, 342 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
327 PAIR(TimeZone, Cet, "CET"), 343 PAIR(TimeZone, Cet, tr("CET")),
328 PAIR(TimeZone, Cst6Cdt, "CST6CDT"), 344 PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
329 PAIR(TimeZone, Cuba, "Cuba"), 345 PAIR(TimeZone, Cuba, tr("Cuba")),
330 PAIR(TimeZone, Eet, "EET"), 346 PAIR(TimeZone, Eet, tr("EET")),
331 PAIR(TimeZone, Egypt, "Egypt"), 347 PAIR(TimeZone, Egypt, tr("Egypt")),
332 PAIR(TimeZone, Eire, "Eire"), 348 PAIR(TimeZone, Eire, tr("Eire")),
333 PAIR(TimeZone, Est, "EST"), 349 PAIR(TimeZone, Est, tr("EST")),
334 PAIR(TimeZone, Est5Edt, "EST5EDT"), 350 PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
335 PAIR(TimeZone, Gb, "GB"), 351 PAIR(TimeZone, Gb, tr("GB")),
336 PAIR(TimeZone, GbEire, "GB-Eire"), 352 PAIR(TimeZone, GbEire, tr("GB-Eire")),
337 PAIR(TimeZone, Gmt, "GMT"), 353 PAIR(TimeZone, Gmt, tr("GMT")),
338 PAIR(TimeZone, GmtPlusZero, "GMT+0"), 354 PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
339 PAIR(TimeZone, GmtMinusZero, "GMT-0"), 355 PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
340 PAIR(TimeZone, GmtZero, "GMT0"), 356 PAIR(TimeZone, GmtZero, tr("GMT0")),
341 PAIR(TimeZone, Greenwich, "Greenwich"), 357 PAIR(TimeZone, Greenwich, tr("Greenwich")),
342 PAIR(TimeZone, Hongkong, "Hongkong"), 358 PAIR(TimeZone, Hongkong, tr("Hongkong")),
343 PAIR(TimeZone, Hst, "HST"), 359 PAIR(TimeZone, Hst, tr("HST")),
344 PAIR(TimeZone, Iceland, "Iceland"), 360 PAIR(TimeZone, Iceland, tr("Iceland")),
345 PAIR(TimeZone, Iran, "Iran"), 361 PAIR(TimeZone, Iran, tr("Iran")),
346 PAIR(TimeZone, Israel, "Israel"), 362 PAIR(TimeZone, Israel, tr("Israel")),
347 PAIR(TimeZone, Jamaica, "Jamaica"), 363 PAIR(TimeZone, Jamaica, tr("Jamaica")),
348 PAIR(TimeZone, Japan, "Japan"), 364 PAIR(TimeZone, Japan, tr("Japan")),
349 PAIR(TimeZone, Kwajalein, "Kwajalein"), 365 PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
350 PAIR(TimeZone, Libya, "Libya"), 366 PAIR(TimeZone, Libya, tr("Libya")),
351 PAIR(TimeZone, Met, "MET"), 367 PAIR(TimeZone, Met, tr("MET")),
352 PAIR(TimeZone, Mst, "MST"), 368 PAIR(TimeZone, Mst, tr("MST")),
353 PAIR(TimeZone, Mst7Mdt, "MST7MDT"), 369 PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
354 PAIR(TimeZone, Navajo, "Navajo"), 370 PAIR(TimeZone, Navajo, tr("Navajo")),
355 PAIR(TimeZone, Nz, "NZ"), 371 PAIR(TimeZone, Nz, tr("NZ")),
356 PAIR(TimeZone, NzChat, "NZ-CHAT"), 372 PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
357 PAIR(TimeZone, Poland, "Poland"), 373 PAIR(TimeZone, Poland, tr("Poland")),
358 PAIR(TimeZone, Portugal, "Portugal"), 374 PAIR(TimeZone, Portugal, tr("Portugal")),
359 PAIR(TimeZone, Prc, "PRC"), 375 PAIR(TimeZone, Prc, tr("PRC")),
360 PAIR(TimeZone, Pst8Pdt, "PST8PDT"), 376 PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
361 PAIR(TimeZone, Roc, "ROC"), 377 PAIR(TimeZone, Roc, tr("ROC")),
362 PAIR(TimeZone, Rok, "ROK"), 378 PAIR(TimeZone, Rok, tr("ROK")),
363 PAIR(TimeZone, Singapore, "Singapore"), 379 PAIR(TimeZone, Singapore, tr("Singapore")),
364 PAIR(TimeZone, Turkey, "Turkey"), 380 PAIR(TimeZone, Turkey, tr("Turkey")),
365 PAIR(TimeZone, Uct, "UCT"), 381 PAIR(TimeZone, Uct, tr("UCT")),
366 PAIR(TimeZone, Universal, "Universal"), 382 PAIR(TimeZone, Universal, tr("Universal")),
367 PAIR(TimeZone, Utc, "UTC"), 383 PAIR(TimeZone, Utc, tr("UTC")),
368 PAIR(TimeZone, WSu, "W-SU"), 384 PAIR(TimeZone, WSu, tr("W-SU")),
369 PAIR(TimeZone, Wet, "WET"), 385 PAIR(TimeZone, Wet, tr("WET")),
370 PAIR(TimeZone, Zulu, "Zulu"), 386 PAIR(TimeZone, Zulu, tr("Zulu")),
371 }}); 387 }});
372 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), 388 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
373 { 389 {
374 PAIR(AudioMode, Mono, "Mono"), 390 PAIR(AudioMode, Mono, tr("Mono")),
375 PAIR(AudioMode, Stereo, "Stereo"), 391 PAIR(AudioMode, Stereo, tr("Stereo")),
376 PAIR(AudioMode, Surround, "Surround"), 392 PAIR(AudioMode, Surround, tr("Surround")),
377 }}); 393 }});
378 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), 394 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
379 { 395 {
380 PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), 396 PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")),
381 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), 397 PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")),
382 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), 398 PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")),
399 }});
400 translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
401 {
402 PAIR(ConsoleMode, Docked, tr("Docked")),
403 PAIR(ConsoleMode, Handheld, tr("Handheld")),
383 }}); 404 }});
384 translations->insert(
385 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
386 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
387 translations->insert( 405 translations->insert(
388 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), 406 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
389 { 407 {
390 PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), 408 PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")),
391 PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), 409 PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")),
392 PAIR(ConfirmStop, Ask_Never, "Never ask"), 410 PAIR(ConfirmStop, Ask_Never, tr("Never ask")),
393 }}); 411 }});
394 412
395#undef PAIR 413#undef PAIR
diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h
index 99a0e808c..d5fc3b8de 100644
--- a/src/yuzu/configuration/shared_translation.h
+++ b/src/yuzu/configuration/shared_translation.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11#include <QString> 11#include <QString>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/settings.h"
13 14
14class QWidget; 15class QWidget;
15 16
@@ -22,4 +23,46 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
22 23
23std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent); 24std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
24 25
26static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
27 {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
28 {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
29 {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
30};
31
32static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
33 {Settings::ScalingFilter::NearestNeighbor,
34 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
35 {Settings::ScalingFilter::Bilinear,
36 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
37 {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
38 {Settings::ScalingFilter::Gaussian,
39 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
40 {Settings::ScalingFilter::ScaleForce,
41 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
42 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
43};
44
45static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
46 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
47 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
48};
49
50static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
51 {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
52 {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
53 {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
54};
55
56static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
57 {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
58 {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
59 {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
60};
61
62static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
63 {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
64 {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
65 {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
66};
67
25} // namespace ConfigurationShared 68} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index ea8d7add4..941683a43 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
194 return group; 194 return group;
195 } 195 }
196 196
197 const auto get_selected = [=]() -> int { 197 const auto get_selected = [this]() -> int {
198 for (const auto& [id, button] : radio_buttons) { 198 for (const auto& [id, button] : radio_buttons) {
199 if (button->isChecked()) { 199 if (button->isChecked()) {
200 return id; 200 return id;
@@ -203,7 +203,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
203 return -1; 203 return -1;
204 }; 204 };
205 205
206 const auto set_index = [=](u32 value) { 206 const auto set_index = [this](u32 value) {
207 for (const auto& [id, button] : radio_buttons) { 207 for (const auto& [id, button] : radio_buttons) {
208 button->setChecked(id == value); 208 button->setChecked(id == value);
209 } 209 }
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 7049c57b6..6d227ef8d 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -36,10 +36,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
36 36
37bool IsDarkTheme() { 37bool IsDarkTheme() {
38 const auto& theme = UISettings::values.theme; 38 const auto& theme = UISettings::values.theme;
39 return theme == QStringLiteral("qdarkstyle") || 39 return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
40 theme == QStringLiteral("qdarkstyle_midnight_blue") || 40 theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
41 theme == QStringLiteral("colorful_dark") ||
42 theme == QStringLiteral("colorful_midnight_blue");
43} 41}
44 42
45} // namespace 43} // namespace
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 7e7d8e252..59b317135 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -278,7 +278,7 @@ void GameList::OnUpdateThemedIcons() {
278 case GameListItemType::CustomDir: { 278 case GameListItemType::CustomDir: {
279 const UISettings::GameDir& game_dir = 279 const UISettings::GameDir& game_dir =
280 UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; 280 UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
281 const QString icon_name = QFileInfo::exists(game_dir.path) 281 const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path))
282 ? QStringLiteral("folder") 282 ? QStringLiteral("folder")
283 : QStringLiteral("bad_folder"); 283 : QStringLiteral("bad_folder");
284 child->setData( 284 child->setData(
@@ -567,9 +567,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
567 QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); 567 QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
568 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 568 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
569 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 569 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
570// TODO: Implement shortcut creation for macOS
571#if !defined(__APPLE__)
570 QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); 572 QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
571 QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); 573 QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
572#ifndef WIN32
573 QAction* create_applications_menu_shortcut = 574 QAction* create_applications_menu_shortcut =
574 shortcut_menu->addAction(tr("Add to Applications Menu")); 575 shortcut_menu->addAction(tr("Add to Applications Menu"));
575#endif 576#endif
@@ -647,10 +648,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
647 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { 648 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
648 emit NavigateToGamedbEntryRequested(program_id, compatibility_list); 649 emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
649 }); 650 });
651// TODO: Implement shortcut creation for macOS
652#if !defined(__APPLE__)
650 connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { 653 connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
651 emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); 654 emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop);
652 }); 655 });
653#ifndef WIN32
654 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { 656 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
655 emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); 657 emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
656 }); 658 });
@@ -725,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
725 }); 727 });
726 728
727 connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { 729 connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
728 emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); 730 emit OpenDirectory(
731 QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path));
729 }); 732 });
730} 733}
731 734
@@ -867,7 +870,7 @@ const QStringList GameList::supported_file_extensions = {
867 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; 870 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
868 871
869void GameList::RefreshGameDirectory() { 872void GameList::RefreshGameDirectory() {
870 if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { 873 if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
871 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); 874 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
872 PopulateAsync(UISettings::values.game_dirs); 875 PopulateAsync(UISettings::values.game_dirs);
873 } 876 }
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 86a0c41d9..c330b574f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -286,13 +286,13 @@ public:
286 setData(QObject::tr("System Titles"), Qt::DisplayRole); 286 setData(QObject::tr("System Titles"), Qt::DisplayRole);
287 break; 287 break;
288 case GameListItemType::CustomDir: { 288 case GameListItemType::CustomDir: {
289 const QString icon_name = QFileInfo::exists(game_dir->path) 289 const QString path = QString::fromStdString(game_dir->path);
290 ? QStringLiteral("folder") 290 const QString icon_name =
291 : QStringLiteral("bad_folder"); 291 QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder");
292 setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( 292 setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
293 icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 293 icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
294 Qt::DecorationRole); 294 Qt::DecorationRole);
295 setData(game_dir->path, Qt::DisplayRole); 295 setData(path, Qt::DisplayRole);
296 break; 296 break;
297 } 297 }
298 default: 298 default:
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 69be21027..dc006832e 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -456,29 +456,29 @@ void GameListWorker::run() {
456 break; 456 break;
457 } 457 }
458 458
459 if (game_dir.path == QStringLiteral("SDMC")) { 459 if (game_dir.path == std::string("SDMC")) {
460 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); 460 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
461 DirEntryReady(game_list_dir); 461 DirEntryReady(game_list_dir);
462 AddTitlesToGameList(game_list_dir); 462 AddTitlesToGameList(game_list_dir);
463 } else if (game_dir.path == QStringLiteral("UserNAND")) { 463 } else if (game_dir.path == std::string("UserNAND")) {
464 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); 464 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
465 DirEntryReady(game_list_dir); 465 DirEntryReady(game_list_dir);
466 AddTitlesToGameList(game_list_dir); 466 AddTitlesToGameList(game_list_dir);
467 } else if (game_dir.path == QStringLiteral("SysNAND")) { 467 } else if (game_dir.path == std::string("SysNAND")) {
468 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); 468 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
469 DirEntryReady(game_list_dir); 469 DirEntryReady(game_list_dir);
470 AddTitlesToGameList(game_list_dir); 470 AddTitlesToGameList(game_list_dir);
471 } else { 471 } else {
472 watch_list.append(game_dir.path); 472 watch_list.append(QString::fromStdString(game_dir.path));
473 auto* const game_list_dir = new GameListDir(game_dir); 473 auto* const game_list_dir = new GameListDir(game_dir);
474 DirEntryReady(game_list_dir); 474 DirEntryReady(game_list_dir);
475 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 475 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
476 game_dir.deep_scan, game_list_dir); 476 game_list_dir);
477 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), 477 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
478 game_dir.deep_scan, game_list_dir); 478 game_list_dir);
479 } 479 }
480 } 480 }
481 481
482 RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); 482 RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); });
483 processing_completed.Set(); 483 processing_completed.Set();
484} 484}
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 6530186c1..eebfbf155 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -19,7 +19,7 @@ void HotkeyRegistry::SaveHotkeys() {
19 for (const auto& hotkey : group.second) { 19 for (const auto& hotkey : group.second) {
20 UISettings::values.shortcuts.push_back( 20 UISettings::values.shortcuts.push_back(
21 {hotkey.first, group.first, 21 {hotkey.first, group.first,
22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), 22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString().toStdString(),
23 hotkey.second.controller_keyseq, 23 hotkey.second.controller_keyseq,
24 hotkey.second.context, hotkey.second.repeat})}); 24 hotkey.second.context, hotkey.second.repeat})});
25 } 25 }
@@ -31,12 +31,12 @@ void HotkeyRegistry::LoadHotkeys() {
31 // beginGroup() 31 // beginGroup()
32 for (auto shortcut : UISettings::values.shortcuts) { 32 for (auto shortcut : UISettings::values.shortcuts) {
33 Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; 33 Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
34 if (!shortcut.shortcut.keyseq.isEmpty()) { 34 if (!shortcut.shortcut.keyseq.empty()) {
35 hk.keyseq = 35 hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq),
36 QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText); 36 QKeySequence::NativeText);
37 hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context); 37 hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
38 } 38 }
39 if (!shortcut.shortcut.controller_keyseq.isEmpty()) { 39 if (!shortcut.shortcut.controller_keyseq.empty()) {
40 hk.controller_keyseq = shortcut.shortcut.controller_keyseq; 40 hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
41 } 41 }
42 if (hk.shortcut) { 42 if (hk.shortcut) {
@@ -51,7 +51,8 @@ void HotkeyRegistry::LoadHotkeys() {
51 } 51 }
52} 52}
53 53
54QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { 54QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string& action,
55 QWidget* widget) {
55 Hotkey& hk = hotkey_groups[group][action]; 56 Hotkey& hk = hotkey_groups[group][action];
56 57
57 if (!hk.shortcut) { 58 if (!hk.shortcut) {
@@ -62,7 +63,8 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
62 return hk.shortcut; 63 return hk.shortcut;
63} 64}
64 65
65ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action, 66ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group,
67 const std::string& action,
66 Core::HID::EmulatedController* controller) { 68 Core::HID::EmulatedController* controller) {
67 Hotkey& hk = hotkey_groups[group][action]; 69 Hotkey& hk = hotkey_groups[group][action];
68 70
@@ -74,12 +76,12 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, co
74 return hk.controller_shortcut; 76 return hk.controller_shortcut;
75} 77}
76 78
77QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { 79QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) {
78 return hotkey_groups[group][action].keyseq; 80 return hotkey_groups[group][action].keyseq;
79} 81}
80 82
81Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, 83Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const std::string& group,
82 const QString& action) { 84 const std::string& action) {
83 return hotkey_groups[group][action].context; 85 return hotkey_groups[group][action].context;
84} 86}
85 87
@@ -101,10 +103,10 @@ void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) {
101 button_sequence = buttons; 103 button_sequence = buttons;
102} 104}
103 105
104void ControllerShortcut::SetKey(const QString& buttons_shortcut) { 106void ControllerShortcut::SetKey(const std::string& buttons_shortcut) {
105 ControllerButtonSequence sequence{}; 107 ControllerButtonSequence sequence{};
106 name = buttons_shortcut.toStdString(); 108 name = buttons_shortcut;
107 std::istringstream command_line(buttons_shortcut.toStdString()); 109 std::istringstream command_line(buttons_shortcut);
108 std::string line; 110 std::string line;
109 while (std::getline(command_line, line, '+')) { 111 while (std::getline(command_line, line, '+')) {
110 if (line.empty()) { 112 if (line.empty()) {
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index 56eee8d82..e11332d2e 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -33,7 +33,7 @@ public:
33 ~ControllerShortcut(); 33 ~ControllerShortcut();
34 34
35 void SetKey(const ControllerButtonSequence& buttons); 35 void SetKey(const ControllerButtonSequence& buttons);
36 void SetKey(const QString& buttons_shortcut); 36 void SetKey(const std::string& buttons_shortcut);
37 37
38 ControllerButtonSequence ButtonSequence() const; 38 ControllerButtonSequence ButtonSequence() const;
39 39
@@ -88,8 +88,8 @@ public:
88 * will be the same. Thus, you shouldn't rely on the caller really being the 88 * will be the same. Thus, you shouldn't rely on the caller really being the
89 * QShortcut's parent. 89 * QShortcut's parent.
90 */ 90 */
91 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); 91 QShortcut* GetHotkey(const std::string& group, const std::string& action, QWidget* widget);
92 ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action, 92 ControllerShortcut* GetControllerHotkey(const std::string& group, const std::string& action,
93 Core::HID::EmulatedController* controller); 93 Core::HID::EmulatedController* controller);
94 94
95 /** 95 /**
@@ -98,7 +98,7 @@ public:
98 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). 98 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
99 * @param action Name of the action (e.g. "Start Emulation", "Load Image"). 99 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
100 */ 100 */
101 QKeySequence GetKeySequence(const QString& group, const QString& action); 101 QKeySequence GetKeySequence(const std::string& group, const std::string& action);
102 102
103 /** 103 /**
104 * Returns a Qt::ShortcutContext object who can be connected to other 104 * Returns a Qt::ShortcutContext object who can be connected to other
@@ -108,20 +108,20 @@ public:
108 * "Debugger"). 108 * "Debugger").
109 * @param action Name of the action (e.g. "Start Emulation", "Load Image"). 109 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
110 */ 110 */
111 Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); 111 Qt::ShortcutContext GetShortcutContext(const std::string& group, const std::string& action);
112 112
113private: 113private:
114 struct Hotkey { 114 struct Hotkey {
115 QKeySequence keyseq; 115 QKeySequence keyseq;
116 QString controller_keyseq; 116 std::string controller_keyseq;
117 QShortcut* shortcut = nullptr; 117 QShortcut* shortcut = nullptr;
118 ControllerShortcut* controller_shortcut = nullptr; 118 ControllerShortcut* controller_shortcut = nullptr;
119 Qt::ShortcutContext context = Qt::WindowShortcut; 119 Qt::ShortcutContext context = Qt::WindowShortcut;
120 bool repeat; 120 bool repeat;
121 }; 121 };
122 122
123 using HotkeyMap = std::map<QString, Hotkey>; 123 using HotkeyMap = std::map<std::string, Hotkey>;
124 using HotkeyGroupMap = std::map<QString, HotkeyMap>; 124 using HotkeyGroupMap = std::map<std::string, HotkeyMap>;
125 125
126 HotkeyGroupMap hotkey_groups; 126 HotkeyGroupMap hotkey_groups;
127}; 127};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 0df163029..dcf68460a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,6 +10,7 @@
10#include <thread> 10#include <thread>
11#include "core/loader/nca.h" 11#include "core/loader/nca.h"
12#include "core/tools/renderdoc.h" 12#include "core/tools/renderdoc.h"
13
13#ifdef __APPLE__ 14#ifdef __APPLE__
14#include <unistd.h> // for chdir 15#include <unistd.h> // for chdir
15#endif 16#endif
@@ -46,6 +47,7 @@
46#include "core/hle/service/am/applet_ae.h" 47#include "core/hle/service/am/applet_ae.h"
47#include "core/hle/service/am/applet_oe.h" 48#include "core/hle/service/am/applet_oe.h"
48#include "core/hle/service/am/applets/applets.h" 49#include "core/hle/service/am/applets/applets.h"
50#include "core/hle/service/set/set_sys.h"
49#include "yuzu/multiplayer/state.h" 51#include "yuzu/multiplayer/state.h"
50#include "yuzu/util/controller_navigation.h" 52#include "yuzu/util/controller_navigation.h"
51 53
@@ -127,6 +129,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
127#include "core/loader/loader.h" 129#include "core/loader/loader.h"
128#include "core/perf_stats.h" 130#include "core/perf_stats.h"
129#include "core/telemetry_session.h" 131#include "core/telemetry_session.h"
132#include "frontend_common/config.h"
130#include "input_common/drivers/tas_input.h" 133#include "input_common/drivers/tas_input.h"
131#include "input_common/drivers/virtual_amiibo.h" 134#include "input_common/drivers/virtual_amiibo.h"
132#include "input_common/main.h" 135#include "input_common/main.h"
@@ -139,9 +142,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
139#include "yuzu/bootmanager.h" 142#include "yuzu/bootmanager.h"
140#include "yuzu/compatdb.h" 143#include "yuzu/compatdb.h"
141#include "yuzu/compatibility_list.h" 144#include "yuzu/compatibility_list.h"
142#include "yuzu/configuration/config.h"
143#include "yuzu/configuration/configure_dialog.h" 145#include "yuzu/configuration/configure_dialog.h"
144#include "yuzu/configuration/configure_input_per_game.h" 146#include "yuzu/configuration/configure_input_per_game.h"
147#include "yuzu/configuration/qt_config.h"
145#include "yuzu/debugger/console.h" 148#include "yuzu/debugger/console.h"
146#include "yuzu/debugger/controller.h" 149#include "yuzu/debugger/controller.h"
147#include "yuzu/debugger/profiler.h" 150#include "yuzu/debugger/profiler.h"
@@ -310,7 +313,7 @@ bool GMainWindow::CheckDarkMode() {
310#endif // __unix__ 313#endif // __unix__
311} 314}
312 315
313GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) 316GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
314 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 317 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
315 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, 318 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
316 vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 319 vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
@@ -675,7 +678,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
675 // Don't forget to apply settings. 678 // Don't forget to apply settings.
676 system->HIDCore().DisableAllControllerConfiguration(); 679 system->HIDCore().DisableAllControllerConfiguration();
677 system->ApplySettings(); 680 system->ApplySettings();
678 config->Save(); 681 config->SaveAllValues();
679 682
680 UpdateStatusButtons(); 683 UpdateStatusButtons();
681 684
@@ -1046,7 +1049,12 @@ void GMainWindow::InitializeWidgets() {
1046 statusBar()->addPermanentWidget(label); 1049 statusBar()->addPermanentWidget(label);
1047 } 1050 }
1048 1051
1049 // TODO (flTobi): Add the widget when multiplayer is fully implemented 1052 firmware_label = new QLabel();
1053 firmware_label->setObjectName(QStringLiteral("FirmwareLabel"));
1054 firmware_label->setVisible(false);
1055 firmware_label->setFocusPolicy(Qt::NoFocus);
1056 statusBar()->addPermanentWidget(firmware_label);
1057
1050 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); 1058 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
1051 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); 1059 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
1052 1060
@@ -1064,12 +1072,6 @@ void GMainWindow::InitializeWidgets() {
1064 volume_slider->setObjectName(QStringLiteral("volume_slider")); 1072 volume_slider->setObjectName(QStringLiteral("volume_slider"));
1065 volume_slider->setMaximum(200); 1073 volume_slider->setMaximum(200);
1066 volume_slider->setPageStep(5); 1074 volume_slider->setPageStep(5);
1067 connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
1068 Settings::values.audio_muted = false;
1069 const auto volume = static_cast<u8>(percentage);
1070 Settings::values.volume.SetValue(volume);
1071 UpdateVolumeUI();
1072 });
1073 volume_popup->layout()->addWidget(volume_slider); 1075 volume_popup->layout()->addWidget(volume_slider);
1074 1076
1075 volume_button = new VolumeButton(); 1077 volume_button = new VolumeButton();
@@ -1077,6 +1079,12 @@ void GMainWindow::InitializeWidgets() {
1077 volume_button->setFocusPolicy(Qt::NoFocus); 1079 volume_button->setFocusPolicy(Qt::NoFocus);
1078 volume_button->setCheckable(true); 1080 volume_button->setCheckable(true);
1079 UpdateVolumeUI(); 1081 UpdateVolumeUI();
1082 connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
1083 Settings::values.audio_muted = false;
1084 const auto volume = static_cast<u8>(percentage);
1085 Settings::values.volume.SetValue(volume);
1086 UpdateVolumeUI();
1087 });
1080 connect(volume_button, &QPushButton::clicked, this, [&] { 1088 connect(volume_button, &QPushButton::clicked, this, [&] {
1081 UpdateVolumeUI(); 1089 UpdateVolumeUI();
1082 volume_popup->setVisible(!volume_popup->isVisible()); 1090 volume_popup->setVisible(!volume_popup->isVisible());
@@ -1128,7 +1136,7 @@ void GMainWindow::InitializeWidgets() {
1128 connect(aa_status_button, &QPushButton::customContextMenuRequested, 1136 connect(aa_status_button, &QPushButton::customContextMenuRequested,
1129 [this](const QPoint& menu_location) { 1137 [this](const QPoint& menu_location) {
1130 QMenu context_menu; 1138 QMenu context_menu;
1131 for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { 1139 for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) {
1132 context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { 1140 context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] {
1133 Settings::values.anti_aliasing.SetValue(aa_text_pair.first); 1141 Settings::values.anti_aliasing.SetValue(aa_text_pair.first);
1134 UpdateAAText(); 1142 UpdateAAText();
@@ -1152,7 +1160,7 @@ void GMainWindow::InitializeWidgets() {
1152 connect(filter_status_button, &QPushButton::customContextMenuRequested, 1160 connect(filter_status_button, &QPushButton::customContextMenuRequested,
1153 [this](const QPoint& menu_location) { 1161 [this](const QPoint& menu_location) {
1154 QMenu context_menu; 1162 QMenu context_menu;
1155 for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { 1163 for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) {
1156 context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { 1164 context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] {
1157 Settings::values.scaling_filter.SetValue(filter_text_pair.first); 1165 Settings::values.scaling_filter.SetValue(filter_text_pair.first);
1158 UpdateFilterText(); 1166 UpdateFilterText();
@@ -1175,7 +1183,7 @@ void GMainWindow::InitializeWidgets() {
1175 [this](const QPoint& menu_location) { 1183 [this](const QPoint& menu_location) {
1176 QMenu context_menu; 1184 QMenu context_menu;
1177 1185
1178 for (auto const& pair : Config::use_docked_mode_texts_map) { 1186 for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
1179 context_menu.addAction(pair.second, [this, &pair] { 1187 context_menu.addAction(pair.second, [this, &pair] {
1180 if (pair.first != Settings::values.use_docked_mode.GetValue()) { 1188 if (pair.first != Settings::values.use_docked_mode.GetValue()) {
1181 OnToggleDockedMode(); 1189 OnToggleDockedMode();
@@ -1199,7 +1207,7 @@ void GMainWindow::InitializeWidgets() {
1199 [this](const QPoint& menu_location) { 1207 [this](const QPoint& menu_location) {
1200 QMenu context_menu; 1208 QMenu context_menu;
1201 1209
1202 for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { 1210 for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
1203 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { 1211 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
1204 continue; 1212 continue;
1205 } 1213 }
@@ -1228,7 +1236,8 @@ void GMainWindow::InitializeWidgets() {
1228 [this](const QPoint& menu_location) { 1236 [this](const QPoint& menu_location) {
1229 QMenu context_menu; 1237 QMenu context_menu;
1230 1238
1231 for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { 1239 for (auto const& renderer_backend_pair :
1240 ConfigurationShared::renderer_backend_texts_map) {
1232 if (renderer_backend_pair.first == Settings::RendererBackend::Null) { 1241 if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
1233 continue; 1242 continue;
1234 } 1243 }
@@ -1293,16 +1302,17 @@ void GMainWindow::InitializeRecentFileMenuActions() {
1293 1302
1294void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, 1303void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
1295 const bool tas_allowed) { 1304 const bool tas_allowed) {
1296 static const QString main_window = QStringLiteral("Main Window"); 1305 static const auto main_window = std::string("Main Window");
1297 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); 1306 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString()));
1298 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); 1307 action->setShortcutContext(
1308 hotkey_registry.GetShortcutContext(main_window, action_name.toStdString()));
1299 action->setAutoRepeat(false); 1309 action->setAutoRepeat(false);
1300 1310
1301 this->addAction(action); 1311 this->addAction(action);
1302 1312
1303 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 1313 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
1304 const auto* controller_hotkey = 1314 const auto* controller_hotkey =
1305 hotkey_registry.GetControllerHotkey(main_window, action_name, controller); 1315 hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller);
1306 connect( 1316 connect(
1307 controller_hotkey, &ControllerShortcut::Activated, this, 1317 controller_hotkey, &ControllerShortcut::Activated, this,
1308 [action, tas_allowed, this] { 1318 [action, tas_allowed, this] {
@@ -1334,10 +1344,11 @@ void GMainWindow::InitializeHotkeys() {
1334 1344
1335 static const QString main_window = QStringLiteral("Main Window"); 1345 static const QString main_window = QStringLiteral("Main Window");
1336 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { 1346 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
1337 const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); 1347 const auto* hotkey =
1348 hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this);
1338 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 1349 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
1339 const auto* controller_hotkey = 1350 const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(
1340 hotkey_registry.GetControllerHotkey(main_window, action_name, controller); 1351 main_window.toStdString(), action_name.toStdString(), controller);
1341 connect(hotkey, &QShortcut::activated, this, function); 1352 connect(hotkey, &QShortcut::activated, this, function);
1342 connect(controller_hotkey, &ControllerShortcut::Activated, this, function, 1353 connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
1343 Qt::QueuedConnection); 1354 Qt::QueuedConnection);
@@ -1574,6 +1585,7 @@ void GMainWindow::ConnectMenuEvents() {
1574 connect_menu(ui->action_Load_Cabinet_Formatter, 1585 connect_menu(ui->action_Load_Cabinet_Formatter,
1575 [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); 1586 [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
1576 connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); 1587 connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
1588 connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu);
1577 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); 1589 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
1578 1590
1579 // TAS 1591 // TAS
@@ -1601,14 +1613,13 @@ void GMainWindow::UpdateMenuState() {
1601 ui->action_Pause, 1613 ui->action_Pause,
1602 }; 1614 };
1603 1615
1604 const std::array applet_actions{ 1616 const std::array applet_actions{ui->action_Load_Album,
1605 ui->action_Load_Album, 1617 ui->action_Load_Cabinet_Nickname_Owner,
1606 ui->action_Load_Cabinet_Nickname_Owner, 1618 ui->action_Load_Cabinet_Eraser,
1607 ui->action_Load_Cabinet_Eraser, 1619 ui->action_Load_Cabinet_Restorer,
1608 ui->action_Load_Cabinet_Restorer, 1620 ui->action_Load_Cabinet_Formatter,
1609 ui->action_Load_Cabinet_Formatter, 1621 ui->action_Load_Mii_Edit,
1610 ui->action_Load_Mii_Edit, 1622 ui->action_Open_Controller_Menu};
1611 };
1612 1623
1613 for (QAction* action : running_actions) { 1624 for (QAction* action : running_actions) {
1614 action->setEnabled(emulation_running); 1625 action->setEnabled(emulation_running);
@@ -1908,12 +1919,16 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
1908void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, 1919void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
1909 StartGameType type, AmLaunchType launch_type) { 1920 StartGameType type, AmLaunchType launch_type) {
1910 LOG_INFO(Frontend, "yuzu starting..."); 1921 LOG_INFO(Frontend, "yuzu starting...");
1911 StoreRecentFile(filename); // Put the filename on top of the list 1922
1923 if (program_id == 0 ||
1924 program_id > static_cast<u64>(Service::AM::Applets::AppletProgramId::MaxProgramId)) {
1925 StoreRecentFile(filename); // Put the filename on top of the list
1926 }
1912 1927
1913 // Save configurations 1928 // Save configurations
1914 UpdateUISettings(); 1929 UpdateUISettings();
1915 game_list->SaveInterfaceLayout(); 1930 game_list->SaveInterfaceLayout();
1916 config->Save(); 1931 config->SaveAllValues();
1917 1932
1918 u64 title_id{0}; 1933 u64 title_id{0};
1919 1934
@@ -1931,7 +1946,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1931 const auto config_file_name = title_id == 0 1946 const auto config_file_name = title_id == 0
1932 ? Common::FS::PathToUTF8String(file_path.filename()) 1947 ? Common::FS::PathToUTF8String(file_path.filename())
1933 : fmt::format("{:016X}", title_id); 1948 : fmt::format("{:016X}", title_id);
1934 Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); 1949 QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
1935 system->HIDCore().ReloadInputDevices(); 1950 system->HIDCore().ReloadInputDevices();
1936 system->ApplySettings(); 1951 system->ApplySettings();
1937 } 1952 }
@@ -2156,6 +2171,10 @@ void GMainWindow::OnEmulationStopped() {
2156 emu_frametime_label->setVisible(false); 2171 emu_frametime_label->setVisible(false);
2157 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); 2172 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
2158 2173
2174 if (!firmware_label->text().isEmpty()) {
2175 firmware_label->setVisible(true);
2176 }
2177
2159 current_game_path.clear(); 2178 current_game_path.clear();
2160 2179
2161 // When closing the game, destroy the GLWindow to clear the context after the game is closed 2180 // When closing the game, destroy the GLWindow to clear the context after the game is closed
@@ -2174,6 +2193,7 @@ void GMainWindow::ShutdownGame() {
2174 return; 2193 return;
2175 } 2194 }
2176 2195
2196 play_time_manager->Stop();
2177 OnShutdownBegin(); 2197 OnShutdownBegin();
2178 OnEmulationStopTimeExpired(); 2198 OnEmulationStopTimeExpired();
2179 OnEmulationStopped(); 2199 OnEmulationStopped();
@@ -2737,7 +2757,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2737 return; 2757 return;
2738 } 2758 }
2739 2759
2740 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 2760 const auto extracted = FileSys::ExtractRomFS(romfs);
2741 if (extracted == nullptr) { 2761 if (extracted == nullptr) {
2742 failed(); 2762 failed();
2743 return; 2763 return;
@@ -2842,170 +2862,259 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
2842 QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); 2862 QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));
2843} 2863}
2844 2864
2845void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, 2865bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
2846 GameListShortcutTarget target) { 2866 const std::string& comment,
2847 // Get path to yuzu executable 2867 const std::filesystem::path& icon_path,
2848 const QStringList args = QApplication::arguments(); 2868 const std::filesystem::path& command,
2849 std::filesystem::path yuzu_command = args[0].toStdString(); 2869 const std::string& arguments, const std::string& categories,
2850 2870 const std::string& keywords, const std::string& name) try {
2851 // If relative path, make it an absolute path 2871#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
2852 if (yuzu_command.c_str()[0] == '.') { 2872 std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
2853 yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; 2873 std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
2874 if (!shortcut_stream.is_open()) {
2875 LOG_ERROR(Frontend, "Failed to create shortcut");
2876 return false;
2854 } 2877 }
2855 2878 // TODO: Migrate fmt::print to std::print in futures STD C++ 23.
2856#if defined(__linux__) 2879 fmt::print(shortcut_stream, "[Desktop Entry]\n");
2857 // Warn once if we are making a shortcut to a volatile AppImage 2880 fmt::print(shortcut_stream, "Type=Application\n");
2858 const std::string appimage_ending = 2881 fmt::print(shortcut_stream, "Version=1.0\n");
2859 std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); 2882 fmt::print(shortcut_stream, "Name={}\n", name);
2860 if (yuzu_command.string().ends_with(appimage_ending) && 2883 if (!comment.empty()) {
2861 !UISettings::values.shortcut_already_warned) { 2884 fmt::print(shortcut_stream, "Comment={}\n", comment);
2862 if (QMessageBox::warning(this, tr("Create Shortcut"), 2885 }
2863 tr("This will create a shortcut to the current AppImage. This may " 2886 if (std::filesystem::is_regular_file(icon_path)) {
2864 "not work well if you update. Continue?"), 2887 fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
2865 QMessageBox::StandardButton::Ok | 2888 }
2866 QMessageBox::StandardButton::Cancel) == 2889 fmt::print(shortcut_stream, "TryExec={}\n", command.string());
2867 QMessageBox::StandardButton::Cancel) { 2890 fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
2868 return; 2891 if (!categories.empty()) {
2892 fmt::print(shortcut_stream, "Categories={}\n", categories);
2893 }
2894 if (!keywords.empty()) {
2895 fmt::print(shortcut_stream, "Keywords={}\n", keywords);
2896 }
2897 return true;
2898#elif defined(_WIN32) // Windows
2899 HRESULT hr = CoInitialize(nullptr);
2900 if (FAILED(hr)) {
2901 LOG_ERROR(Frontend, "CoInitialize failed");
2902 return false;
2903 }
2904 SCOPE_EXIT({ CoUninitialize(); });
2905 IShellLinkW* ps1 = nullptr;
2906 IPersistFile* persist_file = nullptr;
2907 SCOPE_EXIT({
2908 if (persist_file != nullptr) {
2909 persist_file->Release();
2869 } 2910 }
2870 UISettings::values.shortcut_already_warned = true; 2911 if (ps1 != nullptr) {
2912 ps1->Release();
2913 }
2914 });
2915 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
2916 reinterpret_cast<void**>(&ps1));
2917 if (FAILED(hres)) {
2918 LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
2919 return false;
2871 } 2920 }
2872#endif // __linux__ 2921 hres = ps1->SetPath(command.c_str());
2873 2922 if (FAILED(hres)) {
2874 std::filesystem::path target_directory{}; 2923 LOG_ERROR(Frontend, "Failed to set path");
2875 2924 return false;
2876 switch (target) {
2877 case GameListShortcutTarget::Desktop: {
2878 const QString desktop_path =
2879 QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
2880 target_directory = desktop_path.toUtf8().toStdString();
2881 break;
2882 } 2925 }
2883 case GameListShortcutTarget::Applications: { 2926 if (!arguments.empty()) {
2884 const QString applications_path = 2927 hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
2885 QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); 2928 if (FAILED(hres)) {
2886 if (applications_path.isEmpty()) { 2929 LOG_ERROR(Frontend, "Failed to set arguments");
2887 const char* home = std::getenv("HOME"); 2930 return false;
2888 if (home != nullptr) {
2889 target_directory = std::filesystem::path(home) / ".local/share/applications";
2890 }
2891 } else {
2892 target_directory = applications_path.toUtf8().toStdString();
2893 } 2931 }
2894 break;
2895 } 2932 }
2896 default: 2933 if (!comment.empty()) {
2897 return; 2934 hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
2935 if (FAILED(hres)) {
2936 LOG_ERROR(Frontend, "Failed to set description");
2937 return false;
2938 }
2898 } 2939 }
2899 2940 if (std::filesystem::is_regular_file(icon_path)) {
2900 const QDir dir(QString::fromStdString(target_directory.generic_string())); 2941 hres = ps1->SetIconLocation(icon_path.c_str(), 0);
2901 if (!dir.exists()) { 2942 if (FAILED(hres)) {
2902 QMessageBox::critical(this, tr("Create Shortcut"), 2943 LOG_ERROR(Frontend, "Failed to set icon location");
2903 tr("Cannot create shortcut. Path \"%1\" does not exist.") 2944 return false;
2904 .arg(QString::fromStdString(target_directory.generic_string())), 2945 }
2905 QMessageBox::StandardButton::Ok);
2906 return;
2907 } 2946 }
2947 hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
2948 if (FAILED(hres)) {
2949 LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
2950 return false;
2951 }
2952 hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
2953 if (FAILED(hres)) {
2954 LOG_ERROR(Frontend, "Failed to save shortcut");
2955 return false;
2956 }
2957 return true;
2958#else // Unsupported platform
2959 return false;
2960#endif
2961} catch (const std::exception& e) {
2962 LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
2963 return false;
2964}
2965// Messages in pre-defined message boxes for less code spaghetti
2966bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
2967 int result = 0;
2968 QMessageBox::StandardButtons buttons;
2969 switch (imsg) {
2970 case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES:
2971 buttons = QMessageBox::Yes | QMessageBox::No;
2972 result =
2973 QMessageBox::information(parent, tr("Create Shortcut"),
2974 tr("Do you want to launch the game in fullscreen?"), buttons);
2975 return result == QMessageBox::Yes;
2976 case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
2977 QMessageBox::information(parent, tr("Create Shortcut"),
2978 tr("Successfully created a shortcut to %1").arg(game_title));
2979 return false;
2980 case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING:
2981 buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
2982 result =
2983 QMessageBox::warning(this, tr("Create Shortcut"),
2984 tr("This will create a shortcut to the current AppImage. This may "
2985 "not work well if you update. Continue?"),
2986 buttons);
2987 return result == QMessageBox::Ok;
2988 default:
2989 buttons = QMessageBox::Ok;
2990 QMessageBox::critical(parent, tr("Create Shortcut"),
2991 tr("Failed to create a shortcut to %1").arg(game_title), buttons);
2992 return false;
2993 }
2994}
2908 2995
2909 const std::string game_file_name = std::filesystem::path(game_path).filename().string(); 2996bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
2910 // Determine full paths for icon and shortcut 2997 std::filesystem::path& out_icon_path) {
2911#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) 2998 // Get path to Yuzu icons directory & icon extension
2912 const char* home = std::getenv("HOME"); 2999 std::string ico_extension = "png";
2913 const std::filesystem::path home_path = (home == nullptr ? "~" : home); 3000#if defined(_WIN32)
2914 const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); 3001 out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir);
2915 3002 ico_extension = "ico";
2916 std::filesystem::path system_icons_path = 3003#elif defined(__linux__) || defined(__FreeBSD__)
2917 (xdg_data_home == nullptr ? home_path / ".local/share/" 3004 out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
2918 : std::filesystem::path(xdg_data_home)) / 3005#endif
2919 "icons/hicolor/256x256"; 3006 // Create icons directory if it doesn't exist
2920 if (!Common::FS::CreateDirs(system_icons_path)) { 3007 if (!Common::FS::CreateDirs(out_icon_path)) {
2921 QMessageBox::critical( 3008 QMessageBox::critical(
2922 this, tr("Create Icon"), 3009 this, tr("Create Icon"),
2923 tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") 3010 tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
2924 .arg(QString::fromStdString(system_icons_path)), 3011 .arg(QString::fromStdString(out_icon_path.string())),
2925 QMessageBox::StandardButton::Ok); 3012 QMessageBox::StandardButton::Ok);
2926 return; 3013 out_icon_path.clear();
3014 return false;
2927 } 3015 }
2928 std::filesystem::path icon_path =
2929 system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name)
2930 : fmt::format("yuzu-{:016X}.png", program_id));
2931 const std::filesystem::path shortcut_path =
2932 target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name)
2933 : fmt::format("yuzu-{:016X}.desktop", program_id));
2934#elif defined(WIN32)
2935 std::filesystem::path icons_path =
2936 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir);
2937 std::filesystem::path icon_path =
2938 icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name)
2939 : fmt::format("yuzu-{:016X}.ico", program_id)));
2940#else
2941 std::string icon_extension;
2942#endif
2943
2944 // Get title from game file
2945 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
2946 system->GetContentProvider()};
2947 const auto control = pm.GetControlMetadata();
2948 const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
2949 3016
2950 std::string title{fmt::format("{:016X}", program_id)}; 3017 // Create icon file path
2951 3018 out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension)
2952 if (control.first != nullptr) { 3019 : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension));
2953 title = control.first->GetApplicationName(); 3020 return true;
2954 } else { 3021}
2955 loader->ReadTitle(title);
2956 }
2957 3022
2958 // Get icon from game file 3023void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
2959 std::vector<u8> icon_image_file{}; 3024 GameListShortcutTarget target) {
2960 if (control.second != nullptr) { 3025 std::string game_title;
2961 icon_image_file = control.second->ReadAllBytes(); 3026 QString qt_game_title;
2962 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { 3027 std::filesystem::path out_icon_path;
2963 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); 3028 // Get path to yuzu executable
3029 const QStringList args = QApplication::arguments();
3030 std::filesystem::path yuzu_command = args[0].toStdString();
3031 // If relative path, make it an absolute path
3032 if (yuzu_command.c_str()[0] == '.') {
3033 yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
2964 } 3034 }
2965 3035 // Shortcut path
2966 QImage icon_data = 3036 std::filesystem::path shortcut_path{};
2967 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); 3037 if (target == GameListShortcutTarget::Desktop) {
2968#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) 3038 shortcut_path =
2969 // Convert and write the icon as a PNG 3039 QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
2970 if (!icon_data.save(QString::fromStdString(icon_path.string()))) { 3040 } else if (target == GameListShortcutTarget::Applications) {
2971 LOG_ERROR(Frontend, "Could not write icon as PNG to file"); 3041 shortcut_path =
3042 QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
3043 }
3044 // Icon path and title
3045 if (std::filesystem::exists(shortcut_path)) {
3046 // Get title from game file
3047 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
3048 system->GetContentProvider()};
3049 const auto control = pm.GetControlMetadata();
3050 const auto loader =
3051 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
3052 game_title = fmt::format("{:016X}", program_id);
3053 if (control.first != nullptr) {
3054 game_title = control.first->GetApplicationName();
3055 } else {
3056 loader->ReadTitle(game_title);
3057 }
3058 // Delete illegal characters from title
3059 const std::string illegal_chars = "<>:\"/\\|?*.";
3060 for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
3061 if (illegal_chars.find(*it) != std::string::npos) {
3062 game_title.erase(it.base() - 1);
3063 }
3064 }
3065 qt_game_title = QString::fromStdString(game_title);
3066 // Get icon from game file
3067 std::vector<u8> icon_image_file{};
3068 if (control.second != nullptr) {
3069 icon_image_file = control.second->ReadAllBytes();
3070 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
3071 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
3072 }
3073 QImage icon_data =
3074 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
3075 if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
3076 if (!SaveIconToFile(out_icon_path, icon_data)) {
3077 LOG_ERROR(Frontend, "Could not write icon to file");
3078 }
3079 }
2972 } else { 3080 } else {
2973 LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); 3081 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
2974 } 3082 qt_game_title);
2975#elif defined(WIN32) 3083 LOG_ERROR(Frontend, "Invalid shortcut target");
2976 if (!SaveIconToFile(icon_path.string(), icon_data)) {
2977 LOG_ERROR(Frontend, "Could not write icon to file");
2978 return; 3084 return;
2979 } 3085 }
3086#if defined(__linux__)
3087 // Special case for AppImages
3088 // Warn once if we are making a shortcut to a volatile AppImage
3089 const std::string appimage_ending =
3090 std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
3091 if (yuzu_command.string().ends_with(appimage_ending) &&
3092 !UISettings::values.shortcut_already_warned) {
3093 if (GMainWindow::CreateShortcutMessagesGUI(
3094 this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
3095 return;
3096 }
3097 UISettings::values.shortcut_already_warned = true;
3098 }
2980#endif // __linux__ 3099#endif // __linux__
2981 3100 // Create shortcut
2982#ifdef _WIN32 3101 std::string arguments = fmt::format("-g \"{:s}\"", game_path);
2983 // Replace characters that are illegal in Windows filenames by a dash 3102 if (GMainWindow::CreateShortcutMessagesGUI(
2984 const std::string illegal_chars = "<>:\"/\\|?*"; 3103 this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
2985 for (char c : illegal_chars) { 3104 arguments = "-f " + arguments;
2986 std::replace(title.begin(), title.end(), c, '_');
2987 } 3105 }
2988 const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); 3106 const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title);
2989#endif
2990
2991 const std::string comment =
2992 tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString();
2993 const std::string arguments = fmt::format("-g \"{:s}\"", game_path);
2994 const std::string categories = "Game;Emulator;Qt;"; 3107 const std::string categories = "Game;Emulator;Qt;";
2995 const std::string keywords = "Switch;Nintendo;"; 3108 const std::string keywords = "Switch;Nintendo;";
2996 3109
2997 if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), 3110 if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command,
2998 yuzu_command.string(), arguments, categories, keywords)) { 3111 arguments, categories, keywords, game_title)) {
2999 QMessageBox::critical(this, tr("Create Shortcut"), 3112 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
3000 tr("Failed to create a shortcut at %1") 3113 qt_game_title);
3001 .arg(QString::fromStdString(shortcut_path.string())));
3002 return; 3114 return;
3003 } 3115 }
3004 3116 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
3005 LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); 3117 qt_game_title);
3006 QMessageBox::information(
3007 this, tr("Create Shortcut"),
3008 tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title)));
3009} 3118}
3010 3119
3011void GMainWindow::OnGameListOpenDirectory(const QString& directory) { 3120void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
@@ -3040,7 +3149,7 @@ void GMainWindow::OnGameListAddDirectory() {
3040 return; 3149 return;
3041 } 3150 }
3042 3151
3043 UISettings::GameDir game_dir{dir_path, false, true}; 3152 UISettings::GameDir game_dir{dir_path.toStdString(), false, true};
3044 if (!UISettings::values.game_dirs.contains(game_dir)) { 3153 if (!UISettings::values.game_dirs.contains(game_dir)) {
3045 UISettings::values.game_dirs.append(game_dir); 3154 UISettings::values.game_dirs.append(game_dir);
3046 game_list->PopulateAsync(UISettings::values.game_dirs); 3155 game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -3086,14 +3195,14 @@ void GMainWindow::OnMenuLoadFile() {
3086 "%1 is an identifier for the Switch executable file extensions.") 3195 "%1 is an identifier for the Switch executable file extensions.")
3087 .arg(extensions); 3196 .arg(extensions);
3088 const QString filename = QFileDialog::getOpenFileName( 3197 const QString filename = QFileDialog::getOpenFileName(
3089 this, tr("Load File"), UISettings::values.roms_path, file_filter); 3198 this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter);
3090 is_load_file_select_active = false; 3199 is_load_file_select_active = false;
3091 3200
3092 if (filename.isEmpty()) { 3201 if (filename.isEmpty()) {
3093 return; 3202 return;
3094 } 3203 }
3095 3204
3096 UISettings::values.roms_path = QFileInfo(filename).path(); 3205 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
3097 BootGame(filename); 3206 BootGame(filename);
3098} 3207}
3099 3208
@@ -3126,7 +3235,8 @@ void GMainWindow::OnMenuInstallToNAND() {
3126 "Image (*.xci)"); 3235 "Image (*.xci)");
3127 3236
3128 QStringList filenames = QFileDialog::getOpenFileNames( 3237 QStringList filenames = QFileDialog::getOpenFileNames(
3129 this, tr("Install Files"), UISettings::values.roms_path, file_filter); 3238 this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path),
3239 file_filter);
3130 3240
3131 if (filenames.isEmpty()) { 3241 if (filenames.isEmpty()) {
3132 return; 3242 return;
@@ -3144,7 +3254,7 @@ void GMainWindow::OnMenuInstallToNAND() {
3144 } 3254 }
3145 3255
3146 // Save folder location of the first selected file 3256 // Save folder location of the first selected file
3147 UISettings::values.roms_path = QFileInfo(filenames[0]).path(); 3257 UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString();
3148 3258
3149 int remaining = filenames.size(); 3259 int remaining = filenames.size();
3150 3260
@@ -3484,12 +3594,12 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
3484} 3594}
3485 3595
3486void GMainWindow::OnExit() { 3596void GMainWindow::OnExit() {
3487 OnStopGame(); 3597 ShutdownGame();
3488} 3598}
3489 3599
3490void GMainWindow::OnSaveConfig() { 3600void GMainWindow::OnSaveConfig() {
3491 system->ApplySettings(); 3601 system->ApplySettings();
3492 config->Save(); 3602 config->SaveAllValues();
3493} 3603}
3494 3604
3495void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 3605void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
@@ -3745,7 +3855,7 @@ void GMainWindow::OnConfigure() {
3745 3855
3746 Settings::values.disabled_addons.clear(); 3856 Settings::values.disabled_addons.clear();
3747 3857
3748 config = std::make_unique<Config>(); 3858 config = std::make_unique<QtConfig>();
3749 UISettings::values.reset_to_defaults = false; 3859 UISettings::values.reset_to_defaults = false;
3750 3860
3751 UISettings::values.game_dirs = std::move(old_game_dirs); 3861 UISettings::values.game_dirs = std::move(old_game_dirs);
@@ -3780,7 +3890,7 @@ void GMainWindow::OnConfigure() {
3780 3890
3781 UISettings::values.configuration_applied = false; 3891 UISettings::values.configuration_applied = false;
3782 3892
3783 config->Save(); 3893 config->SaveAllValues();
3784 3894
3785 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { 3895 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
3786 render_window->installEventFilter(render_window); 3896 render_window->installEventFilter(render_window);
@@ -3996,68 +4106,8 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
3996 UISettings::values.configuration_applied = false; 4106 UISettings::values.configuration_applied = false;
3997 4107
3998 if (!is_powered_on) { 4108 if (!is_powered_on) {
3999 config->Save(); 4109 config->SaveAllValues();
4000 }
4001}
4002
4003bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title,
4004 const std::string& comment, const std::string& icon_path,
4005 const std::string& command, const std::string& arguments,
4006 const std::string& categories, const std::string& keywords) {
4007#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
4008 // This desktop file template was writing referencing
4009 // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
4010 std::string shortcut_contents{};
4011 shortcut_contents.append("[Desktop Entry]\n");
4012 shortcut_contents.append("Type=Application\n");
4013 shortcut_contents.append("Version=1.0\n");
4014 shortcut_contents.append(fmt::format("Name={:s}\n", title));
4015 shortcut_contents.append(fmt::format("Comment={:s}\n", comment));
4016 shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path));
4017 shortcut_contents.append(fmt::format("TryExec={:s}\n", command));
4018 shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments));
4019 shortcut_contents.append(fmt::format("Categories={:s}\n", categories));
4020 shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords));
4021
4022 std::ofstream shortcut_stream(shortcut_path);
4023 if (!shortcut_stream.is_open()) {
4024 LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path);
4025 return false;
4026 } 4110 }
4027 shortcut_stream << shortcut_contents;
4028 shortcut_stream.close();
4029
4030 return true;
4031#elif defined(WIN32)
4032 IShellLinkW* shell_link;
4033 auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
4034 (void**)&shell_link);
4035 if (FAILED(hres)) {
4036 return false;
4037 }
4038 shell_link->SetPath(
4039 Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to
4040 shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data());
4041 shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data());
4042 shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0);
4043
4044 IPersistFile* persist_file;
4045 hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file);
4046 if (FAILED(hres)) {
4047 return false;
4048 }
4049
4050 hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE);
4051 if (FAILED(hres)) {
4052 return false;
4053 }
4054
4055 persist_file->Release();
4056 shell_link->Release();
4057
4058 return true;
4059#endif
4060 return false;
4061} 4111}
4062 4112
4063void GMainWindow::OnLoadAmiibo() { 4113void GMainWindow::OnLoadAmiibo() {
@@ -4098,7 +4148,6 @@ void GMainWindow::OnLoadAmiibo() {
4098bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, 4148bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
4099 QMessageBox::StandardButtons buttons, 4149 QMessageBox::StandardButtons buttons,
4100 QMessageBox::StandardButton defaultButton) { 4150 QMessageBox::StandardButton defaultButton) {
4101
4102 QMessageBox* box_dialog = new QMessageBox(parent); 4151 QMessageBox* box_dialog = new QMessageBox(parent);
4103 box_dialog->setWindowTitle(title); 4152 box_dialog->setWindowTitle(title);
4104 box_dialog->setText(text); 4153 box_dialog->setText(text);
@@ -4272,7 +4321,7 @@ void GMainWindow::OnToggleStatusBar() {
4272} 4321}
4273 4322
4274void GMainWindow::OnAlbum() { 4323void GMainWindow::OnAlbum() {
4275 constexpr u64 AlbumId = 0x010000000000100Dull; 4324 constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer);
4276 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4325 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4277 if (!bis_system) { 4326 if (!bis_system) {
4278 QMessageBox::warning(this, tr("No firmware available"), 4327 QMessageBox::warning(this, tr("No firmware available"),
@@ -4290,12 +4339,12 @@ void GMainWindow::OnAlbum() {
4290 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); 4339 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer);
4291 4340
4292 const auto filename = QString::fromStdString(album_nca->GetFullPath()); 4341 const auto filename = QString::fromStdString(album_nca->GetFullPath());
4293 UISettings::values.roms_path = QFileInfo(filename).path(); 4342 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4294 BootGame(filename); 4343 BootGame(filename, AlbumId);
4295} 4344}
4296 4345
4297void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { 4346void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
4298 constexpr u64 CabinetId = 0x0100000000001002ull; 4347 constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet);
4299 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4348 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4300 if (!bis_system) { 4349 if (!bis_system) {
4301 QMessageBox::warning(this, tr("No firmware available"), 4350 QMessageBox::warning(this, tr("No firmware available"),
@@ -4314,12 +4363,12 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
4314 system->GetAppletManager().SetCabinetMode(mode); 4363 system->GetAppletManager().SetCabinetMode(mode);
4315 4364
4316 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); 4365 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
4317 UISettings::values.roms_path = QFileInfo(filename).path(); 4366 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4318 BootGame(filename); 4367 BootGame(filename, CabinetId);
4319} 4368}
4320 4369
4321void GMainWindow::OnMiiEdit() { 4370void GMainWindow::OnMiiEdit() {
4322 constexpr u64 MiiEditId = 0x0100000000001009ull; 4371 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
4323 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4372 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4324 if (!bis_system) { 4373 if (!bis_system) {
4325 QMessageBox::warning(this, tr("No firmware available"), 4374 QMessageBox::warning(this, tr("No firmware available"),
@@ -4337,8 +4386,33 @@ void GMainWindow::OnMiiEdit() {
4337 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); 4386 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit);
4338 4387
4339 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); 4388 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
4340 UISettings::values.roms_path = QFileInfo(filename).path(); 4389 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4341 BootGame(filename); 4390 BootGame(filename, MiiEditId);
4391}
4392
4393void GMainWindow::OnOpenControllerMenu() {
4394 constexpr u64 ControllerAppletId =
4395 static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller);
4396 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4397 if (!bis_system) {
4398 QMessageBox::warning(this, tr("No firmware available"),
4399 tr("Please install the firmware to use the Controller Menu."));
4400 return;
4401 }
4402
4403 auto controller_applet_nca =
4404 bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
4405 if (!controller_applet_nca) {
4406 QMessageBox::warning(this, tr("Controller Applet"),
4407 tr("Controller Menu is not available. Please reinstall firmware."));
4408 return;
4409 }
4410
4411 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller);
4412
4413 const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
4414 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4415 BootGame(filename, ControllerAppletId);
4342} 4416}
4343 4417
4344void GMainWindow::OnCaptureScreenshot() { 4418void GMainWindow::OnCaptureScreenshot() {
@@ -4527,11 +4601,13 @@ void GMainWindow::UpdateStatusBar() {
4527 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); 4601 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
4528 game_fps_label->setVisible(true); 4602 game_fps_label->setVisible(true);
4529 emu_frametime_label->setVisible(true); 4603 emu_frametime_label->setVisible(true);
4604 firmware_label->setVisible(false);
4530} 4605}
4531 4606
4532void GMainWindow::UpdateGPUAccuracyButton() { 4607void GMainWindow::UpdateGPUAccuracyButton() {
4533 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); 4608 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
4534 const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; 4609 const auto gpu_accuracy_text =
4610 ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
4535 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); 4611 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
4536 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); 4612 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
4537} 4613}
@@ -4540,31 +4616,32 @@ void GMainWindow::UpdateDockedButton() {
4540 const auto console_mode = Settings::values.use_docked_mode.GetValue(); 4616 const auto console_mode = Settings::values.use_docked_mode.GetValue();
4541 dock_status_button->setChecked(Settings::IsDockedMode()); 4617 dock_status_button->setChecked(Settings::IsDockedMode());
4542 dock_status_button->setText( 4618 dock_status_button->setText(
4543 Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); 4619 ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
4544} 4620}
4545 4621
4546void GMainWindow::UpdateAPIText() { 4622void GMainWindow::UpdateAPIText() {
4547 const auto api = Settings::values.renderer_backend.GetValue(); 4623 const auto api = Settings::values.renderer_backend.GetValue();
4548 const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; 4624 const auto renderer_status_text =
4625 ConfigurationShared::renderer_backend_texts_map.find(api)->second;
4549 renderer_status_button->setText( 4626 renderer_status_button->setText(
4550 api == Settings::RendererBackend::OpenGL 4627 api == Settings::RendererBackend::OpenGL
4551 ? tr("%1 %2").arg( 4628 ? tr("%1 %2").arg(renderer_status_text.toUpper(),
4552 renderer_status_text.toUpper(), 4629 ConfigurationShared::shader_backend_texts_map
4553 Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) 4630 .find(Settings::values.shader_backend.GetValue())
4554 ->second) 4631 ->second)
4555 : renderer_status_text.toUpper()); 4632 : renderer_status_text.toUpper());
4556} 4633}
4557 4634
4558void GMainWindow::UpdateFilterText() { 4635void GMainWindow::UpdateFilterText() {
4559 const auto filter = Settings::values.scaling_filter.GetValue(); 4636 const auto filter = Settings::values.scaling_filter.GetValue();
4560 const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; 4637 const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second;
4561 filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") 4638 filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
4562 : filter_text.toUpper()); 4639 : filter_text.toUpper());
4563} 4640}
4564 4641
4565void GMainWindow::UpdateAAText() { 4642void GMainWindow::UpdateAAText() {
4566 const auto aa_mode = Settings::values.anti_aliasing.GetValue(); 4643 const auto aa_mode = Settings::values.anti_aliasing.GetValue();
4567 const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; 4644 const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second;
4568 aa_status_button->setText(aa_mode == Settings::AntiAliasing::None 4645 aa_status_button->setText(aa_mode == Settings::AntiAliasing::None
4569 ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) 4646 ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA"))
4570 : aa_text.toUpper()); 4647 : aa_text.toUpper());
@@ -4744,6 +4821,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
4744 "games.")); 4821 "games."));
4745 } 4822 }
4746 4823
4824 SetFirmwareVersion();
4825
4747 if (behavior == ReinitializeKeyBehavior::Warning) { 4826 if (behavior == ReinitializeKeyBehavior::Warning) {
4748 game_list->PopulateAsync(UISettings::values.game_dirs); 4827 game_list->PopulateAsync(UISettings::values.game_dirs);
4749 } 4828 }
@@ -4771,7 +4850,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
4771} 4850}
4772 4851
4773bool GMainWindow::CheckFirmwarePresence() { 4852bool GMainWindow::CheckFirmwarePresence() {
4774 constexpr u64 MiiEditId = 0x0100000000001009ull; 4853 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
4775 4854
4776 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4855 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4777 if (!bis_system) { 4856 if (!bis_system) {
@@ -4786,6 +4865,28 @@ bool GMainWindow::CheckFirmwarePresence() {
4786 return true; 4865 return true;
4787} 4866}
4788 4867
4868void GMainWindow::SetFirmwareVersion() {
4869 Service::Set::FirmwareVersionFormat firmware_data{};
4870 const auto result = Service::Set::GetFirmwareVersionImpl(
4871 firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2);
4872
4873 if (result.IsError() || !CheckFirmwarePresence()) {
4874 LOG_INFO(Frontend, "Installed firmware: No firmware available");
4875 firmware_label->setVisible(false);
4876 return;
4877 }
4878
4879 firmware_label->setVisible(true);
4880
4881 const std::string display_version(firmware_data.display_version.data());
4882 const std::string display_title(firmware_data.display_title.data());
4883
4884 LOG_INFO(Frontend, "Installed firmware: {}", display_title);
4885
4886 firmware_label->setText(QString::fromStdString(display_version));
4887 firmware_label->setToolTip(QString::fromStdString(display_title));
4888}
4889
4789bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, 4890bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
4790 u64* selected_title_id, u8* selected_content_record_type) { 4891 u64* selected_title_id, u8* selected_content_record_type) {
4791 using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; 4892 using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
@@ -4847,7 +4948,12 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe
4847} 4948}
4848 4949
4849bool GMainWindow::ConfirmClose() { 4950bool GMainWindow::ConfirmClose() {
4850 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { 4951 if (emu_thread == nullptr ||
4952 UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
4953 return true;
4954 }
4955 if (!system->GetExitLocked() &&
4956 UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) {
4851 return true; 4957 return true;
4852 } 4958 }
4853 const auto text = tr("Are you sure you want to close yuzu?"); 4959 const auto text = tr("Are you sure you want to close yuzu?");
@@ -4862,6 +4968,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
4862 4968
4863 UpdateUISettings(); 4969 UpdateUISettings();
4864 game_list->SaveInterfaceLayout(); 4970 game_list->SaveInterfaceLayout();
4971 UISettings::SaveWindowState();
4865 hotkey_registry.SaveHotkeys(); 4972 hotkey_registry.SaveHotkeys();
4866 4973
4867 // Unload controllers early 4974 // Unload controllers early
@@ -4952,7 +5059,7 @@ bool GMainWindow::ConfirmChangeGame() {
4952} 5059}
4953 5060
4954bool GMainWindow::ConfirmForceLockedExit() { 5061bool GMainWindow::ConfirmForceLockedExit() {
4955 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { 5062 if (emu_thread == nullptr) {
4956 return true; 5063 return true;
4957 } 5064 }
4958 const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" 5065 const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"
@@ -5016,9 +5123,9 @@ static void AdjustLinkColor() {
5016} 5123}
5017 5124
5018void GMainWindow::UpdateUITheme() { 5125void GMainWindow::UpdateUITheme() {
5019 const QString default_theme = 5126 const QString default_theme = QString::fromUtf8(
5020 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); 5127 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
5021 QString current_theme = UISettings::values.theme; 5128 QString current_theme = QString::fromStdString(UISettings::values.theme);
5022 5129
5023 if (current_theme.isEmpty()) { 5130 if (current_theme.isEmpty()) {
5024 current_theme = default_theme; 5131 current_theme = default_theme;
@@ -5046,7 +5153,7 @@ void GMainWindow::UpdateUITheme() {
5046 QFile f(theme_uri); 5153 QFile f(theme_uri);
5047 if (!f.open(QFile::ReadOnly | QFile::Text)) { 5154 if (!f.open(QFile::ReadOnly | QFile::Text)) {
5048 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", 5155 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
5049 UISettings::values.theme.toStdString()); 5156 UISettings::values.theme);
5050 current_theme = default_theme; 5157 current_theme = default_theme;
5051 } 5158 }
5052 } 5159 }
@@ -5059,7 +5166,7 @@ void GMainWindow::UpdateUITheme() {
5059 setStyleSheet(ts.readAll()); 5166 setStyleSheet(ts.readAll());
5060 } else { 5167 } else {
5061 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", 5168 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
5062 UISettings::values.theme.toStdString()); 5169 UISettings::values.theme);
5063 qApp->setStyleSheet({}); 5170 qApp->setStyleSheet({});
5064 setStyleSheet({}); 5171 setStyleSheet({});
5065 } 5172 }
@@ -5068,27 +5175,28 @@ void GMainWindow::UpdateUITheme() {
5068void GMainWindow::LoadTranslation() { 5175void GMainWindow::LoadTranslation() {
5069 bool loaded; 5176 bool loaded;
5070 5177
5071 if (UISettings::values.language.isEmpty()) { 5178 if (UISettings::values.language.empty()) {
5072 // If the selected language is empty, use system locale 5179 // If the selected language is empty, use system locale
5073 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); 5180 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
5074 } else { 5181 } else {
5075 // Otherwise load from the specified file 5182 // Otherwise load from the specified file
5076 loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); 5183 loaded = translator.load(QString::fromStdString(UISettings::values.language),
5184 QStringLiteral(":/languages/"));
5077 } 5185 }
5078 5186
5079 if (loaded) { 5187 if (loaded) {
5080 qApp->installTranslator(&translator); 5188 qApp->installTranslator(&translator);
5081 } else { 5189 } else {
5082 UISettings::values.language = QStringLiteral("en"); 5190 UISettings::values.language = std::string("en");
5083 } 5191 }
5084} 5192}
5085 5193
5086void GMainWindow::OnLanguageChanged(const QString& locale) { 5194void GMainWindow::OnLanguageChanged(const QString& locale) {
5087 if (UISettings::values.language != QStringLiteral("en")) { 5195 if (UISettings::values.language != std::string("en")) {
5088 qApp->removeTranslator(&translator); 5196 qApp->removeTranslator(&translator);
5089 } 5197 }
5090 5198
5091 UISettings::values.language = locale; 5199 UISettings::values.language = locale.toStdString();
5092 LoadTranslation(); 5200 LoadTranslation();
5093 ui->retranslateUi(this); 5201 ui->retranslateUi(this);
5094 multiplayer_state->retranslateUi(); 5202 multiplayer_state->retranslateUi();
@@ -5114,7 +5222,7 @@ void GMainWindow::changeEvent(QEvent* event) {
5114 // UpdateUITheme is a decent work around 5222 // UpdateUITheme is a decent work around
5115 if (event->type() == QEvent::PaletteChange) { 5223 if (event->type() == QEvent::PaletteChange) {
5116 const QPalette test_palette(qApp->palette()); 5224 const QPalette test_palette(qApp->palette());
5117 const QString current_theme = UISettings::values.theme; 5225 const QString current_theme = QString::fromStdString(UISettings::values.theme);
5118 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too 5226 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
5119 static QColor last_window_color; 5227 static QColor last_window_color;
5120 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); 5228 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
@@ -5208,7 +5316,8 @@ static void SetHighDPIAttributes() {
5208} 5316}
5209 5317
5210int main(int argc, char* argv[]) { 5318int main(int argc, char* argv[]) {
5211 std::unique_ptr<Config> config = std::make_unique<Config>(); 5319 std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>();
5320 UISettings::RestoreWindowState(config);
5212 bool has_broken_vulkan = false; 5321 bool has_broken_vulkan = false;
5213 bool is_child = false; 5322 bool is_child = false;
5214 if (CheckEnvVars(&is_child)) { 5323 if (CheckEnvVars(&is_child)) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index f9c6efe4f..e99d58995 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <optional> 7#include <optional>
8 8
9#include <filesystem>
9#include <QMainWindow> 10#include <QMainWindow>
10#include <QMessageBox> 11#include <QMessageBox>
11#include <QPushButton> 12#include <QPushButton>
@@ -14,6 +15,7 @@
14 15
15#include "common/announce_multiplayer_room.h" 16#include "common/announce_multiplayer_room.h"
16#include "common/common_types.h" 17#include "common/common_types.h"
18#include "configuration/qt_config.h"
17#include "input_common/drivers/tas_input.h" 19#include "input_common/drivers/tas_input.h"
18#include "yuzu/compatibility_list.h" 20#include "yuzu/compatibility_list.h"
19#include "yuzu/hotkeys.h" 21#include "yuzu/hotkeys.h"
@@ -25,7 +27,7 @@
25#include <QtDBus/QtDBus> 27#include <QtDBus/QtDBus>
26#endif 28#endif
27 29
28class Config; 30class QtConfig;
29class ClickableLabel; 31class ClickableLabel;
30class EmuThread; 32class EmuThread;
31class GameList; 33class GameList;
@@ -174,10 +176,17 @@ class GMainWindow : public QMainWindow {
174 UI_EMU_STOPPING, 176 UI_EMU_STOPPING,
175 }; 177 };
176 178
179 enum {
180 CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
181 CREATE_SHORTCUT_MSGBOX_SUCCESS,
182 CREATE_SHORTCUT_MSGBOX_ERROR,
183 CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
184 };
185
177public: 186public:
178 void filterBarSetChecked(bool state); 187 void filterBarSetChecked(bool state);
179 void UpdateUITheme(); 188 void UpdateUITheme();
180 explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); 189 explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
181 ~GMainWindow() override; 190 ~GMainWindow() override;
182 191
183 bool DropAction(QDropEvent* event); 192 bool DropAction(QDropEvent* event);
@@ -402,6 +411,7 @@ private slots:
402 void OnAlbum(); 411 void OnAlbum();
403 void OnCabinet(Service::NFP::CabinetMode mode); 412 void OnCabinet(Service::NFP::CabinetMode mode);
404 void OnMiiEdit(); 413 void OnMiiEdit();
414 void OnOpenControllerMenu();
405 void OnCaptureScreenshot(); 415 void OnCaptureScreenshot();
406 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 416 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
407 void OnLanguageChanged(const QString& locale); 417 void OnLanguageChanged(const QString& locale);
@@ -448,6 +458,7 @@ private:
448 bool CheckDarkMode(); 458 bool CheckDarkMode();
449 bool CheckSystemArchiveDecryption(); 459 bool CheckSystemArchiveDecryption();
450 bool CheckFirmwarePresence(); 460 bool CheckFirmwarePresence();
461 void SetFirmwareVersion();
451 void ConfigureFilesystemProvider(const std::string& filepath); 462 void ConfigureFilesystemProvider(const std::string& filepath);
452 /** 463 /**
453 * Open (or not) the right confirm dialog based on current setting and game exit lock 464 * Open (or not) the right confirm dialog based on current setting and game exit lock
@@ -456,11 +467,14 @@ private:
456 bool ConfirmShutdownGame(); 467 bool ConfirmShutdownGame();
457 468
458 QString GetTasStateDescription() const; 469 QString GetTasStateDescription() const;
459 bool CreateShortcut(const std::string& shortcut_path, const std::string& title, 470 bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
460 const std::string& comment, const std::string& icon_path, 471 bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
461 const std::string& command, const std::string& arguments, 472 std::filesystem::path& out_icon_path);
462 const std::string& categories, const std::string& keywords); 473 bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment,
463 474 const std::filesystem::path& icon_path,
475 const std::filesystem::path& command, const std::string& arguments,
476 const std::string& categories, const std::string& keywords,
477 const std::string& name);
464 /** 478 /**
465 * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog 479 * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
466 * The only difference is that it returns a boolean. 480 * The only difference is that it returns a boolean.
@@ -499,6 +513,7 @@ private:
499 QLabel* game_fps_label = nullptr; 513 QLabel* game_fps_label = nullptr;
500 QLabel* emu_frametime_label = nullptr; 514 QLabel* emu_frametime_label = nullptr;
501 QLabel* tas_label = nullptr; 515 QLabel* tas_label = nullptr;
516 QLabel* firmware_label = nullptr;
502 QPushButton* gpu_accuracy_button = nullptr; 517 QPushButton* gpu_accuracy_button = nullptr;
503 QPushButton* renderer_status_button = nullptr; 518 QPushButton* renderer_status_button = nullptr;
504 QPushButton* dock_status_button = nullptr; 519 QPushButton* dock_status_button = nullptr;
@@ -509,7 +524,7 @@ private:
509 QSlider* volume_slider = nullptr; 524 QSlider* volume_slider = nullptr;
510 QTimer status_bar_update_timer; 525 QTimer status_bar_update_timer;
511 526
512 std::unique_ptr<Config> config; 527 std::unique_ptr<QtConfig> config;
513 528
514 // Whether emulation is currently running in yuzu. 529 // Whether emulation is currently running in yuzu.
515 bool emulation_running = false; 530 bool emulation_running = false;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 88684ffb5..e53f9951e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,7 @@
25 </property> 25 </property>
26 <widget class="QWidget" name="centralwidget"> 26 <widget class="QWidget" name="centralwidget">
27 <layout class="QHBoxLayout" name="horizontalLayout"> 27 <layout class="QHBoxLayout" name="horizontalLayout">
28 <property name="margin"> 28 <property name="margin" stdset="0">
29 <number>0</number> 29 <number>0</number>
30 </property> 30 </property>
31 </layout> 31 </layout>
@@ -36,7 +36,7 @@
36 <x>0</x> 36 <x>0</x>
37 <y>0</y> 37 <y>0</y>
38 <width>1280</width> 38 <width>1280</width>
39 <height>26</height> 39 <height>21</height>
40 </rect> 40 </rect>
41 </property> 41 </property>
42 <widget class="QMenu" name="menu_File"> 42 <widget class="QMenu" name="menu_File">
@@ -162,6 +162,7 @@
162 <addaction name="menu_cabinet_applet"/> 162 <addaction name="menu_cabinet_applet"/>
163 <addaction name="action_Load_Album"/> 163 <addaction name="action_Load_Album"/>
164 <addaction name="action_Load_Mii_Edit"/> 164 <addaction name="action_Load_Mii_Edit"/>
165 <addaction name="action_Open_Controller_Menu"/>
165 <addaction name="separator"/> 166 <addaction name="separator"/>
166 <addaction name="action_Capture_Screenshot"/> 167 <addaction name="action_Capture_Screenshot"/>
167 <addaction name="menuTAS"/> 168 <addaction name="menuTAS"/>
@@ -382,9 +383,9 @@
382 </property> 383 </property>
383 </action> 384 </action>
384 <action name="action_Load_Album"> 385 <action name="action_Load_Album">
385 <property name="text"> 386 <property name="text">
386 <string>Open &amp;Album</string> 387 <string>Open &amp;Album</string>
387 </property> 388 </property>
388 </action> 389 </action>
389 <action name="action_Load_Cabinet_Nickname_Owner"> 390 <action name="action_Load_Cabinet_Nickname_Owner">
390 <property name="text"> 391 <property name="text">
@@ -407,9 +408,9 @@
407 </property> 408 </property>
408 </action> 409 </action>
409 <action name="action_Load_Mii_Edit"> 410 <action name="action_Load_Mii_Edit">
410 <property name="text"> 411 <property name="text">
411 <string>Open &amp;Mii Editor</string> 412 <string>Open &amp;Mii Editor</string>
412 </property> 413 </property>
413 </action> 414 </action>
414 <action name="action_Configure_Tas"> 415 <action name="action_Configure_Tas">
415 <property name="text"> 416 <property name="text">
@@ -454,6 +455,11 @@
454 <string>R&amp;ecord</string> 455 <string>R&amp;ecord</string>
455 </property> 456 </property>
456 </action> 457 </action>
458 <action name="action_Open_Controller_Menu">
459 <property name="text">
460 <string>Open &amp;Controller Menu</string>
461 </property>
462 </action>
457 </widget> 463 </widget>
458 <resources> 464 <resources>
459 <include location="yuzu.qrc"/> 465 <include location="yuzu.qrc"/>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 1c833767b..7bb7e95af 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -1,6 +1,9 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <QSettings>
5#include "common/fs/fs.h"
6#include "common/fs/path_util.h"
4#include "yuzu/uisettings.h" 7#include "yuzu/uisettings.h"
5 8
6#ifndef CANNOT_EXPLICITLY_INSTANTIATE 9#ifndef CANNOT_EXPLICITLY_INSTANTIATE
@@ -15,6 +18,8 @@ template class Setting<unsigned long long>;
15} // namespace Settings 18} // namespace Settings
16#endif 19#endif
17 20
21namespace FS = Common::FS;
22
18namespace UISettings { 23namespace UISettings {
19 24
20const Themes themes{{ 25const Themes themes{{
@@ -28,10 +33,8 @@ const Themes themes{{
28 33
29bool IsDarkTheme() { 34bool IsDarkTheme() {
30 const auto& theme = UISettings::values.theme; 35 const auto& theme = UISettings::values.theme;
31 return theme == QStringLiteral("qdarkstyle") || 36 return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
32 theme == QStringLiteral("qdarkstyle_midnight_blue") || 37 theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
33 theme == QStringLiteral("colorful_dark") ||
34 theme == QStringLiteral("colorful_midnight_blue");
35} 38}
36 39
37Values values = {}; 40Values values = {};
@@ -52,4 +55,58 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
52 return height * 16 / 9; 55 return height * 16 / 9;
53} 56}
54 57
58void SaveWindowState() {
59 const auto window_state_config_loc =
60 FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
61
62 void(FS::CreateParentDir(window_state_config_loc));
63 QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
64
65 config.setValue(QStringLiteral("geometry"), values.geometry);
66 config.setValue(QStringLiteral("state"), values.state);
67 config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry);
68 config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state);
69 config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry);
70
71 config.sync();
72}
73
74void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) {
75 const auto window_state_config_loc =
76 FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
77
78 // Migrate window state from old location
79 if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) {
80 const auto config_loc =
81 FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini");
82 QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat);
83
84 config.beginGroup(QStringLiteral("UI"));
85 config.beginGroup(QStringLiteral("UILayout"));
86 values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
87 values.state = config.value(QStringLiteral("state")).toByteArray();
88 values.renderwindow_geometry =
89 config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
90 values.gamelist_header_state =
91 config.value(QStringLiteral("gameListHeaderState")).toByteArray();
92 values.microprofile_geometry =
93 config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
94 config.endGroup();
95 config.endGroup();
96 return;
97 }
98
99 void(FS::CreateParentDir(window_state_config_loc));
100 const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
101
102 values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
103 values.state = config.value(QStringLiteral("state")).toByteArray();
104 values.renderwindow_geometry =
105 config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
106 values.gamelist_header_state =
107 config.value(QStringLiteral("gameListHeaderState")).toByteArray();
108 values.microprofile_geometry =
109 config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
110}
111
55} // namespace UISettings 112} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index b62ff620c..549a39e1b 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -14,6 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/settings.h" 15#include "common/settings.h"
16#include "common/settings_enums.h" 16#include "common/settings_enums.h"
17#include "configuration/qt_config.h"
17 18
18using Settings::Category; 19using Settings::Category;
19using Settings::ConfirmStop; 20using Settings::ConfirmStop;
@@ -37,15 +38,15 @@ namespace UISettings {
37bool IsDarkTheme(); 38bool IsDarkTheme();
38 39
39struct ContextualShortcut { 40struct ContextualShortcut {
40 QString keyseq; 41 std::string keyseq;
41 QString controller_keyseq; 42 std::string controller_keyseq;
42 int context; 43 int context;
43 bool repeat; 44 bool repeat;
44}; 45};
45 46
46struct Shortcut { 47struct Shortcut {
47 QString name; 48 std::string name;
48 QString group; 49 std::string group;
49 ContextualShortcut shortcut; 50 ContextualShortcut shortcut;
50}; 51};
51 52
@@ -58,11 +59,19 @@ enum class Theme {
58 MidnightBlueColorful, 59 MidnightBlueColorful,
59}; 60};
60 61
62static constexpr Theme default_theme{
63#ifdef _WIN32
64 Theme::DarkColorful
65#else
66 Theme::DefaultColorful
67#endif
68};
69
61using Themes = std::array<std::pair<const char*, const char*>, 6>; 70using Themes = std::array<std::pair<const char*, const char*>, 6>;
62extern const Themes themes; 71extern const Themes themes;
63 72
64struct GameDir { 73struct GameDir {
65 QString path; 74 std::string path;
66 bool deep_scan = false; 75 bool deep_scan = false;
67 bool expanded = false; 76 bool expanded = false;
68 bool operator==(const GameDir& rhs) const { 77 bool operator==(const GameDir& rhs) const {
@@ -93,10 +102,6 @@ struct Values {
93 Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; 102 Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
94 Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui}; 103 Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};
95 104
96 Setting<bool> confirm_before_closing{
97 linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
98 true, true};
99
100 SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage, 105 SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
101 ConfirmStop::Ask_Always, 106 ConfirmStop::Ask_Always,
102 "confirmStop", 107 "confirmStop",
@@ -113,9 +118,13 @@ struct Values {
113 Settings::Specialization::Default, 118 Settings::Specialization::Default,
114 true, 119 true,
115 true}; 120 true};
116 Setting<bool> mute_when_in_background{ 121 Setting<bool> mute_when_in_background{linkage,
117 linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, 122 false,
118 true, true}; 123 "muteWhenInBackground",
124 Category::UiAudio,
125 Settings::Specialization::Default,
126 true,
127 true};
119 Setting<bool> hide_mouse{ 128 Setting<bool> hide_mouse{
120 linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, 129 linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
121 true, true}; 130 true, true};
@@ -144,15 +153,15 @@ struct Values {
144 Category::Screenshots}; 153 Category::Screenshots};
145 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; 154 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
146 155
147 QString roms_path; 156 std::string roms_path;
148 QString symbols_path; 157 std::string symbols_path;
149 QString game_dir_deprecated; 158 std::string game_dir_deprecated;
150 bool game_dir_deprecated_deepscan; 159 bool game_dir_deprecated_deepscan;
151 QVector<UISettings::GameDir> game_dirs; 160 QVector<GameDir> game_dirs;
152 QStringList recent_files; 161 QStringList recent_files;
153 QString language; 162 std::string language;
154 163
155 QString theme; 164 std::string theme;
156 165
157 // Shortcut name <Shortcut, context> 166 // Shortcut name <Shortcut, context>
158 std::vector<Shortcut> shortcuts; 167 std::vector<Shortcut> shortcuts;
@@ -206,6 +215,54 @@ extern Values values;
206 215
207u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); 216u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
208 217
218void SaveWindowState();
219void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
220
221// This shouldn't have anything except static initializers (no functions). So
222// QKeySequence(...).toString() is NOT ALLOWED HERE.
223// This must be in alphabetical order according to action name as it must have the same order as
224// UISetting::values.shortcuts, which is alphabetically ordered.
225// clang-format off
226const std::array<Shortcut, 23> default_hotkeys{{
227 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
228 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
229 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
230 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
231 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}},
232 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}},
233 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}},
234 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}},
235 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}},
236 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}},
237 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
238 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
239 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
240 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
241 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
242 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
243 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
244 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
245 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}},
246 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}},
247 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
248 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
249 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
250}};
251// clang-format on
252
209} // namespace UISettings 253} // namespace UISettings
210 254
211Q_DECLARE_METATYPE(UISettings::GameDir*); 255Q_DECLARE_METATYPE(UISettings::GameDir*);
256
257// These metatype declarations cannot be in common/settings.h because core is devoid of QT
258Q_DECLARE_METATYPE(Settings::CpuAccuracy);
259Q_DECLARE_METATYPE(Settings::GpuAccuracy);
260Q_DECLARE_METATYPE(Settings::FullscreenMode);
261Q_DECLARE_METATYPE(Settings::NvdecEmulation);
262Q_DECLARE_METATYPE(Settings::ResolutionSetup);
263Q_DECLARE_METATYPE(Settings::ScalingFilter);
264Q_DECLARE_METATYPE(Settings::AntiAliasing);
265Q_DECLARE_METATYPE(Settings::RendererBackend);
266Q_DECLARE_METATYPE(Settings::ShaderBackend);
267Q_DECLARE_METATYPE(Settings::AstcRecompression);
268Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index f2854c8ec..e22cf84bf 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -4,7 +4,10 @@
4#include <array> 4#include <array>
5#include <cmath> 5#include <cmath>
6#include <QPainter> 6#include <QPainter>
7
8#include "common/logging/log.h"
7#include "yuzu/util/util.h" 9#include "yuzu/util/util.h"
10
8#ifdef _WIN32 11#ifdef _WIN32
9#include <windows.h> 12#include <windows.h>
10#include "common/fs/file.h" 13#include "common/fs/file.h"
@@ -42,7 +45,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
42 return circle_pixmap; 45 return circle_pixmap;
43} 46}
44 47
45bool SaveIconToFile(const std::string_view path, const QImage& image) { 48bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
46#if defined(WIN32) 49#if defined(WIN32)
47#pragma pack(push, 2) 50#pragma pack(push, 2)
48 struct IconDir { 51 struct IconDir {
@@ -73,7 +76,7 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
73 .id_count = static_cast<WORD>(scale_sizes.size()), 76 .id_count = static_cast<WORD>(scale_sizes.size()),
74 }; 77 };
75 78
76 Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, 79 Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
77 Common::FS::FileType::BinaryFile); 80 Common::FS::FileType::BinaryFile);
78 if (!icon_file.IsOpen()) { 81 if (!icon_file.IsOpen()) {
79 return false; 82 return false;
@@ -135,6 +138,14 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
135 icon_file.Close(); 138 icon_file.Close();
136 139
137 return true; 140 return true;
141#elif defined(__linux__) || defined(__FreeBSD__)
142 // Convert and write the icon as a PNG
143 if (!image.save(QString::fromStdString(icon_path.string()))) {
144 LOG_ERROR(Frontend, "Could not write icon as PNG to file");
145 } else {
146 LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
147 }
148 return true;
138#else 149#else
139 return false; 150 return false;
140#endif 151#endif
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h
index 09c14ce3f..4094cf6c2 100644
--- a/src/yuzu/util/util.h
+++ b/src/yuzu/util/util.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <filesystem>
6#include <QFont> 7#include <QFont>
7#include <QString> 8#include <QString>
8 9
@@ -25,4 +26,4 @@
25 * @param image The image to save 26 * @param image The image to save
26 * @return bool If the operation succeeded 27 * @return bool If the operation succeeded
27 */ 28 */
28[[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); 29[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
index 92f10d315..ab0d39c25 100644
--- a/src/yuzu/vk_device_info.cpp
+++ b/src/yuzu/vk_device_info.cpp
@@ -31,6 +31,7 @@ void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
31 // Create a test window with a Vulkan surface type for checking present modes. 31 // Create a test window with a Vulkan surface type for checking present modes.
32 QWindow test_window(window); 32 QWindow test_window(window);
33 test_window.setSurfaceType(QWindow::VulkanSurface); 33 test_window.setSurfaceType(QWindow::VulkanSurface);
34 test_window.create();
34 auto wsi = QtCommon::GetWindowSystemInfo(&test_window); 35 auto wsi = QtCommon::GetWindowSystemInfo(&test_window);
35 36
36 vk::InstanceDispatch dld; 37 vk::InstanceDispatch dld;
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 46eddf423..fbeba8813 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -13,9 +13,6 @@ function(create_resource file output filename)
13endfunction() 13endfunction()
14 14
15add_executable(yuzu-cmd 15add_executable(yuzu-cmd
16 config.cpp
17 config.h
18 default_ini.h
19 emu_window/emu_window_sdl2.cpp 16 emu_window/emu_window_sdl2.cpp
20 emu_window/emu_window_sdl2.h 17 emu_window/emu_window_sdl2.h
21 emu_window/emu_window_sdl2_gl.cpp 18 emu_window/emu_window_sdl2_gl.cpp
@@ -25,14 +22,16 @@ add_executable(yuzu-cmd
25 emu_window/emu_window_sdl2_vk.cpp 22 emu_window/emu_window_sdl2_vk.cpp
26 emu_window/emu_window_sdl2_vk.h 23 emu_window/emu_window_sdl2_vk.h
27 precompiled_headers.h 24 precompiled_headers.h
25 sdl_config.cpp
26 sdl_config.h
28 yuzu.cpp 27 yuzu.cpp
29 yuzu.rc 28 yuzu.rc
30) 29)
31 30
32create_target_directory_groups(yuzu-cmd) 31create_target_directory_groups(yuzu-cmd)
33 32
34target_link_libraries(yuzu-cmd PRIVATE common core input_common) 33target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common)
35target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) 34target_link_libraries(yuzu-cmd PRIVATE glad)
36if (MSVC) 35if (MSVC)
37 target_link_libraries(yuzu-cmd PRIVATE getopt) 36 target_link_libraries(yuzu-cmd PRIVATE getopt)
38endif() 37endif()
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
deleted file mode 100644
index 0d25ff400..000000000
--- a/src/yuzu_cmd/config.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <memory>
5#include <optional>
6#include <sstream>
7#include <INIReader.h>
8#include <SDL.h>
9#include "common/fs/file.h"
10#include "common/fs/fs.h"
11#include "common/fs/path_util.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "core/hle/service/acc/profile_manager.h"
15#include "input_common/main.h"
16#include "yuzu_cmd/config.h"
17#include "yuzu_cmd/default_ini.h"
18
19namespace FS = Common::FS;
20
21const std::filesystem::path default_config_path =
22 FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
23
24Config::Config(std::optional<std::filesystem::path> config_path)
25 : sdl2_config_loc{config_path.value_or(default_config_path)},
26 sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} {
27 Reload();
28}
29
30Config::~Config() = default;
31
32bool Config::LoadINI(const std::string& default_contents, bool retry) {
33 const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc);
34 if (sdl2_config->ParseError() < 0) {
35 if (retry) {
36 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
37 config_loc_str);
38
39 void(FS::CreateParentDir(sdl2_config_loc));
40 void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents));
41
42 sdl2_config = std::make_unique<INIReader>(config_loc_str);
43
44 return LoadINI(default_contents, false);
45 }
46 LOG_ERROR(Config, "Failed.");
47 return false;
48 }
49 LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
50 return true;
51}
52
53static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
54 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
55 SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
56 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
57};
58
59static const std::array<int, Settings::NativeMotion::NumMotions> default_motions = {
60 SDL_SCANCODE_7,
61 SDL_SCANCODE_8,
62};
63
64static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
65 {
66 SDL_SCANCODE_UP,
67 SDL_SCANCODE_DOWN,
68 SDL_SCANCODE_LEFT,
69 SDL_SCANCODE_RIGHT,
70 SDL_SCANCODE_D,
71 },
72 {
73 SDL_SCANCODE_I,
74 SDL_SCANCODE_K,
75 SDL_SCANCODE_J,
76 SDL_SCANCODE_L,
77 SDL_SCANCODE_D,
78 },
79}};
80
81template <>
82void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
83 std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
84 if (setting_value.empty()) {
85 setting_value = setting.GetDefault();
86 }
87 setting = std::move(setting_value);
88}
89
90template <>
91void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
92 setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
93}
94
95template <typename Type, bool ranged>
96void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
97 setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
98 static_cast<long>(setting.GetDefault())));
99}
100
101void Config::ReadCategory(Settings::Category category) {
102 for (const auto setting : Settings::values.linkage.by_category[category]) {
103 const char* category_name = [&]() {
104 if (category == Settings::Category::Controls) {
105 // For compatibility with older configs
106 return "ControlsGeneral";
107 } else {
108 return Settings::TranslateCategory(category);
109 }
110 }();
111 std::string setting_value =
112 sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString());
113 setting->LoadString(setting_value);
114 }
115}
116
117void Config::ReadValues() {
118 // Controls
119 ReadCategory(Settings::Category::Controls);
120
121 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
122 auto& player = Settings::values.players.GetValue()[p];
123
124 const auto group = fmt::format("ControlsP{}", p);
125 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
126 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
127 player.buttons[i] =
128 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
129 if (player.buttons[i].empty()) {
130 player.buttons[i] = default_param;
131 }
132 }
133
134 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
135 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
136 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
137 default_analogs[i][3], default_analogs[i][4], 0.5f);
138 player.analogs[i] =
139 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
140 if (player.analogs[i].empty()) {
141 player.analogs[i] = default_param;
142 }
143 }
144
145 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
146 const std::string default_param =
147 InputCommon::GenerateKeyboardParam(default_motions[i]);
148 auto& player_motions = player.motions[i];
149
150 player_motions =
151 sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param);
152 if (player_motions.empty()) {
153 player_motions = default_param;
154 }
155 }
156
157 player.connected = sdl2_config->GetBoolean(group, "connected", false);
158 }
159
160 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
161 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
162 Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
163 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
164 default_param);
165 if (Settings::values.debug_pad_buttons[i].empty())
166 Settings::values.debug_pad_buttons[i] = default_param;
167 }
168
169 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
170 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
171 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
172 default_analogs[i][3], default_analogs[i][4], 0.5f);
173 Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
174 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
175 default_param);
176 if (Settings::values.debug_pad_analogs[i].empty())
177 Settings::values.debug_pad_analogs[i] = default_param;
178 }
179
180 Settings::values.touchscreen.enabled =
181 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
182 Settings::values.touchscreen.rotation_angle =
183 sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
184 Settings::values.touchscreen.diameter_x =
185 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
186 Settings::values.touchscreen.diameter_y =
187 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
188
189 int num_touch_from_button_maps =
190 sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
191 if (num_touch_from_button_maps > 0) {
192 for (int i = 0; i < num_touch_from_button_maps; ++i) {
193 Settings::TouchFromButtonMap map;
194 map.name = sdl2_config->Get("ControlsGeneral",
195 std::string("touch_from_button_maps_") + std::to_string(i) +
196 std::string("_name"),
197 "default");
198 const int num_touch_maps = sdl2_config->GetInteger(
199 "ControlsGeneral",
200 std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
201 0);
202 map.buttons.reserve(num_touch_maps);
203
204 for (int j = 0; j < num_touch_maps; ++j) {
205 std::string touch_mapping =
206 sdl2_config->Get("ControlsGeneral",
207 std::string("touch_from_button_maps_") + std::to_string(i) +
208 std::string("_bind_") + std::to_string(j),
209 "");
210 map.buttons.emplace_back(std::move(touch_mapping));
211 }
212
213 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
214 }
215 } else {
216 Settings::values.touch_from_button_maps.emplace_back(
217 Settings::TouchFromButtonMap{"default", {}});
218 num_touch_from_button_maps = 1;
219 }
220 Settings::values.touch_from_button_map_index = std::clamp(
221 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
222
223 ReadCategory(Settings::Category::Audio);
224 ReadCategory(Settings::Category::Core);
225 ReadCategory(Settings::Category::Cpu);
226 ReadCategory(Settings::Category::CpuDebug);
227 ReadCategory(Settings::Category::CpuUnsafe);
228 ReadCategory(Settings::Category::Renderer);
229 ReadCategory(Settings::Category::RendererAdvanced);
230 ReadCategory(Settings::Category::RendererDebug);
231 ReadCategory(Settings::Category::System);
232 ReadCategory(Settings::Category::SystemAudio);
233 ReadCategory(Settings::Category::DataStorage);
234 ReadCategory(Settings::Category::Debugging);
235 ReadCategory(Settings::Category::DebuggingGraphics);
236 ReadCategory(Settings::Category::Miscellaneous);
237 ReadCategory(Settings::Category::Network);
238 ReadCategory(Settings::Category::WebService);
239
240 // Data Storage
241 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
242 sdl2_config->Get("Data Storage", "nand_directory",
243 FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
244 FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
245 sdl2_config->Get("Data Storage", "sdmc_directory",
246 FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
247 FS::SetYuzuPath(FS::YuzuPath::LoadDir,
248 sdl2_config->Get("Data Storage", "load_directory",
249 FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
250 FS::SetYuzuPath(FS::YuzuPath::DumpDir,
251 sdl2_config->Get("Data Storage", "dump_directory",
252 FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
253
254 // Debugging
255 Settings::values.record_frame_times =
256 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
257
258 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
259 std::stringstream ss(title_list);
260 std::string line;
261 while (std::getline(ss, line, '|')) {
262 const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
263 const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, "");
264
265 std::stringstream inner_ss(disabled_list);
266 std::string inner_line;
267 std::vector<std::string> out;
268 while (std::getline(inner_ss, inner_line, '|')) {
269 out.push_back(inner_line);
270 }
271
272 Settings::values.disabled_addons.insert_or_assign(title_id, out);
273 }
274}
275
276void Config::Reload() {
277 LoadINI(DefaultINI::sdl2_config_file);
278 ReadValues();
279}
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
deleted file mode 100644
index 512591a39..000000000
--- a/src/yuzu_cmd/config.h
+++ /dev/null
@@ -1,38 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <filesystem>
7#include <memory>
8#include <optional>
9#include <string>
10
11#include "common/settings.h"
12
13class INIReader;
14
15class Config {
16 std::filesystem::path sdl2_config_loc;
17 std::unique_ptr<INIReader> sdl2_config;
18
19 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues();
21
22public:
23 explicit Config(std::optional<std::filesystem::path> config_path);
24 ~Config();
25
26 void Reload();
27
28private:
29 /**
30 * Applies a value read from the sdl2_config to a Setting.
31 *
32 * @param group The name of the INI group
33 * @param setting The yuzu setting to modify
34 */
35 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
37 void ReadCategory(Settings::Category category);
38};
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
deleted file mode 100644
index 119e22183..000000000
--- a/src/yuzu_cmd/default_ini.h
+++ /dev/null
@@ -1,553 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace DefaultINI {
7
8const char* sdl2_config_file =
9 R"(
10[ControlsP0]
11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
13# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15
16# Indicates if this player should be connected at boot
17# 0 (default): Disabled, 1: Enabled
18connected=
19
20# for button input, the following devices are available:
21# - "keyboard" (default) for keyboard input. Required parameters:
22# - "code": the code of the key to bind
23# - "sdl" for joystick input using SDL. Required parameters:
24# - "guid": SDL identification GUID of the joystick
25# - "port": the index of the joystick to bind
26# - "button"(optional): the index of the button to bind
27# - "hat"(optional): the index of the hat to bind as direction buttons
28# - "axis"(optional): the index of the axis to bind
29# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
30# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
31# triggered if the axis value crosses
32# - "direction"(only used for axis): "+" means the button is triggered when the axis value
33# is greater than the threshold; "-" means the button is triggered when the axis value
34# is smaller than the threshold
35button_a=
36button_b=
37button_x=
38button_y=
39button_lstick=
40button_rstick=
41button_l=
42button_r=
43button_zl=
44button_zr=
45button_plus=
46button_minus=
47button_dleft=
48button_dup=
49button_dright=
50button_ddown=
51button_lstick_left=
52button_lstick_up=
53button_lstick_right=
54button_lstick_down=
55button_sl=
56button_sr=
57button_home=
58button_screenshot=
59
60# for analog input, the following devices are available:
61# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
62# - "up", "down", "left", "right": sub-devices for each direction.
63# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
64# - "modifier": sub-devices as a modifier.
65# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
66# Must be in range of 0.0-1.0. Defaults to 0.5
67# - "sdl" for joystick input using SDL. Required parameters:
68# - "guid": SDL identification GUID of the joystick
69# - "port": the index of the joystick to bind
70# - "axis_x": the index of the axis to bind as x-axis (default to 0)
71# - "axis_y": the index of the axis to bind as y-axis (default to 1)
72lstick=
73rstick=
74
75# for motion input, the following devices are available:
76# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
77# - "code": the code of the key to bind
78# - "sdl" for motion input using SDL. Required parameters:
79# - "guid": SDL identification GUID of the joystick
80# - "port": the index of the joystick to bind
81# - "motion": the index of the motion sensor to bind
82# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
83# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
84# - "port": the port of the cemu hook server
85# - "pad": the index of the joystick
86# - "motion": the index of the motion sensor of the joystick to bind
87motionleft=
88motionright=
89
90[ControlsGeneral]
91# To use the debug_pad, prepend `debug_pad_` before each button setting above.
92# i.e. debug_pad_button_a=
93
94# Enable debug pad inputs to the guest
95# 0 (default): Disabled, 1: Enabled
96debug_pad_enabled =
97
98# Enable sdl raw input. Allows to configure up to 8 xinput controllers.
99# 0 (default): Disabled, 1: Enabled
100enable_raw_input =
101
102# Enable yuzu joycon driver instead of SDL drive.
103# 0: Disabled, 1 (default): Enabled
104enable_joycon_driver =
105
106# Emulates an analog input from buttons. Allowing to dial any angle.
107# 0 (default): Disabled, 1: Enabled
108emulate_analog_keyboard =
109
110# Whether to enable or disable vibration
111# 0: Disabled, 1 (default): Enabled
112vibration_enabled=
113
114# Whether to enable or disable accurate vibrations
115# 0 (default): Disabled, 1: Enabled
116enable_accurate_vibrations=
117
118# Enables controller motion inputs
119# 0: Disabled, 1 (default): Enabled
120motion_enabled =
121
122# Defines the udp device's touch screen coordinate system for cemuhookudp devices
123# - "min_x", "min_y", "max_x", "max_y"
124touch_device=
125
126# for mapping buttons to touch inputs.
127#touch_from_button_map=1
128#touch_from_button_maps_0_name=default
129#touch_from_button_maps_0_count=2
130#touch_from_button_maps_0_bind_0=foo
131#touch_from_button_maps_0_bind_1=bar
132# etc.
133
134# List of Cemuhook UDP servers, delimited by ','.
135# Default: 127.0.0.1:26760
136# Example: 127.0.0.1:26760,123.4.5.67:26761
137udp_input_servers =
138
139# Enable controlling an axis via a mouse input.
140# 0 (default): Off, 1: On
141mouse_panning =
142
143# Set mouse panning horizontal sensitivity.
144# Default: 50.0
145mouse_panning_x_sensitivity =
146
147# Set mouse panning vertical sensitivity.
148# Default: 50.0
149mouse_panning_y_sensitivity =
150
151# Set mouse panning deadzone horizontal counterweight.
152# Default: 0.0
153mouse_panning_deadzone_x_counterweight =
154
155# Set mouse panning deadzone vertical counterweight.
156# Default: 0.0
157mouse_panning_deadzone_y_counterweight =
158
159# Set mouse panning stick decay strength.
160# Default: 22.0
161mouse_panning_decay_strength =
162
163# Set mouse panning stick minimum decay.
164# Default: 5.0
165mouse_panning_minimum_decay =
166
167# Emulate an analog control stick from keyboard inputs.
168# 0 (default): Disabled, 1: Enabled
169emulate_analog_keyboard =
170
171# Enable mouse inputs to the guest
172# 0 (default): Disabled, 1: Enabled
173mouse_enabled =
174
175# Enable keyboard inputs to the guest
176# 0 (default): Disabled, 1: Enabled
177keyboard_enabled =
178
179)"
180 R"(
181[Core]
182# Whether to use multi-core for CPU emulation
183# 0: Disabled, 1 (default): Enabled
184use_multi_core =
185
186# Enable unsafe extended guest system memory layout (8GB DRAM)
187# 0 (default): Disabled, 1: Enabled
188use_unsafe_extended_memory_layout =
189
190[Cpu]
191# Adjusts various optimizations.
192# Auto-select mode enables choice unsafe optimizations.
193# Accurate enables only safe optimizations.
194# Unsafe allows any unsafe optimizations.
195# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
196cpu_accuracy =
197
198# Allow disabling safe optimizations.
199# 0 (default): Disabled, 1: Enabled
200cpu_debug_mode =
201
202# Enable inline page tables optimization (faster guest memory access)
203# 0: Disabled, 1 (default): Enabled
204cpuopt_page_tables =
205
206# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
207# 0: Disabled, 1 (default): Enabled
208cpuopt_block_linking =
209
210# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
211# 0: Disabled, 1 (default): Enabled
212cpuopt_return_stack_buffer =
213
214# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
215# 0: Disabled, 1 (default): Enabled
216cpuopt_fast_dispatcher =
217
218# Enable context elimination CPU Optimization (reduce host memory use for guest context)
219# 0: Disabled, 1 (default): Enabled
220cpuopt_context_elimination =
221
222# Enable constant propagation CPU optimization (basic IR optimization)
223# 0: Disabled, 1 (default): Enabled
224cpuopt_const_prop =
225
226# Enable miscellaneous CPU optimizations (basic IR optimization)
227# 0: Disabled, 1 (default): Enabled
228cpuopt_misc_ir =
229
230# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
231# 0: Disabled, 1 (default): Enabled
232cpuopt_reduce_misalign_checks =
233
234# Enable Host MMU Emulation (faster guest memory access)
235# 0: Disabled, 1 (default): Enabled
236cpuopt_fastmem =
237
238# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
239# 0: Disabled, 1 (default): Enabled
240cpuopt_fastmem_exclusives =
241
242# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
243# 0: Disabled, 1 (default): Enabled
244cpuopt_recompile_exclusives =
245
246# Enable optimization to ignore invalid memory accesses (faster guest memory access)
247# 0: Disabled, 1 (default): Enabled
248cpuopt_ignore_memory_aborts =
249
250# Enable unfuse FMA (improve performance on CPUs without FMA)
251# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
252# 0: Disabled, 1 (default): Enabled
253cpuopt_unsafe_unfuse_fma =
254
255# Enable faster FRSQRTE and FRECPE
256# Only enabled if cpu_accuracy is set to Unsafe.
257# 0: Disabled, 1 (default): Enabled
258cpuopt_unsafe_reduce_fp_error =
259
260# Enable faster ASIMD instructions (32 bits only)
261# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
262# 0: Disabled, 1 (default): Enabled
263cpuopt_unsafe_ignore_standard_fpcr =
264
265# Enable inaccurate NaN handling
266# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
267# 0: Disabled, 1 (default): Enabled
268cpuopt_unsafe_inaccurate_nan =
269
270# Disable address space checks (64 bits only)
271# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
272# 0: Disabled, 1 (default): Enabled
273cpuopt_unsafe_fastmem_check =
274
275# Enable faster exclusive instructions
276# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
277# 0: Disabled, 1 (default): Enabled
278cpuopt_unsafe_ignore_global_monitor =
279
280)"
281 R"(
282[Renderer]
283# Which backend API to use.
284# 0: OpenGL, 1 (default): Vulkan
285backend =
286
287# Whether to enable asynchronous presentation (Vulkan only)
288# 0 (default): Off, 1: On
289async_presentation =
290
291# Enable graphics API debugging mode.
292# 0 (default): Disabled, 1: Enabled
293debug =
294
295# Enable shader feedback.
296# 0 (default): Disabled, 1: Enabled
297renderer_shader_feedback =
298
299# Enable Nsight Aftermath crash dumps
300# 0 (default): Disabled, 1: Enabled
301nsight_aftermath =
302
303# Disable shader loop safety checks, executing the shader without loop logic changes
304# 0 (default): Disabled, 1: Enabled
305disable_shader_loop_safety_checks =
306
307# Which Vulkan physical device to use (defaults to 0)
308vulkan_device =
309
310# 0: 0.5x (360p/540p) [EXPERIMENTAL]
311# 1: 0.75x (540p/810p) [EXPERIMENTAL]
312# 2 (default): 1x (720p/1080p)
313# 3: 1.5x (1080p/1620p) [EXPERIMENTAL]
314# 4: 2x (1440p/2160p)
315# 5: 3x (2160p/3240p)
316# 6: 4x (2880p/4320p)
317# 7: 5x (3600p/5400p)
318# 8: 6x (4320p/6480p)
319# 9: 7x (5040p/7560p)
320# 10: 8x (5760/8640p)
321resolution_setup =
322
323# Pixel filter to use when up- or down-sampling rendered frames.
324# 0: Nearest Neighbor
325# 1 (default): Bilinear
326# 2: Bicubic
327# 3: Gaussian
328# 4: ScaleForce
329# 5: AMD FidelityFXâ„¢ï¸ Super Resolution
330scaling_filter =
331
332# Anti-Aliasing (AA)
333# 0 (default): None, 1: FXAA, 2: SMAA
334anti_aliasing =
335
336# Whether to use fullscreen or borderless window mode
337# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
338fullscreen_mode =
339
340# Aspect ratio
341# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
342aspect_ratio =
343
344# Anisotropic filtering
345# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
346max_anisotropy =
347
348# Whether to enable VSync or not.
349# OpenGL: Values other than 0 enable VSync
350# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
351# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
352# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
353# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
354# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
355# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed
356use_vsync =
357
358# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
359# not available and GLASM is selected, GLSL will be used.
360# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
361shader_backend =
362
363# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
364# 0: Off, 1 (default): On
365use_reactive_flushing =
366
367# Whether to allow asynchronous shader building.
368# 0 (default): Off, 1: On
369use_asynchronous_shaders =
370
371# NVDEC emulation.
372# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
373nvdec_emulation =
374
375# Accelerate ASTC texture decoding.
376# 0: Off, 1 (default): On
377accelerate_astc =
378
379# Decode ASTC textures asynchronously.
380# 0 (default): Off, 1: On
381async_astc =
382
383# Recompress ASTC textures to a different format.
384# 0 (default): Uncompressed, 1: BC1 (Low quality), 2: BC3: (Medium quality)
385async_astc =
386
387# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
388# 0: Off, 1: On (default)
389use_speed_limit =
390
391# Limits the speed of the game to run no faster than this value as a percentage of target speed
392# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
393speed_limit =
394
395# Whether to use disk based shader cache
396# 0: Off, 1 (default): On
397use_disk_shader_cache =
398
399# Which gpu accuracy level to use
400# 0: Normal, 1 (default): High, 2: Extreme (Very slow)
401gpu_accuracy =
402
403# Whether to use asynchronous GPU emulation
404# 0 : Off (slow), 1 (default): On (fast)
405use_asynchronous_gpu_emulation =
406
407# Inform the guest that GPU operations completed more quickly than they did.
408# 0: Off, 1 (default): On
409use_fast_gpu_time =
410
411# Whether to use garbage collection or not for GPU caches.
412# 0 (default): Off, 1: On
413use_caches_gc =
414
415# The clear color for the renderer. What shows up on the sides of the bottom screen.
416# Must be in range of 0-255. Defaults to 0 for all.
417bg_red =
418bg_blue =
419bg_green =
420
421)"
422 R"(
423[Audio]
424# Which audio output engine to use.
425# auto (default): Auto-select
426# cubeb: Cubeb audio engine (if available)
427# sdl2: SDL2 audio engine (if available)
428# null: No audio output
429output_engine =
430
431# Which audio device to use.
432# auto (default): Auto-select
433output_device =
434
435# Output volume.
436# 100 (default): 100%, 0; mute
437volume =
438
439[Data Storage]
440# Whether to create a virtual SD card.
441# 1 (default): Yes, 0: No
442use_virtual_sd =
443
444# Whether or not to enable gamecard emulation
445# 1: Yes, 0 (default): No
446gamecard_inserted =
447
448# Whether or not the gamecard should be emulated as the current game
449# If 'gamecard_inserted' is 0 this setting is irrelevant
450# 1: Yes, 0 (default): No
451gamecard_current_game =
452
453# Path to an XCI file to use as the gamecard
454# If 'gamecard_inserted' is 0 this setting is irrelevant
455# If 'gamecard_current_game' is 1 this setting is irrelevant
456gamecard_path =
457
458[System]
459# Whether the system is docked
460# 1 (default): Yes, 0: No
461use_docked_mode =
462
463# Sets the seed for the RNG generator built into the switch
464# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
465rng_seed_enabled =
466rng_seed =
467
468# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
469# This will auto-increment, with the time set being the time the game is started
470# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
471custom_rtc_enabled =
472custom_rtc =
473
474# Sets the systems language index
475# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
476# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
477# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
478language_index =
479
480# The system region that yuzu will use during emulation
481# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
482region_index =
483
484# The system time zone that yuzu will use during emulation
485# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
486time_zone_index =
487
488# Sets the sound output mode.
489# 0: Mono, 1 (default): Stereo, 2: Surround
490sound_index =
491
492[Miscellaneous]
493# A filter which removes logs below a certain logging level.
494# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
495log_filter = *:Trace
496
497# Use developer keys
498# 0 (default): Disabled, 1: Enabled
499use_dev_keys =
500
501[Debugging]
502# Record frame time data, can be found in the log directory. Boolean value
503record_frame_times =
504# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
505dump_exefs=false
506# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
507dump_nso=false
508# Determines whether or not yuzu will save the filesystem access log.
509enable_fs_access_log=false
510# Enables verbose reporting services
511reporting_services =
512# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
513# false: Retail/Normal Mode (default), true: Kiosk Mode
514quest_flag =
515# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
516# false: Disabled (default), true: Enabled
517use_debug_asserts =
518# Determines whether unimplemented HLE service calls should be automatically stubbed.
519# false: Disabled (default), true: Enabled
520use_auto_stub =
521# Enables/Disables the macro JIT compiler
522disable_macro_jit=false
523# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
524# false: Disabled (default), true: Enabled
525use_gdbstub=false
526# The port to use for the GDB server, if it is enabled.
527gdbstub_port=6543
528
529[WebService]
530# Whether or not to enable telemetry
531# 0: No, 1 (default): Yes
532enable_telemetry =
533# URL for Web API
534web_api_url = https://api.yuzu-emu.org
535# Username and token for yuzu Web Service
536# See https://profile.yuzu-emu.org/ for more info
537yuzu_username =
538yuzu_token =
539
540[Network]
541# Name of the network interface device to use with yuzu LAN play.
542# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
543# e.g. On Windows: 'Ethernet', 'Wi-Fi'
544network_interface =
545
546[AddOns]
547# Used to disable add-ons
548# List of title IDs of games that will have add-ons disabled (separated by '|'):
549title_ids =
550# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
551# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
552)";
553} // namespace DefaultINI
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
new file mode 100644
index 000000000..39fd8050c
--- /dev/null
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -0,0 +1,257 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h
5#define SDL_MAIN_HANDLED
6#include <SDL.h>
7
8#include "input_common/main.h"
9#include "sdl_config.h"
10
11const std::array<int, Settings::NativeButton::NumButtons> SdlConfig::default_buttons = {
12 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
13 SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
14 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
15};
16
17const std::array<int, Settings::NativeMotion::NumMotions> SdlConfig::default_motions = {
18 SDL_SCANCODE_7,
19 SDL_SCANCODE_8,
20};
21
22const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{
23 {
24 {
25 SDL_SCANCODE_UP,
26 SDL_SCANCODE_DOWN,
27 SDL_SCANCODE_LEFT,
28 SDL_SCANCODE_RIGHT,
29 },
30 {
31 SDL_SCANCODE_I,
32 SDL_SCANCODE_K,
33 SDL_SCANCODE_J,
34 SDL_SCANCODE_L,
35 },
36 }};
37
38const std::array<int, 2> SdlConfig::default_stick_mod = {
39 SDL_SCANCODE_D,
40 0,
41};
42
43const std::array<int, 2> SdlConfig::default_ringcon_analogs{{
44 0,
45 0,
46}};
47
48SdlConfig::SdlConfig(const std::optional<std::string> config_path) {
49 Initialize(config_path);
50 ReadSdlValues();
51 SaveSdlValues();
52}
53
54SdlConfig::~SdlConfig() {
55 if (global) {
56 SdlConfig::SaveAllValues();
57 }
58}
59
60void SdlConfig::ReloadAllValues() {
61 Reload();
62 ReadSdlValues();
63 SaveSdlValues();
64}
65
66void SdlConfig::SaveAllValues() {
67 Save();
68 SaveSdlValues();
69}
70
71void SdlConfig::ReadSdlValues() {
72 ReadSdlControlValues();
73}
74
75void SdlConfig::ReadSdlControlValues() {
76 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
77
78 Settings::values.players.SetGlobal(!IsCustomConfig());
79 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
80 ReadSdlPlayerValues(p);
81 }
82 if (IsCustomConfig()) {
83 EndGroup();
84 return;
85 }
86 ReadDebugControlValues();
87 ReadHidbusValues();
88
89 EndGroup();
90}
91
92void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
93 std::string player_prefix;
94 if (type != ConfigType::InputProfile) {
95 player_prefix.append("player_").append(ToString(player_index)).append("_");
96 }
97
98 auto& player = Settings::values.players.GetValue()[player_index];
99 if (IsCustomConfig()) {
100 const auto profile_name =
101 ReadStringSetting(std::string(player_prefix).append("profile_name"));
102 if (profile_name.empty()) {
103 // Use the global input config
104 player = Settings::values.players.GetValue(true)[player_index];
105 return;
106 }
107 }
108
109 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
110 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
111 auto& player_buttons = player.buttons[i];
112
113 player_buttons = ReadStringSetting(
114 std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
115 if (player_buttons.empty()) {
116 player_buttons = default_param;
117 }
118 }
119
120 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
121 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
122 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
123 default_analogs[i][3], default_stick_mod[i], 0.5f);
124 auto& player_analogs = player.analogs[i];
125
126 player_analogs = ReadStringSetting(
127 std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
128 if (player_analogs.empty()) {
129 player_analogs = default_param;
130 }
131 }
132
133 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
134 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
135 auto& player_motions = player.motions[i];
136
137 player_motions = ReadStringSetting(
138 std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
139 if (player_motions.empty()) {
140 player_motions = default_param;
141 }
142 }
143}
144
145void SdlConfig::ReadDebugControlValues() {
146 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
147 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
148 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
149 debug_pad_buttons = ReadStringSetting(
150 std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
151 if (debug_pad_buttons.empty()) {
152 debug_pad_buttons = default_param;
153 }
154 }
155 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
156 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
157 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
158 default_analogs[i][3], default_stick_mod[i], 0.5f);
159 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
160 debug_pad_analogs = ReadStringSetting(
161 std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
162 if (debug_pad_analogs.empty()) {
163 debug_pad_analogs = default_param;
164 }
165 }
166}
167
168void SdlConfig::ReadHidbusValues() {
169 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
170 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
171 auto& ringcon_analogs = Settings::values.ringcon_analogs;
172
173 ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
174 if (ringcon_analogs.empty()) {
175 ringcon_analogs = default_param;
176 }
177}
178
179void SdlConfig::SaveSdlValues() {
180 SaveSdlControlValues();
181
182 WriteToIni();
183}
184
185void SdlConfig::SaveSdlControlValues() {
186 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
187
188 Settings::values.players.SetGlobal(!IsCustomConfig());
189 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
190 SaveSdlPlayerValues(p);
191 }
192 if (IsCustomConfig()) {
193 EndGroup();
194 return;
195 }
196 SaveDebugControlValues();
197 SaveHidbusValues();
198
199 EndGroup();
200}
201
202void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
203 std::string player_prefix;
204 if (type != ConfigType::InputProfile) {
205 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
206 }
207
208 const auto& player = Settings::values.players.GetValue()[player_index];
209 if (IsCustomConfig() && player.profile_name.empty()) {
210 // No custom profile selected
211 return;
212 }
213
214 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
215 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
216 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
217 player.buttons[i], std::make_optional(default_param));
218 }
219 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
220 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
221 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
222 default_analogs[i][3], default_stick_mod[i], 0.5f);
223 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
224 player.analogs[i], std::make_optional(default_param));
225 }
226 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
227 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
228 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
229 player.motions[i], std::make_optional(default_param));
230 }
231}
232
233void SdlConfig::SaveDebugControlValues() {
234 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
235 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
236 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
237 Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
238 }
239 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
240 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
241 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
242 default_analogs[i][3], default_stick_mod[i], 0.5f);
243 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
244 Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
245 }
246}
247
248void SdlConfig::SaveHidbusValues() {
249 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
250 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
251 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
252 std::make_optional(default_param));
253}
254
255std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {
256 return Settings::values.linkage.by_category[category];
257}
diff --git a/src/yuzu_cmd/sdl_config.h b/src/yuzu_cmd/sdl_config.h
new file mode 100644
index 000000000..1fd1c692d
--- /dev/null
+++ b/src/yuzu_cmd/sdl_config.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "frontend_common/config.h"
7
8class SdlConfig final : public Config {
9public:
10 explicit SdlConfig(std::optional<std::string> config_path);
11 ~SdlConfig() override;
12
13 void ReloadAllValues() override;
14 void SaveAllValues() override;
15
16protected:
17 void ReadSdlValues();
18 void ReadSdlPlayerValues(std::size_t player_index);
19 void ReadSdlControlValues();
20 void ReadHidbusValues() override;
21 void ReadDebugControlValues() override;
22 void ReadPathValues() override {}
23 void ReadShortcutValues() override {}
24 void ReadUIValues() override {}
25 void ReadUIGamelistValues() override {}
26 void ReadUILayoutValues() override {}
27 void ReadMultiplayerValues() override {}
28
29 void SaveSdlValues();
30 void SaveSdlPlayerValues(std::size_t player_index);
31 void SaveSdlControlValues();
32 void SaveHidbusValues() override;
33 void SaveDebugControlValues() override;
34 void SavePathValues() override {}
35 void SaveShortcutValues() override {}
36 void SaveUIValues() override {}
37 void SaveUIGamelistValues() override {}
38 void SaveUILayoutValues() override {}
39 void SaveMultiplayerValues() override {}
40
41 std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
42
43public:
44 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
45 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
46 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
47 static const std::array<int, 2> default_stick_mod;
48 static const std::array<int, 2> default_ringcon_analogs;
49};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 087cfaa26..0416d5951 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -29,10 +29,11 @@
29#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
30#include "core/loader/loader.h" 30#include "core/loader/loader.h"
31#include "core/telemetry_session.h" 31#include "core/telemetry_session.h"
32#include "frontend_common/config.h"
32#include "input_common/main.h" 33#include "input_common/main.h"
33#include "network/network.h" 34#include "network/network.h"
35#include "sdl_config.h"
34#include "video_core/renderer_base.h" 36#include "video_core/renderer_base.h"
35#include "yuzu_cmd/config.h"
36#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 37#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
37#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 38#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
38#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" 39#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
@@ -300,7 +301,7 @@ int main(int argc, char** argv) {
300 } 301 }
301 } 302 }
302 303
303 Config config{config_path}; 304 SdlConfig config{config_path};
304 305
305 // apply the log_filter setting 306 // apply the log_filter setting
306 // the logger was initialized before and doesn't pick up the filter on its own 307 // the logger was initialized before and doesn't pick up the filter on its own