summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.codespellrc2
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt6
-rw-r--r--externals/CMakeLists.txt6
m---------externals/SDL0
-rw-r--r--externals/nx_tzdb/CMakeLists.txt101
-rw-r--r--externals/nx_tzdb/ListFilesInDirectory.cmake8
-rw-r--r--externals/nx_tzdb/NxTzdbCreateHeader.cmake46
-rw-r--r--externals/nx_tzdb/include/nx_tzdb.h27
-rw-r--r--externals/nx_tzdb/tzdb_template.h.in18
m---------externals/nx_tzdb/tzdb_to_nx0
m---------externals/vcpkg0
-rw-r--r--src/android/app/build.gradle.kts48
-rw-r--r--src/android/app/src/main/AndroidManifest.xml4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt71
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt239
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt89
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt104
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt35
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt244
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt172
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt62
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt327
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt150
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt59
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt6
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt1
-rw-r--r--src/android/app/src/main/jni/config.cpp8
-rw-r--r--src/android/app/src/main/jni/default_ini.h6
-rw-r--r--src/android/app/src/main/jni/native.cpp321
-rw-r--r--src/android/app/src/main/jni/native.h165
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_mute.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_pause.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_play.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_pip_unmute.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_system_update_alt.xml9
-rw-r--r--src/android/app/src/main/res/layout/activity_emulation.xml16
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml82
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml55
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml28
-rw-r--r--src/android/app/src/main/res/navigation/emulation_navigation.xml18
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml14
-rw-r--r--src/android/app/src/main/res/values-de/strings.xml332
-rw-r--r--src/android/app/src/main/res/values-es/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-fr/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-it/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-ja/strings.xml335
-rw-r--r--src/android/app/src/main/res/values-ko/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-nb/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-pl/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-pt-rBR/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-pt-rPT/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-ru/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-uk/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-zh-rCN/strings.xml337
-rw-r--r--src/android/app/src/main/res/values-zh-rTW/strings.xml336
-rw-r--r--src/android/app/src/main/res/values/arrays.xml23
-rw-r--r--src/android/app/src/main/res/values/integers.xml64
-rw-r--r--src/android/app/src/main/res/values/strings.xml57
-rw-r--r--src/android/app/src/main/res/xml/locales_config.xml17
-rw-r--r--src/android/build.gradle.kts9
-rw-r--r--src/audio_core/device/audio_buffers.h8
-rw-r--r--src/audio_core/device/device_session.cpp12
-rw-r--r--src/audio_core/device/device_session.h7
-rw-r--r--src/audio_core/in/audio_in_system.cpp5
-rw-r--r--src/audio_core/out/audio_out_system.cpp4
-rw-r--r--src/audio_core/renderer/adsp/adsp.cpp1
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp5
-rw-r--r--src/audio_core/renderer/adsp/command_list_processor.cpp1
-rw-r--r--src/audio_core/renderer/command/data_source/decode.cpp23
-rw-r--r--src/audio_core/renderer/command/effect/compressor.cpp8
-rw-r--r--src/audio_core/renderer/command/effect/delay.cpp14
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp4
-rw-r--r--src/audio_core/renderer/command/effect/light_limiter.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp12
-rw-r--r--src/audio_core/renderer/command/performance/performance.cpp15
-rw-r--r--src/audio_core/renderer/command/sink/circular_buffer.cpp4
-rw-r--r--src/audio_core/renderer/command/sink/device.cpp5
-rw-r--r--src/audio_core/renderer/mix/mix_context.cpp6
-rw-r--r--src/audio_core/renderer/nodes/node_states.cpp4
-rw-r--r--src/audio_core/renderer/nodes/node_states.h2
-rw-r--r--src/audio_core/renderer/system.cpp1
-rw-r--r--src/audio_core/sink/null_sink.h2
-rw-r--r--src/audio_core/sink/sink_stream.cpp16
-rw-r--r--src/audio_core/sink/sink_stream.h5
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/fs/fs.cpp41
-rw-r--r--src/common/fs/fs_android.h5
-rw-r--r--src/common/fs/fs_types.h2
-rw-r--r--src/common/input.h43
-rw-r--r--src/common/ring_buffer.h3
-rw-r--r--src/common/scratch_buffer.h9
-rw-r--r--src/common/settings.cpp31
-rw-r--r--src/common/settings.h1
-rw-r--r--src/common/steady_clock.cpp5
-rw-r--r--src/common/thread.h2
-rw-r--r--src/common/time_zone.cpp63
-rw-r--r--src/common/time_zone.h6
-rw-r--r--src/common/wall_clock.cpp77
-rw-r--r--src/common/wall_clock.h89
-rw-r--r--src/common/x64/cpu_detect.cpp3
-rw-r--r--src/common/x64/cpu_wait.cpp20
-rw-r--r--src/common/x64/native_clock.cpp166
-rw-r--r--src/common/x64/native_clock.h59
-rw-r--r--src/common/x64/rdtsc.cpp39
-rw-r--r--src/common/x64/rdtsc.h37
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/arm/arm_interface.cpp84
-rw-r--r--src/core/arm/arm_interface.h37
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h29
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp64
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h16
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp64
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h18
-rw-r--r--src/core/arm/dynarmic/dynarmic_cp15.cpp (renamed from src/core/arm/dynarmic/arm_dynarmic_cp15.cpp)2
-rw-r--r--src/core/arm/dynarmic/dynarmic_cp15.h (renamed from src/core/arm/dynarmic/arm_dynarmic_cp15.h)0
-rw-r--r--src/core/arm/dynarmic/dynarmic_exclusive_monitor.cpp (renamed from src/core/arm/dynarmic/arm_exclusive_monitor.cpp)2
-rw-r--r--src/core/arm/dynarmic/dynarmic_exclusive_monitor.h (renamed from src/core/arm/dynarmic/arm_exclusive_monitor.h)0
-rw-r--r--src/core/arm/exclusive_monitor.cpp2
-rw-r--r--src/core/core.cpp26
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/core_timing.cpp52
-rw-r--r--src/core/core_timing.h14
-rw-r--r--src/core/core_timing_util.h58
-rw-r--r--src/core/file_sys/patch_manager.cpp9
-rw-r--r--src/core/file_sys/submission_package.h1
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp706
-rw-r--r--src/core/file_sys/vfs_concat.cpp14
-rw-r--r--src/core/file_sys/vfs_real.cpp232
-rw-r--r--src/core/file_sys/vfs_real.h44
-rw-r--r--src/core/hid/emulated_controller.cpp93
-rw-r--r--src/core/hid/emulated_controller.h28
-rw-r--r--src/core/hid/input_converter.cpp6
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp5
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.cpp23
-rw-r--r--src/core/hle/kernel/k_thread.h7
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp7
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp10
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_tick.cpp10
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp2
-rw-r--r--src/core/hle/service/audio/audin_u.cpp16
-rw-r--r--src/core/hle/service/audio/audout_u.cpp15
-rw-r--r--src/core/hle/service/audio/audren_u.cpp22
-rw-r--r--src/core/hle/service/audio/audren_u.h1
-rw-r--r--src/core/hle/service/audio/hwopus.cpp9
-rw-r--r--src/core/hle/service/hid/hidbus.cpp1
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp44
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.h9
-rw-r--r--src/core/hle/service/nfc/common/device.cpp270
-rw-r--r--src/core/hle/service/nfc/common/device.h21
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp15
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h2
-rw-r--r--src/core/hle/service/nfc/mifare_result.h2
-rw-r--r--src/core/hle/service/nfc/mifare_types.h11
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp20
-rw-r--r--src/core/hle/service/nfc/nfc_result.h3
-rw-r--r--src/core/hle/service/nfc/nfc_types.h37
-rw-r--r--src/core/hle/service/nfp/nfp_types.h26
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h30
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp19
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp32
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h38
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp59
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h20
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp24
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h3
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp15
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h3
-rw-r--r--src/core/hle/service/nvnflinger/parcel.h7
-rw-r--r--src/core/hle/service/server_manager.cpp7
-rw-r--r--src/core/hle/service/server_manager.h4
-rw-r--r--src/core/hle/service/time/clock_types.h13
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp2
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp2
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/time/time_manager.cpp34
-rw-r--r--src/core/hle/service/time/time_manager.h4
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp5
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp26
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp161
-rw-r--r--src/core/hle/service/time/time_zone_manager.h8
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp67
-rw-r--r--src/core/hle/service/time/time_zone_service.h3
-rw-r--r--src/input_common/drivers/joycon.cpp133
-rw-r--r--src/input_common/drivers/joycon.h18
-rw-r--r--src/input_common/drivers/sdl_driver.cpp32
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp132
-rw-r--r--src/input_common/drivers/virtual_amiibo.h17
-rw-r--r--src/input_common/helpers/joycon_driver.cpp160
-rw-r--r--src/input_common/helpers/joycon_driver.h7
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp8
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h59
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp390
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h34
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp4
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h4
-rw-r--r--src/input_common/input_engine.h34
-rw-r--r--src/input_common/input_poller.cpp24
-rw-r--r--src/shader_recompiler/CMakeLists.txt2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp6
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp4
-rw-r--r--src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp44
-rw-r--r--src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp185
-rw-r--r--src/shader_recompiler/ir_opt/passes.h2
-rw-r--r--src/shader_recompiler/runtime_info.h3
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h21
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h7
-rw-r--r--src/video_core/cdma_pusher.h1
-rw-r--r--src/video_core/dma_pusher.h8
-rw-r--r--src/video_core/engines/draw_manager.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.cpp35
-rw-r--r--src/video_core/gpu.cpp13
-rw-r--r--src/video_core/host1x/codecs/h264.cpp4
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/opengl_lmem_warmup.comp47
-rw-r--r--src/video_core/memory_manager.cpp17
-rw-r--r--src/video_core/memory_manager.h15
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp19
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp24
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h5
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.h10
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp28
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_context.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp48
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp23
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp78
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
-rw-r--r--src/video_core/shader_cache.cpp4
-rw-r--r--src/video_core/texture_cache/image_base.h5
-rw-r--r--src/video_core/texture_cache/image_info.cpp20
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp52
-rw-r--r--src/video_core/texture_cache/image_view_base.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h70
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h26
-rw-r--r--src/video_core/texture_cache/util.cpp48
-rw-r--r--src/video_core/texture_cache/util.h31
-rw-r--r--src/video_core/textures/texture.cpp7
-rw-r--r--src/video_core/transform_feedback.cpp8
-rw-r--r--src/video_core/transform_feedback.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp29
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h44
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp5
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp55
-rw-r--r--src/yuzu/configuration/configure_graphics.h3
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp10
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui10
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp5
-rw-r--r--src/yuzu/configuration/configure_per_game.h3
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui2
-rw-r--r--src/yuzu/configuration/configure_system.cpp3
-rw-r--r--src/yuzu/main.cpp72
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/vk_device_info.cpp61
-rw-r--r--src/yuzu/vk_device_info.h36
-rw-r--r--vcpkg.json2
353 files changed, 11010 insertions, 3522 deletions
diff --git a/.codespellrc b/.codespellrc
index 29bcf3502..01ddd2362 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -2,5 +2,5 @@
2; SPDX-License-Identifier: GPL-2.0-or-later 2; SPDX-License-Identifier: GPL-2.0-or-later
3 3
4[codespell] 4[codespell]
5skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES 5skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
6ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink 6ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
diff --git a/.gitignore b/.gitignore
index a5f7248c7..fbadb208b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,8 @@ CMakeSettings.json
26# OSX global filetypes 26# OSX global filetypes
27# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) 27# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc)
28.DS_Store 28.DS_Store
29.DS_Store?
30._*
29.AppleDouble 31.AppleDouble
30.LSOverride 32.LSOverride
31.Spotlight-V100 33.Spotlight-V100
diff --git a/.gitmodules b/.gitmodules
index 95eae8109..89f2ad924 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -52,3 +52,6 @@
52[submodule "libadrenotools"] 52[submodule "libadrenotools"]
53 path = externals/libadrenotools 53 path = externals/libadrenotools
54 url = https://github.com/bylaws/libadrenotools 54 url = https://github.com/bylaws/libadrenotools
55[submodule "tzdb_to_nx"]
56 path = externals/nx_tzdb/tzdb_to_nx
57 url = https://github.com/lat9nq/tzdb_to_nx.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d03bbf94..f5ef0ef50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,6 +59,8 @@ option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON)
59 59
60option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) 60option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
61 61
62option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
63
62CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) 64CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
63 65
64# On Android, fetch and compile libcxx before doing anything else 66# On Android, fetch and compile libcxx before doing anything else
@@ -487,7 +489,7 @@ if (ENABLE_SDL2)
487 if (YUZU_USE_BUNDLED_SDL2) 489 if (YUZU_USE_BUNDLED_SDL2)
488 # Detect toolchain and platform 490 # Detect toolchain and platform
489 if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) 491 if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
490 set(SDL2_VER "SDL2-2.0.18") 492 set(SDL2_VER "SDL2-2.28.0")
491 else() 493 else()
492 message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.") 494 message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
493 endif() 495 endif()
@@ -507,7 +509,7 @@ if (ENABLE_SDL2)
507 elseif (YUZU_USE_EXTERNAL_SDL2) 509 elseif (YUZU_USE_EXTERNAL_SDL2)
508 message(STATUS "Using SDL2 from externals.") 510 message(STATUS "Using SDL2 from externals.")
509 else() 511 else()
510 find_package(SDL2 2.0.18 REQUIRED) 512 find_package(SDL2 2.26.4 REQUIRED)
511 endif() 513 endif()
512endif() 514endif()
513 515
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 500eb21e3..7cce27d51 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -63,8 +63,9 @@ if (YUZU_USE_EXTERNAL_SDL2)
63 # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers 63 # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
64 # Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095) 64 # Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
65 # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) 65 # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
66 # CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
66 set(SDL_UNUSED_SUBSYSTEMS 67 set(SDL_UNUSED_SUBSYSTEMS
67 CPUinfo File Filesystem 68 File Filesystem
68 Locale Power Render) 69 Locale Power Render)
69 foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) 70 foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
70 string(TOUPPER ${_SUB} _OPT) 71 string(TOUPPER ${_SUB} _OPT)
@@ -139,6 +140,9 @@ if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
139 add_subdirectory(Vulkan-Headers) 140 add_subdirectory(Vulkan-Headers)
140endif() 141endif()
141 142
143# TZDB (Time Zone Database)
144add_subdirectory(nx_tzdb)
145
142if (NOT TARGET LLVM::Demangle) 146if (NOT TARGET LLVM::Demangle)
143 add_library(demangle demangle/ItaniumDemangle.cpp) 147 add_library(demangle demangle/ItaniumDemangle.cpp)
144 target_include_directories(demangle PUBLIC ./demangle) 148 target_include_directories(demangle PUBLIC ./demangle)
diff --git a/externals/SDL b/externals/SDL
Subproject f17058b562c8a1090c0c996b42982721ace9090 Subproject 491fba1d06a4810645092b2559b9cc94abeb23b
diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt
new file mode 100644
index 000000000..593786250
--- /dev/null
+++ b/externals/nx_tzdb/CMakeLists.txt
@@ -0,0 +1,101 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
5
6add_library(nx_tzdb INTERFACE)
7
8find_program(GIT git)
9find_program(GNU_MAKE make)
10find_program(DATE_PROG date)
11
12set(CAN_BUILD_NX_TZDB true)
13
14if (NOT GIT)
15 set(CAN_BUILD_NX_TZDB false)
16endif()
17if (NOT GNU_MAKE)
18 set(CAN_BUILD_NX_TZDB false)
19endif()
20if (NOT DATE_PROG)
21 set(CAN_BUILD_NX_TZDB false)
22endif()
23if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
24 # tzdb_to_nx currently requires a posix-compliant host
25 # MinGW and Android are handled here due to the executable format being different from the host system
26 # TODO (lat9nq): cross-compiling support
27 set(CAN_BUILD_NX_TZDB false)
28endif()
29
30set(NX_TZDB_VERSION "220816")
31set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
32
33set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
34
35if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ARCHIVE})
36 set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")
37
38 message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...")
39 file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE}
40 STATUS NX_TZDB_DOWNLOAD_STATUS)
41 list(GET NX_TZDB_DOWNLOAD_STATUS 0 NX_TZDB_DOWNLOAD_STATUS_CODE)
42 if (NOT NX_TZDB_DOWNLOAD_STATUS_CODE EQUAL 0)
43 message(FATAL_ERROR "Time zone data download failed (status code ${NX_TZDB_DOWNLOAD_STATUS_CODE})")
44 endif()
45
46 file(ARCHIVE_EXTRACT
47 INPUT
48 ${NX_TZDB_ARCHIVE}
49 DESTINATION
50 ${NX_TZDB_ROMFS_DIR})
51elseif (CAN_BUILD_NX_TZDB AND NOT YUZU_DOWNLOAD_TIME_ZONE_DATA)
52 add_subdirectory(tzdb_to_nx)
53 add_dependencies(nx_tzdb x80e)
54
55 set(NX_TZDB_ROMFS_DIR "${NX_TZDB_DIR}")
56endif()
57
58target_include_directories(nx_tzdb
59 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
60 INTERFACE ${NX_TZDB_INCLUDE_DIR})
61
62function(CreateHeader ZONE_PATH HEADER_NAME)
63 set(HEADER_PATH "${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h")
64 add_custom_command(
65 OUTPUT
66 ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h
67 COMMAND
68 ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/NxTzdbCreateHeader.cmake
69 ${ZONE_PATH}
70 ${HEADER_NAME}
71 ${NX_TZDB_INCLUDE_DIR}
72 ${CMAKE_CURRENT_SOURCE_DIR}
73 DEPENDS
74 tzdb_template.h.in
75 NxTzdbCreateHeader.cmake)
76
77 target_sources(nx_tzdb PRIVATE ${HEADER_PATH})
78endfunction()
79
80CreateHeader(${NX_TZDB_ROMFS_DIR} base)
81CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo zoneinfo)
82CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Africa africa)
83CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America america)
84CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Argentina america_argentina)
85CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Indiana america_indiana)
86CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Kentucky america_kentucky)
87CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/North_Dakota america_north_dakota)
88CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Antarctica antarctica)
89CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Arctic arctic)
90CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Asia asia)
91CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Atlantic atlantic)
92CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Australia australia)
93CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Brazil brazil)
94CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Canada canada)
95CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Chile chile)
96CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Etc etc)
97CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Europe europe)
98CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Indian indian)
99CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Mexico mexico)
100CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Pacific pacific)
101CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/US us)
diff --git a/externals/nx_tzdb/ListFilesInDirectory.cmake b/externals/nx_tzdb/ListFilesInDirectory.cmake
new file mode 100644
index 000000000..35a9e726a
--- /dev/null
+++ b/externals/nx_tzdb/ListFilesInDirectory.cmake
@@ -0,0 +1,8 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4# CMake does not have a way to list the files in a specific directory,
5# so we need this script to do that for us in a platform-agnostic fashion
6
7file(GLOB FILE_LIST LIST_DIRECTORIES false RELATIVE ${CMAKE_SOURCE_DIR} "*")
8execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${FILE_LIST};")
diff --git a/externals/nx_tzdb/NxTzdbCreateHeader.cmake b/externals/nx_tzdb/NxTzdbCreateHeader.cmake
new file mode 100644
index 000000000..8c29e1167
--- /dev/null
+++ b/externals/nx_tzdb/NxTzdbCreateHeader.cmake
@@ -0,0 +1,46 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4set(ZONE_PATH ${CMAKE_ARGV3})
5set(HEADER_NAME ${CMAKE_ARGV4})
6set(NX_TZDB_INCLUDE_DIR ${CMAKE_ARGV5})
7set(NX_TZDB_SOURCE_DIR ${CMAKE_ARGV6})
8
9execute_process(
10 COMMAND ${CMAKE_COMMAND} -P ${NX_TZDB_SOURCE_DIR}/ListFilesInDirectory.cmake
11 WORKING_DIRECTORY ${ZONE_PATH}
12 OUTPUT_VARIABLE FILE_LIST)
13
14set(DIRECTORY_NAME ${HEADER_NAME})
15
16set(FILE_DATA "")
17foreach(ZONE_FILE ${FILE_LIST})
18 if (ZONE_FILE STREQUAL "\n")
19 continue()
20 endif()
21
22 string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{")
23
24 file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX)
25 string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN)
26 foreach(I RANGE 0 ${ZONE_DATA_LEN} 2)
27 math(EXPR BREAK_LINE "(${I} + 2) % 38")
28
29 string(SUBSTRING "${ZONE_DATA}" "${I}" 2 HEX_DATA)
30 if (NOT HEX_DATA)
31 break()
32 endif()
33
34 string(APPEND FILE_DATA "0x${HEX_DATA},")
35 if (BREAK_LINE EQUAL 0)
36 string(APPEND FILE_DATA "\n")
37 else()
38 string(APPEND FILE_DATA " ")
39 endif()
40 endforeach()
41
42 string(APPEND FILE_DATA "}},\n")
43endforeach()
44
45file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN)
46file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}")
diff --git a/externals/nx_tzdb/include/nx_tzdb.h b/externals/nx_tzdb/include/nx_tzdb.h
new file mode 100644
index 000000000..1f7c6069a
--- /dev/null
+++ b/externals/nx_tzdb/include/nx_tzdb.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "nx_tzdb/africa.h"
7#include "nx_tzdb/america.h"
8#include "nx_tzdb/america_argentina.h"
9#include "nx_tzdb/america_indiana.h"
10#include "nx_tzdb/america_kentucky.h"
11#include "nx_tzdb/america_north_dakota.h"
12#include "nx_tzdb/antarctica.h"
13#include "nx_tzdb/arctic.h"
14#include "nx_tzdb/asia.h"
15#include "nx_tzdb/atlantic.h"
16#include "nx_tzdb/australia.h"
17#include "nx_tzdb/base.h"
18#include "nx_tzdb/brazil.h"
19#include "nx_tzdb/canada.h"
20#include "nx_tzdb/chile.h"
21#include "nx_tzdb/etc.h"
22#include "nx_tzdb/europe.h"
23#include "nx_tzdb/indian.h"
24#include "nx_tzdb/mexico.h"
25#include "nx_tzdb/pacific.h"
26#include "nx_tzdb/us.h"
27#include "nx_tzdb/zoneinfo.h"
diff --git a/externals/nx_tzdb/tzdb_template.h.in b/externals/nx_tzdb/tzdb_template.h.in
new file mode 100644
index 000000000..289d002ea
--- /dev/null
+++ b/externals/nx_tzdb/tzdb_template.h.in
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <cstdint>
7#include <map>
8#include <vector>
9
10namespace NxTzdb {
11
12// clang-format off
13const static std::map<const char*, const std::vector<uint8_t>> @DIRECTORY_NAME@ =
14{
15@FILE_DATA@};
16// clang-format on
17
18} // namespace NxTzdb
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx
new file mode 160000
Subproject 212afa2394a74226dcf1b7996a570aae17debb6
diff --git a/externals/vcpkg b/externals/vcpkg
Subproject 656fcc6ab2b05c6d999b7eaca717027ac3738f7 Subproject cbf56573a987527b39272e88cbdd11389b78c6e
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index d4698ae1c..bab4f4d0f 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -2,12 +2,17 @@
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4import android.annotation.SuppressLint 4import android.annotation.SuppressLint
5import kotlin.collections.setOf
6import org.jetbrains.kotlin.konan.properties.Properties
7import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
5 8
6plugins { 9plugins {
7 id("com.android.application") 10 id("com.android.application")
8 id("org.jetbrains.kotlin.android") 11 id("org.jetbrains.kotlin.android")
9 id("kotlin-parcelize") 12 id("kotlin-parcelize")
10 kotlin("plugin.serialization") version "1.8.21" 13 kotlin("plugin.serialization") version "1.8.21"
14 id("androidx.navigation.safeargs.kotlin")
15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
11} 16}
12 17
13/** 18/**
@@ -42,24 +47,27 @@ android {
42 jniLibs.useLegacyPackaging = true 47 jniLibs.useLegacyPackaging = true
43 } 48 }
44 49
45 lint {
46 // This is important as it will run lint but not abort on error
47 // Lint has some overly obnoxious "errors" that should really be warnings
48 abortOnError = false
49
50 //Uncomment disable lines for test builds...
51 //disable 'MissingTranslation'bin
52 //disable 'ExtraTranslation'
53 }
54
55 defaultConfig { 50 defaultConfig {
56 // TODO If this is ever modified, change application_id in strings.xml 51 // TODO If this is ever modified, change application_id in strings.xml
57 applicationId = "org.yuzu.yuzu_emu" 52 applicationId = "org.yuzu.yuzu_emu"
58 minSdk = 30 53 minSdk = 30
59 targetSdk = 33 54 targetSdk = 33
60 versionCode = 1
61 versionName = getGitVersion() 55 versionName = getGitVersion()
62 56
57 // If you want to use autoVersion for the versionCode, create a property in local.properties
58 // named "autoVersioned" and set it to "true"
59 val properties = Properties()
60 val versionProperty = try {
61 properties.load(project.rootProject.file("local.properties").inputStream())
62 properties.getProperty("autoVersioned") ?: ""
63 } catch (e: Exception) { "" }
64
65 versionCode = if (versionProperty == "true") {
66 autoVersion
67 } else {
68 1
69 }
70
63 ndk { 71 ndk {
64 @SuppressLint("ChromeOsAbiSupport") 72 @SuppressLint("ChromeOsAbiSupport")
65 abiFilters += listOf("arm64-v8a") 73 abiFilters += listOf("arm64-v8a")
@@ -152,6 +160,24 @@ android {
152 } 160 }
153} 161}
154 162
163tasks.getByPath("preBuild").dependsOn("ktlintCheck")
164
165ktlint {
166 version.set("0.47.1")
167 android.set(true)
168 ignoreFailures.set(false)
169 disabledRules.set(
170 setOf(
171 "no-wildcard-imports",
172 "package-name",
173 "import-ordering"
174 )
175 )
176 reporters {
177 reporter(ReporterType.CHECKSTYLE)
178 }
179}
180
155dependencies { 181dependencies {
156 implementation("androidx.core:core-ktx:1.10.1") 182 implementation("androidx.core:core-ktx:1.10.1")
157 implementation("androidx.appcompat:appcompat:1.6.1") 183 implementation("androidx.appcompat:appcompat:1.6.1")
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 1e92098ec..e31ad69e2 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -24,6 +24,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
24 android:hasFragileUserData="true" 24 android:hasFragileUserData="true"
25 android:supportsRtl="true" 25 android:supportsRtl="true"
26 android:isGame="true" 26 android:isGame="true"
27 android:localeConfig="@xml/locales_config"
27 android:banner="@drawable/tv_banner" 28 android:banner="@drawable/tv_banner"
28 android:extractNativeLibs="true" 29 android:extractNativeLibs="true"
29 android:fullBackupContent="@xml/data_extraction_rules" 30 android:fullBackupContent="@xml/data_extraction_rules"
@@ -52,8 +53,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
52 <activity 53 <activity
53 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" 54 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
54 android:theme="@style/Theme.Yuzu.Main" 55 android:theme="@style/Theme.Yuzu.Main"
55 android:launchMode="singleTop"
56 android:screenOrientation="userLandscape" 56 android:screenOrientation="userLandscape"
57 android:supportsPictureInPicture="true"
58 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
57 android:exported="true"> 59 android:exported="true">
58 60
59 <intent-filter> 61 <intent-filter>
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 22af9e435..9c32e044c 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
@@ -14,16 +14,18 @@ import android.widget.TextView
14import androidx.annotation.Keep 14import androidx.annotation.Keep
15import androidx.fragment.app.DialogFragment 15import androidx.fragment.app.DialogFragment
16import com.google.android.material.dialog.MaterialAlertDialogBuilder 16import com.google.android.material.dialog.MaterialAlertDialogBuilder
17import java.lang.ref.WeakReference
17import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext 18import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
18import org.yuzu.yuzu_emu.activities.EmulationActivity 19import org.yuzu.yuzu_emu.activities.EmulationActivity
19import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath 20import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
21import org.yuzu.yuzu_emu.utils.FileUtil.exists
20import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize 22import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
23import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
21import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri 24import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
22import org.yuzu.yuzu_emu.utils.Log.error 25import org.yuzu.yuzu_emu.utils.Log.error
23import org.yuzu.yuzu_emu.utils.Log.verbose 26import org.yuzu.yuzu_emu.utils.Log.verbose
24import org.yuzu.yuzu_emu.utils.Log.warning 27import org.yuzu.yuzu_emu.utils.Log.warning
25import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable 28import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
26import java.lang.ref.WeakReference
27 29
28/** 30/**
29 * Class which contains methods that interact 31 * Class which contains methods that interact
@@ -74,7 +76,9 @@ object NativeLibrary {
74 fun openContentUri(path: String?, openmode: String?): Int { 76 fun openContentUri(path: String?, openmode: String?): Int {
75 return if (isNativePath(path!!)) { 77 return if (isNativePath(path!!)) {
76 YuzuApplication.documentsTree!!.openContentUri(path, openmode) 78 YuzuApplication.documentsTree!!.openContentUri(path, openmode)
77 } else openContentUri(appContext, path, openmode) 79 } else {
80 openContentUri(appContext, path, openmode)
81 }
78 } 82 }
79 83
80 @Keep 84 @Keep
@@ -82,7 +86,29 @@ object NativeLibrary {
82 fun getSize(path: String?): Long { 86 fun getSize(path: String?): Long {
83 return if (isNativePath(path!!)) { 87 return if (isNativePath(path!!)) {
84 YuzuApplication.documentsTree!!.getFileSize(path) 88 YuzuApplication.documentsTree!!.getFileSize(path)
85 } else getFileSize(appContext, path) 89 } else {
90 getFileSize(appContext, path)
91 }
92 }
93
94 @Keep
95 @JvmStatic
96 fun exists(path: String?): Boolean {
97 return if (isNativePath(path!!)) {
98 YuzuApplication.documentsTree!!.exists(path)
99 } else {
100 exists(appContext, path)
101 }
102 }
103
104 @Keep
105 @JvmStatic
106 fun isDirectory(path: String?): Boolean {
107 return if (isNativePath(path!!)) {
108 YuzuApplication.documentsTree!!.isDirectory(path)
109 } else {
110 isDirectory(appContext, path)
111 }
86 } 112 }
87 113
88 /** 114 /**
@@ -227,6 +253,8 @@ object NativeLibrary {
227 253
228 external fun setAppDirectory(directory: String) 254 external fun setAppDirectory(directory: String)
229 255
256 external fun installFileToNand(filename: String): Int
257
230 external fun initializeGpuDriver( 258 external fun initializeGpuDriver(
231 hookLibDir: String?, 259 hookLibDir: String?,
232 customDriverDir: String?, 260 customDriverDir: String?,
@@ -258,7 +286,7 @@ object NativeLibrary {
258 /** 286 /**
259 * Unpauses emulation from a paused state. 287 * Unpauses emulation from a paused state.
260 */ 288 */
261 external fun unPauseEmulation() 289 external fun unpauseEmulation()
262 290
263 /** 291 /**
264 * Pauses emulation. 292 * Pauses emulation.
@@ -281,6 +309,26 @@ object NativeLibrary {
281 external fun isRunning(): Boolean 309 external fun isRunning(): Boolean
282 310
283 /** 311 /**
312 * Returns true if emulation is paused.
313 */
314 external fun isPaused(): Boolean
315
316 /**
317 * Mutes emulation sound
318 */
319 external fun muteAudio(): Boolean
320
321 /**
322 * Unmutes emulation sound
323 */
324 external fun unmuteAudio(): Boolean
325
326 /**
327 * Returns true if emulation audio is muted.
328 */
329 external fun isMuted(): Boolean
330
331 /**
284 * Returns the performance stats for the current game 332 * Returns the performance stats for the current game
285 */ 333 */
286 external fun getPerfStats(): DoubleArray 334 external fun getPerfStats(): DoubleArray
@@ -429,7 +477,9 @@ object NativeLibrary {
429 Html.FROM_HTML_MODE_LEGACY 477 Html.FROM_HTML_MODE_LEGACY
430 ) 478 )
431 ) 479 )
432 .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() } 480 .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
481 emulationActivity.finish()
482 }
433 .setOnDismissListener { emulationActivity.finish() } 483 .setOnDismissListener { emulationActivity.finish() }
434 emulationActivity.runOnUiThread { 484 emulationActivity.runOnUiThread {
435 val alert = builder.create() 485 val alert = builder.create()
@@ -507,4 +557,15 @@ object NativeLibrary {
507 const val RELEASED = 0 557 const val RELEASED = 0
508 const val PRESSED = 1 558 const val PRESSED = 1
509 } 559 }
560
561 /**
562 * Result from installFileToNand
563 */
564 object InstallFileToNandResult {
565 const val Success = 0
566 const val SuccessFileOverwritten = 1
567 const val Error = 2
568 const val ErrorBaseGame = 3
569 const val ErrorFilenameExtension = 4
570 }
510} 571}
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 4c947b786..04ab6a220 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
@@ -7,12 +7,12 @@ import android.app.Application
7import android.app.NotificationChannel 7import android.app.NotificationChannel
8import android.app.NotificationManager 8import android.app.NotificationManager
9import android.content.Context 9import android.content.Context
10import java.io.File
10import org.yuzu.yuzu_emu.utils.DirectoryInitialization 11import org.yuzu.yuzu_emu.utils.DirectoryInitialization
11import org.yuzu.yuzu_emu.utils.DocumentsTree 12import org.yuzu.yuzu_emu.utils.DocumentsTree
12import org.yuzu.yuzu_emu.utils.GpuDriverHelper 13import org.yuzu.yuzu_emu.utils.GpuDriverHelper
13import java.io.File
14 14
15fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir 15fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
16 16
17class YuzuApplication : Application() { 17class YuzuApplication : Application() {
18 private fun createNotificationChannels() { 18 private fun createNotificationChannels() {
@@ -21,7 +21,9 @@ class YuzuApplication : Application() {
21 getString(R.string.emulation_notification_channel_name), 21 getString(R.string.emulation_notification_channel_name),
22 NotificationManager.IMPORTANCE_LOW 22 NotificationManager.IMPORTANCE_LOW
23 ) 23 )
24 emulationChannel.description = getString(R.string.emulation_notification_channel_description) 24 emulationChannel.description = getString(
25 R.string.emulation_notification_channel_description
26 )
25 emulationChannel.setSound(null, null) 27 emulationChannel.setSound(null, null)
26 emulationChannel.vibrationPattern = null 28 emulationChannel.vibrationPattern = null
27 29
@@ -48,7 +50,7 @@ class YuzuApplication : Application() {
48 GpuDriverHelper.initializeDriverParameters(applicationContext) 50 GpuDriverHelper.initializeDriverParameters(applicationContext)
49 NativeLibrary.logDeviceInfo() 51 NativeLibrary.logDeviceInfo()
50 52
51 createNotificationChannels(); 53 createNotificationChannels()
52 } 54 }
53 55
54 companion object { 56 companion object {
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 20a0394f5..ae665ed2e 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
@@ -4,49 +4,57 @@
4package org.yuzu.yuzu_emu.activities 4package org.yuzu.yuzu_emu.activities
5 5
6import android.app.Activity 6import android.app.Activity
7import android.app.PendingIntent
8import android.app.PictureInPictureParams
9import android.app.RemoteAction
10import android.content.BroadcastReceiver
7import android.content.Context 11import android.content.Context
8import android.content.Intent 12import android.content.Intent
13import android.content.IntentFilter
14import android.content.res.Configuration
9import android.graphics.Rect 15import android.graphics.Rect
16import android.graphics.drawable.Icon
10import android.hardware.Sensor 17import android.hardware.Sensor
11import android.hardware.SensorEvent 18import android.hardware.SensorEvent
12import android.hardware.SensorEventListener 19import android.hardware.SensorEventListener
13import android.hardware.SensorManager 20import android.hardware.SensorManager
21import android.os.Build
14import android.os.Bundle 22import android.os.Bundle
23import android.util.Rational
15import android.view.InputDevice 24import android.view.InputDevice
16import android.view.KeyEvent 25import android.view.KeyEvent
17import android.view.MotionEvent 26import android.view.MotionEvent
18import android.view.Surface 27import android.view.Surface
19import android.view.View 28import android.view.View
20import android.view.inputmethod.InputMethodManager 29import android.view.inputmethod.InputMethodManager
30import android.widget.Toast
21import androidx.activity.viewModels 31import androidx.activity.viewModels
22import androidx.appcompat.app.AppCompatActivity 32import androidx.appcompat.app.AppCompatActivity
23import androidx.core.view.WindowCompat 33import androidx.core.view.WindowCompat
24import androidx.core.view.WindowInsetsCompat 34import androidx.core.view.WindowInsetsCompat
25import androidx.core.view.WindowInsetsControllerCompat 35import androidx.core.view.WindowInsetsControllerCompat
26import androidx.lifecycle.Lifecycle 36import androidx.navigation.fragment.NavHostFragment
27import androidx.lifecycle.lifecycleScope
28import androidx.lifecycle.repeatOnLifecycle
29import androidx.window.layout.WindowInfoTracker
30import kotlinx.coroutines.Dispatchers
31import kotlinx.coroutines.launch
32import org.yuzu.yuzu_emu.NativeLibrary 37import org.yuzu.yuzu_emu.NativeLibrary
33import org.yuzu.yuzu_emu.R 38import org.yuzu.yuzu_emu.R
39import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
40import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
41import org.yuzu.yuzu_emu.features.settings.model.IntSetting
34import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel 42import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
35import org.yuzu.yuzu_emu.fragments.EmulationFragment
36import org.yuzu.yuzu_emu.model.Game 43import org.yuzu.yuzu_emu.model.Game
37import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 44import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
38import org.yuzu.yuzu_emu.utils.ForegroundService 45import org.yuzu.yuzu_emu.utils.ForegroundService
39import org.yuzu.yuzu_emu.utils.InputHandler 46import org.yuzu.yuzu_emu.utils.InputHandler
47import org.yuzu.yuzu_emu.utils.MemoryUtil
40import org.yuzu.yuzu_emu.utils.NfcReader 48import org.yuzu.yuzu_emu.utils.NfcReader
41import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
42import org.yuzu.yuzu_emu.utils.ThemeHelper 49import org.yuzu.yuzu_emu.utils.ThemeHelper
43import kotlin.math.roundToInt 50import kotlin.math.roundToInt
44 51
45class EmulationActivity : AppCompatActivity(), SensorEventListener { 52class EmulationActivity : AppCompatActivity(), SensorEventListener {
53 private lateinit var binding: ActivityEmulationBinding
54
46 private var controllerMappingHelper: ControllerMappingHelper? = null 55 private var controllerMappingHelper: ControllerMappingHelper? = null
47 56
48 var isActivityRecreated = false 57 var isActivityRecreated = false
49 private var emulationFragment: EmulationFragment? = null
50 private lateinit var nfcReader: NfcReader 58 private lateinit var nfcReader: NfcReader
51 private lateinit var inputHandler: InputHandler 59 private lateinit var inputHandler: InputHandler
52 60
@@ -55,7 +63,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
55 private var motionTimestamp: Long = 0 63 private var motionTimestamp: Long = 0
56 private var flipMotionOrientation: Boolean = false 64 private var flipMotionOrientation: Boolean = false
57 65
58 private lateinit var game: Game 66 private val actionPause = "ACTION_EMULATOR_PAUSE"
67 private val actionPlay = "ACTION_EMULATOR_PLAY"
68 private val actionMute = "ACTION_EMULATOR_MUTE"
69 private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
59 70
60 private val settingsViewModel: SettingsViewModel by viewModels() 71 private val settingsViewModel: SettingsViewModel by viewModels()
61 72
@@ -70,45 +81,42 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
70 settingsViewModel.settings.loadSettings() 81 settingsViewModel.settings.loadSettings()
71 82
72 super.onCreate(savedInstanceState) 83 super.onCreate(savedInstanceState)
73 if (savedInstanceState == null) { 84
74 // Get params we were passed 85 binding = ActivityEmulationBinding.inflate(layoutInflater)
75 game = intent.parcelable(EXTRA_SELECTED_GAME)!! 86 setContentView(binding.root)
76 isActivityRecreated = false 87
77 } else { 88 val navHostFragment =
78 isActivityRecreated = true 89 supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
79 restoreState(savedInstanceState) 90 val navController = navHostFragment.navController
80 } 91 navController
92 .setGraph(R.navigation.emulation_navigation, intent.extras)
93
94 isActivityRecreated = savedInstanceState != null
95
81 controllerMappingHelper = ControllerMappingHelper() 96 controllerMappingHelper = ControllerMappingHelper()
82 97
83 // Set these options now so that the SurfaceView the game renders into is the right size. 98 // Set these options now so that the SurfaceView the game renders into is the right size.
84 enableFullscreenImmersive() 99 enableFullscreenImmersive()
85 100
86 setContentView(R.layout.activity_emulation)
87 window.decorView.setBackgroundColor(getColor(android.R.color.black)) 101 window.decorView.setBackgroundColor(getColor(android.R.color.black))
88 102
89 // Find or create the EmulationFragment
90 emulationFragment =
91 supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
92 if (emulationFragment == null) {
93 emulationFragment = EmulationFragment.newInstance(game)
94 supportFragmentManager.beginTransaction()
95 .add(R.id.frame_emulation_fragment, emulationFragment!!)
96 .commit()
97 }
98 title = game.title
99
100 nfcReader = NfcReader(this) 103 nfcReader = NfcReader(this)
101 nfcReader.initialize() 104 nfcReader.initialize()
102 105
103 inputHandler = InputHandler() 106 inputHandler = InputHandler()
104 inputHandler.initialize() 107 inputHandler.initialize()
105 108
106 lifecycleScope.launch(Dispatchers.Main) { 109 val memoryUtil = MemoryUtil(this)
107 lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { 110 if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
108 WindowInfoTracker.getOrCreate(this@EmulationActivity) 111 Toast.makeText(
109 .windowLayoutInfo(this@EmulationActivity) 112 this,
110 .collect { emulationFragment?.updateCurrentLayout(this@EmulationActivity, it) } 113 getString(
111 } 114 R.string.device_memory_inadequate,
115 memoryUtil.getDeviceRAM(),
116 "8 ${getString(R.string.memory_gigabyte)}"
117 ),
118 Toast.LENGTH_LONG
119 ).show()
112 } 120 }
113 121
114 // Start a foreground service to prevent the app from getting killed in the background 122 // Start a foreground service to prevent the app from getting killed in the background
@@ -143,6 +151,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
143 super.onResume() 151 super.onResume()
144 nfcReader.startScanning() 152 nfcReader.startScanning()
145 startMotionSensorListener() 153 startMotionSensorListener()
154
155 buildPictureInPictureParams()
146 } 156 }
147 157
148 override fun onPause() { 158 override fun onPause() {
@@ -151,17 +161,22 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
151 stopMotionSensorListener() 161 stopMotionSensorListener()
152 } 162 }
153 163
164 override fun onUserLeaveHint() {
165 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
166 if (BooleanSetting.PICTURE_IN_PICTURE.boolean && !isInPictureInPictureMode) {
167 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
168 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
169 enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
170 }
171 }
172 }
173
154 override fun onNewIntent(intent: Intent) { 174 override fun onNewIntent(intent: Intent) {
155 super.onNewIntent(intent) 175 super.onNewIntent(intent)
156 setIntent(intent) 176 setIntent(intent)
157 nfcReader.onNewIntent(intent) 177 nfcReader.onNewIntent(intent)
158 } 178 }
159 179
160 override fun onSaveInstanceState(outState: Bundle) {
161 outState.putParcelable(EXTRA_SELECTED_GAME, game)
162 super.onSaveInstanceState(outState)
163 }
164
165 override fun dispatchKeyEvent(event: KeyEvent): Boolean { 180 override fun dispatchKeyEvent(event: KeyEvent): Boolean {
166 if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && 181 if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
167 event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD 182 event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
@@ -248,10 +263,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
248 263
249 override fun onAccuracyChanged(sensor: Sensor, i: Int) {} 264 override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
250 265
251 private fun restoreState(savedInstanceState: Bundle) {
252 game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
253 }
254
255 private fun enableFullscreenImmersive() { 266 private fun enableFullscreenImmersive() {
256 WindowCompat.setDecorFitsSystemWindows(window, false) 267 WindowCompat.setDecorFitsSystemWindows(window, false)
257 268
@@ -262,6 +273,144 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
262 } 273 }
263 } 274 }
264 275
276 private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder():
277 PictureInPictureParams.Builder {
278 val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) {
279 0 -> Rational(16, 9)
280 1 -> Rational(4, 3)
281 2 -> Rational(21, 9)
282 3 -> Rational(16, 10)
283 else -> null // Best fit
284 }
285 return this.apply { aspectRatio?.let { setAspectRatio(it) } }
286 }
287
288 private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder():
289 PictureInPictureParams.Builder {
290 val pictureInPictureActions: MutableList<RemoteAction> = mutableListOf()
291 val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
292
293 if (NativeLibrary.isPaused()) {
294 val playIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_play)
295 val playPendingIntent = PendingIntent.getBroadcast(
296 this@EmulationActivity,
297 R.drawable.ic_pip_play,
298 Intent(actionPlay),
299 pendingFlags
300 )
301 val playRemoteAction = RemoteAction(
302 playIcon,
303 getString(R.string.play),
304 getString(R.string.play),
305 playPendingIntent
306 )
307 pictureInPictureActions.add(playRemoteAction)
308 } else {
309 val pauseIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_pause)
310 val pausePendingIntent = PendingIntent.getBroadcast(
311 this@EmulationActivity,
312 R.drawable.ic_pip_pause,
313 Intent(actionPause),
314 pendingFlags
315 )
316 val pauseRemoteAction = RemoteAction(
317 pauseIcon,
318 getString(R.string.pause),
319 getString(R.string.pause),
320 pausePendingIntent
321 )
322 pictureInPictureActions.add(pauseRemoteAction)
323 }
324
325 if (NativeLibrary.isMuted()) {
326 val unmuteIcon = Icon.createWithResource(
327 this@EmulationActivity,
328 R.drawable.ic_pip_unmute
329 )
330 val unmutePendingIntent = PendingIntent.getBroadcast(
331 this@EmulationActivity,
332 R.drawable.ic_pip_unmute,
333 Intent(actionUnmute),
334 pendingFlags
335 )
336 val unmuteRemoteAction = RemoteAction(
337 unmuteIcon,
338 getString(R.string.unmute),
339 getString(R.string.unmute),
340 unmutePendingIntent
341 )
342 pictureInPictureActions.add(unmuteRemoteAction)
343 } else {
344 val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute)
345 val mutePendingIntent = PendingIntent.getBroadcast(
346 this@EmulationActivity,
347 R.drawable.ic_pip_mute,
348 Intent(actionMute),
349 pendingFlags
350 )
351 val muteRemoteAction = RemoteAction(
352 muteIcon,
353 getString(R.string.mute),
354 getString(R.string.mute),
355 mutePendingIntent
356 )
357 pictureInPictureActions.add(muteRemoteAction)
358 }
359
360 return this.apply { setActions(pictureInPictureActions) }
361 }
362
363 fun buildPictureInPictureParams() {
364 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
365 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
366 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
367 pictureInPictureParamsBuilder.setAutoEnterEnabled(
368 BooleanSetting.PICTURE_IN_PICTURE.boolean
369 )
370 }
371 setPictureInPictureParams(pictureInPictureParamsBuilder.build())
372 }
373
374 private var pictureInPictureReceiver = object : BroadcastReceiver() {
375 override fun onReceive(context: Context?, intent: Intent) {
376 if (intent.action == actionPlay) {
377 if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation()
378 } else if (intent.action == actionPause) {
379 if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
380 }
381 if (intent.action == actionUnmute) {
382 if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
383 } else if (intent.action == actionMute) {
384 if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
385 }
386 buildPictureInPictureParams()
387 }
388 }
389
390 override fun onPictureInPictureModeChanged(
391 isInPictureInPictureMode: Boolean,
392 newConfig: Configuration
393 ) {
394 super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
395 if (isInPictureInPictureMode) {
396 IntentFilter().apply {
397 addAction(actionPause)
398 addAction(actionPlay)
399 addAction(actionMute)
400 addAction(actionUnmute)
401 }.also {
402 registerReceiver(pictureInPictureReceiver, it)
403 }
404 } else {
405 try {
406 unregisterReceiver(pictureInPictureReceiver)
407 } catch (ignored: Exception) {
408 }
409 // Always resume audio, since there is no UI button
410 if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
411 }
412 }
413
265 private fun startMotionSensorListener() { 414 private fun startMotionSensorListener() {
266 val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager 415 val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
267 val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) 416 val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
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 7f9e2e2d4..e91277d35 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
@@ -16,6 +16,7 @@ import androidx.appcompat.app.AppCompatActivity
16import androidx.documentfile.provider.DocumentFile 16import androidx.documentfile.provider.DocumentFile
17import androidx.lifecycle.ViewModelProvider 17import androidx.lifecycle.ViewModelProvider
18import androidx.lifecycle.lifecycleScope 18import androidx.lifecycle.lifecycleScope
19import androidx.navigation.findNavController
19import androidx.preference.PreferenceManager 20import androidx.preference.PreferenceManager
20import androidx.recyclerview.widget.AsyncDifferConfig 21import androidx.recyclerview.widget.AsyncDifferConfig
21import androidx.recyclerview.widget.DiffUtil 22import androidx.recyclerview.widget.DiffUtil
@@ -23,13 +24,13 @@ import androidx.recyclerview.widget.ListAdapter
23import androidx.recyclerview.widget.RecyclerView 24import androidx.recyclerview.widget.RecyclerView
24import coil.load 25import coil.load
25import kotlinx.coroutines.launch 26import kotlinx.coroutines.launch
27import org.yuzu.yuzu_emu.HomeNavigationDirections
26import org.yuzu.yuzu_emu.NativeLibrary 28import org.yuzu.yuzu_emu.NativeLibrary
27import org.yuzu.yuzu_emu.R 29import org.yuzu.yuzu_emu.R
28import org.yuzu.yuzu_emu.YuzuApplication 30import org.yuzu.yuzu_emu.YuzuApplication
31import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
29import org.yuzu.yuzu_emu.databinding.CardGameBinding 32import org.yuzu.yuzu_emu.databinding.CardGameBinding
30import org.yuzu.yuzu_emu.activities.EmulationActivity
31import org.yuzu.yuzu_emu.model.Game 33import org.yuzu.yuzu_emu.model.Game
32import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
33import org.yuzu.yuzu_emu.model.GamesViewModel 34import org.yuzu.yuzu_emu.model.GamesViewModel
34 35
35class GameAdapter(private val activity: AppCompatActivity) : 36class GameAdapter(private val activity: AppCompatActivity) :
@@ -58,7 +59,10 @@ class GameAdapter(private val activity: AppCompatActivity) :
58 override fun onClick(view: View) { 59 override fun onClick(view: View) {
59 val holder = view.tag as GameViewHolder 60 val holder = view.tag as GameViewHolder
60 61
61 val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true 62 val gameExists = DocumentFile.fromSingleUri(
63 YuzuApplication.appContext,
64 Uri.parse(holder.game.path)
65 )?.exists() == true
62 if (!gameExists) { 66 if (!gameExists) {
63 Toast.makeText( 67 Toast.makeText(
64 YuzuApplication.appContext, 68 YuzuApplication.appContext,
@@ -78,7 +82,8 @@ class GameAdapter(private val activity: AppCompatActivity) :
78 ) 82 )
79 .apply() 83 .apply()
80 84
81 EmulationActivity.launch(activity, holder.game) 85 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
86 view.findNavController().navigate(action)
82 } 87 }
83 88
84 inner class GameViewHolder(val binding: CardGameBinding) : 89 inner class GameViewHolder(val binding: CardGameBinding) :
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index b719dd539..d3df3bc81 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -58,11 +58,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
58 ) 58 )
59 59
60 when (option.titleId) { 60 when (option.titleId) {
61 R.string.get_early_access -> binding.optionLayout.background = 61 R.string.get_early_access ->
62 ContextCompat.getDrawable( 62 binding.optionLayout.background =
63 binding.optionCard.context, 63 ContextCompat.getDrawable(
64 R.drawable.premium_background 64 binding.optionCard.context,
65 ) 65 R.drawable.premium_background
66 )
66 } 67 }
67 } 68 }
68 } 69 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
index 82a6712b6..e058067c9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
@@ -12,10 +12,10 @@ import android.view.WindowInsets
12import android.view.inputmethod.InputMethodManager 12import android.view.inputmethod.InputMethodManager
13import androidx.annotation.Keep 13import androidx.annotation.Keep
14import androidx.core.view.ViewCompat 14import androidx.core.view.ViewCompat
15import java.io.Serializable
15import org.yuzu.yuzu_emu.NativeLibrary 16import org.yuzu.yuzu_emu.NativeLibrary
16import org.yuzu.yuzu_emu.R 17import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment 18import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
18import java.io.Serializable
19 19
20@Keep 20@Keep
21object SoftwareKeyboard { 21object SoftwareKeyboard {
@@ -40,19 +40,22 @@ object SoftwareKeyboard {
40 // There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result. 40 // There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
41 val handler = Handler(Looper.myLooper()!!) 41 val handler = Handler(Looper.myLooper()!!)
42 val delayMs = 500 42 val delayMs = 500
43 handler.postDelayed(object : Runnable { 43 handler.postDelayed(
44 override fun run() { 44 object : Runnable {
45 val insets = ViewCompat.getRootWindowInsets(overlayView) 45 override fun run() {
46 val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime()) 46 val insets = ViewCompat.getRootWindowInsets(overlayView)
47 if (isKeyboardVisible) { 47 val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
48 handler.postDelayed(this, delayMs.toLong()) 48 if (isKeyboardVisible) {
49 return 49 handler.postDelayed(this, delayMs.toLong())
50 } 50 return
51 }
51 52
52 // No longer visible, submit the result. 53 // No longer visible, submit the result.
53 NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER) 54 NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
54 } 55 }
55 }, delayMs.toLong()) 56 },
57 delayMs.toLong()
58 )
56 } 59 }
57 60
58 @JvmStatic 61 @JvmStatic
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
index 3b1559c80..a18efef19 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
@@ -20,7 +20,10 @@ object DiskShaderCacheProgress {
20 emulationActivity.getString(R.string.loading), 20 emulationActivity.getString(R.string.loading),
21 emulationActivity.getString(R.string.preparing_shaders) 21 emulationActivity.getString(R.string.preparing_shaders)
22 ) 22 )
23 fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG) 23 fragment.show(
24 emulationActivity.supportFragmentManager,
25 ShaderProgressDialogFragment.TAG
26 )
24 } 27 }
25 synchronized(finishLock) { finishLock.wait() } 28 synchronized(finishLock) { finishLock.wait() }
26 } 29 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
index 2c68c9ac3..8a8e0a6e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
@@ -62,7 +62,9 @@ class ShaderProgressDialogFragment : DialogFragment() {
62 shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg -> 62 shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
63 alertDialog.setMessage(msg) 63 alertDialog.setMessage(msg)
64 } 64 }
65 synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() } 65 synchronized(DiskShaderCacheProgress.finishLock) {
66 DiskShaderCacheProgress.finishLock.notifyAll()
67 }
66 } 68 }
67 69
68 override fun onDestroyView() { 70 override fun onDestroyView() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
index 4c3a9ca80..f3be156b5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
@@ -13,11 +13,11 @@ import android.os.ParcelFileDescriptor
13import android.provider.DocumentsContract 13import android.provider.DocumentsContract
14import android.provider.DocumentsProvider 14import android.provider.DocumentsProvider
15import android.webkit.MimeTypeMap 15import android.webkit.MimeTypeMap
16import java.io.*
16import org.yuzu.yuzu_emu.BuildConfig 17import org.yuzu.yuzu_emu.BuildConfig
17import org.yuzu.yuzu_emu.R 18import org.yuzu.yuzu_emu.R
18import org.yuzu.yuzu_emu.YuzuApplication 19import org.yuzu.yuzu_emu.YuzuApplication
19import org.yuzu.yuzu_emu.getPublicFilesDir 20import org.yuzu.yuzu_emu.getPublicFilesDir
20import java.io.*
21 21
22class DocumentProvider : DocumentsProvider() { 22class DocumentProvider : DocumentsProvider() {
23 private val baseDirectory: File 23 private val baseDirectory: File
@@ -44,7 +44,7 @@ class DocumentProvider : DocumentsProvider() {
44 DocumentsContract.Document.COLUMN_SIZE 44 DocumentsContract.Document.COLUMN_SIZE
45 ) 45 )
46 46
47 const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user" 47 const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".user"
48 const val ROOT_ID: String = "root" 48 const val ROOT_ID: String = "root"
49 } 49 }
50 50
@@ -58,7 +58,11 @@ class DocumentProvider : DocumentsProvider() {
58 private fun getFile(documentId: String): File { 58 private fun getFile(documentId: String): File {
59 if (documentId.startsWith(ROOT_ID)) { 59 if (documentId.startsWith(ROOT_ID)) {
60 val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1)) 60 val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1))
61 if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found") 61 if (!file.exists()) {
62 throw FileNotFoundException(
63 "${file.absolutePath} ($documentId) not found"
64 )
65 }
62 return file 66 return file
63 } else { 67 } else {
64 throw FileNotFoundException("'$documentId' is not in any known root") 68 throw FileNotFoundException("'$documentId' is not in any known root")
@@ -80,7 +84,8 @@ class DocumentProvider : DocumentsProvider() {
80 add(DocumentsContract.Root.COLUMN_SUMMARY, null) 84 add(DocumentsContract.Root.COLUMN_SUMMARY, null)
81 add( 85 add(
82 DocumentsContract.Root.COLUMN_FLAGS, 86 DocumentsContract.Root.COLUMN_FLAGS,
83 DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD 87 DocumentsContract.Root.FLAG_SUPPORTS_CREATE or
88 DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
84 ) 89 )
85 add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name)) 90 add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name))
86 add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory)) 91 add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
@@ -127,11 +132,13 @@ class DocumentProvider : DocumentsProvider() {
127 132
128 try { 133 try {
129 if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) { 134 if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
130 if (!newFile.mkdir()) 135 if (!newFile.mkdir()) {
131 throw IOException("Failed to create directory") 136 throw IOException("Failed to create directory")
137 }
132 } else { 138 } else {
133 if (!newFile.createNewFile()) 139 if (!newFile.createNewFile()) {
134 throw IOException("Failed to create file") 140 throw IOException("Failed to create file")
141 }
135 } 142 }
136 } catch (e: IOException) { 143 } catch (e: IOException) {
137 throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}") 144 throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}")
@@ -142,8 +149,9 @@ class DocumentProvider : DocumentsProvider() {
142 149
143 override fun deleteDocument(documentId: String?) { 150 override fun deleteDocument(documentId: String?) {
144 val file = getFile(documentId!!) 151 val file = getFile(documentId!!)
145 if (!file.delete()) 152 if (!file.delete()) {
146 throw FileNotFoundException("Couldn't delete document with ID '$documentId'") 153 throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
154 }
147 } 155 }
148 156
149 override fun removeDocument(documentId: String, parentDocumentId: String?) { 157 override fun removeDocument(documentId: String, parentDocumentId: String?) {
@@ -151,38 +159,55 @@ class DocumentProvider : DocumentsProvider() {
151 val file = getFile(documentId) 159 val file = getFile(documentId)
152 160
153 if (parent == file || file.parentFile == null || file.parentFile!! == parent) { 161 if (parent == file || file.parentFile == null || file.parentFile!! == parent) {
154 if (!file.delete()) 162 if (!file.delete()) {
155 throw FileNotFoundException("Couldn't delete document with ID '$documentId'") 163 throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
164 }
156 } else { 165 } else {
157 throw FileNotFoundException("Couldn't delete document with ID '$documentId'") 166 throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
158 } 167 }
159 } 168 }
160 169
161 override fun renameDocument(documentId: String?, displayName: String?): String { 170 override fun renameDocument(documentId: String?, displayName: String?): String {
162 if (displayName == null) 171 if (displayName == null) {
163 throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null") 172 throw FileNotFoundException(
173 "Couldn't rename document '$documentId' as the new name is null"
174 )
175 }
164 176
165 val sourceFile = getFile(documentId!!) 177 val sourceFile = getFile(documentId!!)
166 val sourceParentFile = sourceFile.parentFile 178 val sourceParentFile = sourceFile.parentFile
167 ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent") 179 ?: throw FileNotFoundException(
180 "Couldn't rename document '$documentId' as it has no parent"
181 )
168 val destFile = sourceParentFile.resolve(displayName) 182 val destFile = sourceParentFile.resolve(displayName)
169 183
170 try { 184 try {
171 if (!sourceFile.renameTo(destFile)) 185 if (!sourceFile.renameTo(destFile)) {
172 throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'") 186 throw FileNotFoundException(
187 "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'"
188 )
189 }
173 } catch (e: Exception) { 190 } catch (e: Exception) {
174 throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}") 191 throw FileNotFoundException(
192 "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': " +
193 "${e.message}"
194 )
175 } 195 }
176 196
177 return getDocumentId(destFile) 197 return getDocumentId(destFile)
178 } 198 }
179 199
180 private fun copyDocument( 200 private fun copyDocument(
181 sourceDocumentId: String, sourceParentDocumentId: String, 201 sourceDocumentId: String,
202 sourceParentDocumentId: String,
182 targetParentDocumentId: String? 203 targetParentDocumentId: String?
183 ): String { 204 ): String {
184 if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) 205 if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) {
185 throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'") 206 throw FileNotFoundException(
207 "Couldn't copy document '$sourceDocumentId' as its parent is not " +
208 "'$sourceParentDocumentId'"
209 )
210 }
186 211
187 return copyDocument(sourceDocumentId, targetParentDocumentId) 212 return copyDocument(sourceDocumentId, targetParentDocumentId)
188 } 213 }
@@ -193,8 +218,13 @@ class DocumentProvider : DocumentsProvider() {
193 val newFile = parent.resolveWithoutConflict(oldFile.name) 218 val newFile = parent.resolveWithoutConflict(oldFile.name)
194 219
195 try { 220 try {
196 if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true))) 221 if (!(
222 newFile.createNewFile() && newFile.setWritable(true) &&
223 newFile.setReadable(true)
224 )
225 ) {
197 throw IOException("Couldn't create new file") 226 throw IOException("Couldn't create new file")
227 }
198 228
199 FileInputStream(oldFile).use { inStream -> 229 FileInputStream(oldFile).use { inStream ->
200 FileOutputStream(newFile).use { outStream -> 230 FileOutputStream(newFile).use { outStream ->
@@ -209,12 +239,14 @@ class DocumentProvider : DocumentsProvider() {
209 } 239 }
210 240
211 override fun moveDocument( 241 override fun moveDocument(
212 sourceDocumentId: String, sourceParentDocumentId: String?, 242 sourceDocumentId: String,
243 sourceParentDocumentId: String?,
213 targetParentDocumentId: String? 244 targetParentDocumentId: String?
214 ): String { 245 ): String {
215 try { 246 try {
216 val newDocumentId = copyDocument( 247 val newDocumentId = copyDocument(
217 sourceDocumentId, sourceParentDocumentId!!, 248 sourceDocumentId,
249 sourceParentDocumentId!!,
218 targetParentDocumentId 250 targetParentDocumentId
219 ) 251 )
220 removeDocument(sourceDocumentId, sourceParentDocumentId) 252 removeDocument(sourceDocumentId, sourceParentDocumentId)
@@ -245,24 +277,30 @@ class DocumentProvider : DocumentsProvider() {
245 add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId) 277 add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
246 add( 278 add(
247 DocumentsContract.Document.COLUMN_DISPLAY_NAME, 279 DocumentsContract.Document.COLUMN_DISPLAY_NAME,
248 if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name 280 if (localFile == baseDirectory) {
281 context!!.getString(R.string.app_name)
282 } else {
283 localFile.name
284 }
249 ) 285 )
250 add(DocumentsContract.Document.COLUMN_SIZE, localFile.length()) 286 add(DocumentsContract.Document.COLUMN_SIZE, localFile.length())
251 add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile)) 287 add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
252 add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified()) 288 add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
253 add(DocumentsContract.Document.COLUMN_FLAGS, flags) 289 add(DocumentsContract.Document.COLUMN_FLAGS, flags)
254 if (localFile == baseDirectory) 290 if (localFile == baseDirectory) {
255 add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu) 291 add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
292 }
256 } 293 }
257 294
258 return cursor 295 return cursor
259 } 296 }
260 297
261 private fun getTypeForFile(file: File): Any { 298 private fun getTypeForFile(file: File): Any {
262 return if (file.isDirectory) 299 return if (file.isDirectory) {
263 DocumentsContract.Document.MIME_TYPE_DIR 300 DocumentsContract.Document.MIME_TYPE_DIR
264 else 301 } else {
265 getTypeForName(file.name) 302 getTypeForName(file.name)
303 }
266 } 304 }
267 305
268 private fun getTypeForName(name: String): Any { 306 private fun getTypeForName(name: String): Any {
@@ -270,8 +308,9 @@ class DocumentProvider : DocumentsProvider() {
270 if (lastDot >= 0) { 308 if (lastDot >= 0) {
271 val extension = name.substring(lastDot + 1) 309 val extension = name.substring(lastDot + 1)
272 val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) 310 val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
273 if (mime != null) 311 if (mime != null) {
274 return mime 312 return mime
313 }
275 } 314 }
276 return "application/octect-stream" 315 return "application/octect-stream"
277 } 316 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 3dfd66779..d41933766 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -8,6 +8,10 @@ enum class BooleanSetting(
8 override val section: String, 8 override val section: String,
9 override val defaultValue: Boolean 9 override val defaultValue: Boolean
10) : AbstractBooleanSetting { 10) : AbstractBooleanSetting {
11 CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false),
12 FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true),
13 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true),
14 PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true),
11 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); 15 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
12 16
13 override var boolean: Boolean = defaultValue 17 override var boolean: Boolean = defaultValue
@@ -27,6 +31,7 @@ enum class BooleanSetting(
27 31
28 companion object { 32 companion object {
29 private val NOT_RUNTIME_EDITABLE = listOf( 33 private val NOT_RUNTIME_EDITABLE = listOf(
34 PICTURE_IN_PICTURE,
30 USE_CUSTOM_RTC 35 USE_CUSTOM_RTC
31 ) 36 )
32 37
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index c5722a5a1..4427a7d9d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -26,13 +26,18 @@ enum class IntSetting(
26 RENDERER_FORCE_MAX_CLOCK( 26 RENDERER_FORCE_MAX_CLOCK(
27 "force_max_clock", 27 "force_max_clock",
28 Settings.SECTION_RENDERER, 28 Settings.SECTION_RENDERER,
29 1 29 0
30 ), 30 ),
31 RENDERER_ASYNCHRONOUS_SHADERS( 31 RENDERER_ASYNCHRONOUS_SHADERS(
32 "use_asynchronous_shaders", 32 "use_asynchronous_shaders",
33 Settings.SECTION_RENDERER, 33 Settings.SECTION_RENDERER,
34 0 34 0
35 ), 35 ),
36 RENDERER_REACTIVE_FLUSHING(
37 "use_reactive_flushing",
38 Settings.SECTION_RENDERER,
39 0
40 ),
36 RENDERER_DEBUG( 41 RENDERER_DEBUG(
37 "debug", 42 "debug",
38 Settings.SECTION_RENDERER, 43 Settings.SECTION_RENDERER,
@@ -88,6 +93,11 @@ enum class IntSetting(
88 Settings.SECTION_RENDERER, 93 Settings.SECTION_RENDERER,
89 0 94 0
90 ), 95 ),
96 RENDERER_SCREEN_LAYOUT(
97 "screen_layout",
98 Settings.SECTION_RENDERER,
99 Settings.LayoutOption_MobileLandscape
100 ),
91 RENDERER_ASPECT_RATIO( 101 RENDERER_ASPECT_RATIO(
92 "aspect_ratio", 102 "aspect_ratio",
93 Settings.SECTION_RENDERER, 103 Settings.SECTION_RENDERER,
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 8df20b928..88afb2223 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
@@ -4,11 +4,11 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import android.text.TextUtils 6import android.text.TextUtils
7import java.util.*
7import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
8import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
9import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView 10import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 11import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
11import java.util.*
12 12
13class Settings { 13class Settings {
14 private var gameId: String? = null 14 private var gameId: String? = null
@@ -133,7 +133,6 @@ class Settings {
133 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" 133 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
134 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" 134 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
135 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" 135 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
136 const val PREF_MENU_SETTINGS_LANDSCAPE = "EmulationMenuSettings_LandscapeScreenLayout"
137 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" 136 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
138 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" 137 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
139 138
@@ -144,6 +143,10 @@ class Settings {
144 143
145 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() 144 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
146 145
146 const val LayoutOption_Unspecified = 0
147 const val LayoutOption_MobilePortrait = 4
148 const val LayoutOption_MobileLandscape = 5
149
147 init { 150 init {
148 configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] = 151 configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
149 listOf( 152 listOf(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 63f95690c..6621289fd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -8,6 +8,7 @@ enum class StringSetting(
8 override val section: String, 8 override val section: String,
9 override val defaultValue: String 9 override val defaultValue: String
10) : AbstractStringSetting { 10) : AbstractStringSetting {
11 AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"),
11 CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); 12 CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
12 13
13 override var string: String = defaultValue 14 override var string: String = defaultValue
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
index 0f8edbfb0..a67001311 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
@@ -3,12 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7
8class HeaderSetting( 6class HeaderSetting(
9 setting: AbstractSetting?, 7 titleId: Int
10 titleId: Int, 8) : SettingsItem(null, titleId, 0) {
11 descriptionId: Int
12) : SettingsItem(setting, titleId, descriptionId) {
13 override val type = TYPE_HEADER 9 override val type = TYPE_HEADER
14} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 9eac9904e..7306ec458 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 6import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
7import org.yuzu.yuzu_emu.features.settings.model.IntSetting
8 7
9class SingleChoiceSetting( 8class SingleChoiceSetting(
10 setting: AbstractIntSetting?, 9 setting: AbstractIntSetting?,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 842648ce4..92d0167ae 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,13 +3,11 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import kotlin.math.roundToInt
6import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
8import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
9import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
10import org.yuzu.yuzu_emu.features.settings.model.IntSetting
11import org.yuzu.yuzu_emu.utils.Log 10import org.yuzu.yuzu_emu.utils.Log
12import kotlin.math.roundToInt
13 11
14class SliderSetting( 12class SliderSetting(
15 setting: AbstractSetting?, 13 setting: AbstractSetting?,
@@ -19,7 +17,7 @@ class SliderSetting(
19 val max: Int, 17 val max: Int,
20 val units: String, 18 val units: String,
21 val key: String? = null, 19 val key: String? = null,
22 val defaultValue: Int? = null, 20 val defaultValue: Int? = null
23) : SettingsItem(setting, titleId, descriptionId) { 21) : SettingsItem(setting, titleId, descriptionId) {
24 override val type = TYPE_SLIDER 22 override val type = TYPE_SLIDER
25 23
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 9e9b00d10..3b6731dcd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -5,24 +5,25 @@ package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
8import org.yuzu.yuzu_emu.features.settings.model.StringSetting
9 8
10class StringSingleChoiceSetting( 9class StringSingleChoiceSetting(
11 val key: String? = null,
12 setting: AbstractSetting?, 10 setting: AbstractSetting?,
13 titleId: Int, 11 titleId: Int,
14 descriptionId: Int, 12 descriptionId: Int,
15 val choicesId: Array<String>, 13 val choices: Array<String>,
16 private val valuesId: Array<String>?, 14 val values: Array<String>?,
15 val key: String? = null,
17 private val defaultValue: String? = null 16 private val defaultValue: String? = null
18) : SettingsItem(setting, titleId, descriptionId) { 17) : SettingsItem(setting, titleId, descriptionId) {
19 override val type = TYPE_STRING_SINGLE_CHOICE 18 override val type = TYPE_STRING_SINGLE_CHOICE
20 19
21 fun getValueAt(index: Int): String? { 20 fun getValueAt(index: Int): String? {
22 if (valuesId == null) return null 21 if (values == null) return null
23 return if (index >= 0 && index < valuesId.size) { 22 return if (index >= 0 && index < values.size) {
24 valuesId[index] 23 values[index]
25 } else "" 24 } else {
25 ""
26 }
26 } 27 }
27 28
28 val selectedValue: String 29 val selectedValue: String
@@ -35,8 +36,8 @@ class StringSingleChoiceSetting(
35 val selectValueIndex: Int 36 val selectValueIndex: Int
36 get() { 37 get() {
37 val selectedValue = selectedValue 38 val selectedValue = selectedValue
38 for (i in valuesId!!.indices) { 39 for (i in values!!.indices) {
39 if (valuesId[i] == selectedValue) { 40 if (values[i] == selectedValue) {
40 return i 41 return i
41 } 42 }
42 } 43 }
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 a3ef59c2f..8a9d13a92 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,8 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7
8class SubmenuSetting( 6class SubmenuSetting(
9 titleId: Int, 7 titleId: Int,
10 descriptionId: Int, 8 descriptionId: Int,
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 72e2cce2a..a5af5a7ae 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
@@ -8,17 +8,18 @@ import android.content.Intent
8import android.os.Bundle 8import android.os.Bundle
9import android.view.Menu 9import android.view.Menu
10import android.view.View 10import android.view.View
11import android.view.ViewGroup.MarginLayoutParams
11import android.widget.Toast 12import android.widget.Toast
13import androidx.activity.OnBackPressedCallback
14import androidx.activity.result.ActivityResultLauncher
12import androidx.activity.viewModels 15import androidx.activity.viewModels
13import androidx.appcompat.app.AppCompatActivity 16import androidx.appcompat.app.AppCompatActivity
14import androidx.core.view.ViewCompat 17import androidx.core.view.ViewCompat
15import androidx.core.view.WindowCompat 18import androidx.core.view.WindowCompat
16import androidx.core.view.WindowInsetsCompat 19import androidx.core.view.WindowInsetsCompat
17import android.view.ViewGroup.MarginLayoutParams
18import androidx.activity.OnBackPressedCallback
19import androidx.core.view.updatePadding 20import androidx.core.view.updatePadding
20import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
21import org.yuzu.yuzu_emu.NativeLibrary 22import java.io.IOException
22import org.yuzu.yuzu_emu.R 23import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 24import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
24import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 25import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -29,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
29import org.yuzu.yuzu_emu.features.settings.model.StringSetting 30import org.yuzu.yuzu_emu.features.settings.model.StringSetting
30import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 31import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
31import org.yuzu.yuzu_emu.utils.* 32import org.yuzu.yuzu_emu.utils.*
32import java.io.IOException
33 33
34class SettingsActivity : AppCompatActivity(), SettingsActivityView { 34class SettingsActivity : AppCompatActivity(), SettingsActivityView {
35 private val presenter = SettingsActivityPresenter(this) 35 private val presenter = SettingsActivityPresenter(this)
@@ -59,7 +59,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
59 setSupportActionBar(binding.toolbarSettings) 59 setSupportActionBar(binding.toolbarSettings)
60 supportActionBar!!.setDisplayHomeAsUpEnabled(true) 60 supportActionBar!!.setDisplayHomeAsUpEnabled(true)
61 61
62 if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) { 62 if (InsetsHelper.getSystemGestureType(applicationContext) !=
63 InsetsHelper.GESTURE_NAVIGATION
64 ) {
63 binding.navigationBarShade.setBackgroundColor( 65 binding.navigationBarShade.setBackgroundColor(
64 ThemeHelper.getColorWithOpacity( 66 ThemeHelper.getColorWithOpacity(
65 MaterialColors.getColor( 67 MaterialColors.getColor(
@@ -75,7 +77,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
75 this, 77 this,
76 object : OnBackPressedCallback(true) { 78 object : OnBackPressedCallback(true) {
77 override fun handleOnBackPressed() = navigateBack() 79 override fun handleOnBackPressed() = navigateBack()
78 }) 80 }
81 )
79 82
80 setInsets() 83 setInsets()
81 } 84 }
@@ -148,11 +151,13 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
148 private fun areSystemAnimationsEnabled(): Boolean { 151 private fun areSystemAnimationsEnabled(): Boolean {
149 val duration = android.provider.Settings.Global.getFloat( 152 val duration = android.provider.Settings.Global.getFloat(
150 contentResolver, 153 contentResolver,
151 android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f 154 android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
155 1f
152 ) 156 )
153 val transition = android.provider.Settings.Global.getFloat( 157 val transition = android.provider.Settings.Global.getFloat(
154 contentResolver, 158 contentResolver,
155 android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f 159 android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
160 1f
156 ) 161 )
157 return duration != 0f && transition != 0f 162 return duration != 0f && transition != 0f
158 } 163 }
@@ -207,7 +212,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
207 get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment? 212 get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
208 213
209 private fun setInsets() { 214 private fun setInsets() {
210 ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat -> 215 ViewCompat.setOnApplyWindowInsetsListener(
216 binding.frameContent
217 ) { view: View, windowInsets: WindowInsetsCompat ->
211 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 218 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
212 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 219 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
213 view.updatePadding( 220 view.updatePadding(
@@ -239,5 +246,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
239 settings.putExtra(ARG_GAME_ID, gameId) 246 settings.putExtra(ARG_GAME_ID, gameId)
240 context.startActivity(settings) 247 context.startActivity(settings)
241 } 248 }
249
250 fun launch(
251 context: Context,
252 launcher: ActivityResultLauncher<Intent>,
253 menuTag: String?,
254 gameId: String?
255 ) {
256 val settings = Intent(context, SettingsActivity::class.java)
257 settings.putExtra(ARG_MENU_TAG, menuTag)
258 settings.putExtra(ARG_GAME_ID, gameId)
259 launcher.launch(settings)
260 }
242 } 261 }
243} 262}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
index 4361d95fb..93e677b21 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.features.settings.ui
6import android.content.Context 6import android.content.Context
7import android.os.Bundle 7import android.os.Bundle
8import android.text.TextUtils 8import android.text.TextUtils
9import java.io.File
9import org.yuzu.yuzu_emu.NativeLibrary 10import org.yuzu.yuzu_emu.NativeLibrary
10import org.yuzu.yuzu_emu.features.settings.model.Settings 11import org.yuzu.yuzu_emu.features.settings.model.Settings
11import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 12import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
12import org.yuzu.yuzu_emu.utils.DirectoryInitialization 13import org.yuzu.yuzu_emu.utils.DirectoryInitialization
13import org.yuzu.yuzu_emu.utils.Log 14import org.yuzu.yuzu_emu.utils.Log
14import java.io.File
15 15
16class SettingsActivityPresenter(private val activityView: SettingsActivityView) { 16class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
17 val settings: Settings get() = activityView.settings 17 val settings: Settings get() = activityView.settings
@@ -46,9 +46,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
46 46
47 private fun prepareDirectoriesIfNeeded() { 47 private fun prepareDirectoriesIfNeeded() {
48 val configFile = 48 val configFile =
49 File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini") 49 File(
50 "${DirectoryInitialization.userDirectory}/config/" +
51 "${SettingsFile.FILE_NAME_CONFIG}.ini"
52 )
50 if (!configFile.exists()) { 53 if (!configFile.exists()) {
51 Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini") 54 Log.error(
55 "${DirectoryInitialization.userDirectory}/config/" +
56 "${SettingsFile.FILE_NAME_CONFIG}.ini"
57 )
52 Log.error("yuzu config file could not be found!") 58 Log.error("yuzu config file could not be found!")
53 } 59 }
54 60
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 1eb4899fc..ce0b92c90 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -13,7 +13,6 @@ import android.view.ViewGroup
13import android.widget.TextView 13import android.widget.TextView
14import androidx.appcompat.app.AlertDialog 14import androidx.appcompat.app.AlertDialog
15import androidx.appcompat.app.AppCompatActivity 15import androidx.appcompat.app.AppCompatActivity
16import androidx.fragment.app.setFragmentResultListener
17import androidx.recyclerview.widget.RecyclerView 16import androidx.recyclerview.widget.RecyclerView
18import com.google.android.material.datepicker.MaterialDatePicker 17import com.google.android.material.datepicker.MaterialDatePicker
19import com.google.android.material.dialog.MaterialAlertDialogBuilder 18import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -139,7 +138,7 @@ class SettingsAdapter(
139 clickedItem = item 138 clickedItem = item
140 dialog = MaterialAlertDialogBuilder(context) 139 dialog = MaterialAlertDialogBuilder(context)
141 .setTitle(item.nameId) 140 .setTitle(item.nameId)
142 .setSingleChoiceItems(item.choicesId, item.selectValueIndex, this) 141 .setSingleChoiceItems(item.choices, item.selectValueIndex, this)
143 .show() 142 .show()
144 } 143 }
145 144
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 867147950..70a74c4dd 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
@@ -50,7 +50,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
50 50
51 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 51 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
52 settingsAdapter = SettingsAdapter(this, requireActivity()) 52 settingsAdapter = SettingsAdapter(this, requireActivity())
53 val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL) 53 val dividerDecoration = MaterialDividerItemDecoration(
54 requireContext(),
55 LinearLayoutManager.VERTICAL
56 )
54 dividerDecoration.isLastItemDecorated = false 57 dividerDecoration.isLastItemDecorated = false
55 binding.listSettings.apply { 58 binding.listSettings.apply {
56 adapter = settingsAdapter 59 adapter = settingsAdapter
@@ -99,7 +102,9 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
99 } 102 }
100 103
101 private fun setInsets() { 104 private fun setInsets() {
102 ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat -> 105 ViewCompat.setOnApplyWindowInsetsListener(
106 binding.listSettings
107 ) { view: View, windowInsets: WindowInsetsCompat ->
103 val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 108 val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
104 view.updatePadding(bottom = insets.bottom) 109 view.updatePadding(bottom = insets.bottom)
105 windowInsets 110 windowInsets
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 061046b2e..59c1d9d54 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
@@ -7,7 +7,6 @@ import android.content.SharedPreferences
7import android.os.Build 7import android.os.Build
8import android.text.TextUtils 8import android.text.TextUtils
9import androidx.preference.PreferenceManager 9import androidx.preference.PreferenceManager
10import com.google.android.material.dialog.MaterialAlertDialogBuilder
11import org.yuzu.yuzu_emu.R 10import org.yuzu.yuzu_emu.R
12import org.yuzu.yuzu_emu.YuzuApplication 11import org.yuzu.yuzu_emu.YuzuApplication
13import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 12import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@@ -43,7 +42,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
43 } 42 }
44 43
45 fun putSetting(setting: AbstractSetting) { 44 fun putSetting(setting: AbstractSetting) {
46 if (setting.section == null) { 45 if (setting.section == null || setting.key == null) {
47 return 46 return
48 } 47 }
49 48
@@ -166,6 +165,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
166 IntSetting.CPU_ACCURACY.defaultValue 165 IntSetting.CPU_ACCURACY.defaultValue
167 ) 166 )
168 ) 167 )
168 add(
169 SwitchSetting(
170 BooleanSetting.PICTURE_IN_PICTURE,
171 R.string.picture_in_picture,
172 R.string.picture_in_picture_description,
173 BooleanSetting.PICTURE_IN_PICTURE.key,
174 BooleanSetting.PICTURE_IN_PICTURE.defaultValue
175 )
176 )
169 } 177 }
170 } 178 }
171 179
@@ -227,7 +235,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
227 private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) { 235 private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
228 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics)) 236 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
229 sl.apply { 237 sl.apply {
230
231 add( 238 add(
232 SingleChoiceSetting( 239 SingleChoiceSetting(
233 IntSetting.RENDERER_ACCURACY, 240 IntSetting.RENDERER_ACCURACY,
@@ -285,6 +292,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
285 ) 292 )
286 add( 293 add(
287 SingleChoiceSetting( 294 SingleChoiceSetting(
295 IntSetting.RENDERER_SCREEN_LAYOUT,
296 R.string.renderer_screen_layout,
297 0,
298 R.array.rendererScreenLayoutNames,
299 R.array.rendererScreenLayoutValues,
300 IntSetting.RENDERER_SCREEN_LAYOUT.key,
301 IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue
302 )
303 )
304 add(
305 SingleChoiceSetting(
288 IntSetting.RENDERER_ASPECT_RATIO, 306 IntSetting.RENDERER_ASPECT_RATIO,
289 R.string.renderer_aspect_ratio, 307 R.string.renderer_aspect_ratio,
290 0, 308 0,
@@ -321,23 +339,45 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
321 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue 339 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
322 ) 340 )
323 ) 341 )
342 add(
343 SwitchSetting(
344 IntSetting.RENDERER_REACTIVE_FLUSHING,
345 R.string.renderer_reactive_flushing,
346 R.string.renderer_reactive_flushing_description,
347 IntSetting.RENDERER_REACTIVE_FLUSHING.key,
348 IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
349 )
350 )
324 } 351 }
325 } 352 }
326 353
327 private fun addAudioSettings(sl: ArrayList<SettingsItem>) { 354 private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
328 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) 355 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
329 sl.add( 356 sl.apply {
330 SliderSetting( 357 add(
331 IntSetting.AUDIO_VOLUME, 358 StringSingleChoiceSetting(
332 R.string.audio_volume, 359 StringSetting.AUDIO_OUTPUT_ENGINE,
333 R.string.audio_volume_description, 360 R.string.audio_output_engine,
334 0, 361 0,
335 100, 362 settingsActivity.resources.getStringArray(R.array.outputEngineEntries),
336 "%", 363 settingsActivity.resources.getStringArray(R.array.outputEngineValues),
337 IntSetting.AUDIO_VOLUME.key, 364 StringSetting.AUDIO_OUTPUT_ENGINE.key,
338 IntSetting.AUDIO_VOLUME.defaultValue 365 StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue
339 ) 366 )
340 ) 367 )
368 add(
369 SliderSetting(
370 IntSetting.AUDIO_VOLUME,
371 R.string.audio_volume,
372 R.string.audio_volume_description,
373 0,
374 100,
375 "%",
376 IntSetting.AUDIO_VOLUME.key,
377 IntSetting.AUDIO_VOLUME.defaultValue
378 )
379 )
380 }
341 } 381 }
342 382
343 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 383 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
@@ -440,6 +480,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
440 private fun addDebugSettings(sl: ArrayList<SettingsItem>) { 480 private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
441 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) 481 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
442 sl.apply { 482 sl.apply {
483 add(HeaderSetting(R.string.gpu))
443 add( 484 add(
444 SingleChoiceSetting( 485 SingleChoiceSetting(
445 IntSetting.RENDERER_BACKEND, 486 IntSetting.RENDERER_BACKEND,
@@ -460,6 +501,39 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
460 IntSetting.RENDERER_DEBUG.defaultValue 501 IntSetting.RENDERER_DEBUG.defaultValue
461 ) 502 )
462 ) 503 )
504
505 add(HeaderSetting(R.string.cpu))
506 add(
507 SwitchSetting(
508 BooleanSetting.CPU_DEBUG_MODE,
509 R.string.cpu_debug_mode,
510 R.string.cpu_debug_mode_description,
511 BooleanSetting.CPU_DEBUG_MODE.key,
512 BooleanSetting.CPU_DEBUG_MODE.defaultValue
513 )
514 )
515
516 val fastmem = object : AbstractBooleanSetting {
517 override var boolean: Boolean
518 get() =
519 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
520 set(value) {
521 BooleanSetting.FASTMEM.boolean = value
522 BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value
523 }
524 override val key: String? = null
525 override val section: String = Settings.SECTION_CPU
526 override val isRuntimeEditable: Boolean = false
527 override val valueAsString: String = ""
528 override val defaultValue: Any = true
529 }
530 add(
531 SwitchSetting(
532 fastmem,
533 R.string.fastmem,
534 0
535 )
536 )
463 } 537 }
464 } 538 }
465} 539}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 04c045e77..7955532ee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -4,15 +4,15 @@
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 org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
10import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
11import java.time.Instant 7import java.time.Instant
12import java.time.ZoneId 8import java.time.ZoneId
13import java.time.ZonedDateTime 9import java.time.ZonedDateTime
14import java.time.format.DateTimeFormatter 10import java.time.format.DateTimeFormatter
15import java.time.format.FormatStyle 11import java.time.format.FormatStyle
12import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
13import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
14import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
15import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
16 16
17class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 17class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
18 SettingViewHolder(binding.root, adapter) { 18 SettingViewHolder(binding.root, adapter) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index de764a27f..e4e321bd3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -26,6 +26,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
26 for (i in values.indices) { 26 for (i in values.indices) {
27 if (values[i] == item.selectedValue) { 27 if (values[i] == item.selectedValue) {
28 binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] 28 binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
29 return
30 }
31 }
32 } else if (item is StringSingleChoiceSetting) {
33 for (i in item.values!!.indices) {
34 if (item.values[i] == item.selectedValue) {
35 binding.textSettingDescription.text = item.choices[i]
36 return
29 } 37 }
30 } 38 }
31 } else { 39 } else {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index b163bd6ca..54f531795 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
6import android.view.View 6import android.view.View
7import android.widget.CompoundButton 7import android.widget.CompoundButton
8import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding 8import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
9import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
10import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
10import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12 12
13class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : 13class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
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 e29bca11d..70a52df5d 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,6 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import java.io.*
7import java.util.*
6import org.ini4j.Wini 8import org.ini4j.Wini
7import org.yuzu.yuzu_emu.NativeLibrary 9import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.R 10import org.yuzu.yuzu_emu.R
@@ -13,8 +15,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
13import org.yuzu.yuzu_emu.utils.BiMap 15import org.yuzu.yuzu_emu.utils.BiMap
14import org.yuzu.yuzu_emu.utils.DirectoryInitialization 16import org.yuzu.yuzu_emu.utils.DirectoryInitialization
15import org.yuzu.yuzu_emu.utils.Log 17import org.yuzu.yuzu_emu.utils.Log
16import java.io.*
17import java.util.*
18 18
19/** 19/**
20 * Contains static methods for interacting with .ini files in which settings are stored. 20 * Contains static methods for interacting with .ini files in which settings are stored.
@@ -137,9 +137,12 @@ object SettingsFile {
137 for (settingKey in sortedKeySet) { 137 for (settingKey in sortedKeySet) {
138 val setting = settings[settingKey] 138 val setting = settings[settingKey]
139 NativeLibrary.setUserSetting( 139 NativeLibrary.setUserSetting(
140 gameId, mapSectionNameFromIni( 140 gameId,
141 mapSectionNameFromIni(
141 section.name 142 section.name
142 ), setting!!.key, setting.valueAsString 143 ),
144 setting!!.key,
145 setting.valueAsString
143 ) 146 )
144 } 147 }
145 } 148 }
@@ -148,13 +151,17 @@ object SettingsFile {
148 private fun mapSectionNameFromIni(generalSectionName: String): String? { 151 private fun mapSectionNameFromIni(generalSectionName: String): String? {
149 return if (sectionsMap.getForward(generalSectionName) != null) { 152 return if (sectionsMap.getForward(generalSectionName) != null) {
150 sectionsMap.getForward(generalSectionName) 153 sectionsMap.getForward(generalSectionName)
151 } else generalSectionName 154 } else {
155 generalSectionName
156 }
152 } 157 }
153 158
154 private fun mapSectionNameToIni(generalSectionName: String): String { 159 private fun mapSectionNameToIni(generalSectionName: String): String {
155 return if (sectionsMap.getBackward(generalSectionName) != null) { 160 return if (sectionsMap.getBackward(generalSectionName) != null) {
156 sectionsMap.getBackward(generalSectionName).toString() 161 sectionsMap.getBackward(generalSectionName).toString()
157 } else generalSectionName 162 } else {
163 generalSectionName
164 }
158 } 165 }
159 166
160 fun getSettingsFile(fileName: String): File { 167 fun getSettingsFile(fileName: String): File {
@@ -237,5 +244,21 @@ object SettingsFile {
237 val setting = settings[key] 244 val setting = settings[key]
238 parser.put(header, setting!!.key, setting.valueAsString) 245 parser.put(header, setting!!.key, setting.valueAsString)
239 } 246 }
247
248 BooleanSetting.values().forEach {
249 if (!keySet.contains(it.key)) {
250 parser.put(header, it.key, it.valueAsString)
251 }
252 }
253 IntSetting.values().forEach {
254 if (!keySet.contains(it.key)) {
255 parser.put(header, it.key, it.valueAsString)
256 }
257 }
258 StringSetting.values().forEach {
259 if (!keySet.contains(it.key)) {
260 parser.put(header, it.key, it.valueAsString)
261 }
262 }
240 } 263 }
241} 264}
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 c92e2755c..2ff827c6b 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
@@ -66,7 +66,11 @@ class AboutFragment : Fragment() {
66 true 66 true
67 } 67 }
68 68
69 binding.buttonContributors.setOnClickListener { openLink(getString(R.string.contributors_link)) } 69 binding.buttonContributors.setOnClickListener {
70 openLink(
71 getString(R.string.contributors_link)
72 )
73 }
70 binding.buttonLicenses.setOnClickListener { 74 binding.buttonLicenses.setOnClickListener {
71 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) 75 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
72 binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment) 76 binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
@@ -101,7 +105,9 @@ class AboutFragment : Fragment() {
101 } 105 }
102 106
103 private fun setInsets() = 107 private fun setInsets() =
104 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> 108 ViewCompat.setOnApplyWindowInsetsListener(
109 binding.root
110 ) { _: View, windowInsets: WindowInsetsCompat ->
105 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 111 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
106 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 112 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
107 113
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
index d8bbc1ce4..dbc16da4a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
@@ -49,7 +49,11 @@ class EarlyAccessFragment : Fragment() {
49 parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack() 49 parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack()
50 } 50 }
51 51
52 binding.getEarlyAccessButton.setOnClickListener { openLink(getString(R.string.play_store_link)) } 52 binding.getEarlyAccessButton.setOnClickListener {
53 openLink(
54 getString(R.string.play_store_link)
55 )
56 }
53 57
54 setInsets() 58 setInsets()
55 } 59 }
@@ -60,7 +64,9 @@ class EarlyAccessFragment : Fragment() {
60 } 64 }
61 65
62 private fun setInsets() = 66 private fun setInsets() =
63 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> 67 ViewCompat.setOnApplyWindowInsetsListener(
68 binding.root
69 ) { _: View, windowInsets: WindowInsetsCompat ->
64 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 70 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
65 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 71 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
66 72
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 9523381cd..09976db62 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
@@ -7,30 +7,39 @@ import android.annotation.SuppressLint
7import android.app.AlertDialog 7import android.app.AlertDialog
8import android.content.Context 8import android.content.Context
9import android.content.DialogInterface 9import android.content.DialogInterface
10import android.content.Intent
10import android.content.SharedPreferences 11import android.content.SharedPreferences
11import android.content.pm.ActivityInfo 12import android.content.pm.ActivityInfo
12import android.content.res.Resources 13import android.content.res.Configuration
13import android.graphics.Color 14import android.graphics.Color
14import android.os.Bundle 15import android.os.Bundle
15import android.os.Handler 16import android.os.Handler
16import android.os.Looper 17import android.os.Looper
17import android.util.Rational 18import android.util.Rational
18import android.util.TypedValue
19import android.view.* 19import android.view.*
20import android.widget.TextView 20import android.widget.TextView
21import androidx.activity.OnBackPressedCallback 21import androidx.activity.OnBackPressedCallback
22import androidx.activity.result.ActivityResultLauncher
23import androidx.activity.result.contract.ActivityResultContracts
22import androidx.appcompat.widget.PopupMenu 24import androidx.appcompat.widget.PopupMenu
23import androidx.core.content.res.ResourcesCompat 25import androidx.core.content.res.ResourcesCompat
24import androidx.core.graphics.Insets 26import androidx.core.graphics.Insets
25import androidx.core.view.ViewCompat 27import androidx.core.view.ViewCompat
26import androidx.core.view.WindowInsetsCompat 28import androidx.core.view.WindowInsetsCompat
27import androidx.core.view.updatePadding 29import androidx.core.view.isVisible
28import androidx.fragment.app.Fragment 30import androidx.fragment.app.Fragment
31import androidx.lifecycle.Lifecycle
32import androidx.lifecycle.lifecycleScope
33import androidx.lifecycle.repeatOnLifecycle
34import androidx.navigation.fragment.navArgs
29import androidx.preference.PreferenceManager 35import androidx.preference.PreferenceManager
30import androidx.window.layout.FoldingFeature 36import androidx.window.layout.FoldingFeature
37import androidx.window.layout.WindowInfoTracker
31import androidx.window.layout.WindowLayoutInfo 38import androidx.window.layout.WindowLayoutInfo
32import com.google.android.material.dialog.MaterialAlertDialogBuilder 39import com.google.android.material.dialog.MaterialAlertDialogBuilder
33import com.google.android.material.slider.Slider 40import com.google.android.material.slider.Slider
41import kotlinx.coroutines.Dispatchers
42import kotlinx.coroutines.launch
34import org.yuzu.yuzu_emu.NativeLibrary 43import org.yuzu.yuzu_emu.NativeLibrary
35import org.yuzu.yuzu_emu.R 44import org.yuzu.yuzu_emu.R
36import org.yuzu.yuzu_emu.YuzuApplication 45import org.yuzu.yuzu_emu.YuzuApplication
@@ -41,9 +50,8 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
41import org.yuzu.yuzu_emu.features.settings.model.Settings 50import org.yuzu.yuzu_emu.features.settings.model.Settings
42import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 51import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 52import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
44import org.yuzu.yuzu_emu.model.Game 53import org.yuzu.yuzu_emu.overlay.InputOverlay
45import org.yuzu.yuzu_emu.utils.* 54import org.yuzu.yuzu_emu.utils.*
46import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
47 55
48class EmulationFragment : Fragment(), SurfaceHolder.Callback { 56class EmulationFragment : Fragment(), SurfaceHolder.Callback {
49 private lateinit var preferences: SharedPreferences 57 private lateinit var preferences: SharedPreferences
@@ -54,13 +62,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
54 private var _binding: FragmentEmulationBinding? = null 62 private var _binding: FragmentEmulationBinding? = null
55 private val binding get() = _binding!! 63 private val binding get() = _binding!!
56 64
57 private lateinit var game: Game 65 val args by navArgs<EmulationFragmentArgs>()
66
67 private var isInFoldableLayout = false
68
69 private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
58 70
59 override fun onAttach(context: Context) { 71 override fun onAttach(context: Context) {
60 super.onAttach(context) 72 super.onAttach(context)
61 if (context is EmulationActivity) { 73 if (context is EmulationActivity) {
62 emulationActivity = context 74 emulationActivity = context
63 NativeLibrary.setEmulationActivity(context) 75 NativeLibrary.setEmulationActivity(context)
76
77 lifecycleScope.launch(Dispatchers.Main) {
78 lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
79 WindowInfoTracker.getOrCreate(context)
80 .windowLayoutInfo(context)
81 .collect { updateFoldableLayout(context, it) }
82 }
83 }
84
85 onReturnFromSettings = context.activityResultRegistry.register(
86 "SettingsResult",
87 ActivityResultContracts.StartActivityForResult()
88 ) { updateScreenLayout() }
64 } else { 89 } else {
65 throw IllegalStateException("EmulationFragment must have EmulationActivity parent") 90 throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
66 } 91 }
@@ -75,8 +100,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
75 // So this fragment doesn't restart on configuration changes; i.e. rotation. 100 // So this fragment doesn't restart on configuration changes; i.e. rotation.
76 retainInstance = true 101 retainInstance = true
77 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 102 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
78 game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!! 103 emulationState = EmulationState(args.game.path)
79 emulationState = EmulationState(game.path)
80 } 104 }
81 105
82 /** 106 /**
@@ -100,7 +124,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
100 updateShowFpsOverlay() 124 updateShowFpsOverlay()
101 125
102 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = 126 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
103 game.title 127 args.game.title
104 binding.inGameMenu.setNavigationItemSelectedListener { 128 binding.inGameMenu.setNavigationItemSelectedListener {
105 when (it.itemId) { 129 when (it.itemId) {
106 R.id.menu_pause_emulation -> { 130 R.id.menu_pause_emulation -> {
@@ -125,7 +149,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
125 } 149 }
126 150
127 R.id.menu_settings -> { 151 R.id.menu_settings -> {
128 SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") 152 SettingsActivity.launch(
153 requireContext(),
154 onReturnFromSettings,
155 SettingsFile.FILE_NAME_CONFIG,
156 ""
157 )
129 true 158 true
130 } 159 }
131 160
@@ -150,9 +179,48 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
150 requireActivity(), 179 requireActivity(),
151 object : OnBackPressedCallback(true) { 180 object : OnBackPressedCallback(true) {
152 override fun handleOnBackPressed() { 181 override fun handleOnBackPressed() {
153 if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open() 182 if (binding.drawerLayout.isOpen) {
183 binding.drawerLayout.close()
184 } else {
185 binding.drawerLayout.open()
186 }
187 }
188 }
189 )
190
191 viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
192 lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
193 WindowInfoTracker.getOrCreate(requireContext())
194 .windowLayoutInfo(requireActivity())
195 .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
196 }
197 }
198 }
199
200 override fun onConfigurationChanged(newConfig: Configuration) {
201 super.onConfigurationChanged(newConfig)
202 if (emulationActivity?.isInPictureInPictureMode == true) {
203 if (binding.drawerLayout.isOpen) {
204 binding.drawerLayout.close()
205 }
206 if (EmulationMenuSettings.showOverlay) {
207 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false }
208 }
209 } else {
210 if (EmulationMenuSettings.showOverlay) {
211 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true }
212 }
213 if (!isInFoldableLayout) {
214 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
215 binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT
216 } else {
217 binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE
154 } 218 }
155 }) 219 }
220 if (!binding.surfaceInputOverlay.isInEditMode) {
221 refreshInputOverlay()
222 }
223 }
156 } 224 }
157 225
158 override fun onResume() { 226 override fun onResume() {
@@ -161,16 +229,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
161 DirectoryInitialization.start(requireContext()) 229 DirectoryInitialization.start(requireContext())
162 } 230 }
163 231
164 binding.surfaceEmulation.setAspectRatio( 232 updateScreenLayout()
165 when (IntSetting.RENDERER_ASPECT_RATIO.int) {
166 0 -> Rational(16, 9)
167 1 -> Rational(4, 3)
168 2 -> Rational(21, 9)
169 3 -> Rational(16, 10)
170 4 -> null // Stretch
171 else -> Rational(16, 9)
172 }
173 )
174 233
175 emulationState.run(emulationActivity!!.isActivityRecreated) 234 emulationState.run(emulationActivity!!.isActivityRecreated)
176 } 235 }
@@ -231,31 +290,72 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
231 } 290 }
232 } 291 }
233 292
234 private val Number.toPx get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics).toInt() 293 @SuppressLint("SourceLockedOrientationActivity")
235 294 private fun updateOrientation() {
236 fun updateCurrentLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) { 295 emulationActivity?.let {
237 val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let { 296 it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) {
238 if (it.isSeparating) { 297 Settings.LayoutOption_MobileLandscape ->
239 emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 298 ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
240 if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { 299 Settings.LayoutOption_MobilePortrait ->
241 binding.surfaceEmulation.layoutParams.height = it.bounds.top 300 ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
242 binding.inGameMenu.layoutParams.height = it.bounds.bottom 301 Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
243 binding.overlayContainer.layoutParams.height = it.bounds.bottom - 48.toPx 302 else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
244 binding.overlayContainer.updatePadding(0, 0, 0, 24.toPx)
245 }
246 } 303 }
247 it.isSeparating 304 }
248 } ?: false 305 }
306
307 private fun updateScreenLayout() {
308 binding.surfaceEmulation.setAspectRatio(
309 when (IntSetting.RENDERER_ASPECT_RATIO.int) {
310 0 -> Rational(16, 9)
311 1 -> Rational(4, 3)
312 2 -> Rational(21, 9)
313 3 -> Rational(16, 10)
314 4 -> null // Stretch
315 else -> Rational(16, 9)
316 }
317 )
318 emulationActivity?.buildPictureInPictureParams()
319 updateOrientation()
320 }
321
322 private fun updateFoldableLayout(
323 emulationActivity: EmulationActivity,
324 newLayoutInfo: WindowLayoutInfo
325 ) {
326 val isFolding =
327 (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
328 if (it.isSeparating) {
329 emulationActivity.requestedOrientation =
330 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
331 if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
332 // Restrict emulation and overlays to the top of the screen
333 binding.emulationContainer.layoutParams.height = it.bounds.top
334 binding.overlayContainer.layoutParams.height = it.bounds.top
335 // Restrict input and menu drawer to the bottom of the screen
336 binding.inputContainer.layoutParams.height = it.bounds.bottom
337 binding.inGameMenu.layoutParams.height = it.bounds.bottom
338
339 isInFoldableLayout = true
340 binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
341 refreshInputOverlay()
342 }
343 }
344 it.isSeparating
345 } ?: false
249 if (!isFolding) { 346 if (!isFolding) {
250 binding.surfaceEmulation.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 347 binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
251 binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 348 binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
252 binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 349 binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
253 binding.overlayContainer.updatePadding(0, 0, 0, 0) 350 binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
254 emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 351 isInFoldableLayout = false
352 updateOrientation()
353 onConfigurationChanged(resources.configuration)
255 } 354 }
256 binding.surfaceInputOverlay.requestLayout() 355 binding.emulationContainer.requestLayout()
257 binding.inGameMenu.requestLayout() 356 binding.inputContainer.requestLayout()
258 binding.overlayContainer.requestLayout() 357 binding.overlayContainer.requestLayout()
358 binding.inGameMenu.requestLayout()
259 } 359 }
260 360
261 override fun surfaceCreated(holder: SurfaceHolder) { 361 override fun surfaceCreated(holder: SurfaceHolder) {
@@ -385,7 +485,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
385 popup.show() 485 popup.show()
386 } 486 }
387 487
488 @SuppressLint("SourceLockedOrientationActivity")
388 private fun startConfiguringControls() { 489 private fun startConfiguringControls() {
490 // Lock the current orientation to prevent editing inconsistencies
491 if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) {
492 emulationActivity?.let {
493 it.requestedOrientation =
494 if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
495 ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
496 } else {
497 ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
498 }
499 }
500 }
389 binding.doneControlConfig.visibility = View.VISIBLE 501 binding.doneControlConfig.visibility = View.VISIBLE
390 binding.surfaceInputOverlay.setIsInEditMode(true) 502 binding.surfaceInputOverlay.setIsInEditMode(true)
391 } 503 }
@@ -393,6 +505,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
393 private fun stopConfiguringControls() { 505 private fun stopConfiguringControls() {
394 binding.doneControlConfig.visibility = View.GONE 506 binding.doneControlConfig.visibility = View.GONE
395 binding.surfaceInputOverlay.setIsInEditMode(false) 507 binding.surfaceInputOverlay.setIsInEditMode(false)
508 // Unlock the orientation if it was locked for editing
509 if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) {
510 emulationActivity?.let {
511 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
512 }
513 }
396 } 514 }
397 515
398 @SuppressLint("SetTextI18n") 516 @SuppressLint("SetTextI18n")
@@ -402,18 +520,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
402 inputScaleSlider.apply { 520 inputScaleSlider.apply {
403 valueTo = 150F 521 valueTo = 150F
404 value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() 522 value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
405 addOnChangeListener(Slider.OnChangeListener { _, value, _ -> 523 addOnChangeListener(
406 inputScaleValue.text = "${value.toInt()}%" 524 Slider.OnChangeListener { _, value, _ ->
407 setControlScale(value.toInt()) 525 inputScaleValue.text = "${value.toInt()}%"
408 }) 526 setControlScale(value.toInt())
527 }
528 )
409 } 529 }
410 inputOpacitySlider.apply { 530 inputOpacitySlider.apply {
411 valueTo = 100F 531 valueTo = 100F
412 value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat() 532 value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
413 addOnChangeListener(Slider.OnChangeListener { _, value, _ -> 533 addOnChangeListener(
414 inputOpacityValue.text = "${value.toInt()}%" 534 Slider.OnChangeListener { _, value, _ ->
415 setControlOpacity(value.toInt()) 535 inputOpacityValue.text = "${value.toInt()}%"
416 }) 536 setControlOpacity(value.toInt())
537 }
538 )
417 } 539 }
418 inputScaleValue.text = "${inputScaleSlider.value.toInt()}%" 540 inputScaleValue.text = "${inputScaleSlider.value.toInt()}%"
419 inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%" 541 inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%"
@@ -445,7 +567,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
445 } 567 }
446 568
447 private fun setInsets() { 569 private fun setInsets() {
448 ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat -> 570 ViewCompat.setOnApplyWindowInsetsListener(
571 binding.inGameMenu
572 ) { v: View, windowInsets: WindowInsetsCompat ->
449 val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 573 val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
450 var left = 0 574 var left = 0
451 var right = 0 575 var right = 0
@@ -565,8 +689,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
565 state = State.PAUSED 689 state = State.PAUSED
566 } 690 }
567 691
568 State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.") 692 State.PAUSED -> Log.warning(
569 else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.") 693 "[EmulationFragment] Surface cleared while emulation paused."
694 )
695 else -> Log.warning(
696 "[EmulationFragment] Surface cleared while emulation stopped."
697 )
570 } 698 }
571 } 699 }
572 } 700 }
@@ -586,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
586 State.PAUSED -> { 714 State.PAUSED -> {
587 Log.debug("[EmulationFragment] Resuming emulation.") 715 Log.debug("[EmulationFragment] Resuming emulation.")
588 NativeLibrary.surfaceChanged(surface) 716 NativeLibrary.surfaceChanged(surface)
589 NativeLibrary.unPauseEmulation() 717 NativeLibrary.unpauseEmulation()
590 } 718 }
591 719
592 else -> Log.debug("[EmulationFragment] Bug, run called while already running.") 720 else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
@@ -601,13 +729,5 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
601 729
602 companion object { 730 companion object {
603 private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) 731 private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
604
605 fun newInstance(game: Game): EmulationFragment {
606 val args = Bundle()
607 args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game)
608 val fragment = EmulationFragment()
609 fragment.arguments = args
610 return fragment
611 }
612 } 732 }
613} 733}
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 bdc337501..5a36ffad4 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
@@ -68,67 +68,109 @@ class HomeSettingsFragment : Fragment() {
68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
69 mainActivity = requireActivity() as MainActivity 69 mainActivity = requireActivity() as MainActivity
70 70
71 val optionsList: MutableList<HomeSetting> = mutableListOf( 71 val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
72 HomeSetting( 72 add(
73 R.string.advanced_settings, 73 HomeSetting(
74 R.string.settings_description, 74 R.string.advanced_settings,
75 R.drawable.ic_settings 75 R.string.settings_description,
76 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }, 76 R.drawable.ic_settings
77 HomeSetting( 77 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
78 R.string.open_user_folder, 78 )
79 R.string.open_user_folder_description, 79 add(
80 R.drawable.ic_folder_open 80 HomeSetting(
81 ) { openFileManager() }, 81 R.string.open_user_folder,
82 HomeSetting( 82 R.string.open_user_folder_description,
83 R.string.preferences_theme, 83 R.drawable.ic_folder_open
84 R.string.theme_and_color_description, 84 ) { openFileManager() }
85 R.drawable.ic_palette 85 )
86 ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }, 86 add(
87 HomeSetting( 87 HomeSetting(
88 R.string.install_gpu_driver, 88 R.string.preferences_theme,
89 R.string.install_gpu_driver_description, 89 R.string.theme_and_color_description,
90 R.drawable.ic_exit 90 R.drawable.ic_palette
91 ) { driverInstaller() }, 91 ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
92 HomeSetting( 92 )
93 R.string.install_amiibo_keys, 93
94 R.string.install_amiibo_keys_description, 94 if (GpuDriverHelper.supportsCustomDriverLoading()) {
95 R.drawable.ic_nfc 95 add(
96 ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }, 96 HomeSetting(
97 HomeSetting( 97 R.string.install_gpu_driver,
98 R.string.select_games_folder, 98 R.string.install_gpu_driver_description,
99 R.string.select_games_folder_description, 99 R.drawable.ic_exit
100 R.drawable.ic_add 100 ) { driverInstaller() }
101 ) { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }, 101 )
102 HomeSetting(
103 R.string.manage_save_data,
104 R.string.import_export_saves_description,
105 R.drawable.ic_save
106 ) { ImportExportSavesFragment().show(parentFragmentManager, ImportExportSavesFragment.TAG) },
107 HomeSetting(
108 R.string.install_prod_keys,
109 R.string.install_prod_keys_description,
110 R.drawable.ic_unlock
111 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
112 HomeSetting(
113 R.string.install_firmware,
114 R.string.install_firmware_description,
115 R.drawable.ic_firmware
116 ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) },
117 HomeSetting(
118 R.string.share_log,
119 R.string.share_log_description,
120 R.drawable.ic_log
121 ) { shareLog() },
122 HomeSetting(
123 R.string.about,
124 R.string.about_description,
125 R.drawable.ic_info_outline
126 ) {
127 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
128 parentFragmentManager.primaryNavigationFragment?.findNavController()
129 ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
130 } 102 }
131 ) 103
104 add(
105 HomeSetting(
106 R.string.install_amiibo_keys,
107 R.string.install_amiibo_keys_description,
108 R.drawable.ic_nfc
109 ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
110 )
111 add(
112 HomeSetting(
113 R.string.install_game_content,
114 R.string.install_game_content_description,
115 R.drawable.ic_system_update_alt
116 ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
117 )
118 add(
119 HomeSetting(
120 R.string.select_games_folder,
121 R.string.select_games_folder_description,
122 R.drawable.ic_add
123 ) {
124 mainActivity.getGamesDirectory.launch(
125 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
126 )
127 }
128 )
129 add(
130 HomeSetting(
131 R.string.manage_save_data,
132 R.string.import_export_saves_description,
133 R.drawable.ic_save
134 ) {
135 ImportExportSavesFragment().show(
136 parentFragmentManager,
137 ImportExportSavesFragment.TAG
138 )
139 }
140 )
141 add(
142 HomeSetting(
143 R.string.install_prod_keys,
144 R.string.install_prod_keys_description,
145 R.drawable.ic_unlock
146 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
147 )
148 add(
149 HomeSetting(
150 R.string.install_firmware,
151 R.string.install_firmware_description,
152 R.drawable.ic_firmware
153 ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
154 )
155 add(
156 HomeSetting(
157 R.string.share_log,
158 R.string.share_log_description,
159 R.drawable.ic_log
160 ) { shareLog() }
161 )
162 add(
163 HomeSetting(
164 R.string.about,
165 R.string.about_description,
166 R.drawable.ic_info_outline
167 ) {
168 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
169 parentFragmentManager.primaryNavigationFragment?.findNavController()
170 ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
171 }
172 )
173 }
132 174
133 if (!BuildConfig.PREMIUM) { 175 if (!BuildConfig.PREMIUM) {
134 optionsList.add( 176 optionsList.add(
@@ -215,7 +257,11 @@ class HomeSettingsFragment : Fragment() {
215 val intent = Intent(action) 257 val intent = Intent(action)
216 intent.addCategory(Intent.CATEGORY_DEFAULT) 258 intent.addCategory(Intent.CATEGORY_DEFAULT)
217 intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID) 259 intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID)
218 intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 260 intent.addFlags(
261 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
262 Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
263 Intent.FLAG_GRANT_WRITE_URI_PERMISSION
264 )
219 return intent 265 return intent
220 } 266 }
221 267
@@ -297,7 +343,9 @@ class HomeSettingsFragment : Fragment() {
297 } 343 }
298 344
299 private fun setInsets() = 345 private fun setInsets() =
300 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> 346 ViewCompat.setOnApplyWindowInsetsListener(
347 binding.root
348 ) { view: View, windowInsets: WindowInsetsCompat ->
301 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 349 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
302 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 350 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
303 val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation) 351 val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
index 36e63bb9e..e1495ee8c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
@@ -15,6 +15,14 @@ import androidx.appcompat.app.AppCompatActivity
15import androidx.documentfile.provider.DocumentFile 15import androidx.documentfile.provider.DocumentFile
16import androidx.fragment.app.DialogFragment 16import androidx.fragment.app.DialogFragment
17import com.google.android.material.dialog.MaterialAlertDialogBuilder 17import com.google.android.material.dialog.MaterialAlertDialogBuilder
18import java.io.BufferedOutputStream
19import java.io.File
20import java.io.FileOutputStream
21import java.io.FilenameFilter
22import java.time.LocalDateTime
23import java.time.format.DateTimeFormatter
24import java.util.zip.ZipEntry
25import java.util.zip.ZipOutputStream
18import kotlinx.coroutines.CoroutineScope 26import kotlinx.coroutines.CoroutineScope
19import kotlinx.coroutines.Dispatchers 27import kotlinx.coroutines.Dispatchers
20import kotlinx.coroutines.launch 28import kotlinx.coroutines.launch
@@ -24,14 +32,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
24import org.yuzu.yuzu_emu.features.DocumentProvider 32import org.yuzu.yuzu_emu.features.DocumentProvider
25import org.yuzu.yuzu_emu.getPublicFilesDir 33import org.yuzu.yuzu_emu.getPublicFilesDir
26import org.yuzu.yuzu_emu.utils.FileUtil 34import org.yuzu.yuzu_emu.utils.FileUtil
27import java.io.BufferedOutputStream
28import java.io.File
29import java.io.FileOutputStream
30import java.io.FilenameFilter
31import java.time.LocalDateTime
32import java.time.format.DateTimeFormatter
33import java.util.zip.ZipEntry
34import java.util.zip.ZipOutputStream
35 35
36class ImportExportSavesFragment : DialogFragment() { 36class ImportExportSavesFragment : DialogFragment() {
37 private val context = YuzuApplication.appContext 37 private val context = YuzuApplication.appContext
@@ -98,7 +98,7 @@ class ImportExportSavesFragment : DialogFragment() {
98 val outputZipFile = File( 98 val outputZipFile = File(
99 tempFolder, 99 tempFolder,
100 "yuzu saves - ${ 100 "yuzu saves - ${
101 LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) 101 LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
102 }.zip" 102 }.zip"
103 ) 103 )
104 outputZipFile.createNewFile() 104 outputZipFile.createNewFile()
@@ -106,12 +106,14 @@ class ImportExportSavesFragment : DialogFragment() {
106 saveFolder.walkTopDown().forEach { file -> 106 saveFolder.walkTopDown().forEach { file ->
107 val zipFileName = 107 val zipFileName =
108 file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/") 108 file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
109 if (zipFileName == "") 109 if (zipFileName == "") {
110 return@forEach 110 return@forEach
111 }
111 val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}") 112 val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
112 zos.putNextEntry(entry) 113 zos.putNextEntry(entry)
113 if (file.isFile) 114 if (file.isFile) {
114 file.inputStream().use { fis -> fis.copyTo(zos) } 115 file.inputStream().use { fis -> fis.copyTo(zos) }
116 }
115 } 117 }
116 } 118 }
117 lastZipCreated = outputZipFile 119 lastZipCreated = outputZipFile
@@ -137,7 +139,8 @@ class ImportExportSavesFragment : DialogFragment() {
137 139
138 withContext(Dispatchers.Main) { 140 withContext(Dispatchers.Main) {
139 val file = DocumentFile.fromSingleUri( 141 val file = DocumentFile.fromSingleUri(
140 context, DocumentsContract.buildDocumentUri( 142 context,
143 DocumentsContract.buildDocumentUri(
141 DocumentProvider.AUTHORITY, 144 DocumentProvider.AUTHORITY,
142 "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" 145 "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
143 ) 146 )
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
index c7880d8cc..739b26f99 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -14,7 +14,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
14import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 14import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
15import org.yuzu.yuzu_emu.model.TaskViewModel 15import org.yuzu.yuzu_emu.model.TaskViewModel
16 16
17
18class IndeterminateProgressDialogFragment : DialogFragment() { 17class IndeterminateProgressDialogFragment : DialogFragment() {
19 private val taskViewModel: TaskViewModel by activityViewModels() 18 private val taskViewModel: TaskViewModel by activityViewModels()
20 19
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
index 59141e823..b6e9129f7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
@@ -113,7 +113,9 @@ class LicensesFragment : Fragment() {
113 } 113 }
114 114
115 private fun setInsets() = 115 private fun setInsets() =
116 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> 116 ViewCompat.setOnApplyWindowInsetsListener(
117 binding.root
118 ) { _: View, windowInsets: WindowInsetsCompat ->
117 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 119 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
118 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 120 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
119 121
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt
new file mode 100644
index 000000000..b29b627e9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.Intent
8import android.net.Uri
9import android.os.Bundle
10import androidx.fragment.app.DialogFragment
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.R
13
14class LongMessageDialogFragment : DialogFragment() {
15 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
16 val titleId = requireArguments().getInt(TITLE)
17 val description = requireArguments().getString(DESCRIPTION)
18 val helpLinkId = requireArguments().getInt(HELP_LINK)
19
20 val dialog = MaterialAlertDialogBuilder(requireContext())
21 .setPositiveButton(R.string.close, null)
22 .setTitle(titleId)
23 .setMessage(description)
24
25 if (helpLinkId != 0) {
26 dialog.setNeutralButton(R.string.learn_more) { _, _ ->
27 openLink(getString(helpLinkId))
28 }
29 }
30
31 return dialog.show()
32 }
33
34 private fun openLink(link: String) {
35 val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
36 startActivity(intent)
37 }
38
39 companion object {
40 const val TAG = "LongMessageDialogFragment"
41
42 private const val TITLE = "Title"
43 private const val DESCRIPTION = "Description"
44 private const val HELP_LINK = "Link"
45
46 fun newInstance(
47 titleId: Int,
48 description: String,
49 helpLinkId: Int = 0
50 ): LongMessageDialogFragment {
51 val dialog = LongMessageDialogFragment()
52 val bundle = Bundle()
53 bundle.apply {
54 putInt(TITLE, titleId)
55 putString(DESCRIPTION, description)
56 putInt(HELP_LINK, helpLinkId)
57 }
58 dialog.arguments = bundle
59 return dialog
60 }
61 }
62}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index adbe3696b..f54dccc69 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels
20import androidx.preference.PreferenceManager 20import androidx.preference.PreferenceManager
21import info.debatty.java.stringsimilarity.Jaccard 21import info.debatty.java.stringsimilarity.Jaccard
22import info.debatty.java.stringsimilarity.JaroWinkler 22import info.debatty.java.stringsimilarity.JaroWinkler
23import java.util.Locale
23import org.yuzu.yuzu_emu.R 24import org.yuzu.yuzu_emu.R
24import org.yuzu.yuzu_emu.YuzuApplication 25import org.yuzu.yuzu_emu.YuzuApplication
25import org.yuzu.yuzu_emu.adapters.GameAdapter 26import org.yuzu.yuzu_emu.adapters.GameAdapter
@@ -28,9 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
28import org.yuzu.yuzu_emu.model.Game 29import org.yuzu.yuzu_emu.model.Game
29import org.yuzu.yuzu_emu.model.GamesViewModel 30import org.yuzu.yuzu_emu.model.GamesViewModel
30import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
31import org.yuzu.yuzu_emu.utils.FileUtil
32import org.yuzu.yuzu_emu.utils.Log
33import java.util.Locale
34 32
35class SearchFragment : Fragment() { 33class SearchFragment : Fragment() {
36 private var _binding: FragmentSearchBinding? = null 34 private var _binding: FragmentSearchBinding? = null
@@ -129,16 +127,13 @@ class SearchFragment : Fragment() {
129 127
130 R.id.chip_homebrew -> baseList.filter { it.isHomebrew } 128 R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
131 129
132 R.id.chip_retail -> baseList.filter { 130 R.id.chip_retail -> baseList.filter { !it.isHomebrew }
133 FileUtil.hasExtension(it.path, "xci")
134 || FileUtil.hasExtension(it.path, "nsp")
135 }
136 131
137 else -> baseList 132 else -> baseList
138 } 133 }
139 134
140 if (binding.searchText.text.toString().isEmpty() 135 if (binding.searchText.text.toString().isEmpty() &&
141 && binding.chipGroup.checkedChipId != View.NO_ID 136 binding.chipGroup.checkedChipId != View.NO_ID
142 ) { 137 ) {
143 gamesViewModel.setSearchedGames(filteredList) 138 gamesViewModel.setSearchedGames(filteredList)
144 return 139 return
@@ -173,14 +168,16 @@ class SearchFragment : Fragment() {
173 private fun focusSearch() { 168 private fun focusSearch() {
174 if (_binding != null) { 169 if (_binding != null) {
175 binding.searchText.requestFocus() 170 binding.searchText.requestFocus()
176 val imm = 171 val imm = requireActivity()
177 requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? 172 .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
178 imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT) 173 imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
179 } 174 }
180 } 175 }
181 176
182 private fun setInsets() = 177 private fun setInsets() =
183 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> 178 ViewCompat.setOnApplyWindowInsetsListener(
179 binding.root
180 ) { view: View, windowInsets: WindowInsetsCompat ->
184 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 181 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
185 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 182 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
186 val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med) 183 val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 258773380..6c4ddaf6b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -25,6 +25,7 @@ import androidx.navigation.findNavController
25import androidx.preference.PreferenceManager 25import androidx.preference.PreferenceManager
26import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback 26import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
27import com.google.android.material.transition.MaterialFadeThrough 27import com.google.android.material.transition.MaterialFadeThrough
28import java.io.File
28import org.yuzu.yuzu_emu.R 29import org.yuzu.yuzu_emu.R
29import org.yuzu.yuzu_emu.YuzuApplication 30import org.yuzu.yuzu_emu.YuzuApplication
30import org.yuzu.yuzu_emu.adapters.SetupAdapter 31import org.yuzu.yuzu_emu.adapters.SetupAdapter
@@ -35,7 +36,6 @@ import org.yuzu.yuzu_emu.model.SetupPage
35import org.yuzu.yuzu_emu.ui.main.MainActivity 36import org.yuzu.yuzu_emu.ui.main.MainActivity
36import org.yuzu.yuzu_emu.utils.DirectoryInitialization 37import org.yuzu.yuzu_emu.utils.DirectoryInitialization
37import org.yuzu.yuzu_emu.utils.GameHelper 38import org.yuzu.yuzu_emu.utils.GameHelper
38import java.io.File
39 39
40class SetupFragment : Fragment() { 40class SetupFragment : Fragment() {
41 private var _binding: FragmentSetupBinding? = null 41 private var _binding: FragmentSetupBinding? = null
@@ -82,7 +82,8 @@ class SetupFragment : Fragment() {
82 requireActivity().finish() 82 requireActivity().finish()
83 } 83 }
84 } 84 }
85 }) 85 }
86 )
86 87
87 requireActivity().window.navigationBarColor = 88 requireActivity().window.navigationBarColor =
88 ContextCompat.getColor(requireContext(), android.R.color.transparent) 89 ContextCompat.getColor(requireContext(), android.R.color.transparent)
@@ -148,14 +149,20 @@ class SetupFragment : Fragment() {
148 R.drawable.ic_add, 149 R.drawable.ic_add,
149 true, 150 true,
150 R.string.add_games, 151 R.string.add_games,
151 { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }, 152 {
153 mainActivity.getGamesDirectory.launch(
154 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
155 )
156 },
152 true, 157 true,
153 R.string.add_games_warning, 158 R.string.add_games_warning,
154 R.string.add_games_warning_description, 159 R.string.add_games_warning_description,
155 R.string.add_games_warning_help, 160 R.string.add_games_warning_help,
156 { 161 {
157 val preferences = 162 val preferences =
158 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 163 PreferenceManager.getDefaultSharedPreferences(
164 YuzuApplication.appContext
165 )
159 preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() 166 preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
160 } 167 }
161 ) 168 )
@@ -260,7 +267,9 @@ class SetupFragment : Fragment() {
260 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 267 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
261 private val permissionLauncher = 268 private val permissionLauncher =
262 registerForActivityResult(ActivityResultContracts.RequestPermission()) { 269 registerForActivityResult(ActivityResultContracts.RequestPermission()) {
263 if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { 270 if (!it &&
271 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
272 ) {
264 PermissionDeniedDialogFragment().show( 273 PermissionDeniedDialogFragment().show(
265 childFragmentManager, 274 childFragmentManager,
266 PermissionDeniedDialogFragment.TAG 275 PermissionDeniedDialogFragment.TAG
@@ -315,7 +324,9 @@ class SetupFragment : Fragment() {
315 } 324 }
316 325
317 private fun setInsets() = 326 private fun setInsets() =
318 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> 327 ViewCompat.setOnApplyWindowInsetsListener(
328 binding.root
329 ) { view: View, windowInsets: WindowInsetsCompat ->
319 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 330 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
320 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 331 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
321 view.setPadding( 332 view.setPadding(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
index be5e4c86c..bdd6ea628 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
@@ -44,7 +44,9 @@ class AutofitGridLayoutManager(
44 override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) { 44 override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) {
45 val width = width 45 val width = width
46 val height = height 46 val height = height
47 if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) { 47 if (columnWidth > 0 && width > 0 && height > 0 &&
48 (isColumnWidthChanged || lastWidth != width || lastHeight != height)
49 ) {
48 val totalSpace: Int = if (orientation == VERTICAL) { 50 val totalSpace: Int = if (orientation == VERTICAL) {
49 width - paddingRight - paddingLeft 51 width - paddingRight - paddingLeft
50 } else { 52 } else {
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 3d6782c49..6527c64ab 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
@@ -4,9 +4,9 @@
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import android.os.Parcelable 6import android.os.Parcelable
7import java.util.HashSet
7import kotlinx.parcelize.Parcelize 8import kotlinx.parcelize.Parcelize
8import kotlinx.serialization.Serializable 9import kotlinx.serialization.Serializable
9import java.util.HashSet
10 10
11@Parcelize 11@Parcelize
12@Serializable 12@Serializable
@@ -23,21 +23,27 @@ class Game(
23 val keyLastPlayedTime get() = "${gameId}_LastPlayed" 23 val keyLastPlayedTime get() = "${gameId}_LastPlayed"
24 24
25 override fun equals(other: Any?): Boolean { 25 override fun equals(other: Any?): Boolean {
26 if (other !is Game) 26 if (other !is Game) {
27 return false 27 return false
28 }
29
30 return hashCode() == other.hashCode()
31 }
28 32
29 return title == other.title 33 override fun hashCode(): Int {
30 && description == other.description 34 var result = title.hashCode()
31 && regions == other.regions 35 result = 31 * result + description.hashCode()
32 && path == other.path 36 result = 31 * result + regions.hashCode()
33 && gameId == other.gameId 37 result = 31 * result + path.hashCode()
34 && company == other.company 38 result = 31 * result + gameId.hashCode()
35 && isHomebrew == other.isHomebrew 39 result = 31 * result + company.hashCode()
40 result = 31 * result + isHomebrew.hashCode()
41 return result
36 } 42 }
37 43
38 companion object { 44 companion object {
39 val extensions: Set<String> = HashSet( 45 val extensions: Set<String> = HashSet(
40 listOf(".xci", ".nsp", ".nca", ".nro") 46 listOf("xci", "nsp", "nca", "nro")
41 ) 47 )
42 } 48 }
43} 49}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index d9b301210..1fe42f922 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData
10import androidx.lifecycle.ViewModel 10import androidx.lifecycle.ViewModel
11import androidx.lifecycle.viewModelScope 11import androidx.lifecycle.viewModelScope
12import androidx.preference.PreferenceManager 12import androidx.preference.PreferenceManager
13import java.util.Locale
13import kotlinx.coroutines.Dispatchers 14import kotlinx.coroutines.Dispatchers
14import kotlinx.coroutines.launch 15import kotlinx.coroutines.launch
15import kotlinx.coroutines.withContext 16import kotlinx.coroutines.withContext
@@ -20,7 +21,6 @@ import kotlinx.serialization.json.Json
20import org.yuzu.yuzu_emu.NativeLibrary 21import org.yuzu.yuzu_emu.NativeLibrary
21import org.yuzu.yuzu_emu.YuzuApplication 22import org.yuzu.yuzu_emu.YuzuApplication
22import org.yuzu.yuzu_emu.utils.GameHelper 23import org.yuzu.yuzu_emu.utils.GameHelper
23import java.util.Locale
24 24
25@OptIn(ExperimentalSerializationApi::class) 25@OptIn(ExperimentalSerializationApi::class)
26class GamesViewModel : ViewModel() { 26class GamesViewModel : ViewModel() {
@@ -99,8 +99,9 @@ class GamesViewModel : ViewModel() {
99 } 99 }
100 100
101 fun reloadGames(directoryChanged: Boolean) { 101 fun reloadGames(directoryChanged: Boolean) {
102 if (isReloading.value == true) 102 if (isReloading.value == true) {
103 return 103 return
104 }
104 _isReloading.postValue(true) 105 _isReloading.postValue(true)
105 106
106 viewModelScope.launch { 107 viewModelScope.launch {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index aa424c768..6251ec783 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.overlay
6import android.app.Activity 6import android.app.Activity
7import android.content.Context 7import android.content.Context
8import android.content.SharedPreferences 8import android.content.SharedPreferences
9import android.content.res.Configuration
10import android.graphics.Bitmap 9import android.graphics.Bitmap
11import android.graphics.Canvas 10import android.graphics.Canvas
12import android.graphics.Point 11import android.graphics.Point
@@ -24,6 +23,8 @@ import android.view.WindowInsets
24import androidx.core.content.ContextCompat 23import androidx.core.content.ContextCompat
25import androidx.preference.PreferenceManager 24import androidx.preference.PreferenceManager
26import androidx.window.layout.WindowMetricsCalculator 25import androidx.window.layout.WindowMetricsCalculator
26import kotlin.math.max
27import kotlin.math.min
27import org.yuzu.yuzu_emu.NativeLibrary 28import org.yuzu.yuzu_emu.NativeLibrary
28import org.yuzu.yuzu_emu.NativeLibrary.ButtonType 29import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
29import org.yuzu.yuzu_emu.NativeLibrary.StickType 30import org.yuzu.yuzu_emu.NativeLibrary.StickType
@@ -31,14 +32,13 @@ import org.yuzu.yuzu_emu.R
31import org.yuzu.yuzu_emu.YuzuApplication 32import org.yuzu.yuzu_emu.YuzuApplication
32import org.yuzu.yuzu_emu.features.settings.model.Settings 33import org.yuzu.yuzu_emu.features.settings.model.Settings
33import org.yuzu.yuzu_emu.utils.EmulationMenuSettings 34import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
34import kotlin.math.max
35import kotlin.math.min
36 35
37/** 36/**
38 * Draws the interactive input overlay on top of the 37 * Draws the interactive input overlay on top of the
39 * [SurfaceView] that is rendering emulation. 38 * [SurfaceView] that is rendering emulation.
40 */ 39 */
41class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs), 40class InputOverlay(context: Context, attrs: AttributeSet?) :
41 SurfaceView(context, attrs),
42 OnTouchListener { 42 OnTouchListener {
43 private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet() 43 private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
44 private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet() 44 private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
@@ -51,12 +51,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
51 51
52 private lateinit var windowInsets: WindowInsets 52 private lateinit var windowInsets: WindowInsets
53 53
54 var orientation = LANDSCAPE
55
54 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
55 super.onLayout(changed, left, top, right, bottom) 57 super.onLayout(changed, left, top, right, bottom)
56 58
57 windowInsets = rootWindowInsets 59 windowInsets = rootWindowInsets
58 60
59 if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) { 61 if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
60 defaultOverlay() 62 defaultOverlay()
61 } 63 }
62 64
@@ -93,7 +95,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
93 95
94 var shouldUpdateView = false 96 var shouldUpdateView = false
95 val playerIndex = 97 val playerIndex =
96 if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device 98 if (NativeLibrary.isHandheldOnly()) {
99 NativeLibrary.ConsoleDevice
100 } else {
101 NativeLibrary.Player1Device
102 }
97 103
98 for (button in overlayButtons) { 104 for (button in overlayButtons) {
99 if (!button.updateStatus(event)) { 105 if (!button.updateStatus(event)) {
@@ -156,8 +162,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
156 shouldUpdateView = true 162 shouldUpdateView = true
157 } 163 }
158 164
159 if (shouldUpdateView) 165 if (shouldUpdateView) {
160 invalidate() 166 invalidate()
167 }
161 168
162 if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) { 169 if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
163 return true 170 return true
@@ -233,10 +240,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
233 val fingerPositionX = event.getX(pointerIndex).toInt() 240 val fingerPositionX = event.getX(pointerIndex).toInt()
234 val fingerPositionY = event.getY(pointerIndex).toInt() 241 val fingerPositionY = event.getY(pointerIndex).toInt()
235 242
236 // TODO: Provide support for portrait layout
237 //val orientation =
238 // if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
239
240 for (button in overlayButtons) { 243 for (button in overlayButtons) {
241 // Determine the button state to apply based on the MotionEvent action flag. 244 // Determine the button state to apply based on the MotionEvent action flag.
242 when (event.action and MotionEvent.ACTION_MASK) { 245 when (event.action and MotionEvent.ACTION_MASK) {
@@ -245,9 +248,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
245 // If no button is being moved now, remember the currently touched button to move. 248 // If no button is being moved now, remember the currently touched button to move.
246 if (buttonBeingConfigured == null && 249 if (buttonBeingConfigured == null &&
247 button.bounds.contains( 250 button.bounds.contains(
248 fingerPositionX, 251 fingerPositionX,
249 fingerPositionY 252 fingerPositionY
250 ) 253 )
251 ) { 254 ) {
252 buttonBeingConfigured = button 255 buttonBeingConfigured = button
253 buttonBeingConfigured!!.onConfigureTouch(event) 256 buttonBeingConfigured!!.onConfigureTouch(event)
@@ -266,7 +269,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
266 buttonBeingConfigured!!.buttonId, 269 buttonBeingConfigured!!.buttonId,
267 buttonBeingConfigured!!.bounds.centerX(), 270 buttonBeingConfigured!!.bounds.centerX(),
268 buttonBeingConfigured!!.bounds.centerY(), 271 buttonBeingConfigured!!.bounds.centerY(),
269 "" 272 orientation
270 ) 273 )
271 buttonBeingConfigured = null 274 buttonBeingConfigured = null
272 } 275 }
@@ -299,7 +302,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
299 dpadBeingConfigured!!.upId, 302 dpadBeingConfigured!!.upId,
300 dpadBeingConfigured!!.bounds.centerX(), 303 dpadBeingConfigured!!.bounds.centerX(),
301 dpadBeingConfigured!!.bounds.centerY(), 304 dpadBeingConfigured!!.bounds.centerY(),
302 "" 305 orientation
303 ) 306 )
304 dpadBeingConfigured = null 307 dpadBeingConfigured = null
305 } 308 }
@@ -311,9 +314,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
311 MotionEvent.ACTION_DOWN, 314 MotionEvent.ACTION_DOWN,
312 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null && 315 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
313 joystick.bounds.contains( 316 joystick.bounds.contains(
314 fingerPositionX, 317 fingerPositionX,
315 fingerPositionY 318 fingerPositionY
316 ) 319 )
317 ) { 320 ) {
318 joystickBeingConfigured = joystick 321 joystickBeingConfigured = joystick
319 joystickBeingConfigured!!.onConfigureTouch(event) 322 joystickBeingConfigured!!.onConfigureTouch(event)
@@ -330,7 +333,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
330 joystickBeingConfigured!!.buttonId, 333 joystickBeingConfigured!!.buttonId,
331 joystickBeingConfigured!!.bounds.centerX(), 334 joystickBeingConfigured!!.bounds.centerX(),
332 joystickBeingConfigured!!.bounds.centerY(), 335 joystickBeingConfigured!!.bounds.centerY(),
333 "" 336 orientation
334 ) 337 )
335 joystickBeingConfigured = null 338 joystickBeingConfigured = null
336 } 339 }
@@ -533,8 +536,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
533 overlayButtons.clear() 536 overlayButtons.clear()
534 overlayDpads.clear() 537 overlayDpads.clear()
535 overlayJoysticks.clear() 538 overlayJoysticks.clear()
536 val orientation =
537 if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
538 539
539 // Add all the enabled overlay items back to the HashSet. 540 // Add all the enabled overlay items back to the HashSet.
540 if (EmulationMenuSettings.showOverlay) { 541 if (EmulationMenuSettings.showOverlay) {
@@ -548,8 +549,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
548 val min = windowSize.first 549 val min = windowSize.first
549 val max = windowSize.second 550 val max = windowSize.second
550 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 551 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
551 .putFloat("$sharedPrefsId$orientation-X", (x - min.x).toFloat() / max.x) 552 .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x)
552 .putFloat("$sharedPrefsId$orientation-Y", (y - min.y).toFloat() / max.y) 553 .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y)
553 .apply() 554 .apply()
554 } 555 }
555 556
@@ -558,145 +559,250 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
558 } 559 }
559 560
560 private fun defaultOverlay() { 561 private fun defaultOverlay() {
561 if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) { 562 if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
562 defaultOverlayLandscape() 563 defaultOverlayByLayout(orientation)
563 } 564 }
564 565
565 resetButtonPlacement() 566 resetButtonPlacement()
566 preferences.edit() 567 preferences.edit()
567 .putBoolean(Settings.PREF_OVERLAY_INIT, true) 568 .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true)
568 .apply() 569 .apply()
569 } 570 }
570 571
571 fun resetButtonPlacement() { 572 fun resetButtonPlacement() {
572 defaultOverlayLandscape() 573 defaultOverlayByLayout(orientation)
573 refreshControls() 574 refreshControls()
574 } 575 }
575 576
576 private fun defaultOverlayLandscape() { 577 private val landscapeResources = arrayOf(
578 R.integer.SWITCH_BUTTON_A_X,
579 R.integer.SWITCH_BUTTON_A_Y,
580 R.integer.SWITCH_BUTTON_B_X,
581 R.integer.SWITCH_BUTTON_B_Y,
582 R.integer.SWITCH_BUTTON_X_X,
583 R.integer.SWITCH_BUTTON_X_Y,
584 R.integer.SWITCH_BUTTON_Y_X,
585 R.integer.SWITCH_BUTTON_Y_Y,
586 R.integer.SWITCH_TRIGGER_ZL_X,
587 R.integer.SWITCH_TRIGGER_ZL_Y,
588 R.integer.SWITCH_TRIGGER_ZR_X,
589 R.integer.SWITCH_TRIGGER_ZR_Y,
590 R.integer.SWITCH_BUTTON_DPAD_X,
591 R.integer.SWITCH_BUTTON_DPAD_Y,
592 R.integer.SWITCH_TRIGGER_L_X,
593 R.integer.SWITCH_TRIGGER_L_Y,
594 R.integer.SWITCH_TRIGGER_R_X,
595 R.integer.SWITCH_TRIGGER_R_Y,
596 R.integer.SWITCH_BUTTON_PLUS_X,
597 R.integer.SWITCH_BUTTON_PLUS_Y,
598 R.integer.SWITCH_BUTTON_MINUS_X,
599 R.integer.SWITCH_BUTTON_MINUS_Y,
600 R.integer.SWITCH_BUTTON_HOME_X,
601 R.integer.SWITCH_BUTTON_HOME_Y,
602 R.integer.SWITCH_BUTTON_CAPTURE_X,
603 R.integer.SWITCH_BUTTON_CAPTURE_Y,
604 R.integer.SWITCH_STICK_R_X,
605 R.integer.SWITCH_STICK_R_Y,
606 R.integer.SWITCH_STICK_L_X,
607 R.integer.SWITCH_STICK_L_Y
608 )
609
610 private val portraitResources = arrayOf(
611 R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
612 R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
613 R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
614 R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
615 R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
616 R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
617 R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
618 R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
619 R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
620 R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
621 R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
622 R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
623 R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
624 R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
625 R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
626 R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
627 R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
628 R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
629 R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
630 R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
631 R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
632 R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
633 R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
634 R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
635 R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
636 R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
637 R.integer.SWITCH_STICK_R_X_PORTRAIT,
638 R.integer.SWITCH_STICK_R_Y_PORTRAIT,
639 R.integer.SWITCH_STICK_L_X_PORTRAIT,
640 R.integer.SWITCH_STICK_L_Y_PORTRAIT
641 )
642
643 private val foldableResources = arrayOf(
644 R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
645 R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
646 R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
647 R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
648 R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
649 R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
650 R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
651 R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
652 R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
653 R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
654 R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
655 R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
656 R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
657 R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
658 R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
659 R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
660 R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
661 R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
662 R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
663 R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
664 R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
665 R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
666 R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
667 R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
668 R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
669 R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
670 R.integer.SWITCH_STICK_R_X_FOLDABLE,
671 R.integer.SWITCH_STICK_R_Y_FOLDABLE,
672 R.integer.SWITCH_STICK_L_X_FOLDABLE,
673 R.integer.SWITCH_STICK_L_Y_FOLDABLE
674 )
675
676 private fun getResourceValue(orientation: String, position: Int): Float {
677 return when (orientation) {
678 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
679 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
680 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
681 }
682 }
683
684 private fun defaultOverlayByLayout(orientation: String) {
577 // Each value represents the position of the button in relation to the screen size without insets. 685 // Each value represents the position of the button in relation to the screen size without insets.
578 preferences.edit() 686 preferences.edit()
579 .putFloat( 687 .putFloat(
580 ButtonType.BUTTON_A.toString() + "-X", 688 ButtonType.BUTTON_A.toString() + "-X$orientation",
581 resources.getInteger(R.integer.SWITCH_BUTTON_A_X).toFloat() / 1000 689 getResourceValue(orientation, 0)
582 ) 690 )
583 .putFloat( 691 .putFloat(
584 ButtonType.BUTTON_A.toString() + "-Y", 692 ButtonType.BUTTON_A.toString() + "-Y$orientation",
585 resources.getInteger(R.integer.SWITCH_BUTTON_A_Y).toFloat() / 1000 693 getResourceValue(orientation, 1)
586 ) 694 )
587 .putFloat( 695 .putFloat(
588 ButtonType.BUTTON_B.toString() + "-X", 696 ButtonType.BUTTON_B.toString() + "-X$orientation",
589 resources.getInteger(R.integer.SWITCH_BUTTON_B_X).toFloat() / 1000 697 getResourceValue(orientation, 2)
590 ) 698 )
591 .putFloat( 699 .putFloat(
592 ButtonType.BUTTON_B.toString() + "-Y", 700 ButtonType.BUTTON_B.toString() + "-Y$orientation",
593 resources.getInteger(R.integer.SWITCH_BUTTON_B_Y).toFloat() / 1000 701 getResourceValue(orientation, 3)
594 ) 702 )
595 .putFloat( 703 .putFloat(
596 ButtonType.BUTTON_X.toString() + "-X", 704 ButtonType.BUTTON_X.toString() + "-X$orientation",
597 resources.getInteger(R.integer.SWITCH_BUTTON_X_X).toFloat() / 1000 705 getResourceValue(orientation, 4)
598 ) 706 )
599 .putFloat( 707 .putFloat(
600 ButtonType.BUTTON_X.toString() + "-Y", 708 ButtonType.BUTTON_X.toString() + "-Y$orientation",
601 resources.getInteger(R.integer.SWITCH_BUTTON_X_Y).toFloat() / 1000 709 getResourceValue(orientation, 5)
602 ) 710 )
603 .putFloat( 711 .putFloat(
604 ButtonType.BUTTON_Y.toString() + "-X", 712 ButtonType.BUTTON_Y.toString() + "-X$orientation",
605 resources.getInteger(R.integer.SWITCH_BUTTON_Y_X).toFloat() / 1000 713 getResourceValue(orientation, 6)
606 ) 714 )
607 .putFloat( 715 .putFloat(
608 ButtonType.BUTTON_Y.toString() + "-Y", 716 ButtonType.BUTTON_Y.toString() + "-Y$orientation",
609 resources.getInteger(R.integer.SWITCH_BUTTON_Y_Y).toFloat() / 1000 717 getResourceValue(orientation, 7)
610 ) 718 )
611 .putFloat( 719 .putFloat(
612 ButtonType.TRIGGER_ZL.toString() + "-X", 720 ButtonType.TRIGGER_ZL.toString() + "-X$orientation",
613 resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_X).toFloat() / 1000 721 getResourceValue(orientation, 8)
614 ) 722 )
615 .putFloat( 723 .putFloat(
616 ButtonType.TRIGGER_ZL.toString() + "-Y", 724 ButtonType.TRIGGER_ZL.toString() + "-Y$orientation",
617 resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_Y).toFloat() / 1000 725 getResourceValue(orientation, 9)
618 ) 726 )
619 .putFloat( 727 .putFloat(
620 ButtonType.TRIGGER_ZR.toString() + "-X", 728 ButtonType.TRIGGER_ZR.toString() + "-X$orientation",
621 resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_X).toFloat() / 1000 729 getResourceValue(orientation, 10)
622 ) 730 )
623 .putFloat( 731 .putFloat(
624 ButtonType.TRIGGER_ZR.toString() + "-Y", 732 ButtonType.TRIGGER_ZR.toString() + "-Y$orientation",
625 resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_Y).toFloat() / 1000 733 getResourceValue(orientation, 11)
626 ) 734 )
627 .putFloat( 735 .putFloat(
628 ButtonType.DPAD_UP.toString() + "-X", 736 ButtonType.DPAD_UP.toString() + "-X$orientation",
629 resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_X).toFloat() / 1000 737 getResourceValue(orientation, 12)
630 ) 738 )
631 .putFloat( 739 .putFloat(
632 ButtonType.DPAD_UP.toString() + "-Y", 740 ButtonType.DPAD_UP.toString() + "-Y$orientation",
633 resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_Y).toFloat() / 1000 741 getResourceValue(orientation, 13)
634 ) 742 )
635 .putFloat( 743 .putFloat(
636 ButtonType.TRIGGER_L.toString() + "-X", 744 ButtonType.TRIGGER_L.toString() + "-X$orientation",
637 resources.getInteger(R.integer.SWITCH_TRIGGER_L_X).toFloat() / 1000 745 getResourceValue(orientation, 14)
638 ) 746 )
639 .putFloat( 747 .putFloat(
640 ButtonType.TRIGGER_L.toString() + "-Y", 748 ButtonType.TRIGGER_L.toString() + "-Y$orientation",
641 resources.getInteger(R.integer.SWITCH_TRIGGER_L_Y).toFloat() / 1000 749 getResourceValue(orientation, 15)
642 ) 750 )
643 .putFloat( 751 .putFloat(
644 ButtonType.TRIGGER_R.toString() + "-X", 752 ButtonType.TRIGGER_R.toString() + "-X$orientation",
645 resources.getInteger(R.integer.SWITCH_TRIGGER_R_X).toFloat() / 1000 753 getResourceValue(orientation, 16)
646 ) 754 )
647 .putFloat( 755 .putFloat(
648 ButtonType.TRIGGER_R.toString() + "-Y", 756 ButtonType.TRIGGER_R.toString() + "-Y$orientation",
649 resources.getInteger(R.integer.SWITCH_TRIGGER_R_Y).toFloat() / 1000 757 getResourceValue(orientation, 17)
650 ) 758 )
651 .putFloat( 759 .putFloat(
652 ButtonType.BUTTON_PLUS.toString() + "-X", 760 ButtonType.BUTTON_PLUS.toString() + "-X$orientation",
653 resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_X).toFloat() / 1000 761 getResourceValue(orientation, 18)
654 ) 762 )
655 .putFloat( 763 .putFloat(
656 ButtonType.BUTTON_PLUS.toString() + "-Y", 764 ButtonType.BUTTON_PLUS.toString() + "-Y$orientation",
657 resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_Y).toFloat() / 1000 765 getResourceValue(orientation, 19)
658 ) 766 )
659 .putFloat( 767 .putFloat(
660 ButtonType.BUTTON_MINUS.toString() + "-X", 768 ButtonType.BUTTON_MINUS.toString() + "-X$orientation",
661 resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_X).toFloat() / 1000 769 getResourceValue(orientation, 20)
662 ) 770 )
663 .putFloat( 771 .putFloat(
664 ButtonType.BUTTON_MINUS.toString() + "-Y", 772 ButtonType.BUTTON_MINUS.toString() + "-Y$orientation",
665 resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_Y).toFloat() / 1000 773 getResourceValue(orientation, 21)
666 ) 774 )
667 .putFloat( 775 .putFloat(
668 ButtonType.BUTTON_HOME.toString() + "-X", 776 ButtonType.BUTTON_HOME.toString() + "-X$orientation",
669 resources.getInteger(R.integer.SWITCH_BUTTON_HOME_X).toFloat() / 1000 777 getResourceValue(orientation, 22)
670 ) 778 )
671 .putFloat( 779 .putFloat(
672 ButtonType.BUTTON_HOME.toString() + "-Y", 780 ButtonType.BUTTON_HOME.toString() + "-Y$orientation",
673 resources.getInteger(R.integer.SWITCH_BUTTON_HOME_Y).toFloat() / 1000 781 getResourceValue(orientation, 23)
674 ) 782 )
675 .putFloat( 783 .putFloat(
676 ButtonType.BUTTON_CAPTURE.toString() + "-X", 784 ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation",
677 resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_X) 785 getResourceValue(orientation, 24)
678 .toFloat() / 1000
679 ) 786 )
680 .putFloat( 787 .putFloat(
681 ButtonType.BUTTON_CAPTURE.toString() + "-Y", 788 ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation",
682 resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_Y) 789 getResourceValue(orientation, 25)
683 .toFloat() / 1000
684 ) 790 )
685 .putFloat( 791 .putFloat(
686 ButtonType.STICK_R.toString() + "-X", 792 ButtonType.STICK_R.toString() + "-X$orientation",
687 resources.getInteger(R.integer.SWITCH_STICK_R_X).toFloat() / 1000 793 getResourceValue(orientation, 26)
688 ) 794 )
689 .putFloat( 795 .putFloat(
690 ButtonType.STICK_R.toString() + "-Y", 796 ButtonType.STICK_R.toString() + "-Y$orientation",
691 resources.getInteger(R.integer.SWITCH_STICK_R_Y).toFloat() / 1000 797 getResourceValue(orientation, 27)
692 ) 798 )
693 .putFloat( 799 .putFloat(
694 ButtonType.STICK_L.toString() + "-X", 800 ButtonType.STICK_L.toString() + "-X$orientation",
695 resources.getInteger(R.integer.SWITCH_STICK_L_X).toFloat() / 1000 801 getResourceValue(orientation, 28)
696 ) 802 )
697 .putFloat( 803 .putFloat(
698 ButtonType.STICK_L.toString() + "-Y", 804 ButtonType.STICK_L.toString() + "-Y$orientation",
699 resources.getInteger(R.integer.SWITCH_STICK_L_Y).toFloat() / 1000 805 getResourceValue(orientation, 29)
700 ) 806 )
701 .apply() 807 .apply()
702 } 808 }
@@ -709,13 +815,17 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
709 private val preferences: SharedPreferences = 815 private val preferences: SharedPreferences =
710 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 816 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
711 817
818 const val LANDSCAPE = ""
819 const val PORTRAIT = "_Portrait"
820 const val FOLDABLE = "_Foldable"
821
712 /** 822 /**
713 * Resizes a [Bitmap] by a given scale factor 823 * Resizes a [Bitmap] by a given scale factor
714 * 824 *
715 * @param context Context for getting the vector drawable 825 * @param context Context for getting the vector drawable
716 * @param drawableId The ID of the drawable to scale. 826 * @param drawableId The ID of the drawable to scale.
717 * @param scale The scale factor for the bitmap. 827 * @param scale The scale factor for the bitmap.
718 * @return The scaled [Bitmap] 828 * @return The scaled [Bitmap]
719 */ 829 */
720 private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap { 830 private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap {
721 val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable 831 val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable
@@ -749,14 +859,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
749 * Gets the safe screen size for drawing the overlay 859 * Gets the safe screen size for drawing the overlay
750 * 860 *
751 * @param context Context for getting the window metrics 861 * @param context Context for getting the window metrics
752 * @return A pair of points, the first being the top left corner of the safe area, 862 * @return A pair of points, the first being the top left corner of the safe area,
753 * the second being the bottom right corner of the safe area 863 * the second being the bottom right corner of the safe area
754 */ 864 */
755 private fun getSafeScreenSize(context: Context): Pair<Point, Point> { 865 private fun getSafeScreenSize(context: Context): Pair<Point, Point> {
756 // Get screen size 866 // Get screen size
757 val windowMetrics = 867 val windowMetrics = WindowMetricsCalculator.getOrCreate()
758 WindowMetricsCalculator.getOrCreate() 868 .computeCurrentWindowMetrics(context as Activity)
759 .computeCurrentWindowMetrics(context as Activity)
760 var maxY = windowMetrics.bounds.height().toFloat() 869 var maxY = windowMetrics.bounds.height().toFloat()
761 var maxX = windowMetrics.bounds.width().toFloat() 870 var maxX = windowMetrics.bounds.width().toFloat()
762 var minY = 0 871 var minY = 0
@@ -768,10 +877,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
768 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 877 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
769 val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout 878 val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
770 if (insets != null) { 879 if (insets != null) {
771 if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) 880 if (insets.boundingRectTop.bottom != 0 &&
772 insets.boundingRectTop.bottom.toFloat() else maxY 881 insets.boundingRectTop.bottom > maxY / 2
773 if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) 882 ) {
774 insets.boundingRectRight.left.toFloat() else maxX 883 maxY = insets.boundingRectTop.bottom.toFloat()
884 }
885 if (insets.boundingRectRight.left != 0 &&
886 insets.boundingRectRight.left > maxX / 2
887 ) {
888 maxX = insets.boundingRectRight.left.toFloat()
889 }
775 890
776 minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left 891 minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
777 minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom 892 minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
@@ -878,8 +993,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
878 993
879 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 994 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
880 // These were set in the input overlay configuration menu. 995 // These were set in the input overlay configuration menu.
881 val xKey = "$buttonId$orientation-X" 996 val xKey = "$buttonId-X$orientation"
882 val yKey = "$buttonId$orientation-Y" 997 val yKey = "$buttonId-Y$orientation"
883 val drawableXPercent = sPrefs.getFloat(xKey, 0f) 998 val drawableXPercent = sPrefs.getFloat(xKey, 0f)
884 val drawableYPercent = sPrefs.getFloat(yKey, 0f) 999 val drawableYPercent = sPrefs.getFloat(yKey, 0f)
885 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1000 val drawableX = (drawableXPercent * max.x + min.x).toInt()
@@ -959,8 +1074,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
959 1074
960 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. 1075 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
961 // These were set in the input overlay configuration menu. 1076 // These were set in the input overlay configuration menu.
962 val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-X", 0f) 1077 val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f)
963 val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-Y", 0f) 1078 val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f)
964 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1079 val drawableX = (drawableXPercent * max.x + min.x).toInt()
965 val drawableY = (drawableYPercent * max.y + min.y).toInt() 1080 val drawableY = (drawableYPercent * max.y + min.y).toInt()
966 val width = overlayDrawable.width 1081 val width = overlayDrawable.width
@@ -1026,8 +1141,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
1026 1141
1027 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1142 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1028 // These were set in the input overlay configuration menu. 1143 // These were set in the input overlay configuration menu.
1029 val drawableXPercent = sPrefs.getFloat("$button$orientation-X", 0f) 1144 val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f)
1030 val drawableYPercent = sPrefs.getFloat("$button$orientation-Y", 0f) 1145 val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f)
1031 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1146 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1032 val drawableY = (drawableYPercent * max.y + min.y).toInt() 1147 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1033 val outerScale = 1.66f 1148 val outerScale = 1.66f
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
index 43d664d21..8aef6f5a5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
@@ -133,7 +133,10 @@ class InputOverlayDrawableDpad(
133 downButtonState = axisY > VIRT_AXIS_DEADZONE 133 downButtonState = axisY > VIRT_AXIS_DEADZONE
134 leftButtonState = axisX < -VIRT_AXIS_DEADZONE 134 leftButtonState = axisX < -VIRT_AXIS_DEADZONE
135 rightButtonState = axisX > VIRT_AXIS_DEADZONE 135 rightButtonState = axisX > VIRT_AXIS_DEADZONE
136 return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState 136 return oldUpState != upButtonState ||
137 oldDownState != downButtonState ||
138 oldLeftState != leftButtonState ||
139 oldRightState != rightButtonState
137 } 140 }
138 return false 141 return false
139 } 142 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index f1d32192a..fb48f584d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -9,12 +9,12 @@ import android.graphics.Canvas
9import android.graphics.Rect 9import android.graphics.Rect
10import android.graphics.drawable.BitmapDrawable 10import android.graphics.drawable.BitmapDrawable
11import android.view.MotionEvent 11import android.view.MotionEvent
12import org.yuzu.yuzu_emu.NativeLibrary
13import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
14import kotlin.math.atan2 12import kotlin.math.atan2
15import kotlin.math.cos 13import kotlin.math.cos
16import kotlin.math.sin 14import kotlin.math.sin
17import kotlin.math.sqrt 15import kotlin.math.sqrt
16import org.yuzu.yuzu_emu.NativeLibrary
17import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
18 18
19/** 19/**
20 * Custom [BitmapDrawable] that is capable 20 * Custom [BitmapDrawable] that is capable
@@ -241,14 +241,22 @@ class InputOverlayDrawableJoystick(
241 private fun setInnerBounds() { 241 private fun setInnerBounds() {
242 var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt() 242 var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
243 var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt() 243 var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
244 if (x > virtBounds.centerX() + virtBounds.width() / 2) x = 244 if (x > virtBounds.centerX() + virtBounds.width() / 2) {
245 virtBounds.centerX() + virtBounds.width() / 2 245 x =
246 if (x < virtBounds.centerX() - virtBounds.width() / 2) x = 246 virtBounds.centerX() + virtBounds.width() / 2
247 virtBounds.centerX() - virtBounds.width() / 2 247 }
248 if (y > virtBounds.centerY() + virtBounds.height() / 2) y = 248 if (x < virtBounds.centerX() - virtBounds.width() / 2) {
249 virtBounds.centerY() + virtBounds.height() / 2 249 x =
250 if (y < virtBounds.centerY() - virtBounds.height() / 2) y = 250 virtBounds.centerX() - virtBounds.width() / 2
251 virtBounds.centerY() - virtBounds.height() / 2 251 }
252 if (y > virtBounds.centerY() + virtBounds.height() / 2) {
253 y =
254 virtBounds.centerY() + virtBounds.height() / 2
255 }
256 if (y < virtBounds.centerY() - virtBounds.height() / 2) {
257 y =
258 virtBounds.centerY() - virtBounds.height() / 2
259 }
252 val width = pressedStateInnerBitmap.bounds.width() / 2 260 val width = pressedStateInnerBitmap.bounds.width() / 2
253 val height = pressedStateInnerBitmap.bounds.height() / 2 261 val height = pressedStateInnerBitmap.bounds.height() / 2
254 defaultStateInnerBitmap.setBounds( 262 defaultStateInnerBitmap.setBounds(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 97eef40d2..b0156dca5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -99,7 +99,9 @@ class GamesFragment : Fragment() {
99 } 99 }
100 shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData -> 100 shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
101 if (shouldSwapData) { 101 if (shouldSwapData) {
102 (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!) 102 (binding.gridGames.adapter as GameAdapter).submitList(
103 gamesViewModel.games.value!!
104 )
103 gamesViewModel.setShouldSwapData(false) 105 gamesViewModel.setShouldSwapData(false)
104 } 106 }
105 } 107 }
@@ -128,7 +130,9 @@ class GamesFragment : Fragment() {
128 } 130 }
129 131
130 private fun setInsets() = 132 private fun setInsets() =
131 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> 133 ViewCompat.setOnApplyWindowInsetsListener(
134 binding.root
135 ) { view: View, windowInsets: WindowInsetsCompat ->
132 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 136 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
133 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 137 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
134 val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large) 138 val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large)
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 3fca0a7e6..f7d7aed1e 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
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.ui.main 4package org.yuzu.yuzu_emu.ui.main
5 5
6import android.content.Intent 6import android.content.Intent
7import android.net.Uri
7import android.os.Bundle 8import android.os.Bundle
8import android.view.View 9import android.view.View
9import android.view.ViewGroup.MarginLayoutParams 10import android.view.ViewGroup.MarginLayoutParams
@@ -26,6 +27,9 @@ import androidx.preference.PreferenceManager
26import com.google.android.material.color.MaterialColors 27import com.google.android.material.color.MaterialColors
27import com.google.android.material.dialog.MaterialAlertDialogBuilder 28import com.google.android.material.dialog.MaterialAlertDialogBuilder
28import com.google.android.material.navigation.NavigationBarView 29import com.google.android.material.navigation.NavigationBarView
30import java.io.File
31import java.io.FilenameFilter
32import java.io.IOException
29import kotlinx.coroutines.Dispatchers 33import kotlinx.coroutines.Dispatchers
30import kotlinx.coroutines.launch 34import kotlinx.coroutines.launch
31import kotlinx.coroutines.withContext 35import kotlinx.coroutines.withContext
@@ -39,13 +43,11 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
39import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 43import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
40import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 44import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
41import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
46import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
43import org.yuzu.yuzu_emu.model.GamesViewModel 48import org.yuzu.yuzu_emu.model.GamesViewModel
44import org.yuzu.yuzu_emu.model.HomeViewModel 49import org.yuzu.yuzu_emu.model.HomeViewModel
45import org.yuzu.yuzu_emu.utils.* 50import org.yuzu.yuzu_emu.utils.*
46import java.io.File
47import java.io.FilenameFilter
48import java.io.IOException
49 51
50class MainActivity : AppCompatActivity(), ThemeProvider { 52class MainActivity : AppCompatActivity(), ThemeProvider {
51 private lateinit var binding: ActivityMainBinding 53 private lateinit var binding: ActivityMainBinding
@@ -86,7 +88,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
86 ThemeHelper.SYSTEM_BAR_ALPHA 88 ThemeHelper.SYSTEM_BAR_ALPHA
87 ) 89 )
88 ) 90 )
89 if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) { 91 if (InsetsHelper.getSystemGestureType(applicationContext) !=
92 InsetsHelper.GESTURE_NAVIGATION
93 ) {
90 binding.navigationBarShade.setBackgroundColor( 94 binding.navigationBarShade.setBackgroundColor(
91 ThemeHelper.getColorWithOpacity( 95 ThemeHelper.getColorWithOpacity(
92 MaterialColors.getColor( 96 MaterialColors.getColor(
@@ -172,7 +176,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
172 binding.navigationView.height.toFloat() * 2 176 binding.navigationView.height.toFloat() * 2
173 translationY(0f) 177 translationY(0f)
174 } else { 178 } else {
175 if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) { 179 if (ViewCompat.getLayoutDirection(binding.navigationView) ==
180 ViewCompat.LAYOUT_DIRECTION_LTR
181 ) {
176 binding.navigationView.translationX = 182 binding.navigationView.translationX =
177 binding.navigationView.width.toFloat() * -2 183 binding.navigationView.width.toFloat() * -2
178 translationX(0f) 184 translationX(0f)
@@ -189,7 +195,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
189 if (smallLayout) { 195 if (smallLayout) {
190 translationY(binding.navigationView.height.toFloat() * 2) 196 translationY(binding.navigationView.height.toFloat() * 2)
191 } else { 197 } else {
192 if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) { 198 if (ViewCompat.getLayoutDirection(binding.navigationView) ==
199 ViewCompat.LAYOUT_DIRECTION_LTR
200 ) {
193 translationX(binding.navigationView.width.toFloat() * -2) 201 translationX(binding.navigationView.width.toFloat() * -2)
194 } else { 202 } else {
195 translationX(binding.navigationView.width.toFloat() * 2) 203 translationX(binding.navigationView.width.toFloat() * 2)
@@ -234,7 +242,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
234 } 242 }
235 243
236 private fun setInsets() = 244 private fun setInsets() =
237 ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat -> 245 ViewCompat.setOnApplyWindowInsetsListener(
246 binding.root
247 ) { _: View, windowInsets: WindowInsetsCompat ->
238 val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 248 val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
239 val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams 249 val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams
240 mlpStatusShade.height = insets.top 250 mlpStatusShade.height = insets.top
@@ -256,8 +266,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
256 266
257 val getGamesDirectory = 267 val getGamesDirectory =
258 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> 268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
259 if (result == null) 269 if (result == null) {
260 return@registerForActivityResult 270 return@registerForActivityResult
271 }
261 272
262 contentResolver.takePersistableUriPermission( 273 contentResolver.takePersistableUriPermission(
263 result, 274 result,
@@ -281,10 +292,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
281 292
282 val getProdKey = 293 val getProdKey =
283 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 294 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
284 if (result == null) 295 if (result == null) {
285 return@registerForActivityResult 296 return@registerForActivityResult
297 }
286 298
287 if (!FileUtil.hasExtension(result, "keys")) { 299 if (FileUtil.getExtension(result) != "keys") {
288 MessageDialogFragment.newInstance( 300 MessageDialogFragment.newInstance(
289 R.string.reading_keys_failure, 301 R.string.reading_keys_failure,
290 R.string.install_prod_keys_failure_extension_description 302 R.string.install_prod_keys_failure_extension_description
@@ -324,8 +336,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
324 336
325 val getFirmware = 337 val getFirmware =
326 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 338 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
327 if (result == null) 339 if (result == null) {
328 return@registerForActivityResult 340 return@registerForActivityResult
341 }
329 342
330 val inputZip = contentResolver.openInputStream(result) 343 val inputZip = contentResolver.openInputStream(result)
331 if (inputZip == null) { 344 if (inputZip == null) {
@@ -376,10 +389,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
376 389
377 val getAmiiboKey = 390 val getAmiiboKey =
378 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 391 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
379 if (result == null) 392 if (result == null) {
380 return@registerForActivityResult 393 return@registerForActivityResult
394 }
381 395
382 if (!FileUtil.hasExtension(result, "bin")) { 396 if (FileUtil.getExtension(result) != "bin") {
383 MessageDialogFragment.newInstance( 397 MessageDialogFragment.newInstance(
384 R.string.reading_keys_failure, 398 R.string.reading_keys_failure,
385 R.string.install_amiibo_keys_failure_extension_description 399 R.string.install_amiibo_keys_failure_extension_description
@@ -418,8 +432,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
418 432
419 val getDriver = 433 val getDriver =
420 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 434 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
421 if (result == null) 435 if (result == null) {
422 return@registerForActivityResult 436 return@registerForActivityResult
437 }
423 438
424 val takeFlags = 439 val takeFlags =
425 Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION 440 Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -467,4 +482,111 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
467 } 482 }
468 } 483 }
469 } 484 }
485
486 val installGameUpdate = registerForActivityResult(
487 ActivityResultContracts.OpenMultipleDocuments()
488 ) { documents: List<Uri> ->
489 if (documents.isNotEmpty()) {
490 IndeterminateProgressDialogFragment.newInstance(
491 this@MainActivity,
492 R.string.install_game_content
493 ) {
494 var installSuccess = 0
495 var installOverwrite = 0
496 var errorBaseGame = 0
497 var errorExtension = 0
498 var errorOther = 0
499 var errorTotal = 0
500 lifecycleScope.launch {
501 documents.forEach {
502 when (NativeLibrary.installFileToNand(it.toString())) {
503 NativeLibrary.InstallFileToNandResult.Success -> {
504 installSuccess += 1
505 }
506
507 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
508 installOverwrite += 1
509 }
510
511 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
512 errorBaseGame += 1
513 }
514
515 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
516 errorExtension += 1
517 }
518
519 else -> {
520 errorOther += 1
521 }
522 }
523 }
524 withContext(Dispatchers.Main) {
525 val separator = System.getProperty("line.separator") ?: "\n"
526 val installResult = StringBuilder()
527 if (installSuccess > 0) {
528 installResult.append(
529 getString(
530 R.string.install_game_content_success_install,
531 installSuccess
532 )
533 )
534 installResult.append(separator)
535 }
536 if (installOverwrite > 0) {
537 installResult.append(
538 getString(
539 R.string.install_game_content_success_overwrite,
540 installOverwrite
541 )
542 )
543 installResult.append(separator)
544 }
545 errorTotal = errorBaseGame + errorExtension + errorOther
546 if (errorTotal > 0) {
547 installResult.append(separator)
548 installResult.append(
549 getString(
550 R.string.install_game_content_failed_count,
551 errorTotal
552 )
553 )
554 installResult.append(separator)
555 if (errorBaseGame > 0) {
556 installResult.append(separator)
557 installResult.append(
558 getString(R.string.install_game_content_failure_base)
559 )
560 installResult.append(separator)
561 }
562 if (errorExtension > 0) {
563 installResult.append(separator)
564 installResult.append(
565 getString(R.string.install_game_content_failure_file_extension)
566 )
567 installResult.append(separator)
568 }
569 if (errorOther > 0) {
570 installResult.append(
571 getString(R.string.install_game_content_failure_description)
572 )
573 installResult.append(separator)
574 }
575 LongMessageDialogFragment.newInstance(
576 R.string.install_game_content_failure,
577 installResult.toString().trim(),
578 R.string.install_game_content_help_link
579 ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
580 } else {
581 LongMessageDialogFragment.newInstance(
582 R.string.install_game_content_success,
583 installResult.toString().trim()
584 ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
585 }
586 }
587 }
588 return@newInstance installSuccess + installOverwrite + errorTotal
589 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
590 }
591 }
470} 592}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
index 791cea904..eeefcdf20 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
@@ -19,7 +19,9 @@ class ControllerMappingHelper {
19 // The two analog triggers generate analog motion events as well as a keycode. 19 // The two analog triggers generate analog motion events as well as a keycode.
20 // We always prefer to use the analog values, so throw away the button press 20 // We always prefer to use the analog values, so throw away the button press
21 keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2 21 keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
22 } else false 22 } else {
23 false
24 }
23 } 25 }
24 26
25 /** 27 /**
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 36c479e6c..2ee63697e 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
@@ -4,8 +4,8 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.content.Context 6import android.content.Context
7import org.yuzu.yuzu_emu.NativeLibrary
8import java.io.IOException 7import java.io.IOException
8import org.yuzu.yuzu_emu.NativeLibrary
9 9
10object DirectoryInitialization { 10object DirectoryInitialization {
11 private var userPath: String? = null 11 private var userPath: String? = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
index cc8ea6b9d..cf226ad94 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
@@ -5,10 +5,10 @@ package org.yuzu.yuzu_emu.utils
5 5
6import android.net.Uri 6import android.net.Uri
7import androidx.documentfile.provider.DocumentFile 7import androidx.documentfile.provider.DocumentFile
8import org.yuzu.yuzu_emu.YuzuApplication
9import org.yuzu.yuzu_emu.model.MinimalDocumentFile
10import java.io.File 8import java.io.File
11import java.util.* 9import java.util.*
10import org.yuzu.yuzu_emu.YuzuApplication
11import org.yuzu.yuzu_emu.model.MinimalDocumentFile
12 12
13class DocumentsTree { 13class DocumentsTree {
14 private var root: DocumentsNode? = null 14 private var root: DocumentsNode? = null
@@ -29,13 +29,20 @@ class DocumentsTree {
29 val node = resolvePath(filepath) 29 val node = resolvePath(filepath)
30 return if (node == null || node.isDirectory) { 30 return if (node == null || node.isDirectory) {
31 0 31 0
32 } else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString()) 32 } else {
33 FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
34 }
33 } 35 }
34 36
35 fun exists(filepath: String): Boolean { 37 fun exists(filepath: String): Boolean {
36 return resolvePath(filepath) != null 38 return resolvePath(filepath) != null
37 } 39 }
38 40
41 fun isDirectory(filepath: String): Boolean {
42 val node = resolvePath(filepath)
43 return node != null && node.isDirectory
44 }
45
39 private fun resolvePath(filepath: String): DocumentsNode? { 46 private fun resolvePath(filepath: String): DocumentsNode? {
40 val tokens = StringTokenizer(filepath, File.separator, false) 47 val tokens = StringTokenizer(filepath, File.separator, false)
41 var iterator = root 48 var iterator = root
@@ -106,7 +113,9 @@ class DocumentsTree {
106 fun isNativePath(path: String): Boolean { 113 fun isNativePath(path: String): Boolean {
107 return if (path.isNotEmpty()) { 114 return if (path.isNotEmpty()) {
108 path[0] == '/' 115 path[0] == '/'
109 } else false 116 } else {
117 false
118 }
110 } 119 }
111 } 120 }
112} 121}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
index e1e7a59d7..7e8f058c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
@@ -11,14 +11,6 @@ object EmulationMenuSettings {
11 private val preferences = 11 private val preferences =
12 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 12 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
13 13
14 // These must match what is defined in src/core/settings.h
15 const val LayoutOption_Default = 0
16 const val LayoutOption_SingleScreen = 1
17 const val LayoutOption_LargeScreen = 2
18 const val LayoutOption_SideScreen = 3
19 const val LayoutOption_MobilePortrait = 4
20 const val LayoutOption_MobileLandscape = 5
21
22 var joystickRelCenter: Boolean 14 var joystickRelCenter: Boolean
23 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true) 15 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
24 set(value) { 16 set(value) {
@@ -41,16 +33,6 @@ object EmulationMenuSettings {
41 .apply() 33 .apply()
42 } 34 }
43 35
44 var landscapeScreenLayout: Int
45 get() = preferences.getInt(
46 Settings.PREF_MENU_SETTINGS_LANDSCAPE,
47 LayoutOption_MobileLandscape
48 )
49 set(value) {
50 preferences.edit()
51 .putInt(Settings.PREF_MENU_SETTINGS_LANDSCAPE, value)
52 .apply()
53 }
54 var showFps: Boolean 36 var showFps: Boolean
55 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false) 37 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
56 set(value) { 38 set(value) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 492b1ad91..142af5f26 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -7,10 +7,7 @@ import android.content.Context
7import android.database.Cursor 7import android.database.Cursor
8import android.net.Uri 8import android.net.Uri
9import android.provider.DocumentsContract 9import android.provider.DocumentsContract
10import android.provider.OpenableColumns
11import androidx.documentfile.provider.DocumentFile 10import androidx.documentfile.provider.DocumentFile
12import org.yuzu.yuzu_emu.YuzuApplication
13import org.yuzu.yuzu_emu.model.MinimalDocumentFile
14import java.io.BufferedInputStream 11import java.io.BufferedInputStream
15import java.io.File 12import java.io.File
16import java.io.FileOutputStream 13import java.io.FileOutputStream
@@ -19,6 +16,8 @@ import java.io.InputStream
19import java.net.URLDecoder 16import java.net.URLDecoder
20import java.util.zip.ZipEntry 17import java.util.zip.ZipEntry
21import java.util.zip.ZipInputStream 18import java.util.zip.ZipInputStream
19import org.yuzu.yuzu_emu.YuzuApplication
20import org.yuzu.yuzu_emu.model.MinimalDocumentFile
22 21
23object FileUtil { 22object FileUtil {
24 const val PATH_TREE = "tree" 23 const val PATH_TREE = "tree"
@@ -185,19 +184,18 @@ object FileUtil {
185 184
186 /** 185 /**
187 * Get file display name from given path 186 * Get file display name from given path
188 * @param path content uri path 187 * @param uri content uri
189 * @return String display name 188 * @return String display name
190 */ 189 */
191 fun getFilename(context: Context, path: String): String { 190 fun getFilename(uri: Uri): String {
192 val resolver = context.contentResolver 191 val resolver = YuzuApplication.appContext.contentResolver
193 val columns = arrayOf( 192 val columns = arrayOf(
194 DocumentsContract.Document.COLUMN_DISPLAY_NAME 193 DocumentsContract.Document.COLUMN_DISPLAY_NAME
195 ) 194 )
196 var filename = "" 195 var filename = ""
197 var c: Cursor? = null 196 var c: Cursor? = null
198 try { 197 try {
199 val mUri = Uri.parse(path) 198 c = resolver.query(uri, columns, null, null, null)
200 c = resolver.query(mUri, columns, null, null, null)
201 c!!.moveToNext() 199 c!!.moveToNext()
202 filename = c.getString(0) 200 filename = c.getString(0)
203 } catch (e: Exception) { 201 } catch (e: Exception) {
@@ -326,25 +324,9 @@ object FileUtil {
326 } 324 }
327 } 325 }
328 326
329 fun hasExtension(path: String, extension: String): Boolean = 327 fun getExtension(uri: Uri): String {
330 path.substring(path.lastIndexOf(".") + 1).contains(extension) 328 val fileName = getFilename(uri)
331 329 return fileName.substring(fileName.lastIndexOf(".") + 1)
332 fun hasExtension(uri: Uri, extension: String): Boolean { 330 .lowercase()
333 val fileName: String?
334 val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
335 val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
336 cursor?.moveToFirst()
337
338 if (nameIndex == null) {
339 return false
340 }
341
342 fileName = cursor.getString(nameIndex)
343 cursor.close()
344
345 if (fileName == null) {
346 return false
347 }
348 return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
349 } 331 }
350} 332}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
index dc9b7c744..086d17606 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
@@ -54,7 +54,7 @@ class ForegroundService : Service() {
54 54
55 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 55 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
56 if (intent == null) { 56 if (intent == null) {
57 return START_NOT_STICKY; 57 return START_NOT_STICKY
58 } 58 }
59 if (intent.action == ACTION_STOP) { 59 if (intent.action == ACTION_STOP) {
60 NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION) 60 NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index 42b207618..f8e7eeca7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -11,7 +11,6 @@ import kotlinx.serialization.json.Json
11import org.yuzu.yuzu_emu.NativeLibrary 11import org.yuzu.yuzu_emu.NativeLibrary
12import org.yuzu.yuzu_emu.YuzuApplication 12import org.yuzu.yuzu_emu.YuzuApplication
13import org.yuzu.yuzu_emu.model.Game 13import org.yuzu.yuzu_emu.model.Game
14import java.util.*
15 14
16object GameHelper { 15object GameHelper {
17 const val KEY_GAME_PATH = "game_path" 16 const val KEY_GAME_PATH = "game_path"
@@ -33,15 +32,9 @@ object GameHelper {
33 val children = FileUtil.listFiles(context, gamesUri) 32 val children = FileUtil.listFiles(context, gamesUri)
34 for (file in children) { 33 for (file in children) {
35 if (!file.isDirectory) { 34 if (!file.isDirectory) {
36 val filename = file.uri.toString() 35 // Check that the file has an extension we care about before trying to read out of it.
37 val extensionStart = filename.lastIndexOf('.') 36 if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
38 if (extensionStart > 0) { 37 games.add(getGame(file.uri))
39 val fileExtension = filename.substring(extensionStart)
40
41 // Check that the file has an extension we care about before trying to read out of it.
42 if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
43 games.add(getGame(filename))
44 }
45 } 38 }
46 } 39 }
47 } 40 }
@@ -59,21 +52,19 @@ object GameHelper {
59 return games.toList() 52 return games.toList()
60 } 53 }
61 54
62 private fun getGame(filePath: String): Game { 55 private fun getGame(uri: Uri): Game {
56 val filePath = uri.toString()
63 var name = NativeLibrary.getTitle(filePath) 57 var name = NativeLibrary.getTitle(filePath)
64 58
65 // If the game's title field is empty, use the filename. 59 // If the game's title field is empty, use the filename.
66 if (name.isEmpty()) { 60 if (name.isEmpty()) {
67 name = filePath.substring(filePath.lastIndexOf("/") + 1) 61 name = FileUtil.getFilename(uri)
68 } 62 }
69 var gameId = NativeLibrary.getGameId(filePath) 63 var gameId = NativeLibrary.getGameId(filePath)
70 64
71 // If the game's ID field is empty, use the filename without extension. 65 // If the game's ID field is empty, use the filename without extension.
72 if (gameId.isEmpty()) { 66 if (gameId.isEmpty()) {
73 gameId = filePath.substring( 67 gameId = name.substring(0, name.lastIndexOf("."))
74 filePath.lastIndexOf("/") + 1,
75 filePath.lastIndexOf(".")
76 )
77 } 68 }
78 69
79 val newGame = Game( 70 val newGame = Game(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index 528011d7f..1d4695a2a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -5,14 +5,14 @@ package org.yuzu.yuzu_emu.utils
5 5
6import android.content.Context 6import android.content.Context
7import android.net.Uri 7import android.net.Uri
8import org.yuzu.yuzu_emu.NativeLibrary
9import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
10import java.io.BufferedInputStream 8import java.io.BufferedInputStream
11import java.io.File 9import java.io.File
12import java.io.FileInputStream 10import java.io.FileInputStream
13import java.io.FileOutputStream 11import java.io.FileOutputStream
14import java.io.IOException 12import java.io.IOException
15import java.util.zip.ZipInputStream 13import java.util.zip.ZipInputStream
14import org.yuzu.yuzu_emu.NativeLibrary
15import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
16 16
17object GpuDriverHelper { 17object GpuDriverHelper {
18 private const val META_JSON_FILENAME = "meta.json" 18 private const val META_JSON_FILENAME = "meta.json"
@@ -113,6 +113,8 @@ object GpuDriverHelper {
113 initializeDriverParameters(context) 113 initializeDriverParameters(context)
114 } 114 }
115 115
116 external fun supportsCustomDriverLoading(): Boolean
117
116 // Parse the custom driver metadata to retrieve the name. 118 // Parse the custom driver metadata to retrieve the name.
117 val customDriverName: String? 119 val customDriverName: String?
118 get() { 120 get() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
index 70bdb4097..a4e64070a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
@@ -3,12 +3,12 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import org.json.JSONException
7import org.json.JSONObject
8import java.io.IOException 6import java.io.IOException
9import java.nio.charset.StandardCharsets 7import java.nio.charset.StandardCharsets
10import java.nio.file.Files 8import java.nio.file.Files
11import java.nio.file.Paths 9import java.nio.file.Paths
10import org.json.JSONException
11import org.json.JSONObject
12 12
13class GpuDriverMetadata(metadataFilePath: String) { 13class GpuDriverMetadata(metadataFilePath: String) {
14 var name: String? = null 14 var name: String? = null
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 24e999b29..e963dfbc1 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
@@ -5,8 +5,8 @@ package org.yuzu.yuzu_emu.utils
5 5
6import android.view.KeyEvent 6import android.view.KeyEvent
7import android.view.MotionEvent 7import android.view.MotionEvent
8import org.yuzu.yuzu_emu.NativeLibrary
9import kotlin.math.sqrt 8import kotlin.math.sqrt
9import org.yuzu.yuzu_emu.NativeLibrary
10 10
11class InputHandler { 11class InputHandler {
12 fun initialize() { 12 fun initialize() {
@@ -68,7 +68,11 @@ class InputHandler {
68 6 -> NativeLibrary.Player6Device 68 6 -> NativeLibrary.Player6Device
69 7 -> NativeLibrary.Player7Device 69 7 -> NativeLibrary.Player7Device
70 8 -> NativeLibrary.Player8Device 70 8 -> NativeLibrary.Player8Device
71 else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device 71 else -> if (NativeLibrary.isHandheldOnly()) {
72 NativeLibrary.ConsoleDevice
73 } else {
74 NativeLibrary.Player1Device
75 }
72 } 76 }
73 } 77 }
74 78
@@ -107,7 +111,11 @@ class InputHandler {
107 } 111 }
108 112
109 private fun getAxisToButton(axis: Float): Int { 113 private fun getAxisToButton(axis: Float): Int {
110 return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED 114 return if (axis > 0.5f) {
115 NativeLibrary.ButtonState.PRESSED
116 } else {
117 NativeLibrary.ButtonState.RELEASED
118 }
111 } 119 }
112 120
113 private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) { 121 private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
@@ -287,7 +295,6 @@ class InputHandler {
287 } 295 }
288 } 296 }
289 297
290
291 private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { 298 private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
292 // Joycon support is half dead. Right joystick doesn't work 299 // Joycon support is half dead. Right joystick doesn't work
293 val playerNumber = getPlayerNumber(event.device.controllerNumber) 300 val playerNumber = getPlayerNumber(event.device.controllerNumber)
@@ -355,6 +362,4 @@ class InputHandler {
355 ) 362 )
356 } 363 }
357 } 364 }
358 365}
359
360} \ No newline at end of file
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
index 19c53c481..595f0d284 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
@@ -4,9 +4,7 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.annotation.SuppressLint 6import android.annotation.SuppressLint
7import android.app.Activity
8import android.content.Context 7import android.content.Context
9import android.graphics.Rect
10 8
11object InsetsHelper { 9object InsetsHelper {
12 const val THREE_BUTTON_NAVIGATION = 0 10 const val THREE_BUTTON_NAVIGATION = 0
@@ -20,12 +18,8 @@ object InsetsHelper {
20 resources.getIdentifier("config_navBarInteractionMode", "integer", "android") 18 resources.getIdentifier("config_navBarInteractionMode", "integer", "android")
21 return if (resourceId != 0) { 19 return if (resourceId != 0) {
22 resources.getInteger(resourceId) 20 resources.getInteger(resourceId)
23 } else 0 21 } else {
24 } 22 0
25 23 }
26 fun getBottomPaddingRequired(activity: Activity): Int {
27 val visibleFrame = Rect()
28 activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
29 return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
30 } 24 }
31} 25}
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
new file mode 100644
index 000000000..18e5fa0b0
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.app.ActivityManager
7import android.content.Context
8import org.yuzu.yuzu_emu.R
9import java.util.Locale
10
11class MemoryUtil(val context: Context) {
12
13 private val Long.floatForm: String
14 get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
15
16 private fun bytesToSizeUnit(size: Long): String {
17 return when {
18 size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
19 size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
20 size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
21 size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
22 size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
23 size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
24 else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
25 }
26 }
27
28 private val totalMemory =
29 with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
30 val memInfo = ActivityManager.MemoryInfo()
31 getMemoryInfo(memInfo)
32 memInfo.totalMem
33 }
34
35 fun isLessThan(minimum: Int, size: Long): Boolean {
36 return when (size) {
37 Kb -> totalMemory < Mb && totalMemory < minimum
38 Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
39 Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
40 Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
41 Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
42 Eb -> totalMemory / Eb < minimum
43 else -> totalMemory < Kb && totalMemory < minimum
44 }
45 }
46
47 fun getDeviceRAM(): String {
48 return bytesToSizeUnit(totalMemory)
49 }
50
51 companion object {
52 const val Kb: Long = 1024
53 const val Mb = Kb * 1024
54 const val Gb = Mb * 1024
55 const val Tb = Gb * 1024
56 const val Pb = Tb * 1024
57 const val Eb = Pb * 1024
58 }
59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
index 344dd8a0a..68ed66565 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
@@ -13,8 +13,8 @@ import android.nfc.tech.NfcA
13import android.os.Build 13import android.os.Build
14import android.os.Handler 14import android.os.Handler
15import android.os.Looper 15import android.os.Looper
16import org.yuzu.yuzu_emu.NativeLibrary
17import java.io.IOException 16import java.io.IOException
17import org.yuzu.yuzu_emu.NativeLibrary
18 18
19class NfcReader(private val activity: Activity) { 19class NfcReader(private val activity: Activity) {
20 private var nfcAdapter: NfcAdapter? = null 20 private var nfcAdapter: NfcAdapter? = null
@@ -25,10 +25,13 @@ class NfcReader(private val activity: Activity) {
25 25
26 pendingIntent = PendingIntent.getActivity( 26 pendingIntent = PendingIntent.getActivity(
27 activity, 27 activity,
28 0, Intent(activity, activity.javaClass), 28 0,
29 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) 29 Intent(activity, activity.javaClass),
30 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
30 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE 31 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
31 else PendingIntent.FLAG_UPDATE_CURRENT 32 } else {
33 PendingIntent.FLAG_UPDATE_CURRENT
34 }
32 ) 35 )
33 36
34 val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) 37 val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
@@ -45,9 +48,9 @@ class NfcReader(private val activity: Activity) {
45 48
46 fun onNewIntent(intent: Intent) { 49 fun onNewIntent(intent: Intent) {
47 val action = intent.action 50 val action = intent.action
48 if (NfcAdapter.ACTION_TAG_DISCOVERED != action 51 if (NfcAdapter.ACTION_TAG_DISCOVERED != action &&
49 && NfcAdapter.ACTION_TECH_DISCOVERED != action 52 NfcAdapter.ACTION_TECH_DISCOVERED != action &&
50 && NfcAdapter.ACTION_NDEF_DISCOVERED != action 53 NfcAdapter.ACTION_NDEF_DISCOVERED != action
51 ) { 54 ) {
52 return 55 return
53 } 56 }
@@ -84,7 +87,7 @@ class NfcReader(private val activity: Activity) {
84 } 87 }
85 88
86 private fun ntag215ReadAll(amiibo: NfcA): ByteArray? { 89 private fun ntag215ReadAll(amiibo: NfcA): ByteArray? {
87 val bufferSize = amiibo.maxTransceiveLength; 90 val bufferSize = amiibo.maxTransceiveLength
88 val tagSize = 0x21C 91 val tagSize = 0x21C
89 val pageSize = 4 92 val pageSize = 4
90 val lastPage = tagSize / pageSize - 1 93 val lastPage = tagSize / pageSize - 1
@@ -103,7 +106,7 @@ class NfcReader(private val activity: Activity) {
103 val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1) 106 val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1)
104 System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize) 107 System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize)
105 } catch (e: IOException) { 108 } catch (e: IOException) {
106 return null; 109 return null
107 } 110 }
108 } 111 }
109 return tagData 112 return tagData
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
index 87ee7f2e6..00e58faec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
@@ -11,30 +11,34 @@ import java.io.Serializable
11 11
12object SerializableHelper { 12object SerializableHelper {
13 inline fun <reified T : Serializable> Bundle.serializable(key: String): T? { 13 inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
14 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) 14 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
15 getSerializable(key, T::class.java) 15 getSerializable(key, T::class.java)
16 else 16 } else {
17 getSerializable(key) as? T 17 getSerializable(key) as? T
18 }
18 } 19 }
19 20
20 inline fun <reified T : Serializable> Intent.serializable(key: String): T? { 21 inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
21 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) 22 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
22 getSerializableExtra(key, T::class.java) 23 getSerializableExtra(key, T::class.java)
23 else 24 } else {
24 getSerializableExtra(key) as? T 25 getSerializableExtra(key) as? T
26 }
25 } 27 }
26 28
27 inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? { 29 inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
28 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) 30 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
29 getParcelable(key, T::class.java) 31 getParcelable(key, T::class.java)
30 else 32 } else {
31 getParcelable(key) as? T 33 getParcelable(key) as? T
34 }
32 } 35 }
33 36
34 inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? { 37 inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
35 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) 38 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
36 getParcelableExtra(key, T::class.java) 39 getParcelableExtra(key, T::class.java)
37 else 40 } else {
38 getParcelableExtra(key) as? T 41 getParcelableExtra(key) as? T
42 }
39 } 43 }
40} 44}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index e55767c0f..f312e24cf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -3,21 +3,19 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.app.Activity
7import android.content.res.Configuration 6import android.content.res.Configuration
8import android.graphics.Color 7import android.graphics.Color
9import androidx.annotation.ColorInt 8import androidx.annotation.ColorInt
10import androidx.appcompat.app.AppCompatActivity 9import androidx.appcompat.app.AppCompatActivity
11import androidx.appcompat.app.AppCompatDelegate 10import androidx.appcompat.app.AppCompatDelegate
12import androidx.core.content.ContextCompat
13import androidx.core.view.WindowCompat 11import androidx.core.view.WindowCompat
14import androidx.core.view.WindowInsetsControllerCompat 12import androidx.core.view.WindowInsetsControllerCompat
15import androidx.preference.PreferenceManager 13import androidx.preference.PreferenceManager
14import kotlin.math.roundToInt
16import org.yuzu.yuzu_emu.R 15import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.YuzuApplication 16import org.yuzu.yuzu_emu.YuzuApplication
18import org.yuzu.yuzu_emu.features.settings.model.Settings 17import org.yuzu.yuzu_emu.features.settings.model.Settings
19import org.yuzu.yuzu_emu.ui.main.ThemeProvider 18import org.yuzu.yuzu_emu.ui.main.ThemeProvider
20import kotlin.math.roundToInt
21 19
22object ThemeHelper { 20object ThemeHelper {
23 const val SYSTEM_BAR_ALPHA = 0.9f 21 const val SYSTEM_BAR_ALPHA = 0.9f
@@ -36,8 +34,8 @@ object ThemeHelper {
36 // Using a specific night mode check because this could apply incorrectly when using the 34 // Using a specific night mode check because this could apply incorrectly when using the
37 // light app mode, dark system mode, and black backgrounds. Launching the settings activity 35 // light app mode, dark system mode, and black backgrounds. Launching the settings activity
38 // will then show light mode colors/navigation bars but with black backgrounds. 36 // will then show light mode colors/navigation bars but with black backgrounds.
39 if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 37 if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
40 && isNightMode(activity) 38 isNightMode(activity)
41 ) { 39 ) {
42 activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark) 40 activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
43 } 41 }
@@ -46,8 +44,10 @@ object ThemeHelper {
46 @ColorInt 44 @ColorInt
47 fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int { 45 fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int {
48 return Color.argb( 46 return Color.argb(
49 (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color), 47 (alphaFactor * Color.alpha(color)).roundToInt(),
50 Color.green(color), Color.blue(color) 48 Color.red(color),
49 Color.green(color),
50 Color.blue(color)
51 ) 51 )
52 } 52 }
53 53
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
index c8ef8c1fd..685ccaa76 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
@@ -38,9 +38,11 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
38 newWidth = width 38 newWidth = width
39 newHeight = (width / aspectRatio).roundToInt() 39 newHeight = (width / aspectRatio).roundToInt()
40 } 40 }
41 setMeasuredDimension(newWidth, newHeight) 41 val left = (width - newWidth) / 2
42 val top = (height - newHeight) / 2
43 setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
42 } else { 44 } else {
43 setMeasuredDimension(width, height) 45 setLeftTopRightBottom(0, 0, width, height)
44 } 46 }
45 } 47 }
46} 48}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 041781577..e2ed08e9f 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -14,7 +14,6 @@ add_library(yuzu-android SHARED
14 id_cache.cpp 14 id_cache.cpp
15 id_cache.h 15 id_cache.h
16 native.cpp 16 native.cpp
17 native.h
18) 17)
19 18
20set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 19set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 2d622a048..43e8aa72a 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -235,9 +235,13 @@ void Config::ReadValues() {
235 Settings::values.async_presentation = 235 Settings::values.async_presentation =
236 config->GetBoolean("Renderer", "async_presentation", true); 236 config->GetBoolean("Renderer", "async_presentation", true);
237 237
238 // Enable force_max_clock by default on Android 238 // Disable force_max_clock by default on Android
239 Settings::values.renderer_force_max_clock = 239 Settings::values.renderer_force_max_clock =
240 config->GetBoolean("Renderer", "force_max_clock", true); 240 config->GetBoolean("Renderer", "force_max_clock", false);
241
242 // Disable use_reactive_flushing by default on Android
243 Settings::values.use_reactive_flushing =
244 config->GetBoolean("Renderer", "use_reactive_flushing", false);
241 245
242 // Audio 246 // Audio
243 ReadSetting("Audio", Settings::values.sink_id); 247 ReadSetting("Audio", Settings::values.sink_id);
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
index c5dfaff54..d81422a74 100644
--- a/src/android/app/src/main/jni/default_ini.h
+++ b/src/android/app/src/main/jni/default_ini.h
@@ -251,7 +251,7 @@ backend =
251# 0: Off, 1 (default): On 251# 0: Off, 1 (default): On
252async_presentation = 252async_presentation =
253 253
254# Enable graphics API debugging mode. 254# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
255# 0 (default): Disabled, 1: Enabled 255# 0 (default): Disabled, 1: Enabled
256force_max_clock = 256force_max_clock =
257 257
@@ -328,6 +328,10 @@ shader_backend =
328# 0 (default): Off, 1: On 328# 0 (default): Off, 1: On
329use_asynchronous_shaders = 329use_asynchronous_shaders =
330 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
331# NVDEC emulation. 335# NVDEC emulation.
332# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding 336# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
333nvdec_emulation = 337nvdec_emulation =
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 7ebed5e6a..8bc6a4a04 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -14,6 +14,7 @@
14#include <android/api-level.h> 14#include <android/api-level.h>
15#include <android/native_window_jni.h> 15#include <android/native_window_jni.h>
16#include <core/loader/nro.h> 16#include <core/loader/nro.h>
17#include <jni.h>
17 18
18#include "common/detached_tasks.h" 19#include "common/detached_tasks.h"
19#include "common/dynamic_library.h" 20#include "common/dynamic_library.h"
@@ -28,7 +29,10 @@
28#include "core/core.h" 29#include "core/core.h"
29#include "core/cpu_manager.h" 30#include "core/cpu_manager.h"
30#include "core/crypto/key_manager.h" 31#include "core/crypto/key_manager.h"
32#include "core/file_sys/card_image.h"
31#include "core/file_sys/registered_cache.h" 33#include "core/file_sys/registered_cache.h"
34#include "core/file_sys/submission_package.h"
35#include "core/file_sys/vfs.h"
32#include "core/file_sys/vfs_real.h" 36#include "core/file_sys/vfs_real.h"
33#include "core/frontend/applets/cabinet.h" 37#include "core/frontend/applets/cabinet.h"
34#include "core/frontend/applets/controller.h" 38#include "core/frontend/applets/controller.h"
@@ -56,6 +60,9 @@
56#include "video_core/rasterizer_interface.h" 60#include "video_core/rasterizer_interface.h"
57#include "video_core/renderer_base.h" 61#include "video_core/renderer_base.h"
58 62
63#define jconst [[maybe_unused]] const auto
64#define jauto [[maybe_unused]] auto
65
59namespace { 66namespace {
60 67
61class EmulationSession final { 68class EmulationSession final {
@@ -94,6 +101,74 @@ public:
94 m_native_window = native_window; 101 m_native_window = native_window;
95 } 102 }
96 103
104 int InstallFileToNand(std::string filename) {
105 jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
106 std::size_t block_size) {
107 if (src == nullptr || dest == nullptr) {
108 return false;
109 }
110 if (!dest->Resize(src->GetSize())) {
111 return false;
112 }
113
114 using namespace Common::Literals;
115 [[maybe_unused]] std::vector<u8> buffer(1_MiB);
116
117 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
118 jconst read = src->Read(buffer.data(), buffer.size(), i);
119 dest->Write(buffer.data(), read, i);
120 }
121 return true;
122 };
123
124 enum InstallResult {
125 Success = 0,
126 SuccessFileOverwritten = 1,
127 InstallError = 2,
128 ErrorBaseGame = 3,
129 ErrorFilenameExtension = 4,
130 };
131
132 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
133 m_system.GetFileSystemController().CreateFactories(*m_vfs);
134
135 [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
136 if (filename.ends_with("nsp")) {
137 nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
138 if (nsp->IsExtractedType()) {
139 return InstallError;
140 }
141 } else if (filename.ends_with("xci")) {
142 jconst xci =
143 std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
144 nsp = xci->GetSecurePartitionNSP();
145 } else {
146 return ErrorFilenameExtension;
147 }
148
149 if (!nsp) {
150 return InstallError;
151 }
152
153 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
154 return InstallError;
155 }
156
157 jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
158 *nsp, true, copy_func);
159
160 switch (res) {
161 case FileSys::InstallResult::Success:
162 return Success;
163 case FileSys::InstallResult::OverwriteExisting:
164 return SuccessFileOverwritten;
165 case FileSys::InstallResult::ErrorBaseInstall:
166 return ErrorBaseGame;
167 default:
168 return InstallError;
169 }
170 }
171
97 void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, 172 void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
98 const std::string& custom_driver_name, 173 const std::string& custom_driver_name,
99 const std::string& file_redirect_dir) { 174 const std::string& file_redirect_dir) {
@@ -131,6 +206,11 @@ public:
131 return m_is_running; 206 return m_is_running;
132 } 207 }
133 208
209 bool IsPaused() const {
210 std::scoped_lock lock(m_mutex);
211 return m_is_running && m_is_paused;
212 }
213
134 const Core::PerfStatsResults& PerfStats() const { 214 const Core::PerfStatsResults& PerfStats() const {
135 std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); 215 std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
136 return m_perf_stats; 216 return m_perf_stats;
@@ -154,14 +234,15 @@ public:
154 m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, 234 m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
155 m_vulkan_library); 235 m_vulkan_library);
156 236
237 m_system.SetFilesystem(m_vfs);
238
157 // Initialize system. 239 // Initialize system.
158 auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); 240 jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
159 m_software_keyboard = android_keyboard.get(); 241 m_software_keyboard = android_keyboard.get();
160 m_system.SetShuttingDown(false); 242 m_system.SetShuttingDown(false);
161 m_system.ApplySettings(); 243 m_system.ApplySettings();
244 Settings::LogSettings();
162 m_system.HIDCore().ReloadInputDevices(); 245 m_system.HIDCore().ReloadInputDevices();
163 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
164 m_system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
165 m_system.SetAppletFrontendSet({ 246 m_system.SetAppletFrontendSet({
166 nullptr, // Amiibo Settings 247 nullptr, // Amiibo Settings
167 nullptr, // Controller Selector 248 nullptr, // Controller Selector
@@ -173,7 +254,8 @@ public:
173 std::move(android_keyboard), // Software Keyboard 254 std::move(android_keyboard), // Software Keyboard
174 nullptr, // Web Browser 255 nullptr, // Web Browser
175 }); 256 });
176 m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); 257 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
258 m_system.GetFileSystemController().CreateFactories(*m_vfs);
177 259
178 // Initialize account manager 260 // Initialize account manager
179 m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); 261 m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
@@ -215,11 +297,13 @@ public:
215 void PauseEmulation() { 297 void PauseEmulation() {
216 std::scoped_lock lock(m_mutex); 298 std::scoped_lock lock(m_mutex);
217 m_system.Pause(); 299 m_system.Pause();
300 m_is_paused = true;
218 } 301 }
219 302
220 void UnPauseEmulation() { 303 void UnPauseEmulation() {
221 std::scoped_lock lock(m_mutex); 304 std::scoped_lock lock(m_mutex);
222 m_system.Run(); 305 m_system.Run();
306 m_is_paused = false;
223 } 307 }
224 308
225 void HaltEmulation() { 309 void HaltEmulation() {
@@ -251,7 +335,7 @@ public:
251 335
252 while (true) { 336 while (true) {
253 { 337 {
254 std::unique_lock lock(m_mutex); 338 [[maybe_unused]] std::unique_lock lock(m_mutex);
255 if (m_cv.wait_for(lock, std::chrono::milliseconds(800), 339 if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
256 [&]() { return !m_is_running; })) { 340 [&]() { return !m_is_running; })) {
257 // Emulation halted. 341 // Emulation halted.
@@ -283,7 +367,7 @@ public:
283 } 367 }
284 368
285 bool IsHandheldOnly() { 369 bool IsHandheldOnly() {
286 const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); 370 jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
287 371
288 if (npad_style_set.fullkey == 1) { 372 if (npad_style_set.fullkey == 1) {
289 return false; 373 return false;
@@ -296,17 +380,17 @@ public:
296 return !Settings::values.use_docked_mode.GetValue(); 380 return !Settings::values.use_docked_mode.GetValue();
297 } 381 }
298 382
299 void SetDeviceType(int index, int type) { 383 void SetDeviceType([[maybe_unused]] int index, int type) {
300 auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); 384 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
301 controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); 385 controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
302 } 386 }
303 387
304 void OnGamepadConnectEvent(int index) { 388 void OnGamepadConnectEvent([[maybe_unused]] int index) {
305 auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); 389 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
306 390
307 // Ensure that player1 is configured correctly and handheld disconnected 391 // Ensure that player1 is configured correctly and handheld disconnected
308 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { 392 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
309 auto handheld = 393 jauto handheld =
310 m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); 394 m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
311 395
312 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { 396 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
@@ -318,7 +402,8 @@ public:
318 402
319 // Ensure that handheld is configured correctly and player 1 disconnected 403 // Ensure that handheld is configured correctly and player 1 disconnected
320 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { 404 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
321 auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 405 jauto player1 =
406 m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
322 407
323 if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { 408 if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
324 player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 409 player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
@@ -332,8 +417,8 @@ public:
332 } 417 }
333 } 418 }
334 419
335 void OnGamepadDisconnectEvent(int index) { 420 void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
336 auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); 421 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
337 controller->Disconnect(); 422 controller->Disconnect();
338 } 423 }
339 424
@@ -349,7 +434,7 @@ private:
349 }; 434 };
350 435
351 RomMetadata GetRomMetadata(const std::string& path) { 436 RomMetadata GetRomMetadata(const std::string& path) {
352 if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { 437 if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
353 return search->second; 438 return search->second;
354 } 439 }
355 440
@@ -357,14 +442,14 @@ private:
357 } 442 }
358 443
359 RomMetadata CacheRomMetadata(const std::string& path) { 444 RomMetadata CacheRomMetadata(const std::string& path) {
360 const auto file = Core::GetGameFileFromPath(m_vfs, path); 445 jconst file = Core::GetGameFileFromPath(m_vfs, path);
361 auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); 446 jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
362 447
363 RomMetadata entry; 448 RomMetadata entry;
364 loader->ReadTitle(entry.title); 449 loader->ReadTitle(entry.title);
365 loader->ReadIcon(entry.icon); 450 loader->ReadIcon(entry.icon);
366 if (loader->GetFileType() == Loader::FileType::NRO) { 451 if (loader->GetFileType() == Loader::FileType::NRO) {
367 auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); 452 jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
368 entry.isHomebrew = loader_nro->IsHomebrew(); 453 entry.isHomebrew = loader_nro->IsHomebrew();
369 } else { 454 } else {
370 entry.isHomebrew = false; 455 entry.isHomebrew = false;
@@ -398,9 +483,10 @@ private:
398 InputCommon::InputSubsystem m_input_subsystem; 483 InputCommon::InputSubsystem m_input_subsystem;
399 Common::DetachedTasks m_detached_tasks; 484 Common::DetachedTasks m_detached_tasks;
400 Core::PerfStatsResults m_perf_stats{}; 485 Core::PerfStatsResults m_perf_stats{};
401 std::shared_ptr<FileSys::RealVfsFilesystem> m_vfs; 486 std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
402 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; 487 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
403 bool m_is_running{}; 488 bool m_is_running{};
489 bool m_is_paused{};
404 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 490 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
405 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; 491 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
406 492
@@ -434,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
434 520
435 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); 521 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
436 522
437 const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); 523 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
438 if (result != Core::SystemResultStatus::Success) { 524 if (result != Core::SystemResultStatus::Success) {
439 return result; 525 return result;
440 } 526 }
@@ -446,72 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
446 532
447extern "C" { 533extern "C" {
448 534
449void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, 535void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
450 [[maybe_unused]] jclass clazz, 536 [[maybe_unused]] jobject surf) {
451 jobject surf) {
452 EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); 537 EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
453 EmulationSession::GetInstance().SurfaceChanged(); 538 EmulationSession::GetInstance().SurfaceChanged();
454} 539}
455 540
456void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, 541void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
457 [[maybe_unused]] jclass clazz) {
458 ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); 542 ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
459 EmulationSession::GetInstance().SetNativeWindow(nullptr); 543 EmulationSession::GetInstance().SetNativeWindow(nullptr);
460 EmulationSession::GetInstance().SurfaceChanged(); 544 EmulationSession::GetInstance().SurfaceChanged();
461} 545}
462 546
463void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, 547void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
464 [[maybe_unused]] jclass clazz, 548 [[maybe_unused]] jstring j_directory) {
465 jstring j_directory) {
466 Common::FS::SetAppDirectory(GetJString(env, j_directory)); 549 Common::FS::SetAppDirectory(GetJString(env, j_directory));
467} 550}
468 551
469void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( 552int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
470 JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, 553 [[maybe_unused]] jstring j_file) {
471 jstring custom_driver_name, jstring file_redirect_dir) { 554 return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
555}
556
557void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
558 jstring hook_lib_dir,
559 jstring custom_driver_dir,
560 jstring custom_driver_name,
561 jstring file_redirect_dir) {
472 EmulationSession::GetInstance().InitializeGpuDriver( 562 EmulationSession::GetInstance().InitializeGpuDriver(
473 GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), 563 GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
474 GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); 564 GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
475} 565}
476 566
477jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, 567[[maybe_unused]] static bool CheckKgslPresent() {
478 [[maybe_unused]] jclass clazz) { 568 constexpr auto KgslPath{"/dev/kgsl-3d0"};
569
570 return access(KgslPath, F_OK) == 0;
571}
572
573[[maybe_unused]] bool SupportsCustomDriver() {
574 return android_get_device_api_level() >= 28 && CheckKgslPresent();
575}
576
577jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
578 JNIEnv* env, jobject instance) {
579#ifdef ARCHITECTURE_arm64
580 // If the KGSL device exists custom drivers can be loaded using adrenotools
581 return SupportsCustomDriver();
582#else
583 return false;
584#endif
585}
586
587jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
479 Core::Crypto::KeyManager::Instance().ReloadKeys(); 588 Core::Crypto::KeyManager::Instance().ReloadKeys();
480 return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded()); 589 return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
481} 590}
482 591
483void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, 592void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) {
484 [[maybe_unused]] jclass clazz) {
485 EmulationSession::GetInstance().UnPauseEmulation(); 593 EmulationSession::GetInstance().UnPauseEmulation();
486} 594}
487 595
488void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, 596void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) {
489 [[maybe_unused]] jclass clazz) {
490 EmulationSession::GetInstance().PauseEmulation(); 597 EmulationSession::GetInstance().PauseEmulation();
491} 598}
492 599
493void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, 600void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) {
494 [[maybe_unused]] jclass clazz) {
495 EmulationSession::GetInstance().HaltEmulation(); 601 EmulationSession::GetInstance().HaltEmulation();
496} 602}
497 603
498void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env, 604void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
499 [[maybe_unused]] jclass clazz) {
500 EmulationSession::GetInstance().ResetRomMetadata(); 605 EmulationSession::GetInstance().ResetRomMetadata();
501} 606}
502 607
503jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, 608jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
504 [[maybe_unused]] jclass clazz) {
505 return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); 609 return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
506} 610}
507 611
508jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, 612jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) {
509 [[maybe_unused]] jclass clazz) { 613 return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
614}
615
616void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
617 Settings::values.audio_muted = true;
618}
619
620void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
621 Settings::values.audio_muted = false;
622}
623
624jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
625 return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
626}
627
628jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
510 return EmulationSession::GetInstance().IsHandheldOnly(); 629 return EmulationSession::GetInstance().IsHandheldOnly();
511} 630}
512 631
513jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, 632jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
514 [[maybe_unused]] jclass clazz,
515 jint j_device, jint j_type) { 633 jint j_device, jint j_type) {
516 if (EmulationSession::GetInstance().IsRunning()) { 634 if (EmulationSession::GetInstance().IsRunning()) {
517 EmulationSession::GetInstance().SetDeviceType(j_device, j_type); 635 EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
@@ -519,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN
519 return static_cast<jboolean>(true); 637 return static_cast<jboolean>(true);
520} 638}
521 639
522jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, 640jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
523 [[maybe_unused]] jclass clazz,
524 jint j_device) { 641 jint j_device) {
525 if (EmulationSession::GetInstance().IsRunning()) { 642 if (EmulationSession::GetInstance().IsRunning()) {
526 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); 643 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -528,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu
528 return static_cast<jboolean>(true); 645 return static_cast<jboolean>(true);
529} 646}
530 647
531jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( 648jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
532 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { 649 jint j_device) {
533 if (EmulationSession::GetInstance().IsRunning()) { 650 if (EmulationSession::GetInstance().IsRunning()) {
534 EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); 651 EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
535 } 652 }
536 return static_cast<jboolean>(true); 653 return static_cast<jboolean>(true);
537} 654}
538jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, 655jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
539 [[maybe_unused]] jclass clazz, 656 jint j_device, jint j_button,
540 [[maybe_unused]] jint j_device, 657 jint action) {
541 jint j_button, jint action) {
542 if (EmulationSession::GetInstance().IsRunning()) { 658 if (EmulationSession::GetInstance().IsRunning()) {
543 // Ensure gamepad is connected 659 // Ensure gamepad is connected
544 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); 660 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -548,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus
548 return static_cast<jboolean>(true); 664 return static_cast<jboolean>(true);
549} 665}
550 666
551jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env, 667jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
552 [[maybe_unused]] jclass clazz,
553 jint j_device, jint stick_id, 668 jint j_device, jint stick_id,
554 jfloat x, jfloat y) { 669 jfloat x, jfloat y) {
555 if (EmulationSession::GetInstance().IsRunning()) { 670 if (EmulationSession::GetInstance().IsRunning()) {
@@ -559,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un
559} 674}
560 675
561jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( 676jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
562 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, 677 JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
563 jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, 678 jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
564 jfloat accel_y, jfloat accel_z) {
565 if (EmulationSession::GetInstance().IsRunning()) { 679 if (EmulationSession::GetInstance().IsRunning()) {
566 EmulationSession::GetInstance().Window().OnGamepadMotionEvent( 680 EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
567 j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); 681 j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
@@ -569,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
569 return static_cast<jboolean>(true); 683 return static_cast<jboolean>(true);
570} 684}
571 685
572jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env, 686jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
573 [[maybe_unused]] jclass clazz,
574 jbyteArray j_data) { 687 jbyteArray j_data) {
575 jboolean isCopy{false}; 688 jboolean isCopy{false};
576 std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), 689 std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
@@ -582,108 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI
582 return static_cast<jboolean>(true); 695 return static_cast<jboolean>(true);
583} 696}
584 697
585jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env, 698jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
586 [[maybe_unused]] jclass clazz) {
587 if (EmulationSession::GetInstance().IsRunning()) { 699 if (EmulationSession::GetInstance().IsRunning()) {
588 EmulationSession::GetInstance().Window().OnRemoveNfcTag(); 700 EmulationSession::GetInstance().Window().OnRemoveNfcTag();
589 } 701 }
590 return static_cast<jboolean>(true); 702 return static_cast<jboolean>(true);
591} 703}
592 704
593void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env, 705void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
594 [[maybe_unused]] jclass clazz, jint id,
595 jfloat x, jfloat y) { 706 jfloat x, jfloat y) {
596 if (EmulationSession::GetInstance().IsRunning()) { 707 if (EmulationSession::GetInstance().IsRunning()) {
597 EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); 708 EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
598 } 709 }
599} 710}
600 711
601void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, 712void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
602 [[maybe_unused]] jclass clazz, jint id,
603 jfloat x, jfloat y) { 713 jfloat x, jfloat y) {
604 if (EmulationSession::GetInstance().IsRunning()) { 714 if (EmulationSession::GetInstance().IsRunning()) {
605 EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); 715 EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
606 } 716 }
607} 717}
608 718
609void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env, 719void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
610 [[maybe_unused]] jclass clazz, jint id) {
611 if (EmulationSession::GetInstance().IsRunning()) { 720 if (EmulationSession::GetInstance().IsRunning()) {
612 EmulationSession::GetInstance().Window().OnTouchReleased(id); 721 EmulationSession::GetInstance().Window().OnTouchReleased(id);
613 } 722 }
614} 723}
615 724
616jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env, 725jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
617 [[maybe_unused]] jclass clazz, 726 jstring j_filename) {
618 [[maybe_unused]] jstring j_filename) { 727 jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
619 auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
620 jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); 728 jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
621 env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), 729 env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
622 reinterpret_cast<jbyte*>(icon_data.data())); 730 reinterpret_cast<jbyte*>(icon_data.data()));
623 return icon; 731 return icon;
624} 732}
625 733
626jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env, 734jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
627 [[maybe_unused]] jclass clazz, 735 jstring j_filename) {
628 [[maybe_unused]] jstring j_filename) { 736 jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
629 auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
630 return env->NewStringUTF(title.c_str()); 737 return env->NewStringUTF(title.c_str());
631} 738}
632 739
633jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env, 740jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
634 [[maybe_unused]] jclass clazz,
635 jstring j_filename) { 741 jstring j_filename) {
636 return j_filename; 742 return j_filename;
637} 743}
638 744
639jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env, 745jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
640 [[maybe_unused]] jclass clazz,
641 jstring j_filename) { 746 jstring j_filename) {
642 return j_filename; 747 return j_filename;
643} 748}
644 749
645jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env, 750jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
646 [[maybe_unused]] jclass clazz, 751 jstring j_filename) {
647 [[maybe_unused]] jstring j_filename) {
648 return env->NewStringUTF(""); 752 return env->NewStringUTF("");
649} 753}
650 754
651jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env, 755jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
652 [[maybe_unused]] jclass clazz, 756 jstring j_filename) {
653 [[maybe_unused]] jstring j_filename) {
654 return env->NewStringUTF(""); 757 return env->NewStringUTF("");
655} 758}
656 759
657jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, 760jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
658 [[maybe_unused]] jclass clazz, 761 jstring j_filename) {
659 [[maybe_unused]] jstring j_filename) {
660 return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); 762 return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
661} 763}
662 764
663void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation 765void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
664 [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
665 // Create the default config.ini. 766 // Create the default config.ini.
666 Config{}; 767 Config{};
667 // Initialize the emulated system. 768 // Initialize the emulated system.
668 EmulationSession::GetInstance().System().Initialize(); 769 EmulationSession::GetInstance().System().Initialize();
669} 770}
670 771
671jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env, 772jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
672 [[maybe_unused]] jclass clazz) {
673 return {}; 773 return {};
674} 774}
675 775
676void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( 776void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
677 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, 777 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
678 [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
679 778
680void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, 779void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
681 [[maybe_unused]] jclass clazz) {
682 Config{}; 780 Config{};
683} 781}
684 782
685jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env, 783jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
686 [[maybe_unused]] jclass clazz,
687 jstring j_game_id, jstring j_section, 784 jstring j_game_id, jstring j_section,
688 jstring j_key) { 785 jstring j_key) {
689 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 786 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -697,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN
697 return env->NewStringUTF(""); 794 return env->NewStringUTF("");
698} 795}
699 796
700void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env, 797void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
701 [[maybe_unused]] jclass clazz,
702 jstring j_game_id, jstring j_section, 798 jstring j_game_id, jstring j_section,
703 jstring j_key, jstring j_value) { 799 jstring j_key, jstring j_value) {
704 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 800 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -712,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn
712 env->ReleaseStringUTFChars(j_value, value.data()); 808 env->ReleaseStringUTFChars(j_value, value.data());
713} 809}
714 810
715void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env, 811void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
716 [[maybe_unused]] jclass clazz,
717 jstring j_game_id) { 812 jstring j_game_id) {
718 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 813 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
719 814
720 env->ReleaseStringUTFChars(j_game_id, game_id.data()); 815 env->ReleaseStringUTFChars(j_game_id, game_id.data());
721} 816}
722 817
723jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env, 818jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
724 [[maybe_unused]] jclass clazz) {
725 jdoubleArray j_stats = env->NewDoubleArray(4); 819 jdoubleArray j_stats = env->NewDoubleArray(4);
726 820
727 if (EmulationSession::GetInstance().IsRunning()) { 821 if (EmulationSession::GetInstance().IsRunning()) {
728 const auto results = EmulationSession::GetInstance().PerfStats(); 822 jconst results = EmulationSession::GetInstance().PerfStats();
729 823
730 // Converting the structure into an array makes it easier to pass it to the frontend 824 // Converting the structure into an array makes it easier to pass it to the frontend
731 double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, 825 double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
@@ -737,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]]
737 return j_stats; 831 return j_stats;
738} 832}
739 833
740void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( 834void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
741 [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} 835 jclass clazz,
836 jstring j_path) {}
742 837
743void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, 838void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
744 [[maybe_unused]] jclass clazz,
745 jstring j_path) { 839 jstring j_path) {
746 const std::string path = GetJString(env, j_path); 840 const std::string path = GetJString(env, j_path);
747 841
@@ -752,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus
752 } 846 }
753} 847}
754 848
755void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env, 849void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) {
756 [[maybe_unused]] jclass clazz) {
757 LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc); 850 LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
758 LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); 851 LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
759} 852}
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
deleted file mode 100644
index 24dcbbcb8..000000000
--- a/src/android/app/src/main/jni/native.h
+++ /dev/null
@@ -1,165 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <jni.h>
7
8// Function calls from the Java side
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
14 jclass clazz);
15
16JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
17 jclass clazz);
18
19JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
20 jclass clazz);
21
22JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
23 jclass clazz);
24
25JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
26 jclass clazz);
27
28JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
29 jclass clazz);
30
31JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
32 jclass clazz,
33 jstring j_device,
34 jstring j_type);
35
36JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
37 JNIEnv* env, jclass clazz, jstring j_device);
38
39JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
40 JNIEnv* env, jclass clazz, jstring j_device);
41
42JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
43 JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
44
45JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
46 JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
47
48JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
49 JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
50
51JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
52 jclass clazz,
53 jbyteArray j_data);
54
55JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
56 jclass clazz);
57
58JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
59 jclass clazz,
60 jfloat x, jfloat y,
61 jboolean pressed);
62
63JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
64 jfloat x, jfloat y);
65
66JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
67 jclass clazz,
68 jstring j_file);
69
70JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
71 jstring j_filename);
72
73JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
74 jclass clazz,
75 jstring j_filename);
76
77JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
78 jstring j_filename);
79
80JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
81 jclass clazz,
82 jstring j_filename);
83
84JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
85 jclass clazz,
86 jstring j_filename);
87
88JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
89 jclass clazz);
90
91JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
92 jclass clazz,
93 jstring j_directory);
94
95JNIEXPORT void JNICALL
96Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver(
97 JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
98 jstring custom_driver_name, jstring file_redirect_dir);
99
100JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
101 jclass clazz);
102
103JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
104 JNIEnv* env, jclass clazz, jstring path_);
105
106JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
107 jclass clazz,
108 jstring path);
109
110JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
111 jclass clazz);
112
113JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
114 jclass clazz);
115JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
116 jboolean enable);
117
118JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
119 jclass clazz);
120
121JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
122 JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
123
124JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
125 JNIEnv* env, jclass clazz, jstring j_path);
126
127JNIEXPORT void JNICALL
128Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
129 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
130
131JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
132 jclass clazz,
133 jobject surf);
134
135JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
136 jclass clazz);
137
138JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
139 jstring j_game_id);
140
141JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
142 jclass clazz);
143
144JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting(
145 JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
146 jstring j_value);
147
148JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
149 JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
150
151JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
152 jclass clazz);
153
154JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
155 jclass clazz);
156
157JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
158 JNIEnv* env, jclass clazz, jstring j_text);
159
160JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput(
161 JNIEnv* env, jclass clazz, jint j_key_code);
162
163#ifdef __cplusplus
164}
165#endif
diff --git a/src/android/app/src/main/res/drawable/ic_pip_mute.xml b/src/android/app/src/main/res/drawable/ic_pip_mute.xml
new file mode 100644
index 000000000..a271c5fe8
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pip_mute.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="@android:color/white"
8 android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_pip_pause.xml b/src/android/app/src/main/res/drawable/ic_pip_pause.xml
new file mode 100644
index 000000000..4a7d4ea03
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pip_pause.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="@android:color/white"
8 android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_pip_play.xml b/src/android/app/src/main/res/drawable/ic_pip_play.xml
new file mode 100644
index 000000000..2303a4623
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pip_play.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="@android:color/white"
8 android:pathData="M8,5v14l11,-7z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_pip_unmute.xml b/src/android/app/src/main/res/drawable/ic_pip_unmute.xml
new file mode 100644
index 000000000..f7ed0862e
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_pip_unmute.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="@android:color/white"
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_system_update_alt.xml b/src/android/app/src/main/res/drawable/ic_system_update_alt.xml
new file mode 100644
index 000000000..0f6adfdb8
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_system_update_alt.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="48dp"
3 android:height="48dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="#FF000000"
8 android:pathData="M140,800q-24,0 -42,-18t-18,-42v-520q0,-24 18,-42t42,-18h250v60L140,220v520h680v-520L570,220v-60h250q24,0 42,18t18,42v520q0,24 -18,42t-42,18L140,800ZM480,615L280,415l43,-43 127,127v-339h60v339l127,-127 43,43 -200,200Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/layout/activity_emulation.xml b/src/android/app/src/main/res/layout/activity_emulation.xml
index f6360a65b..139065d3d 100644
--- a/src/android/app/src/main/res/layout/activity_emulation.xml
+++ b/src/android/app/src/main/res/layout/activity_emulation.xml
@@ -1,13 +1,9 @@
1<FrameLayout 1<androidx.fragment.app.FragmentContainerView
2 xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:android="http://schemas.android.com/apk/res/android"
3 android:id="@+id/frame_content" 3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 android:id="@+id/fragment_container"
5 android:name="androidx.navigation.fragment.NavHostFragment"
4 android:layout_width="match_parent" 6 android:layout_width="match_parent"
5 android:layout_height="match_parent" 7 android:layout_height="match_parent"
6 android:keepScreenOn="true"> 8 android:keepScreenOn="true"
7 9 app:defaultNavHost="true" />
8 <FrameLayout
9 android:id="@+id/frame_emulation_fragment"
10 android:layout_width="match_parent"
11 android:layout_height="match_parent" />
12
13</FrameLayout>
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 09b789b6b..e54a10e8f 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -12,49 +12,65 @@
12 android:layout_width="match_parent" 12 android:layout_width="match_parent"
13 android:layout_height="match_parent"> 13 android:layout_height="match_parent">
14 14
15 <!-- This is what everything is rendered to during emulation --> 15 <FrameLayout
16 <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView 16 android:id="@+id/emulation_container"
17 android:id="@+id/surface_emulation"
18 android:layout_width="match_parent" 17 android:layout_width="match_parent"
19 android:layout_height="match_parent" 18 android:layout_height="match_parent">
20 android:layout_gravity="center" 19
21 android:focusable="false" 20 <!-- This is what everything is rendered to during emulation -->
22 android:focusableInTouchMode="false" /> 21 <org.yuzu.yuzu_emu.views.FixedRatioSurfaceView
22 android:id="@+id/surface_emulation"
23 android:layout_width="match_parent"
24 android:layout_height="match_parent"
25 android:layout_gravity="center"
26 android:focusable="false"
27 android:focusableInTouchMode="false" />
28
29 </FrameLayout>
23 30
24 <FrameLayout 31 <FrameLayout
25 android:id="@+id/overlay_container" 32 android:id="@+id/input_container"
26 android:layout_width="match_parent" 33 android:layout_width="match_parent"
27 android:layout_height="match_parent" 34 android:layout_height="match_parent"
28 android:layout_gravity="bottom"> 35 android:layout_gravity="bottom">
29 36
30 <!-- This is the onscreen input overlay --> 37 <!-- This is the onscreen input overlay -->
31 <org.yuzu.yuzu_emu.overlay.InputOverlay 38 <org.yuzu.yuzu_emu.overlay.InputOverlay
32 android:id="@+id/surface_input_overlay" 39 android:id="@+id/surface_input_overlay"
40 android:layout_width="match_parent"
41 android:layout_height="match_parent"
42 android:layout_gravity="center"
43 android:focusable="true"
44 android:focusableInTouchMode="true" />
45
46 <Button
47 style="@style/Widget.Material3.Button.ElevatedButton"
48 android:id="@+id/done_control_config"
49 android:layout_width="wrap_content"
50 android:layout_height="wrap_content"
51 android:layout_gravity="center"
52 android:text="@string/emulation_done"
53 android:visibility="gone" />
54
55 </FrameLayout>
56
57 <FrameLayout
58 android:id="@+id/overlay_container"
33 android:layout_width="match_parent" 59 android:layout_width="match_parent"
34 android:layout_height="match_parent" 60 android:layout_height="match_parent">
35 android:focusable="true"
36 android:focusableInTouchMode="true" />
37 61
38 <TextView 62 <TextView
39 android:id="@+id/show_fps_text" 63 android:id="@+id/show_fps_text"
40 android:layout_width="wrap_content" 64 android:layout_width="wrap_content"
41 android:layout_height="wrap_content" 65 android:layout_height="wrap_content"
42 android:layout_gravity="left" 66 android:layout_gravity="left"
43 android:clickable="false" 67 android:clickable="false"
44 android:focusable="false" 68 android:focusable="false"
45 android:shadowColor="@android:color/black" 69 android:shadowColor="@android:color/black"
46 android:textColor="@android:color/white" 70 android:textColor="@android:color/white"
47 android:textSize="12sp" 71 android:textSize="12sp"
48 tools:ignore="RtlHardcoded" /> 72 tools:ignore="RtlHardcoded" />
49 73
50 <Button
51 style="@style/Widget.Material3.Button.ElevatedButton"
52 android:id="@+id/done_control_config"
53 android:layout_width="wrap_content"
54 android:layout_height="wrap_content"
55 android:layout_gravity="center"
56 android:text="@string/emulation_done"
57 android:visibility="gone" />
58 </FrameLayout> 74 </FrameLayout>
59 75
60 </androidx.coordinatorlayout.widget.CoordinatorLayout> 76 </androidx.coordinatorlayout.widget.CoordinatorLayout>
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 599d845ad..a5767adee 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
@@ -1,16 +1,16 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
3 xmlns:tools="http://schemas.android.com/tools" 4 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent" 5 android:layout_width="match_parent"
5 android:layout_height="wrap_content" 6 android:layout_height="wrap_content"
6 xmlns:app="http://schemas.android.com/apk/res-auto"
7 android:background="?android:attr/selectableItemBackground" 7 android:background="?android:attr/selectableItemBackground"
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:paddingStart="@dimen/spacing_large" 12 android:paddingStart="@dimen/spacing_large"
12 android:paddingEnd="24dp" 13 android:paddingEnd="24dp">
13 android:paddingVertical="@dimen/spacing_large">
14 14
15 <com.google.android.material.materialswitch.MaterialSwitch 15 <com.google.android.material.materialswitch.MaterialSwitch
16 android:id="@+id/switch_widget" 16 android:id="@+id/switch_widget"
@@ -19,32 +19,35 @@
19 android:layout_alignParentEnd="true" 19 android:layout_alignParentEnd="true"
20 android:layout_centerVertical="true" /> 20 android:layout_centerVertical="true" />
21 21
22 <com.google.android.material.textview.MaterialTextView 22 <LinearLayout
23 style="@style/TextAppearance.Material3.BodySmall" 23 android:layout_width="match_parent"
24 android:id="@+id/text_setting_description"
25 android:layout_width="wrap_content"
26 android:layout_height="wrap_content"
27 android:layout_alignParentStart="true"
28 android:layout_alignStart="@+id/text_setting_name"
29 android:layout_below="@+id/text_setting_name"
30 android:layout_marginEnd="@dimen/spacing_large"
31 android:layout_marginTop="@dimen/spacing_small"
32 android:layout_toStartOf="@+id/switch_widget"
33 android:textAlignment="viewStart"
34 tools:text="@string/frame_limit_enable_description" />
35
36 <com.google.android.material.textview.MaterialTextView
37 style="@style/TextAppearance.Material3.HeadlineMedium"
38 android:id="@+id/text_setting_name"
39 android:layout_width="0dp"
40 android:layout_height="wrap_content" 24 android:layout_height="wrap_content"
41 android:layout_alignParentStart="true"
42 android:layout_alignParentTop="true" 25 android:layout_alignParentTop="true"
26 android:layout_centerVertical="true"
43 android:layout_marginEnd="@dimen/spacing_large" 27 android:layout_marginEnd="@dimen/spacing_large"
44 android:layout_toStartOf="@+id/switch_widget" 28 android:layout_toStartOf="@+id/switch_widget"
45 android:textSize="16sp" 29 android:gravity="center_vertical"
46 android:textAlignment="viewStart" 30 android:orientation="vertical">
47 app:lineHeight="28dp" 31
48 tools:text="@string/frame_limit_enable" /> 32 <com.google.android.material.textview.MaterialTextView
33 android:id="@+id/text_setting_name"
34 style="@style/TextAppearance.Material3.HeadlineMedium"
35 android:layout_width="wrap_content"
36 android:layout_height="wrap_content"
37 android:textAlignment="viewStart"
38 android:textSize="16sp"
39 app:lineHeight="28dp"
40 tools:text="@string/frame_limit_enable" />
41
42 <com.google.android.material.textview.MaterialTextView
43 android:id="@+id/text_setting_description"
44 style="@style/TextAppearance.Material3.BodySmall"
45 android:layout_width="wrap_content"
46 android:layout_height="wrap_content"
47 android:layout_marginTop="@dimen/spacing_small"
48 android:textAlignment="viewStart"
49 tools:text="@string/frame_limit_enable_description" />
50
51 </LinearLayout>
49 52
50</RelativeLayout> 53</RelativeLayout>
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 abd24df6f..cf85bc0da 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
@@ -1,20 +1,14 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 2<com.google.android.material.textview.MaterialTextView xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools" 3 xmlns:tools="http://schemas.android.com/tools"
4 android:id="@+id/text_header_name"
5 style="@style/TextAppearance.Material3.TitleSmall"
4 android:layout_width="match_parent" 6 android:layout_width="match_parent"
5 android:layout_height="48dp" 7 android:layout_height="wrap_content"
6 android:paddingVertical="4dp" 8 android:layout_gravity="start|center_vertical"
7 android:paddingHorizontal="@dimen/spacing_large"> 9 android:paddingHorizontal="@dimen/spacing_large"
8 10 android:paddingVertical="16dp"
9 <com.google.android.material.textview.MaterialTextView 11 android:textAlignment="viewStart"
10 style="@style/TextAppearance.Material3.TitleSmall" 12 android:textColor="?attr/colorPrimary"
11 android:id="@+id/text_header_name" 13 android:textStyle="bold"
12 android:layout_width="match_parent" 14 tools:text="CPU Settings" />
13 android:layout_height="wrap_content"
14 android:layout_gravity="start|center_vertical"
15 android:textColor="?attr/colorPrimary"
16 android:textAlignment="viewStart"
17 android:textStyle="bold"
18 tools:text="CPU Settings" />
19
20</FrameLayout>
diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml
new file mode 100644
index 000000000..8208f4c2c
--- /dev/null
+++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml
@@ -0,0 +1,18 @@
1<?xml version="1.0" encoding="utf-8"?>
2<navigation 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/emulation_navigation"
6 app:startDestination="@id/emulationFragment">
7
8 <fragment
9 android:id="@+id/emulationFragment"
10 android:name="org.yuzu.yuzu_emu.fragments.EmulationFragment"
11 android:label="fragment_emulation"
12 tools:layout="@layout/fragment_emulation" >
13 <argument
14 android:name="game"
15 app:argType="org.yuzu.yuzu_emu.model.Game" />
16 </fragment>
17
18</navigation>
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml
index 48072683e..fcebba726 100644
--- a/src/android/app/src/main/res/navigation/home_navigation.xml
+++ b/src/android/app/src/main/res/navigation/home_navigation.xml
@@ -56,4 +56,18 @@
56 android:name="org.yuzu.yuzu_emu.fragments.LicensesFragment" 56 android:name="org.yuzu.yuzu_emu.fragments.LicensesFragment"
57 android:label="LicensesFragment" /> 57 android:label="LicensesFragment" />
58 58
59 <activity
60 android:id="@+id/emulationActivity"
61 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
62 android:label="EmulationActivity">
63 <argument
64 android:name="game"
65 app:argType="org.yuzu.yuzu_emu.model.Game" />
66 </activity>
67
68 <action
69 android:id="@+id/action_global_emulationActivity"
70 app:destination="@id/emulationActivity"
71 app:launchSingleTop="true" />
72
59</navigation> 73</navigation>
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
new file mode 100644
index 000000000..969223ef8
--- /dev/null
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -0,0 +1,332 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
5 <string name="emulation_notification_channel_name">Emulation ist aktiv</string>
6 <string name="emulation_notification_channel_description">Zeigt eine dauerhafte Benachrichtigung an, wenn die Emulation läuft.</string>
7 <string name="emulation_notification_running">yuzu läuft</string>
8 <string name="notice_notification_channel_name">Hinweise und Fehler</string>
9 <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string>
10 <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Willkommen!</string>
14 <string name="welcome_description">Erfahre wie man &lt;b>yuzu&lt;/b> einrichtet und beginne mit der Emulation.</string>
15 <string name="get_started">Erste Schritte</string>
16 <string name="keys">Schlüssel</string>
17 <string name="keys_description">Wähle deine &lt;b>prod.keys&lt;/b> Datei mit dem Button unten aus.</string>
18 <string name="select_keys">Schlüssel auswählen</string>
19 <string name="games">Spiele</string>
20 <string name="games_description">Wähle mit dem Knopf unten den &lt;b>Spiele&lt;/b>-Ordner aus.</string>
21 <string name="done">Fertig</string>
22 <string name="done_description">Wir können loslegen.\nViel Spaß!</string>
23 <string name="text_continue">Fortsetzen</string>
24 <string name="next">Weiter</string>
25 <string name="back">Zurück</string>
26 <string name="add_games">Spiele hinzufügen</string>
27 <string name="add_games_description">Spieleverzeichnis auswählen</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Spiele</string>
31 <string name="home_search">Suche</string>
32 <string name="home_settings">Einstellungen</string>
33 <string name="empty_gamelist">Es wurden keine Dateien gefunden oder es wurde noch kein Spielverzeichnis ausgewählt.</string>
34 <string name="search_and_filter_games">Spiele suchen und filtern</string>
35 <string name="select_games_folder">Spieleverzeichnis auswählen</string>
36 <string name="select_games_folder_description">Erlaubt yuzu die Spieleliste zu füllen</string>
37 <string name="add_games_warning">Auswahl des Spieleverzeichnisses überspringen?</string>
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_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Spiele suchen</string>
41 <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string>
42 <string name="install_prod_keys">prod.keys installieren</string>
43 <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string>
44 <string name="install_prod_keys_warning">Hinzufügen der Schlüssel überspringen?</string>
45 <string name="install_prod_keys_warning_description">Für die Emulation von Spielen sind gültige Schlüssel erforderlich. Wenn du fortfährst, funktionieren nur Homebrew-Anwendungen.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Benachrichtigungen</string>
48 <string name="notifications_description">Erteile mit dem Knopf unten die Berechtigung, Benachrichtigungen zu senden.</string>
49 <string name="give_permission">Berechtigung erteilen</string>
50 <string name="notification_warning_description">yuzu wird dich nicht über wichtige Informationen benachrichtigen können.</string>
51 <string name="permission_denied">Zugriff verweigert</string>
52 <string name="permission_denied_description">Du hast diese Berechtigung zu oft verweigert und musst sie nun manuell in den Systemeinstellungen erteilen.</string>
53 <string name="about">Über</string>
54 <string name="about_description">Build-Version, Credits und mehr</string>
55 <string name="warning_help">Hilfe</string>
56 <string name="warning_skip">Überspringen</string>
57 <string name="warning_cancel">Abbrechen</string>
58 <string name="install_amiibo_keys">Amiibo-Schlüssel installieren</string>
59 <string name="install_amiibo_keys_description">Benötigt um Amiibos im Spiel zu verwenden</string>
60 <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string>
61 <string name="install_keys_success">Schlüssel erfolgreich installiert</string>
62 <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string>
63 <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>
65 <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>
67 <string name="advanced_settings">Erweiterte Einstellungen</string>
68 <string name="settings_description">Emulatoreinstellungen konfigurieren</string>
69 <string name="search_recently_played">Kürzlich gespielt</string>
70 <string name="search_recently_added">Kürzlich hinzugefügt</string>
71 <string name="search_retail">Spiele</string>
72 <string name="search_homebrew">Homebrew</string>
73 <string name="open_user_folder">yuzu-Ordner öffnen</string>
74 <string name="open_user_folder_description">yuzu\'s interne Dateien verwalten</string>
75 <string name="theme_and_color_description">Das Aussehen der App ändern</string>
76 <string name="no_file_manager">Kein Dateimanager gefunden</string>
77 <string name="notification_no_directory_link">yuzu-Verzeichnis konnte nicht geöffnet werden</string>
78 <string name="notification_no_directory_link_description">Bitte suche den Benutzerordner manuell über die Seitenleiste des Dateimanagers.</string>
79 <string name="manage_save_data">Speicherdaten verwalten</string>
80 <string name="manage_save_data_description">Speicherdaten gefunden. Bitte wähle unten eine Option aus.</string>
81 <string name="import_export_saves_description">Speicherdaten importieren oder exportieren</string>
82 <string name="import_export_saves_no_profile">Keine Speicherdaten gefunden. Bitte starte ein Spiel und versuche es erneut.</string>
83 <string name="save_file_imported_success">Erfolgreich importiert</string>
84 <string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string>
85 <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string>
86 <string name="import_saves">Importieren</string>
87 <string name="export_saves">Exportieren</string>
88
89 <!-- About screen strings -->
90 <string name="gaia_is_not_real">Gaia ist nicht real</string>
91 <string name="copied_to_clipboard">In die Zwischenablage kopiert</string>
92 <string name="about_app_description">Ein quelloffener Switch-Emulator</string>
93 <string name="contributors">Beitragende</string>
94 <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string>
95 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
96 <string name="build">Build</string>
97 <string name="support_link">https://discord.gg/u77vRWY</string>
98 <string name="website_link">https://yuzu-emu.org/</string>
99 <string name="github_link">https://github.com/yuzu-emu</string>
100
101 <!-- Early access upgrade strings -->
102 <string name="early_access">Early Access</string>
103 <string name="get_early_access">Early Access bekommen</string>
104 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
105 <string name="get_early_access_description">Neueste Features, frühzeitiger Zugriff auf Updates und mehr</string>
106 <string name="early_access_benefits">Early Access Vorteile</string>
107 <string name="cutting_edge_features">Neueste Features</string>
108 <string name="early_access_updates">Früherer Zugriff auf Updates</string>
109 <string name="no_manual_installation">Keine manuelle Installation</string>
110 <string name="prioritized_support">Priorisierte Unterstützung</string>
111 <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string>
112 <string name="are_you_interested">Bist du interessiert?</string>
113
114 <!-- General settings strings -->
115 <string name="frame_limit_enable">Geschwindigkeitsbegrenzung aktivieren</string>
116 <string name="frame_limit_enable_description">Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen Prozentsatz der normalen Geschwindigkeit begrenzt.</string>
117 <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string>
118 <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>
119 <string name="cpu_accuracy">CPU-Genauigkeit</string>
120
121 <!-- System settings strings -->
122 <string name="use_docked_mode">Dock-Modus</string>
123 <string name="use_docked_mode_description">Emuliert im Dock-Modus, was die Auflösung verbessert, aber die Leistung senkt.</string>
124 <string name="emulated_region">Emulierte Region</string>
125 <string name="emulated_language">Emulierte Sprache</string>
126 <string name="select_rtc_date">RTC-Datum auswählen</string>
127 <string name="select_rtc_time">RTC-Zeit auswählen</string>
128 <string name="use_custom_rtc">Benutzerdefinierte RTC aktivieren</string>
129 <string name="use_custom_rtc_description">Mit dieser Einstellung kann eine benutzerdefinierte Echtzeituhr unabhängig von der aktuellen Systemzeit verwendet werden.</string>
130 <string name="set_custom_rtc">Benutzerdefinierte RTC einstellen</string>
131
132 <!-- Graphics settings strings -->
133 <string name="renderer_api">API</string>
134 <string name="renderer_accuracy">Genauigkeitsstufe</string>
135 <string name="renderer_resolution">Auflösung</string>
136 <string name="renderer_vsync">VSync-Modus</string>
137 <string name="renderer_aspect_ratio">Seitenverhältnis</string>
138 <string name="renderer_scaling_filter">Fensteranpassungsfilter</string>
139 <string name="renderer_anti_aliasing">Kantenglättungs-Methode</string>
140 <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string>
141 <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string>
142 <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string>
143 <string name="renderer_asynchronous_shaders_description">Kompiliert Shader asynchron, was Ruckler reduziert, aber zu Glitches führen kann.</string>
144 <string name="renderer_debug">Grafik-Debugging aktivieren</string>
145 <string name="renderer_debug_description">Wenn aktiviert, schaltet die Grafik-API in einen langsameren Debugging-Modus.</string>
146 <string name="use_disk_shader_cache">Nutze Festplatten-Shader-Cache</string>
147 <string name="use_disk_shader_cache_description">Ruckeln wird durch das Speichern und Laden von generierten Shadern auf der Festplatte reduziert.</string>
148
149 <!-- Audio settings strings -->
150 <string name="audio_volume">Lautstärke</string>
151 <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string>
152
153 <!-- Miscellaneous -->
154 <string name="slider_default">Standard</string>
155 <string name="ini_saved">Einstellungen gespeichert</string>
156 <string name="gameid_saved">Einstellungen für %1$s gespeichert</string>
157 <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string>
158 <string name="loading">Lädt...</string>
159 <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string>
160 <string name="reset_to_default">Auf Standard zurücksetzen</string>
161 <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string>
162 <string name="reset_all_settings_description">Alle erweiterten Einstellungen werden auf ihren Standardwert zurückgesetzt. Dies kann nicht rückgängig gemacht werden.</string>
163 <string name="settings_reset">Einstellungen zurückgesetzt</string>
164 <string name="close">Schließen</string>
165 <string name="learn_more">Mehr erfahren</string>
166
167 <!-- GPU driver installation -->
168 <string name="select_gpu_driver">GPU-Treiber auswählen</string>
169 <string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
170 <string name="select_gpu_driver_install">Installieren</string>
171 <string name="select_gpu_driver_default">Standard</string>
172 <string name="select_gpu_driver_install_success">%s wurde installiert</string>
173 <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
174 <string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
175 <string name="system_gpu_driver">System GPU-Treiber</string>
176 <string name="installing_driver">Treiber wird installiert...</string>
177
178 <!-- Preferences Screen -->
179 <string name="preferences_settings">Einstellungen</string>
180 <string name="preferences_general">Allgemein</string>
181 <string name="preferences_system">System</string>
182 <string name="preferences_graphics">Grafik</string>
183 <string name="preferences_audio">Audio</string>
184 <string name="preferences_theme">Theme und Farbe</string>
185
186 <!-- ROM loading errors -->
187 <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string>
188 <string name="loader_error_encrypted_keys_description"><![CDATA[Bitte stelle sicher dass die <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> Datei installiert ist, damit Spiele entschlüsselt werden können.]]></string>
189 <string name="loader_error_video_core">Bei der Initialisierung des Videokerns ist ein Fehler aufgetreten</string>
190 <string name="loader_error_video_core_description">Dies wird normalerweise durch einen inkompatiblen GPU-Treiber verursacht. Die Installation eines passenden GPU-Treibers kann dieses Problem beheben.</string>
191 <string name="loader_error_invalid_format">ROM konnte nicht geladen werden</string>
192 <string name="loader_error_file_not_found">ROM-Datei existiert nicht</string>
193
194 <!-- Emulation Menu -->
195 <string name="emulation_exit">Emulation beenden</string>
196 <string name="emulation_done">Fertig</string>
197 <string name="emulation_fps_counter">FPS Zähler</string>
198 <string name="emulation_toggle_controls">Steuerung umschalten</string>
199 <string name="emulation_rel_stick_center">Relative Stick-Mitte</string>
200 <string name="emulation_dpad_slide">DPad Slide</string>
201 <string name="emulation_haptics">Haptik</string>
202 <string name="emulation_show_overlay">Overlay anzeigen</string>
203 <string name="emulation_toggle_all">Alle umschalten</string>
204 <string name="emulation_control_adjust">Overlay anpassen</string>
205 <string name="emulation_control_scale">Größe</string>
206 <string name="emulation_control_opacity">Transparenz</string>
207 <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string>
208 <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string>
209 <string name="emulation_pause">Emulation pausieren</string>
210 <string name="emulation_unpause">Emulation fortsetzen</string>
211 <string name="emulation_input_overlay">Overlay-Optionen</string>
212 <string name="emulation_game_loading">Spiel lädt…</string>
213
214 <string name="load_settings">Lädt Einstellungen...</string>
215
216 <!-- Software keyboard -->
217 <string name="software_keyboard">Software-Tastatur</string>
218
219 <!-- Errors and warnings -->
220 <string name="abort_button">Abbrechen</string>
221 <string name="continue_button">Fortsetzen</string>
222 <string name="system_archive_not_found">Systemarchiv nicht gefunden</string>
223 <string name="system_archive_general">Ein System-Archiv</string>
224 <string name="save_load_error">Speicher-/Ladefehler</string>
225 <string name="fatal_error">Schwerwiegender Fehler</string>
226 <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>
227 <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>
228
229 <!-- Region Names -->
230 <string name="region_japan">Japan</string>
231 <string name="region_usa">USA</string>
232 <string name="region_europe">Europa</string>
233 <string name="region_australia">Australien</string>
234 <string name="region_china">China</string>
235 <string name="region_korea">Korea</string>
236 <string name="region_taiwan">Taiwan</string>
237
238 <!-- Language Names -->
239 <string name="language_japanese">Japanisch (日本語)</string>
240 <string name="language_english">Englisch</string>
241 <string name="language_french">Französisch (Français)</string>
242 <string name="langauge_german">Deutsch (German)</string>
243 <string name="language_italian">Italienisch (Italiano)</string>
244 <string name="language_spanish">Spanisch (Español)</string>
245 <string name="language_chinese">Chinesisch (简体中文)</string>
246 <string name="language_korean">Koreanisch (한국어)</string>
247 <string name="language_dutch">Niederländisch (Nederlands)</string>
248 <string name="language_portuguese">Portugiesisch (Português)</string>
249 <string name="language_russian">Russisch (Русский)</string>
250 <string name="language_taiwanese">Taiwanesisch (台湾)</string>
251 <string name="language_british_english">Britisches Englisch</string>
252 <string name="language_canadian_french">Kanadisches Französisch (Français canadien)</string>
253 <string name="language_latin_american_spanish">Lateinamerikanisches Spanisch (Español latinoamericano)</string>
254 <string name="language_simplified_chinese">Vereinfachtes Chinesisch (简体中文)</string>
255 <string name="language_traditional_chinese">Traditionelles Chinesisch (正體中文)</string>
256 <string name="language_brazilian_portuguese">Brasilianisches Portugiesisch (Português do Brasil)</string>
257
258 <!-- Renderer APIs -->
259 <string name="renderer_vulkan">Vulkan</string>
260 <string name="renderer_none">Keiner</string>
261
262 <!-- Renderer Accuracy -->
263 <string name="renderer_accuracy_normal">Normal</string>
264 <string name="renderer_accuracy_high">Hoch</string>
265 <string name="renderer_accuracy_extreme">Extrem (Langsam)</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) (Langsam)</string>
272 <string name="resolution_three">3X (2160p/3240p) (Langsam)</string>
273 <string name="resolution_four">4X (2880p/4320p) (Langsam)</string>
274
275 <!-- Renderer VSync -->
276 <string name="renderer_vsync_immediate">Direkt (Aus)</string>
277 <string name="renderer_vsync_mailbox">Mailbox</string>
278 <string name="renderer_vsync_fifo">FIFO (An)</string>
279 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
280
281 <!-- Scaling Filters -->
282 <string name="scaling_filter_nearest_neighbor">Nächste-Nachbarn</string>
283 <string name="scaling_filter_bilinear">Bilinear</string>
284 <string name="scaling_filter_bicubic">Bikubisch</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">Keiner</string>
291 <string name="anti_aliasing_fxaa">FXAA</string>
292 <string name="anti_aliasing_smaa">SMAA</string>
293
294 <!-- Aspect Ratios -->
295 <string name="ratio_default">Standard (16:9)</string>
296 <string name="ratio_force_four_three">4:3 erzwingen</string>
297 <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string>
298 <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string>
299 <string name="ratio_stretch">Auf Fenster anpassen</string>
300
301 <!-- CPU Accuracy -->
302 <string name="cpu_accuracy_accurate">Akkurat</string>
303 <string name="cpu_accuracy_unsafe">Unsicher</string>
304 <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string>
305
306 <!-- Gamepad Buttons -->
307 <string name="gamepad_d_pad">Steuerkreuz</string>
308 <string name="gamepad_left_stick">Linker Analogstick</string>
309 <string name="gamepad_right_stick">Rechter Analogstick</string>
310 <string name="gamepad_home">Home</string>
311 <string name="gamepad_screenshot">Screenshot</string>
312
313 <!-- Disk shader cache -->
314 <string name="preparing_shaders">Shader werden vorbereitet</string>
315 <string name="building_shaders">Shader werden erstellt</string>
316
317 <!-- Theme options -->
318 <string name="change_app_theme">App-Theme ändern</string>
319 <string name="theme_default">Standard</string>
320 <string name="theme_material_you">Material You</string>
321
322 <!-- Theme Modes -->
323 <string name="change_theme_mode">Theme-Modus ändern</string>
324 <string name="theme_mode_follow_system">System folgen</string>
325 <string name="theme_mode_light">Hell</string>
326 <string name="theme_mode_dark">Dunkel</string>
327
328 <!-- Black backgrounds theme -->
329 <string name="use_black_backgrounds">Schwarze Hintergünde verwenden</string>
330 <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string>
331
332</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
new file mode 100644
index 000000000..986e80e50
--- /dev/null
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
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>
7 <string name="emulation_notification_running">yuzu esta ejecutándose</string>
8 <string name="notice_notification_channel_name">Avisos y errores</string>
9 <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string>
10 <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">¡Bienvenido!</string>
14 <string name="welcome_description">Aprende cómo configurar &lt;b>yuzu&lt;/b> y avanza a la emulación.</string>
15 <string name="get_started">Empezar</string>
16 <string name="keys">Claves</string>
17 <string name="keys_description">Selecciona el archivo &lt;b>prod.keys&lt;/b> utilizando el botón de abajo.</string>
18 <string name="select_keys">Seleccionar las claves</string>
19 <string name="games">Juegos</string>
20 <string name="games_description">Selecciona la carpeta &lt;b>Games&lt;/b> utilizando el botón de abajo</string>
21 <string name="done">Hecho</string>
22 <string name="done_description">Todo listo.\n¡Disfrute de sus juegos!</string>
23 <string name="text_continue">Continuar</string>
24 <string name="next">Siguiente</string>
25 <string name="back">Atrás</string>
26 <string name="add_games">Añadir Juegos</string>
27 <string name="add_games_description">Selecciona la carpeta de juegos</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Juegos</string>
31 <string name="home_search">Buscar</string>
32 <string name="home_settings">Ajustes</string>
33 <string name="empty_gamelist">No se ha encontrado ningún archivo o aún no se ha seleccionado ningún directorio de juegos.</string>
34 <string name="search_and_filter_games">Busca y filtra juegos</string>
35 <string name="select_games_folder">Seleccionar carpeta de juegos</string>
36 <string name="select_games_folder_description">Permite que yuzu llene la lista de juegos</string>
37 <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_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Buscar Juegos</string>
41 <string name="games_dir_selected">Directorio de juegos seleccionado</string>
42 <string name="install_prod_keys">Instalar prod.keys</string>
43 <string name="install_prod_keys_description">Requerido para descifrar juegos</string>
44 <string name="install_prod_keys_warning">¿Omitir agregar claves?</string>
45 <string name="install_prod_keys_warning_description">Se requieren claves válidas para emular juegos. Solo las aplicaciones homebrew funcionarán si continúas.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Notificaciones</string>
48 <string name="notifications_description">Otorgue el permiso de notificación con el botón de abajo.</string>
49 <string name="give_permission">Conceder permiso</string>
50 <string name="notification_warning">¿Omitir conceder el permiso de notificación?</string>
51 <string name="notification_warning_description">yuzu no podrá notificarte información importante.</string>
52 <string name="permission_denied">Permiso denegado</string>
53 <string name="permission_denied_description">Negó este permiso demasiadas veces y ahora debe otorgarlo manualmente en la configuración del sistema.</string>
54 <string name="about">Acerca de</string>
55 <string name="about_description">Versión, créditos y más</string>
56 <string name="warning_help">Ayuda</string>
57 <string name="warning_skip">Siguiente</string>
58 <string name="warning_cancel">Cancelar</string>
59 <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>
61 <string name="invalid_keys_file">Archivo de claves inválido seleccionado</string>
62 <string name="install_keys_success">Claves instaladas correctamente</string>
63 <string name="reading_keys_failure">Error al leer las claves de cifrado</string>
64 <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>
66 <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>
68 <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>
70 <string name="settings_description">Configurar las opciones del emulador</string>
71 <string name="search_recently_played">Jugado recientemente</string>
72 <string name="search_recently_added">Añadido recientemente</string>
73 <string name="search_retail">Juegos</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Abrir la carpeta de yuzu</string>
76 <string name="open_user_folder_description">Administrar los archivos internos de yuzu</string>
77 <string name="theme_and_color_description">Modificar la apariencia de la aplicación</string>
78 <string name="no_file_manager">Explorador de archivos no encontrado</string>
79 <string name="notification_no_directory_link">No se pudo abrir la carpeta yuzu</string>
80 <string name="notification_no_directory_link_description">Por favor, busque la carpeta user con el panel lateral del explorador de archivos de forma manual.</string>
81 <string name="manage_save_data">Administrar datos de guardado</string>
82 <string name="manage_save_data_description">Guardar los datos encontrados. Por favor, seleccione una opción de abajo.</string>
83 <string name="import_export_saves_description">Importar o exportar archivos de guardado</string>
84 <string name="import_export_saves_no_profile">No se han encontrado datos de guardado. Por favor, ejecute un juego y vuelva a intentarlo.</string>
85 <string name="save_file_imported_success">Importado correctamente</string>
86 <string name="save_file_invalid_zip_structure">Estructura del directorio de guardado no válido</string>
87 <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string>
88 <string name="import_saves">Importar</string>
89 <string name="export_saves">Exportar</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia no es real</string>
93 <string name="copied_to_clipboard">Copiado al portapapeles</string>
94 <string name="about_app_description">Un emulador de Switch de código abierto</string>
95 <string name="contributors">Contribuidores</string>
96 <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Versión</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Early Access</string>
105 <string name="get_early_access">Conseguir Early Access</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Funciones de vanguardia, acceso anticipado a actualizaciones y más</string>
108 <string name="early_access_benefits">Beneficios Early Access</string>
109 <string name="cutting_edge_features">Características de vanguardia</string>
110 <string name="early_access_updates">Acceso anticipado a las actualizaciones</string>
111 <string name="no_manual_installation">Sin instalación manual</string>
112 <string name="prioritized_support">Soporte prioritario</string>
113 <string name="helping_game_preservation">Ayudarás a la preservación de juegos</string>
114 <string name="our_eternal_gratitude">Nuestra eterna gratitud</string>
115 <string name="are_you_interested">¿Estás interesado?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Activar limite de velocidad</string>
119 <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>
120 <string name="frame_limit_slider">Limitar porcentaje de velocidad</string>
121 <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>
122 <string name="cpu_accuracy">Precisión de CPU</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Modo sobremesa</string>
126 <string name="use_docked_mode_description">Emula en modo sobremesa, lo que aumenta la resolución perjudicando el rendimiento.</string>
127 <string name="emulated_region">Región emulada</string>
128 <string name="emulated_language">Idioma emulado</string>
129 <string name="select_rtc_date">Seleccionar Fecha RTC</string>
130 <string name="select_rtc_time">Seleccionar Tiempo RTC</string>
131 <string name="use_custom_rtc">Habilitar RTC Personalizado</string>
132 <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>
133 <string name="set_custom_rtc">Establecer RTC Personalizado</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Nivel de precisión</string>
138 <string name="renderer_resolution">Resolución</string>
139 <string name="renderer_vsync">Modo VSync</string>
140 <string name="renderer_aspect_ratio">Relación de aspecto</string>
141 <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string>
142 <string name="renderer_anti_aliasing">Metodo Anti Aliasing</string>
143 <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string>
144 <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>
145 <string name="renderer_asynchronous_shaders">Usar shaders asíncronos</string>
146 <string name="renderer_asynchronous_shaders_description">Compila shaders de forma asincrónica, lo que reducirá los parones pero puede introducir fallos.</string>
147 <string name="renderer_debug">Habilitar la depuración de gráficos</string>
148 <string name="renderer_debug_description">Cuando esté marcado, la API de gráficos entra en un modo de depuración más lento.</string>
149 <string name="use_disk_shader_cache">Usar caché de shaders en disco</string>
150 <string name="use_disk_shader_cache_description">Reduzca los parones almacenando y cargando shaders generados en el disco.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Volumen</string>
154 <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Predeterminado</string>
158 <string name="ini_saved">Configuración guardada</string>
159 <string name="gameid_saved">Configuración guardada para %1$s</string>
160 <string name="error_saving">Error guardando %1$s.ini: %2$s</string>
161 <string name="loading">Cargando...</string>
162 <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string>
163 <string name="reset_to_default">Restablecer a predeterminado</string>
164 <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string>
165 <string name="reset_all_settings_description">Todas las configuraciones avanzadas se restablecerán a su configuración predeterminada. Esto no se puede deshacer.</string>
166 <string name="settings_reset">Reiniciar la configuracion</string>
167 <string name="close">Cerrar</string>
168 <string name="learn_more">Más información</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Seleccionar driver GPU</string>
172 <string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
173 <string name="select_gpu_driver_install">Instalar</string>
174 <string name="select_gpu_driver_default">Predeterminado</string>
175 <string name="select_gpu_driver_install_success">Instalado %s</string>
176 <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
177 <string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
178 <string name="system_gpu_driver">Driver GPU del sistema</string>
179 <string name="installing_driver">Instalando driver...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Ajustes</string>
183 <string name="preferences_general">General</string>
184 <string name="preferences_system">Sistema</string>
185 <string name="preferences_graphics">Gráficos</string>
186 <string name="preferences_audio">Audio</string>
187 <string name="preferences_theme">Tema y color</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">Su ROM está encriptada</string>
191 <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>
192 <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>
193 <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>
194 <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>
195 <string name="loader_error_invalid_format">No se pudo cargar la ROM</string>
196 <string name="loader_error_file_not_found">Archivo ROM no existe</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Salir de la emulación</string>
200 <string name="emulation_done">Hecho</string>
201 <string name="emulation_fps_counter">Contador de FPS</string>
202 <string name="emulation_toggle_controls">Alternar Controles</string>
203 <string name="emulation_rel_stick_center">Centro Relativo del Stick</string>
204 <string name="emulation_dpad_slide">Deslizamiento de la Cruceta</string>
205 <string name="emulation_haptics">Hápticos</string>
206 <string name="emulation_show_overlay">Mostrar pantalla</string>
207 <string name="emulation_toggle_all">Alternar Todo</string>
208 <string name="emulation_control_adjust">Ajustar pantalla</string>
209 <string name="emulation_control_scale">Escala</string>
210 <string name="emulation_control_opacity">Opacidad</string>
211 <string name="emulation_touch_overlay_reset">Reiniciar pantalla</string>
212 <string name="emulation_touch_overlay_edit">Editar pantalla</string>
213 <string name="emulation_pause">Pausar Emulación</string>
214 <string name="emulation_unpause">Reanudar Emulación</string>
215 <string name="emulation_input_overlay">Opciones de pantalla </string>
216 <string name="emulation_game_loading">Cargando juego...</string>
217
218 <string name="load_settings">Cargando configuración...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Software del teclado</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Abortar</string>
225 <string name="continue_button">Continuar</string>
226 <string name="system_archive_not_found">Archivo del sistema no encontrado</string>
227 <string name="system_archive_not_found_message">%s no se ha encontrado. Vacíe los archivos de su sistema.\nContinuar con la emulación puede provocar bloqueos y errores.</string>
228 <string name="system_archive_general">Un archivo del sistema</string>
229 <string name="save_load_error">Error de Guardado/Carga</string>
230 <string name="fatal_error">Error fatal</string>
231 <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>
232 <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>
233
234 <!-- Region Names -->
235 <string name="region_japan">Japón</string>
236 <string name="region_usa">EEUU</string>
237 <string name="region_europe">Europa</string>
238 <string name="region_australia">Australia</string>
239 <string name="region_china">China</string>
240 <string name="region_korea">Corea</string>
241 <string name="region_taiwan">Taiwán</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Japonés (日本語)</string>
245 <string name="language_english">Inglés (English)</string>
246 <string name="language_french">Francés (Français)</string>
247 <string name="langauge_german">Alemán (deutsch)</string>
248 <string name="language_italian">Italiano (Italiano)</string>
249 <string name="language_spanish">Español (Español)</string>
250 <string name="language_chinese">Chino (简体中文)</string>
251 <string name="language_korean">Coreano (한국어)</string>
252 <string name="language_dutch">Holandés (nederlands)</string>
253 <string name="language_portuguese">Portugués (Português)</string>
254 <string name="language_russian">Ruso (Русский)</string>
255 <string name="language_taiwanese">Taiwanés (台湾)</string>
256 <string name="language_british_english">Inglés británico</string>
257 <string name="language_canadian_french">Francés Canadiense (Français canadien)</string>
258 <string name="language_latin_american_spanish">Español Latinoamericano (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">Chino Simplificado (简体中文)</string>
260 <string name="language_traditional_chinese">Chino tradicional (正體中文)</string>
261 <string name="language_brazilian_portuguese">Portugués Brasileño (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Ninguno</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normal</string>
269 <string name="renderer_accuracy_high">Alto</string>
270 <string name="renderer_accuracy_extreme">Extremo (Lento)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">x1 (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Lento)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Lento)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Lento)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Inmediata (Desactivado)</string>
282 <string name="renderer_vsync_mailbox">Mailbox</string>
283 <string name="renderer_vsync_fifo">FIFO (Activado)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relajado</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Vecino más próximo</string>
288 <string name="scaling_filter_bilinear">Bilineal</string>
289 <string name="scaling_filter_bicubic">Bicúbico</string>
290 <string name="scaling_filter_gaussian">Gaussiano</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolución</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Ninguno</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Predeterminado (16:9)</string>
301 <string name="ratio_force_four_three">Forzar 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Forzar 21:9</string>
303 <string name="ratio_force_sixteen_ten">Forzar 16:10</string>
304 <string name="ratio_stretch">Ajustar a la ventana</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Preciso</string>
308 <string name="cpu_accuracy_unsafe">Impreciso</string>
309 <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">Cruceta</string>
313 <string name="gamepad_left_stick">Palanca izquierda</string>
314 <string name="gamepad_right_stick">Palanca derecha</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Captura de pantalla</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Preparando shaders</string>
320 <string name="building_shaders">Construyendo shaders</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Cambiar Tema</string>
324 <string name="theme_default">Predeterminado</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Cambiar modo del tema</string>
329 <string name="theme_mode_follow_system">Igual al sistema</string>
330 <string name="theme_mode_light">Claro</string>
331 <string name="theme_mode_dark">Oscuro</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Usar Fondos Negros</string>
335 <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string>
336
337</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
new file mode 100644
index 000000000..14a9b2d5c
--- /dev/null
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
5 <string name="emulation_notification_channel_name">L\'émulation est active</string>
6 <string name="emulation_notification_channel_description">Affiche une notification persistante lorsque l\'émulation est en cours d\'exécution.</string>
7 <string name="emulation_notification_running">yuzu est en cours d\'exécution</string>
8 <string name="notice_notification_channel_name">Avis et erreurs</string>
9 <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string>
10 <string name="notification_permission_not_granted">Permission de notification non accordée !</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Bienvenue !</string>
14 <string name="welcome_description">Apprenez à configurer &lt;b>yuzu&lt;/b> et passez à l\'émulation.</string>
15 <string name="get_started">Commencer</string>
16 <string name="keys">Clés</string>
17 <string name="keys_description">Sélectionnez votre fichier &lt;b>prod.keys&lt;/b> avec le bouton ci-dessous.</string>
18 <string name="select_keys">Sélectionner les clés</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>
21 <string name="done">Terminé</string>
22 <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string>
23 <string name="text_continue">Continuer</string>
24 <string name="next">Suivant</string>
25 <string name="back">Retour</string>
26 <string name="add_games">Ajouter des jeux</string>
27 <string name="add_games_description">Sélectionner votre dossier de jeux</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Jeux</string>
31 <string name="home_search">Rechercher</string>
32 <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="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_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_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="home_search_games">Rechercher des jeux</string>
41 <string name="games_dir_selected">Répertoire de jeux sélectionné</string>
42 <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>
44 <string name="install_prod_keys_warning">Sauter l\'ajout des clés ?</string>
45 <string name="install_prod_keys_warning_description">Des clés valides sont nécessaires pour émuler des jeux commerciaux. Seules les applications homebrew fonctionneront si vous continuez.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Notifications</string>
48 <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string>
49 <string name="give_permission">Donner la permission</string>
50 <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>
52 <string name="permission_denied">Permission refusée</string>
53 <string name="permission_denied_description">Vous avez refusé cette permission trop de fois et vous devez maintenant l\'accorder manuellement dans les paramètres système.</string>
54 <string name="about">À propos</string>
55 <string name="about_description">Numéro de build, crédits et plus encore</string>
56 <string name="warning_help">Aide</string>
57 <string name="warning_skip">Sauter</string>
58 <string name="warning_cancel">Annuler</string>
59 <string name="install_amiibo_keys">Installer les clés Amiibo</string>
60 <string name="install_amiibo_keys_description">Nécessaire pour utiliser les Amiibo en jeu</string>
61 <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>
63 <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string>
64 <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>
66 <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>
68 <string name="install_gpu_driver_description">Installez des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string>
69 <string name="advanced_settings">Paramètres avancés</string>
70 <string name="settings_description">Configurer les paramètres de l\'émulateur</string>
71 <string name="search_recently_played">Joué récemment</string>
72 <string name="search_recently_added">Ajouté récemment</string>
73 <string name="search_retail">Commercial</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Ouvrir le dossier de yuzu</string>
76 <string name="open_user_folder_description">Gérer les fichiers internes de yuzu</string>
77 <string name="theme_and_color_description">Modifier l\'apparence de l\'application</string>
78 <string name="no_file_manager">Aucun gestionnaire de fichiers trouvé</string>
79 <string name="notification_no_directory_link">Impossible d\'ouvrir le répertoire de yuzu</string>
80 <string name="notification_no_directory_link_description">Veuillez localiser manuellement le dossier utilisateur avec le panneau latéral du gestionnaire de fichiers.</string>
81 <string name="manage_save_data">Gérer les données de sauvegarde</string>
82 <string name="manage_save_data_description">Données de sauvegarde trouvées. Veuillez sélectionner une option ci-dessous.</string>
83 <string name="import_export_saves_description">Importer ou exporter des fichiers de sauvegarde</string>
84 <string name="import_export_saves_no_profile">Aucune données de sauvegarde trouvées. Veuillez lancer un jeu et réessayer.</string>
85 <string name="save_file_imported_success">Importé avec succès</string>
86 <string name="save_file_invalid_zip_structure">Structure de répertoire de sauvegarde non valide</string>
87 <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string>
88 <string name="import_saves">Importer</string>
89 <string name="export_saves">Exporter</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia n\'est pas réel</string>
93 <string name="copied_to_clipboard">Copié dans le presse-papier</string>
94 <string name="about_app_description">Un émulateur Switch open source</string>
95 <string name="contributors">Contributeurs</string>
96 <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Build</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Early Access</string>
105 <string name="get_early_access">Obtenir l\'Early Access</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Fonctionnalités de pointe, accès anticipé aux mises à jour, et plus encore</string>
108 <string name="early_access_benefits">Avantages de l\'Early Access</string>
109 <string name="cutting_edge_features">Fonctionnalités de pointe</string>
110 <string name="early_access_updates">Accès anticipé aux mises à jour</string>
111 <string name="no_manual_installation">Pas d\'installation manuelle</string>
112 <string name="prioritized_support">Assistance prioritaire</string>
113 <string name="helping_game_preservation">Contribuer à la préservation des jeux</string>
114 <string name="our_eternal_gratitude">Notre gratitude éternelle</string>
115 <string name="are_you_interested">Es tu intéressé ?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Activer la vitesse limite</string>
119 <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>
120 <string name="frame_limit_slider">Limite en pourcentage de vitesse</string>
121 <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>
122 <string name="cpu_accuracy">Précision du CPU</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Mode TV</string>
126 <string name="use_docked_mode_description">Émuler en mode TV augmente la résolution au détriment des performances.</string>
127 <string name="emulated_region">Région émulée</string>
128 <string name="emulated_language">Langue émulée</string>
129 <string name="select_rtc_date">Sélectionner la date RTC</string>
130 <string name="select_rtc_time">Sélectionner l\'heure RTC</string>
131 <string name="use_custom_rtc">Activer l\'horloge RTC personnalisée</string>
132 <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>
133 <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Niveau de précision</string>
138 <string name="renderer_resolution">Résolution</string>
139 <string name="renderer_vsync">Mode VSync</string>
140 <string name="renderer_aspect_ratio">Format</string>
141 <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string>
142 <string name="renderer_anti_aliasing">Méthode d\'anticrénelage :</string>
143 <string name="renderer_force_max_clock">Forcer la fréquence d\'horloge maximale (Adreno uniquement)</string>
144 <string name="renderer_force_max_clock_description">Force le GPU à fonctionner au maximum d\'horloges possibles (les contraintes thermiques seront toujours appliquées).</string>
145 <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string>
146 <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>
147 <string name="renderer_debug">Activer le débogage des graphismes</string>
148 <string name="renderer_debug_description">Lorsque cette case est cochée, l\'API graphique entre dans un mode de débogage plus lent.</string>
149 <string name="use_disk_shader_cache">Utiliser les shader cache de disque</string>
150 <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>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Volume</string>
154 <string name="audio_volume_description">Spécifie le volume de la sortie audio.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Défaut</string>
158 <string name="ini_saved">Paramètres enregistrés</string>
159 <string name="gameid_saved">Paramètres enregistrés pour %1$s</string>
160 <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string>
161 <string name="loading">Chargement...</string>
162 <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string>
163 <string name="reset_to_default">Réinitialiser par défaut</string>
164 <string name="reset_all_settings">Réinitialiser tous les réglages ?</string>
165 <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>
166 <string name="settings_reset">Paramètres réinitialisés</string>
167 <string name="close">Fermer</string>
168 <string name="learn_more">Plus d\'informations</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Sélectionner le pilote du GPU</string>
172 <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
173 <string name="select_gpu_driver_install">Installer</string>
174 <string name="select_gpu_driver_default">Défaut</string>
175 <string name="select_gpu_driver_install_success">%s Installé</string>
176 <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
177 <string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
178 <string name="system_gpu_driver">Pilote du GPU du système</string>
179 <string name="installing_driver">Installation du pilote...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Paramètres</string>
183 <string name="preferences_general">Général</string>
184 <string name="preferences_system">Système</string>
185 <string name="preferences_graphics">Vidéo</string>
186 <string name="preferences_audio">Audio</string>
187 <string name="preferences_theme">Thème et couleur</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">Votre ROM est cryptée</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string>
194 <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>
195 <string name="loader_error_invalid_format">Impossible de charger la ROM</string>
196 <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Quitter l\'émulation</string>
200 <string name="emulation_done">Terminé</string>
201 <string name="emulation_fps_counter">Compteur FPS</string>
202 <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string>
203 <string name="emulation_rel_stick_center">Centre du stick relatif</string>
204 <string name="emulation_dpad_slide">Glissement du DPad</string>
205 <string name="emulation_haptics">Haptique</string>
206 <string name="emulation_show_overlay">Afficher l\'overlay</string>
207 <string name="emulation_toggle_all">Tout basculer</string>
208 <string name="emulation_control_adjust">Ajuster l\'overlay</string>
209 <string name="emulation_control_scale">Échelle</string>
210 <string name="emulation_control_opacity">Opacité</string>
211 <string name="emulation_touch_overlay_reset">Réinitialiser l\'overlay</string>
212 <string name="emulation_touch_overlay_edit">Modifier l\'overlay</string>
213 <string name="emulation_pause">Mettre en pause l\'émulation</string>
214 <string name="emulation_unpause">Reprendre l\'émulation</string>
215 <string name="emulation_input_overlay">Options de l\'overlay</string>
216 <string name="emulation_game_loading">Chargement du jeu...</string>
217
218 <string name="load_settings">Chargement des paramètres…</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Clavier virtuel</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Abandonner</string>
225 <string name="continue_button">Continuer</string>
226 <string name="system_archive_not_found">Archive système introuvable</string>
227 <string name="system_archive_not_found_message">%s est manquant. Veuillez dumper vos archives système.\nContinuer peut entraîner des plantages et des bogues.</string>
228 <string name="system_archive_general">Une archive système</string>
229 <string name="save_load_error">Erreur de sauvegarde/chargement</string>
230 <string name="fatal_error">Erreur fatale</string>
231 <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>
232 <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>
233
234 <!-- Region Names -->
235 <string name="region_japan">Japon</string>
236 <string name="region_usa">É.-U.A.</string>
237 <string name="region_europe">Europe</string>
238 <string name="region_australia">Australie</string>
239 <string name="region_china">Chine</string>
240 <string name="region_korea">Corée</string>
241 <string name="region_taiwan">Taïwan</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Japonais (日本語)</string>
245 <string name="language_english">Anglais</string>
246 <string name="language_french">Français (Français)</string>
247 <string name="langauge_german">Allemand (Deutsch)</string>
248 <string name="language_italian">Italien (Italiano)</string>
249 <string name="language_spanish">Espagnol (Español)</string>
250 <string name="language_chinese">Chinois (简体中文)</string>
251 <string name="language_korean">Coréen (한국어)</string>
252 <string name="language_dutch">Néerlandais (Nederlands)</string>
253 <string name="language_portuguese">Portugais (Português)</string>
254 <string name="language_russian">Russe (Русский)</string>
255 <string name="language_taiwanese">Taïwanais (台湾)</string>
256 <string name="language_british_english">Anglais Britannique</string>
257 <string name="language_canadian_french">Français canadien (Français canadien)</string>
258 <string name="language_latin_american_spanish">Espagnol latino-américain (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">Chinois simplifié (简体中文)</string>
260 <string name="language_traditional_chinese">Chinois Traditionnel (正體中文)</string>
261 <string name="language_brazilian_portuguese">Portugais brésilien (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Aucune</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normal</string>
269 <string name="renderer_accuracy_high">Haut</string>
270 <string name="renderer_accuracy_extreme">Extrême (Lent)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Lent)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Lent)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Lent)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Immédiat (Désactivé)</string>
282 <string name="renderer_vsync_mailbox">Mailbox</string>
283 <string name="renderer_vsync_fifo">FIFO (Activé)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO souple</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Plus proche voisin</string>
288 <string name="scaling_filter_bilinear">Bilinéaire</string>
289 <string name="scaling_filter_bicubic">Bicubique</string>
290 <string name="scaling_filter_gaussian">Gaussien</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Aucune</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Par défaut (16:9)</string>
301 <string name="ratio_force_four_three">Forcer le 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Forcer le 21:9</string>
303 <string name="ratio_force_sixteen_ten">Forcer le 16:10</string>
304 <string name="ratio_stretch">Étirer à la fenêtre</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Précis</string>
308 <string name="cpu_accuracy_unsafe">Risqué</string>
309 <string name="cpu_accuracy_paranoid">Paranoïaque (Lent)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">Pavé directionnel</string>
313 <string name="gamepad_left_stick">Stick Gauche</string>
314 <string name="gamepad_right_stick">Stick Droit</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Capture d\'écran</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Préparation des shaders</string>
320 <string name="building_shaders">Compilation des shaders</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Changer le thème de l\'application</string>
324 <string name="theme_default">Défaut</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Changer le mode de thème</string>
329 <string name="theme_mode_follow_system">Automatique</string>
330 <string name="theme_mode_light">Lumineux</string>
331 <string name="theme_mode_dark">Sombre</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Utiliser des arrière-plans noirs</string>
335 <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer des arrière-plans noirs.</string>
336
337</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
new file mode 100644
index 000000000..47a4cfa31
--- /dev/null
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
5 <string name="emulation_notification_channel_name">L\'emulatore è attivo</string>
6 <string name="emulation_notification_channel_description">Mostra una notifica persistente quando l\'emulatore è in esecuzione.</string>
7 <string name="emulation_notification_running">yuzu è in esecuzione</string>
8 <string name="notice_notification_channel_name">Avvisi ed errori</string>
9 <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string>
10 <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Benvenuto!</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>
16 <string name="keys">Pulsanti</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>
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>
21 <string name="done">Fatto</string>
22 <string name="done_description">È tutto pronto.\nDivertiti a giocare!</string>
23 <string name="text_continue">Continua</string>
24 <string name="next">Successivo</string>
25 <string name="back">Indietro</string>
26 <string name="add_games">Aggiungi giochi</string>
27 <string name="add_games_description">Seleziona la cartella dei giochi</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Giochi</string>
31 <string name="home_search">Cerca</string>
32 <string name="home_settings">Impostazioni</string>
33 <string name="empty_gamelist">Non sono stati trovati file o non è stata ancora selezionata alcuna directory di gioco.</string>
34 <string name="search_and_filter_games">Cerca e filtra i giochi</string>
35 <string name="select_games_folder">Seleziona la cartella di gioco</string>
36 <string name="select_games_folder_description">Consente a yuzu di popolare l\'elenco dei giochi</string>
37 <string name="add_games_warning">Saltare la selezione della cartella dei giochi?</string>
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_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Cerca giochi</string>
41 <string name="games_dir_selected">Cartella dei giochi selezionata</string>
42 <string name="install_prod_keys">Installa prod.keys</string>
43 <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string>
44 <string name="install_prod_keys_warning">Saltare l\'aggiunta delle chiavi?</string>
45 <string name="install_prod_keys_warning_description">Sono necessarie delle chiavi valide per emulare i giochi. Se continui, funzioneranno solo le app homebrew.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Notifiche</string>
48 <string name="notifications_description">Concedi l\'autorizzazione alle notifiche con il pulsante in basso.</string>
49 <string name="give_permission">Concedere l\'autorizzazione</string>
50 <string name="notification_warning">Saltare la concessione dell\'autorizzazione alle notifiche?</string>
51 <string name="notification_warning_description">yuzu non sarà in grado di notificarti informazioni importanti.</string>
52 <string name="permission_denied">Permesso negato</string>
53 <string name="permission_denied_description">Hai negato l\'autorizzazione troppe volte ed ora devi concederla manualmente nelle impostazioni di sistema.</string>
54 <string name="about">Informazioni</string>
55 <string name="about_description">Versione build, crediti ed altro</string>
56 <string name="warning_help">Aiuto</string>
57 <string name="warning_skip">Salta</string>
58 <string name="warning_cancel">Annulla</string>
59 <string name="install_amiibo_keys">Installa le chiavi degli Amiibo</string>
60 <string name="install_amiibo_keys_description">Necessario per usare gli Amiibo in gioco</string>
61 <string name="invalid_keys_file">Selezionate chiavi non valide</string>
62 <string name="install_keys_success">Chiavi installate correttamente</string>
63 <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string>
64 <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>
66 <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>
68 <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string>
69 <string name="advanced_settings">Impostazioni avanzate</string>
70 <string name="settings_description">Configura le impostazioni dell\'emulatore</string>
71 <string name="search_recently_played">Giocato recentemente</string>
72 <string name="search_recently_added">Aggiunto recentemente</string>
73 <string name="search_retail">Rivenditore</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Apri la cartella di yuzu</string>
76 <string name="open_user_folder_description">Gestisci i file interni di yuzu</string>
77 <string name="theme_and_color_description">Modifica l\'aspetto dell\'app</string>
78 <string name="no_file_manager">Nessun file manager trovato</string>
79 <string name="notification_no_directory_link">Impossibile aprire la cartella di yuzu</string>
80 <string name="notification_no_directory_link_description">Per favore individua la cartella dell\'utente manualmente con il pannello laterale del file manager.</string>
81 <string name="manage_save_data">Gestisci i salvataggi</string>
82 <string name="manage_save_data_description">Salvataggio non trovato. Seleziona un\'opzione di seguito.</string>
83 <string name="import_export_saves_description">Importa o esporta i salvataggi</string>
84 <string name="import_export_saves_no_profile">Nessun salvataggio trovato. Avvia un gioco e riprova.</string>
85 <string name="save_file_imported_success">Importato con successo</string>
86 <string name="save_file_invalid_zip_structure">La struttura della cartella dei salvataggi è invalida</string>
87 <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string>
88 <string name="import_saves">Importa</string>
89 <string name="export_saves">Esporta</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia non è reale</string>
93 <string name="copied_to_clipboard">Copiato negli appunti</string>
94 <string name="about_app_description">Un emulatore della Switch open-source.</string>
95 <string name="contributors">Collaboratori</string>
96 <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Compilazione</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Accesso Anticipato</string>
105 <string name="get_early_access">Ottieni l\'accesso anticipato</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Funzionalità all\'avanguardia, aggiornamenti in anticipo e altro</string>
108 <string name="early_access_benefits">Vantaggi dell\'accesso anticipato</string>
109 <string name="cutting_edge_features">Funzionalità all\'avanguardia</string>
110 <string name="early_access_updates">Accesso anticipato agli aggiornamenti</string>
111 <string name="no_manual_installation">Non installare manualmente.</string>
112 <string name="prioritized_support">Supporto prioritario</string>
113 <string name="helping_game_preservation">Aiuta a preservare il gioco</string>
114 <string name="our_eternal_gratitude">La nostra gratitudine eterna</string>
115 <string name="are_you_interested">Sei interessato?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Abilita il limite di velocità</string>
119 <string name="frame_limit_enable_description">Quando abilitato, la velocità di emulazione verrà limitata a una specifica percentuale della velocità normale.</string>
120 <string name="frame_limit_slider">Limite velocità percentuale</string>
121 <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>
122 <string name="cpu_accuracy">Accuratezza della CPU</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Modalità docked</string>
126 <string name="use_docked_mode_description">Emula in modalità docked, questo aumenta la risoluzione a spese delle performance.</string>
127 <string name="emulated_region">Regione emulata</string>
128 <string name="emulated_language">Lingua emulata</string>
129 <string name="select_rtc_date">Seleziona la data dall\'orologio in tempo reale</string>
130 <string name="select_rtc_time">Seleziona il tempo dall\'orologio in tempo reale</string>
131 <string name="use_custom_rtc">Abilità l\'orologio in tempo reale personalizzato</string>
132 <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>
133 <string name="set_custom_rtc">Imposta l\'orologio in tempo reale personalizzato</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Livello di accuratezza</string>
138 <string name="renderer_resolution">Risoluzione</string>
139 <string name="renderer_vsync">Modalità VSync</string>
140 <string name="renderer_aspect_ratio">Rapporto d\'aspetto</string>
141 <string name="renderer_scaling_filter">Filtro di adattamento alla finestra</string>
142 <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string>
143 <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string>
144 <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string>
145 <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string>
146 <string name="renderer_asynchronous_shaders_description">Compila le shaders asincronamente, questo riduce lo shutter ma potrebbe introdurre dei glitch. </string>
147 <string name="renderer_debug">Abilità il debug grafico</string>
148 <string name="renderer_debug_description">Quando l\'opzione è selezionata, l\'API grafica entra in una modalità di debug più lenta</string>
149 <string name="use_disk_shader_cache">Usa cache shader su disco</string>
150 <string name="use_disk_shader_cache_description">Riduce lo stuttering salvando e caricando le shader generate sul disco.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Volume</string>
154 <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Predefinito</string>
158 <string name="ini_saved">Impostazioni salvate</string>
159 <string name="gameid_saved">Impostazioni salvate per %1$s</string>
160 <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string>
161 <string name="loading">Caricamento…</string>
162 <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string>
163 <string name="reset_to_default">Riportare alle impostazioni originali</string>
164 <string name="reset_all_settings">Resettare tutte le impostazioni?</string>
165 <string name="reset_all_settings_description">Tutte le Impostazioni Avanzate saranno ripristinate a quelle originali. Questa operazione non è reversibile</string>
166 <string name="settings_reset">Reimposta le impostazioni</string>
167 <string name="close">Chiudi</string>
168 <string name="learn_more">Per saperne di più</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Seleziona il driver della GPU</string>
172 <string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
173 <string name="select_gpu_driver_install">Installa</string>
174 <string name="select_gpu_driver_default">Predefinito</string>
175 <string name="select_gpu_driver_install_success">Installato%s</string>
176 <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
177 <string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
178 <string name="system_gpu_driver">Driver GPU del sistema</string>
179 <string name="installing_driver">Installando i driver...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Impostazioni</string>
183 <string name="preferences_general">Generali</string>
184 <string name="preferences_system">Sistema</string>
185 <string name="preferences_graphics">Grafica</string>
186 <string name="preferences_audio">Audio</string>
187 <string name="preferences_theme">Tema e colori</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">La tua ROM è criptata</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string>
194 <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>
195 <string name="loader_error_invalid_format">Impossibile caricare la ROM</string>
196 <string name="loader_error_file_not_found">Il file della ROM non esiste</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Uscire dall\'emulazione</string>
200 <string name="emulation_done">Fatto</string>
201 <string name="emulation_fps_counter">Contatore degli FPS</string>
202 <string name="emulation_toggle_controls">Controlli a interruttore</string>
203 <string name="emulation_rel_stick_center">Centro relativo degli Stick</string>
204 <string name="emulation_dpad_slide">Slittamento del Pad Direzionale</string>
205 <string name="emulation_haptics">Aptico</string>
206 <string name="emulation_show_overlay">Mostra Overlay</string>
207 <string name="emulation_toggle_all">Attiva/disattiva tutto</string>
208 <string name="emulation_control_adjust">Aggiusta Overlay</string>
209 <string name="emulation_control_scale">Scala</string>
210 <string name="emulation_control_opacity">Opacità</string>
211 <string name="emulation_touch_overlay_reset">Reimposta Overlay</string>
212 <string name="emulation_touch_overlay_edit">Modifica Overlay</string>
213 <string name="emulation_pause">Metti in pausa l\'emulazione</string>
214 <string name="emulation_unpause">Riprendi Emulazione</string>
215 <string name="emulation_input_overlay">Impostazioni Overlay</string>
216 <string name="emulation_game_loading">Caricamento del gioco...</string>
217
218 <string name="load_settings">Caricamento delle impostazioni...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Tastiera software</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Interrompi</string>
225 <string name="continue_button">Continua</string>
226 <string name="system_archive_not_found">Archivio di sistema non trovato</string>
227 <string name="system_archive_not_found_message">%s è mancante. Per favore esegui il dump degli archivi del tuo sistema.\nContinuare ad emulare potrebbe portare bug o causare crash.</string>
228 <string name="system_archive_general">Un archivio di sistema</string>
229 <string name="save_load_error">Errore di salvataggio/caricamento</string>
230 <string name="fatal_error">Errore Fatale</string>
231 <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>
232 <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">Giappone</string>
236 <string name="region_usa">USA</string>
237 <string name="region_europe">Europa</string>
238 <string name="region_australia">Australia</string>
239 <string name="region_china">Cina</string>
240 <string name="region_korea">Corea</string>
241 <string name="region_taiwan">Taiwan</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Giapponese (日本語)</string>
245 <string name="language_english">Inglese (English)</string>
246 <string name="language_french">Francese (Français)</string>
247 <string name="langauge_german">Tedesco (Deutsch)</string>
248 <string name="language_italian">Italiano (Italiano)</string>
249 <string name="language_spanish">Spagnolo (Español)</string>
250 <string name="language_chinese">Cinese (简体中文)</string>
251 <string name="language_korean">Coreano (한국어)</string>
252 <string name="language_dutch">Olandese (Nederlands)</string>
253 <string name="language_portuguese">Portoghese (Português)</string>
254 <string name="language_russian">Russo (Русский)</string>
255 <string name="language_taiwanese">Taiwanese (台湾)</string>
256 <string name="language_british_english">Inglese britannico</string>
257 <string name="language_canadian_french">Francese Canadese (Français canadien)</string>
258 <string name="language_latin_american_spanish">Spagnolo Latino Americano (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">Cinese Semplificato (简体中文)</string>
260 <string name="language_traditional_chinese">Cinese tradizionale (正體中文)</string>
261 <string name="language_brazilian_portuguese">Portoghese (Português)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Nessuna</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normale</string>
269 <string name="renderer_accuracy_high">Alta</string>
270 <string name="renderer_accuracy_extreme">Estrema (Lenta)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Slow)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Slow)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Slow)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Immediato (Off)</string>
282 <string name="renderer_vsync_mailbox">Cassella postale</string>
283 <string name="renderer_vsync_fifo">FIFO (On)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Rilassato</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Nearest neighbor</string>
288 <string name="scaling_filter_bilinear">Bilineare</string>
289 <string name="scaling_filter_bicubic">Bicubico</string>
290 <string name="scaling_filter_gaussian">Gaussiano</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™️ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Nessuna</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Predefinito (16:9)</string>
301 <string name="ratio_force_four_three">Forza 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Forza 21:9</string>
303 <string name="ratio_force_sixteen_ten">Forza 16:10</string>
304 <string name="ratio_stretch">Allunga a finestra</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Accurata</string>
308 <string name="cpu_accuracy_unsafe">Non sicura</string>
309 <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">D-Pad</string>
313 <string name="gamepad_left_stick">Levetta sinistra</string>
314 <string name="gamepad_right_stick">Levetta destra</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Screenshot</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Preparazione degli shaders</string>
320 <string name="building_shaders">Costruendo gli shaders</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Cambia il tema dell\'app</string>
324 <string name="theme_default">Predefinito</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Cambia la modalità del tema</string>
329 <string name="theme_mode_follow_system">Segue il Sistema</string>
330 <string name="theme_mode_light">Chiaro</string>
331 <string name="theme_mode_dark">Scuro</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Usa sfondi neri</string>
335 <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string>
336
337</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
new file mode 100644
index 000000000..46eda9ef7
--- /dev/null
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -0,0 +1,335 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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_description">問題が発生したときに通知を表示します。</string>
9 <string name="notification_permission_not_granted">通知が許可されていません!</string>
10
11 <!-- Setup strings -->
12 <string name="welcome">ようこそ!</string>
13 <string name="welcome_description">&lt;b>yuzu&lt;/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
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">yuzu がゲームリストに追加できるようにします</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">製品版ゲームのエミュレーションには、有効なキーが必要です。続行すると自作アプリしか機能しません。</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">yuzuは重要なお知らせを通知できません。</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="invalid_keys_error">暗号化キーが無効です</string>
64 <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
65 <string name="install_keys_failure_description">選択されたファイルが不正または破損しています。キーを再ダンプしてください。</string>
66 <string name="install_gpu_driver">GPUドライバーをインストール</string>
67 <string name="install_gpu_driver_description">代替ドライバーをインストールしてパフォーマンスや精度を向上させます</string>
68 <string name="advanced_settings">高度な設定</string>
69 <string name="settings_description">エミュレーターの設定を構成します</string>
70 <string name="search_recently_played">最近プレイした</string>
71 <string name="search_recently_added">最近追加された</string>
72 <string name="search_retail">製品版</string>
73 <string name="search_homebrew">自作</string>
74 <string name="open_user_folder">yuzu フォルダを開く</string>
75 <string name="open_user_folder_description">yuzu内部のファイルを管理します</string>
76 <string name="theme_and_color_description">アプリの見た目を変更</string>
77 <string name="no_file_manager">ファイルマネージャーが見つかりませんでした</string>
78 <string name="notification_no_directory_link">yuzuのディレクトリを開けません</string>
79 <string name="notification_no_directory_link_description">ファイルマネージャのサイドパネルでユーザーフォルダを手動で探してください。</string>
80 <string name="manage_save_data">セーブデータを管理</string>
81 <string name="manage_save_data_description">セーブデータが見つかりました。以下のオプションから選択してください。</string>
82 <string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string>
83 <string name="import_export_saves_no_profile">セーブデータがありません。ゲームを起動してから再度お試しください。</string>
84 <string name="save_file_imported_success">インポートが完了しました</string>
85 <string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string>
86 <string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string>
87 <string name="import_saves">インポート</string>
88 <string name="export_saves">エクスポート</string>
89
90 <!-- About screen strings -->
91 <string name="gaia_is_not_real">ガイアは実在しない</string>
92 <string name="copied_to_clipboard">クリップボードにコピーしました</string>
93 <string name="about_app_description">オープンソースのSwitchエミュレータ</string>
94 <string name="contributors">貢献者</string>
95 <string name="contributors_description">yuzuチームの\u2764で作られた</string>
96 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
97 <string name="build">ビルド</string>
98 <string name="support_link">https://discord.gg/u77vRWY</string>
99 <string name="website_link">https://yuzu-emu.org/</string>
100 <string name="github_link">https://github.com/yuzu-emu</string>
101
102 <!-- Early access upgrade strings -->
103 <string name="early_access">早期アクセス</string>
104 <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>
106 <string name="get_early_access_description">最先端の機能、アップデートの早期アクセスなど</string>
107 <string name="early_access_benefits">早期アクセスのメリット</string>
108 <string name="cutting_edge_features">最先端の機能</string>
109 <string name="early_access_updates">アップデートの早期アクセス</string>
110 <string name="no_manual_installation">手動インストールが不要</string>
111 <string name="prioritized_support">優先的なサポート</string>
112 <string name="helping_game_preservation">ゲームの保存に貢献</string>
113 <string name="our_eternal_gratitude">私たちの永遠の感謝</string>
114 <string name="are_you_interested">興味がありますか?</string>
115
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>
120 <string name="frame_limit_slider_description">エミュレーション速度を制限する割合を指定します。デフォルトの100%では、エミュレーションは通常の速度に制限されます。値が高いまたは低いほど、速度制限が増加または減少します。</string>
121 <string name="cpu_accuracy">CPU精度</string>
122
123 <!-- System settings strings -->
124 <string name="use_docked_mode">TVモード</string>
125 <string name="use_docked_mode_description">TVモードでエミュレートします。パフォーマンスが犠牲になりますが、解像度が向上します。</string>
126 <string name="emulated_region">地域</string>
127 <string name="emulated_language">言語</string>
128 <string name="select_rtc_date">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 -->
135 <string name="renderer_api">API</string>
136 <string name="renderer_accuracy">精度</string>
137 <string name="renderer_resolution">解像度</string>
138 <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>
143 <string name="renderer_force_max_clock_description">GPUを可能な限り最大クロックで動作させます (過熱制限は引き続き適用されます)。</string>
144 <string name="renderer_asynchronous_shaders">非同期シェーダー</string>
145 <string name="renderer_asynchronous_shaders_description">シェーダーを非同期でコンパイルします。コマ落ちが軽減されますが、不具合が発生する可能性があります。</string>
146 <string name="renderer_debug">グラフィックデバッグ</string>
147 <string name="renderer_debug_description">オンにすると、グラフィック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>
153 <string name="audio_volume_description">オーディオ出力の音量を指定します</string>
154
155 <!-- Miscellaneous -->
156 <string name="slider_default">デフォルト</string>
157 <string name="ini_saved">設定を保存しました</string>
158 <string name="gameid_saved">%1$sの設定を保存しました</string>
159 <string name="error_saving">%1$s.ini の保存エラー: %2$s</string>
160 <string name="loading">読み込み中…</string>
161 <string name="reset_setting_confirmation">この設定を初期値にリセットしますか?</string>
162 <string name="reset_to_default">初期設定に戻す</string>
163 <string name="reset_all_settings">すべての設定をリセットしますか?</string>
164 <string name="reset_all_settings_description">すべての詳細設定が初期設定に戻されます。この操作は元に戻せません。</string>
165 <string name="settings_reset">設定をリセットしました</string>
166 <string name="close">閉じる</string>
167 <string name="learn_more">詳細情報</string>
168
169 <!-- GPU driver installation -->
170 <string name="select_gpu_driver">GPUドライバを選択</string>
171 <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
172 <string name="select_gpu_driver_install">インストール</string>
173 <string name="select_gpu_driver_default">デフォルト</string>
174 <string name="select_gpu_driver_install_success">%s をインストールしました</string>
175 <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
176 <string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
177 <string name="system_gpu_driver">システムのGPUドライバ</string>
178 <string name="installing_driver">インストール中…</string>
179
180 <!-- Preferences Screen -->
181 <string name="preferences_settings">設定</string>
182 <string name="preferences_general">全般</string>
183 <string name="preferences_system">システム</string>
184 <string name="preferences_graphics">グラフィック</string>
185 <string name="preferences_audio">サウンド</string>
186 <string name="preferences_theme">テーマと色</string>
187
188 <!-- ROM loading errors -->
189 <string name="loader_error_encrypted">ROMが暗号化されています</string>
190 <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>
191 <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームを復号化するために <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルがインストールされていることを確認してください。]]></string>
192 <string name="loader_error_video_core">ビデオコアの初期化中にエラーが発生しました</string>
193 <string name="loader_error_video_core_description">これは通常、互換性のないGPUドライバーが原因で発生します。 カスタムGPUドライバーをインストールすると、問題が解決する可能性があります。</string>
194 <string name="loader_error_invalid_format">ROMの読み込みに失敗しました</string>
195 <string name="loader_error_file_not_found">ROMファイルが存在しません</string>
196
197 <!-- Emulation Menu -->
198 <string name="emulation_exit">エミュレーションを終了</string>
199 <string name="emulation_done">完了</string>
200 <string name="emulation_fps_counter">FPSカウンター</string>
201 <string name="emulation_toggle_controls">コントロールを切り替え</string>
202 <string name="emulation_dpad_slide">十字キーのスライド操作</string>
203 <string name="emulation_haptics">振動</string>
204 <string name="emulation_show_overlay">オーバーレイを表示</string>
205 <string name="emulation_toggle_all">すべて選択</string>
206 <string name="emulation_control_adjust">オーバーレイを調整</string>
207 <string name="emulation_control_scale">大きさ</string>
208 <string name="emulation_control_opacity">不透明度</string>
209 <string name="emulation_touch_overlay_reset">リセット</string>
210 <string name="emulation_touch_overlay_edit">オーバーレイを編集</string>
211 <string name="emulation_pause">エミュレーションを一時停止</string>
212 <string name="emulation_unpause">エミュレーションを再開</string>
213 <string name="emulation_input_overlay">オーバーレイオプション</string>
214 <string name="emulation_game_loading">ロード中…</string>
215
216 <string name="load_settings">設定をロード中…</string>
217
218 <!-- Software keyboard -->
219 <string name="software_keyboard">ソフトウェアキーボード</string>
220
221 <!-- Errors and warnings -->
222 <string name="abort_button">中断</string>
223 <string name="continue_button">続行</string>
224 <string name="system_archive_not_found">システムアーカイブが見つかりません</string>
225 <string name="system_archive_not_found_message">%s が見つかりません。システムアーカイブをダンプしてください。\nエミュレーションを続行すると、クラッシュやバグが発生する可能性があります。</string>
226 <string name="system_archive_general">システムアーカイブ</string>
227 <string name="save_load_error">セーブ/ロード エラー</string>
228 <string name="fatal_error">致命的なエラー</string>
229 <string name="fatal_error_message">致命的なエラーが発生しました。詳細はログを確認してください。\nエミュレーションを続行するとクラッシュやバグが発生する可能性があります。</string>
230 <string name="performance_warning">この設定をオフにすると、エミュレーションのパフォーマンスが著しく低下します!最高の体験を得るためには、この設定を有効にしておくことをお勧めします。</string>
231
232 <!-- Region Names -->
233 <string name="region_japan">日本</string>
234 <string name="region_usa">アメリカ</string>
235 <string name="region_europe">ヨーロッパ</string>
236 <string name="region_australia">オーストラリア</string>
237 <string name="region_china">中国</string>
238 <string name="region_korea">韓国</string>
239 <string name="region_taiwan">台湾</string>
240
241 <!-- Language Names -->
242 <string name="language_japanese">日本語</string>
243 <string name="language_english">英語</string>
244 <string name="language_french">フランス語 (Français)</string>
245 <string name="langauge_german">ドイツ語 (Deutsch)</string>
246 <string name="language_italian">イタリア語 (Italiano)</string>
247 <string name="language_spanish">スペイン語 (Español)</string>
248 <string name="language_chinese">中国語 (简体中文)</string>
249 <string name="language_korean">韓国語 (한국어)</string>
250 <string name="language_dutch">オランダ語 (Nederlands)</string>
251 <string name="language_portuguese">ポルトガル語 (Português)</string>
252 <string name="language_russian">ロシア語 (Русский)</string>
253 <string name="language_taiwanese">台湾語 (台湾)</string>
254 <string name="language_british_english">イギリス英語</string>
255 <string name="language_canadian_french">フランス語(カナダ) (Français canadien)</string>
256 <string name="language_latin_american_spanish">スペイン語(ラテンアメリカ) (Español latinoamericano)</string>
257 <string name="language_simplified_chinese">中国語 (简体中文)</string>
258 <string name="language_traditional_chinese">繁体字中国語 (正體中文)</string>
259 <string name="language_brazilian_portuguese">ポルトガル語(ブラジル) (Português do Brasil)</string>
260
261 <!-- Renderer APIs -->
262 <string name="renderer_vulkan">Vulkan</string>
263 <string name="renderer_none">なし</string>
264
265 <!-- Renderer Accuracy -->
266 <string name="renderer_accuracy_normal">標準</string>
267 <string name="renderer_accuracy_high">高い</string>
268 <string name="renderer_accuracy_extreme">最高 (低速)</string>
269
270 <!-- Resolutions -->
271 <string name="resolution_half">0.5X (360p/540p)</string>
272 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
273 <string name="resolution_one">1X (720p/1080p)</string>
274 <string name="resolution_two">2X (1440p/2160p) (低速)</string>
275 <string name="resolution_three">3X (2160p/3240p) (低速)</string>
276 <string name="resolution_four">4X (2880p/4320p) (低速)</string>
277
278 <!-- Renderer VSync -->
279 <string name="renderer_vsync_immediate">Immediate (オフ)</string>
280 <string name="renderer_vsync_mailbox">Mailbox</string>
281 <string name="renderer_vsync_fifo">FIFO (オン)</string>
282 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
283
284 <!-- Scaling Filters -->
285 <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string>
286 <string name="scaling_filter_bilinear">Bilinear</string>
287 <string name="scaling_filter_bicubic">Bicubic</string>
288 <string name="scaling_filter_gaussian">Gaussian</string>
289 <string name="scaling_filter_scale_force">ScaleForce</string>
290 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
291
292 <!-- Anti-Aliasing -->
293 <string name="anti_aliasing_none">なし</string>
294 <string name="anti_aliasing_fxaa">FXAA</string>
295 <string name="anti_aliasing_smaa">SMAA</string>
296
297 <!-- Aspect Ratios -->
298 <string name="ratio_default">デフォルト (16:9)</string>
299 <string name="ratio_force_four_three">強制 4:3</string>
300 <string name="ratio_force_twenty_one_nine">強制 21:9</string>
301 <string name="ratio_force_sixteen_ten">強制 16:10</string>
302 <string name="ratio_stretch">ウィンドウに合わせる</string>
303
304 <!-- CPU Accuracy -->
305 <string name="cpu_accuracy_accurate">正確</string>
306 <string name="cpu_accuracy_unsafe">不安定</string>
307 <string name="cpu_accuracy_paranoid">パラノイド (低速)</string>
308
309 <!-- Gamepad Buttons -->
310 <string name="gamepad_d_pad">方向ボタン</string>
311 <string name="gamepad_left_stick">Lスティック</string>
312 <string name="gamepad_right_stick">Rスティック</string>
313 <string name="gamepad_home">HOMEボタン</string>
314 <string name="gamepad_screenshot">スクリーンショット</string>
315
316 <!-- Disk shader cache -->
317 <string name="preparing_shaders">シェーダーを準備しています</string>
318 <string name="building_shaders">シェーダーを構築しています</string>
319
320 <!-- Theme options -->
321 <string name="change_app_theme">アプリのテーマ</string>
322 <string name="theme_default">デフォルト</string>
323 <string name="theme_material_you">Material You</string>
324
325 <!-- Theme Modes -->
326 <string name="change_theme_mode">テーマモード</string>
327 <string name="theme_mode_follow_system">システムに従う</string>
328 <string name="theme_mode_light">ライト</string>
329 <string name="theme_mode_dark">ダーク</string>
330
331 <!-- Black backgrounds theme -->
332 <string name="use_black_backgrounds">黒色の背景を使用</string>
333 <string name="use_black_backgrounds_description">ダークテーマの使用時は、黒色の背景を有効にしてください。</string>
334
335</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
new file mode 100644
index 000000000..5da80ab4b
--- /dev/null
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
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">Keys</string>
17 <string name="keys_description">아래 버튼을 사용하여 &lt;b>prod.keys&lt;/b> 파일을 선택합니다.</string>
18 <string name="select_keys">keys 선택</string>
19 <string name="games">게임</string>
20 <string name="games_description">아래 버튼으로 &lt;b>게임&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
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">yuzu가 게임 목록을 채울 수 있도록 허용</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="games_dir_selected">게임 디렉터리 선택</string>
42 <string name="install_prod_keys">prod.keys 설치</string>
43 <string name="install_prod_keys_description">판매용 게임 암호 해독에 요구</string>
44 <string name="install_prod_keys_warning">keys 추가를 건너뛰겠습니까?</string>
45 <string name="install_prod_keys_warning_description">정품 게임을 에뮬레이트하려면 유효한 keys가 필요합니다. 계속하면 자체 제작 앱만 작동합니다.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">알림</string>
48 <string name="notifications_description">아래 버튼으로 알림 권한을 부여합니다.</string>
49 <string name="give_permission">권한 부여</string>
50 <string name="notification_warning">알림 권한 부여를 건너뛰겠습니까?</string>
51 <string name="notification_warning_description">yuzu는 중요한 정보를 알려드리지 않습니다.</string>
52 <string name="permission_denied">권한 거부됨</string>
53 <string name="permission_denied_description">이 권한을 너무 많이 거부했으므로 이제 시스템 설정에서 수동으로 권한을 부여해야 합니다.</string>
54 <string name="about">정보</string>
55 <string name="about_description">빌드 버전, 크레딧 등</string>
56 <string name="warning_help">도움말</string>
57 <string name="warning_skip">건너뛰기</string>
58 <string name="warning_cancel">취소</string>
59 <string name="install_amiibo_keys">Amiibo keys 설치</string>
60 <string name="install_amiibo_keys_description">게임에서 아미보 사용 시 필요</string>
61 <string name="invalid_keys_file">잘못된 keys 파일 선택</string>
62 <string name="install_keys_success">keys가 성공적으로 설치됨</string>
63 <string name="reading_keys_failure">암호화 keys 읽기 오류</string>
64 <string name="invalid_keys_error">잘못된 암호화 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">선택한 파일이 잘못되었거나 손상되었습니다. keys를 다시 덤프하세요.</string>
67 <string name="install_gpu_driver">GPU 드라이버 설치</string>
68 <string name="install_gpu_driver_description">잠재적으로 더 나은 성능 또는 정확성을 위해 대체 드라이버를 설치하세요.</string>
69 <string name="advanced_settings">고급 설정</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>
74 <string name="search_homebrew">홈브류</string>
75 <string name="open_user_folder">yuzu 폴더 열기</string>
76 <string name="open_user_folder_description">yuzu의 내부 파일 관리</string>
77 <string name="theme_and_color_description">앱 모양 수정</string>
78 <string name="no_file_manager">파일 관리자를 찾을 수 없음</string>
79 <string name="notification_no_directory_link">yuzu 디렉토리를 열 수 없음</string>
80 <string name="notification_no_directory_link_description">파일 관리자의 사이드 패널에서 사용자 폴더를 수동으로 찾아주세요.</string>
81 <string name="manage_save_data">저장 데이터 관리</string>
82 <string name="manage_save_data_description">데이터를 저장했습니다. 아래에서 옵션을 선택하세요.</string>
83 <string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string>
84 <string name="import_export_saves_no_profile">저장 데이터를 찾을 수 없습니다. 게임을 실행한 후 다시 시도하세요.</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">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string>
88 <string name="import_saves">가져오기</string>
89 <string name="export_saves">내보내기</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">가이아는 진짜가 아님</string>
93 <string name="copied_to_clipboard">클립보드에 복사</string>
94 <string name="about_app_description">오픈 소스 스위치 에뮬레이터</string>
95 <string name="contributors">기여자</string>
96 <string name="contributors_description">yuzu 팀의 \u2764로 제작</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">빌드</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">미리 체험하기</string>
105 <string name="get_early_access">미리 체험하기 신청</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">최첨단 기능, 미리 체험하기 업데이트 등</string>
108 <string name="early_access_benefits">미리 체험하기 혜택</string>
109 <string name="cutting_edge_features">최첨단 기능</string>
110 <string name="early_access_updates">미리 체험하기 업데이트</string>
111 <string name="no_manual_installation">수동 설치 불필요</string>
112 <string name="prioritized_support">우선 지원</string>
113 <string name="helping_game_preservation">게임 보존 도움주기</string>
114 <string name="our_eternal_gratitude">영원한 감사의 마음을 전합니다</string>
115 <string name="are_you_interested">관심 있으세요?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">제한 속도 활성화</string>
119 <string name="frame_limit_enable_description">활성화하면 에뮬레이션 속도가 정상 속도의 지정된 비율로 제한됩니다.</string>
120 <string name="frame_limit_slider">속도 제한 비율</string>
121 <string name="frame_limit_slider_description">에뮬레이션 속도를 제한할 비율을 지정합니다. 기본값인 100%로 설정하면 에뮬레이션이 정상 속도로 제한됩니다. 값이 높거나 낮으면 속도 제한이 증가하거나 감소합니다.</string>
122 <string name="cpu_accuracy">CPU 정확도</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">도킹 모드</string>
126 <string name="use_docked_mode_description">도킹 모드에서 에뮬레이션하면 성능이 저하되는 대신 해상도가 향상됩니다.</string>
127 <string name="emulated_region">에뮬레이트된 지역</string>
128 <string name="emulated_language">에뮬레이트된 언어</string>
129 <string name="select_rtc_date">RTC 날짜 선택</string>
130 <string name="select_rtc_time">RTC 시간 선택</string>
131 <string name="use_custom_rtc">커스텀 RTC 활성화</string>
132 <string name="use_custom_rtc_description">이 설정을 사용하면 현재 시스템 시간과 별도로 사용자 지정 실시간 시계를 설정할 수 있음</string>
133 <string name="set_custom_rtc">커스텀 RTC 설정</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">정확도 수준</string>
138 <string name="renderer_resolution">해상도</string>
139 <string name="renderer_vsync">수직동기화 모드</string>
140 <string name="renderer_aspect_ratio">화면비</string>
141 <string name="renderer_scaling_filter">창 적응 필터</string>
142 <string name="renderer_anti_aliasing">안티-에일리어싱 방법</string>
143 <string name="renderer_force_max_clock">최대 클럭 강제 설정 (아드레노만 해당)</string>
144 <string name="renderer_force_max_clock_description">GPU가 가능한 최대 클럭으로 실행되도록 강제합니다 (열 제약 조건은 여전히 적용됩니다).</string>
145 <string name="renderer_asynchronous_shaders">비동기 셰이더 사용</string>
146 <string name="renderer_asynchronous_shaders_description">셰이더를 비동기식으로 컴파일하므로 끊김 현상이 줄어들지만 글리치가 발생할 수 있습니다.</string>
147 <string name="renderer_debug">그래픽 디버깅 활성화</string>
148 <string name="renderer_debug_description">이 옵션을 선택하면 그래픽 API가 느린 디버깅 모드로 전환됩니다.</string>
149 <string name="use_disk_shader_cache">디스크 셰이더 캐시 사용</string>
150 <string name="use_disk_shader_cache_description">생성된 셰이더를 디스크에 저장하고 불러오기하여 끊김 현상을 줄입니다.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">볼륨</string>
154 <string name="audio_volume_description">오디오 출력의 볼륨을 지정합니다.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">기본값</string>
158 <string name="ini_saved">저장된 설정</string>
159 <string name="gameid_saved">%1$s를 위해 저장된 설정</string>
160 <string name="error_saving">%1$s.ini 저장 중 오류: %2$s</string>
161 <string name="loading">불러오기 중...</string>
162 <string name="reset_setting_confirmation">이 설정을 기본값으로 되돌리겠습니까?</string>
163 <string name="reset_to_default">기본값으로 재설정</string>
164 <string name="reset_all_settings">모든 설정을 초기화하겠습니까?</string>
165 <string name="reset_all_settings_description">모든 고급 설정이 기본 구성으로 재설정됩니다. 이 설정은 되돌릴 수 없습니다.</string>
166 <string name="settings_reset">설정 초기화</string>
167 <string name="close">닫기</string>
168 <string name="learn_more">자세히 알아보기</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">GPU 드라이버 선택</string>
172 <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
173 <string name="select_gpu_driver_install">설치</string>
174 <string name="select_gpu_driver_default">기본값</string>
175 <string name="select_gpu_driver_install_success">설치된 %s</string>
176 <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
177 <string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
178 <string name="system_gpu_driver">시스템 GPU 드라이버</string>
179 <string name="installing_driver">드라이버 설치 중...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">설정</string>
183 <string name="preferences_general">일반</string>
184 <string name="preferences_system">시스템</string>
185 <string name="preferences_graphics">그래픽</string>
186 <string name="preferences_audio">오디오</string>
187 <string name="preferences_theme">테마 및 색상</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">롬이 암호화되었음</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">비디오 코어를 초기화하는 동안 오류 발생</string>
194 <string name="loader_error_video_core_description">이 문제는 일반적으로 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다.</string>
195 <string name="loader_error_invalid_format">롬을 불러올 수 없음</string>
196 <string name="loader_error_file_not_found">롬 파일이 존재하지 않음</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">에뮬레이션 종료</string>
200 <string name="emulation_done">완료</string>
201 <string name="emulation_fps_counter">FPS 카운터</string>
202 <string name="emulation_toggle_controls">토글 제어</string>
203 <string name="emulation_rel_stick_center">상대 스틱 센터</string>
204 <string name="emulation_dpad_slide">십자패드 슬라이드</string>
205 <string name="emulation_haptics">햅틱</string>
206 <string name="emulation_show_overlay">오버레이 표시</string>
207 <string name="emulation_toggle_all">모두 토글</string>
208 <string name="emulation_control_adjust">오버레이 조정</string>
209 <string name="emulation_control_scale">스케일</string>
210 <string name="emulation_control_opacity">불투명도</string>
211 <string name="emulation_touch_overlay_reset">오버레이 재설정</string>
212 <string name="emulation_touch_overlay_edit">오버레이 편집</string>
213 <string name="emulation_pause">에뮬레이션 일시 중지</string>
214 <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string>
215 <string name="emulation_input_overlay">오버레이 옵션</string>
216 <string name="emulation_game_loading">게임 불러오기 중...</string>
217
218 <string name="load_settings">설정 불러오기 중...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">가상 키보드</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">정보</string>
225 <string name="continue_button">계속</string>
226 <string name="system_archive_not_found">시스템 아카이브를 찾을 수 없음</string>
227 <string name="system_archive_not_found_message">%s가 누락되었습니다. 시스템 아카이브를 덤프하세요.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string>
228 <string name="system_archive_general">시스템 아카이브</string>
229 <string name="save_load_error">저장하기/불러오기 오류</string>
230 <string name="fatal_error">치명적인 오류</string>
231 <string name="fatal_error_message">치명적인 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string>
232 <string name="performance_warning">이 설정을 끄면 에뮬레이션 성능이 크게 저하됩니다! 최상의 환경을 위해 이 설정을 활성화된 상태로 두는 것이 좋습니다.</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">일본</string>
236 <string name="region_usa">미국</string>
237 <string name="region_europe">유럽</string>
238 <string name="region_australia">호주</string>
239 <string name="region_china">중국</string>
240 <string name="region_korea">대한민국</string>
241 <string name="region_taiwan">타이완</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">일본어 (日本語)</string>
245 <string name="language_english">영어 (English)</string>
246 <string name="language_french">프랑스어 (Français)</string>
247 <string name="langauge_german">독일어(Deutsch)</string>
248 <string name="language_italian">이탈리아어 (Italiano)</string>
249 <string name="language_spanish">스페인어 (Español)</string>
250 <string name="language_chinese">중국어 (简体中文)</string>
251 <string name="language_korean">한국어 (Korean)</string>
252 <string name="language_dutch">네덜란드어 (Nederlands)</string>
253 <string name="language_portuguese">포르투갈어 (Português)</string>
254 <string name="language_russian">러시아어 (Русский)</string>
255 <string name="language_taiwanese">대만어 (台湾)</string>
256 <string name="language_british_english">영어 (British English)</string>
257 <string name="language_canadian_french">캐나다 프랑스어 (Français canadien)</string>
258 <string name="language_latin_american_spanish">라틴 아메리카 스페인어 (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">중국어 간체 (简体中文)</string>
260 <string name="language_traditional_chinese">중국어 번체 (正體中文)</string>
261 <string name="language_brazilian_portuguese">브라질 포르투갈어 (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">불칸</string>
265 <string name="renderer_none">없음</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">보통</string>
269 <string name="renderer_accuracy_high">높음</string>
270 <string name="renderer_accuracy_extreme">극한 (느림)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (느림)</string>
277 <string name="resolution_three">3X (2160p/3240p) (느림)</string>
278 <string name="resolution_four">4X (2880p/4320p) (느림)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">즉시 (끔)</string>
282 <string name="renderer_vsync_mailbox">메일박스</string>
283 <string name="renderer_vsync_fifo">FIFO (켬)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO 릴랙스</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">가장 가까운 이웃</string>
288 <string name="scaling_filter_bilinear">이중선형</string>
289 <string name="scaling_filter_bicubic">고등차수보간</string>
290 <string name="scaling_filter_gaussian">가우시안</string>
291 <string name="scaling_filter_scale_force">스케일포스</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ 초고해상도</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">없음</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">기본 (16:9)</string>
301 <string name="ratio_force_four_three">강제 4:3</string>
302 <string name="ratio_force_twenty_one_nine">강제 21:9</string>
303 <string name="ratio_force_sixteen_ten">강제 16:10</string>
304 <string name="ratio_stretch">창에 맞게 늘림</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">정확함</string>
308 <string name="cpu_accuracy_unsafe">안전하지 않음</string>
309 <string name="cpu_accuracy_paranoid">편집증 (느림)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">십자패드</string>
313 <string name="gamepad_left_stick">L 스틱</string>
314 <string name="gamepad_right_stick">R 스틱</string>
315 <string name="gamepad_home">홈</string>
316 <string name="gamepad_screenshot">스크린샷</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">셰이더 준비하기</string>
320 <string name="building_shaders">셰이더 빌드 중</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">앱 테마 변경</string>
324 <string name="theme_default">기본값</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">테마 모드 변경</string>
329 <string name="theme_mode_follow_system">팔로우 시스템</string>
330 <string name="theme_mode_light">밝음</string>
331 <string name="theme_mode_dark">어두움</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">검은색 배경 사용</string>
335 <string name="use_black_backgrounds_description">어두운 테마를 사용할 때는 검은색 배경을 적용합니다.</string>
336
337</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
new file mode 100644
index 000000000..3e1f9bce5
--- /dev/null
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
5 <string name="emulation_notification_channel_name">Emulering er aktiv</string>
6 <string name="emulation_notification_channel_description">Viser et vedvarende varsel når emuleringen kjører.</string>
7 <string name="emulation_notification_running">Yuzu kjører</string>
8 <string name="notice_notification_channel_name">Merknader og feil</string>
9 <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string>
10 <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Velkommen!</string>
14 <string name="welcome_description">Lær å sette opp &lt;b>yuzu&lt;/b> og hopp inn i emulering.</string>
15 <string name="get_started">Kom i gang</string>
16 <string name="keys">Nøkler</string>
17 <string name="keys_description">Velg din &lt;b>prod.keys&lt;/b> fil ved å bruke knappen under.</string>
18 <string name="select_keys">Velg nøkler</string>
19 <string name="games">Spill</string>
20 <string name="games_description">Velg din &lt;b>Spill&lt;/b> mappe ved å bruke knappen under.</string>
21 <string name="done">Ferdig</string>
22 <string name="done_description">Nå er du klar.\nGled deg til å spille!</string>
23 <string name="text_continue">Fortsett</string>
24 <string name="next">Neste</string>
25 <string name="back">Tilbake</string>
26 <string name="add_games">Legg til spill</string>
27 <string name="add_games_description">Velg din spillmappe</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Spill</string>
31 <string name="home_search">Søk</string>
32 <string name="home_settings">Innstillinger</string>
33 <string name="empty_gamelist">Ingen filer ble funnet eller ingen spillkatalog er valgt ennå.</string>
34 <string name="search_and_filter_games">Søk og filtrer spill</string>
35 <string name="select_games_folder">Velg spillmappe</string>
36 <string name="select_games_folder_description">Gjør det mulig for yuzu å fylle ut spillelisten.</string>
37 <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>
39 <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>
41 <string name="games_dir_selected">Spillkatalogen er valgt</string>
42 <string name="install_prod_keys">Installer prod.keys</string>
43 <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string>
44 <string name="install_prod_keys_warning">Hoppe over å legge til nøkler?</string>
45 <string name="install_prod_keys_warning_description">Gyldige nøkler er påkrevd for å emulere spill. Bare hjemmebryggede apper vil fungere hvis du fortsetter.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Varsler</string>
48 <string name="notifications_description">Gi varslingstillatelse med knappen nedenfor.</string>
49 <string name="give_permission">Gi tillatelse</string>
50 <string name="notification_warning">Hoppe over å gi tillatelse til varsling?</string>
51 <string name="notification_warning_description">yuzu vil ikke kunne varsle deg om viktig informasjon.</string>
52 <string name="permission_denied">Tillatelse avslått</string>
53 <string name="permission_denied_description">Du har nektet denne tillatelsen for mange ganger, og nå må du gi den manuelt i systeminnstillingene.</string>
54 <string name="about">Om</string>
55 <string name="about_description">Byggeversjon, kildehenvisninger og mer</string>
56 <string name="warning_help">Hjelp</string>
57 <string name="warning_skip">Hopp over</string>
58 <string name="warning_cancel">Avbryt</string>
59 <string name="install_amiibo_keys">Installer Amiibo-nøkler</string>
60 <string name="install_amiibo_keys_description">Kreves for å bruke Amiibo i spillet</string>
61 <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string>
62 <string name="install_keys_success">Nøkler vellykket installert</string>
63 <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string>
64 <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="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string>
67 <string name="install_gpu_driver">Installer GPU-driver</string>
68 <string name="install_gpu_driver_description">Installer alternative drivere for potensielt bedre ytelse eller nøyaktighet.</string>
69 <string name="advanced_settings">Avanserte innstillinger</string>
70 <string name="settings_description">Konfigurere emulatorinnstillinger</string>
71 <string name="search_recently_played">Nylig spilt</string>
72 <string name="search_recently_added">Nylig lagt til</string>
73 <string name="search_retail">Butikkhandel</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Åpne yuzu-mappen</string>
76 <string name="open_user_folder_description">Administrere yuzus interne filer</string>
77 <string name="theme_and_color_description">Endre appens utseende</string>
78 <string name="no_file_manager">Ingen filbehandler funnet</string>
79 <string name="notification_no_directory_link">Kunne ikke åpne yuzu-katalogen</string>
80 <string name="notification_no_directory_link_description">Finn brukermappen manuelt med filbehandlingens sidepanel.</string>
81 <string name="manage_save_data">Administrere lagringsdata</string>
82 <string name="manage_save_data_description">Lagringsdata funnet. Velg et alternativ nedenfor.</string>
83 <string name="import_export_saves_description">Importer eller eksporter lagringsfiler</string>
84 <string name="import_export_saves_no_profile">Ingen lagringsdata funnet. Start et nytt spill og prøv på nytt.</string>
85 <string name="save_file_imported_success">Vellykket import</string>
86 <string name="save_file_invalid_zip_structure">Ugyldig struktur for lagringskatalog</string>
87 <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string>
88 <string name="import_saves">Importer</string>
89 <string name="export_saves">Eksporter</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia er ikke ekte</string>
93 <string name="copied_to_clipboard">Kopiert til utklippstavlen</string>
94 <string name="about_app_description">En Switch-emulator med åpen kildekode</string>
95 <string name="contributors">Bidragsytere</string>
96 <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Bygg</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Tidlig tilgang</string>
105 <string name="get_early_access">Få tidlig tilgang</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Banebrytende funksjoner, tidlig tilgang til oppdateringer og mye mer.</string>
108 <string name="early_access_benefits">Fordeler ved tidlig tilgang</string>
109 <string name="cutting_edge_features">Avanserte funksjoner</string>
110 <string name="early_access_updates">Tidlig tilgang til oppdateringer</string>
111 <string name="no_manual_installation">Ingen manuell installasjon</string>
112 <string name="prioritized_support">Prioritert støtte</string>
113 <string name="helping_game_preservation">Bidra til bevaring av spill</string>
114 <string name="our_eternal_gratitude">Vår evige takknemlighet</string>
115 <string name="are_you_interested">Er du interessert?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Aktiver hastighetsbegrensning</string>
119 <string name="frame_limit_enable_description">Når aktivert, begrenses emuleringshastigheten til en angitt prosentandel av normal hastighet.</string>
120 <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string>
121 <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>
122 <string name="cpu_accuracy">CPU-nøyaktighet</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Dokket modus</string>
126 <string name="use_docked_mode_description">Emulerer i dokket modus, noe som øker oppløsningen på bekostning av ytelsen.</string>
127 <string name="emulated_region">Emulert region</string>
128 <string name="emulated_language">Emulert språk</string>
129 <string name="select_rtc_date">Velg RTC-dato</string>
130 <string name="select_rtc_time">Velg RTC-tid</string>
131 <string name="use_custom_rtc">Aktiver egendefinert RTC</string>
132 <string name="use_custom_rtc_description">Med denne innstillingen kan du stille inn en egendefinert sanntidsklokke som er atskilt fra gjeldende systemtid.</string>
133 <string name="set_custom_rtc">Angi egendefinert RTC</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Nøyaktighetsnivå</string>
138 <string name="renderer_resolution">Oppløsning</string>
139 <string name="renderer_vsync">VSync-modus</string>
140 <string name="renderer_aspect_ratio">Størrelsesforhold</string>
141 <string name="renderer_scaling_filter">Filter for vindustilpasning</string>
142 <string name="renderer_anti_aliasing">Anti-Aliasing-metode</string>
143 <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string>
144 <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string>
145 <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string>
146 <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakkingen, men kan føre til feil.</string>
147 <string name="renderer_debug">Aktiver feilsøking av grafikk</string>
148 <string name="renderer_debug_description">Når dette er merket av, går grafikk-API-et inn i en langsommere feilsøkingsmodus.</string>
149 <string name="use_disk_shader_cache">Bruk disk shader-cache</string>
150 <string name="use_disk_shader_cache_description">Reduser hakking ved å lagre og laste inn genererte shaders på disken.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Volum</string>
154 <string name="audio_volume_description">Angir volumet på lydutgangen.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Standard</string>
158 <string name="ini_saved">Lagrede innstillinger</string>
159 <string name="gameid_saved">Lagrede innstillinger for %1$s</string>
160 <string name="error_saving">Feil ved lagring av %1$s.ini: %2$s</string>
161 <string name="loading">Lastes inn...</string>
162 <string name="reset_setting_confirmation">Vil du tilbakestille denne innstillingen til standardverdien?</string>
163 <string name="reset_to_default">Tilbakestill til standardinnstillingene</string>
164 <string name="reset_all_settings">Tilbakestille alle innstillinger?</string>
165 <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string>
166 <string name="settings_reset">Tilbakestilling av innstillinger</string>
167 <string name="close">Lukk</string>
168 <string name="learn_more">Lær Mer</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Velg GPU-driver</string>
172 <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
173 <string name="select_gpu_driver_install">Installer</string>
174 <string name="select_gpu_driver_default">Standard</string>
175 <string name="select_gpu_driver_install_success">Installert %s</string>
176 <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
177 <string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
178 <string name="system_gpu_driver">Systemets GPU-driver</string>
179 <string name="installing_driver">Installerer driver...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Innstillinger</string>
183 <string name="preferences_general">Generelt</string>
184 <string name="preferences_system">System</string>
185 <string name="preferences_graphics">Grafikk</string>
186 <string name="preferences_audio">Lyd</string>
187 <string name="preferences_theme">Tema og farge</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">ROM-en din er kryptert</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string>
194 <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>
195 <string name="loader_error_invalid_format">Kunne ikke laste inn ROM</string>
196 <string name="loader_error_file_not_found">ROM-filen finnes ikke</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Avslutt emulering</string>
200 <string name="emulation_done">Ferdig</string>
201 <string name="emulation_fps_counter">FPS-teller</string>
202 <string name="emulation_toggle_controls">Veksle kontroller</string>
203 <string name="emulation_rel_stick_center">Relativt senter for stikken</string>
204 <string name="emulation_dpad_slide">DPad-skyveplate</string>
205 <string name="emulation_haptics">Haptikk</string>
206 <string name="emulation_show_overlay">Vis overlegg</string>
207 <string name="emulation_toggle_all">Slå av alt</string>
208 <string name="emulation_control_adjust">Juster overlegg</string>
209 <string name="emulation_control_scale">Skaler</string>
210 <string name="emulation_control_opacity">Gjennomsiktighet</string>
211 <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string>
212 <string name="emulation_touch_overlay_edit">Rediger overlegg</string>
213 <string name="emulation_pause">Pause Emulering</string>
214 <string name="emulation_unpause">Opphev pausing av emulering</string>
215 <string name="emulation_input_overlay">Alternativer for overlegg</string>
216 <string name="emulation_game_loading">Spillet lastes inn...</string>
217
218 <string name="load_settings">Laster inn innstillinger...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Programvare Tastatur</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Avbryt</string>
225 <string name="continue_button">Fortsett</string>
226 <string name="system_archive_not_found">System Arkiv Ikke Funnet</string>
227 <string name="system_archive_not_found_message">%s mangler. Dump systemarkivene dine.\nFortsatt emulering kan føre til krasj og feil.</string>
228 <string name="system_archive_general">Et systemarkiv</string>
229 <string name="save_load_error">Feil ved lagring/innlasting</string>
230 <string name="fatal_error">Fatal Feil</string>
231 <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>
232 <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>
233
234 <!-- Region Names -->
235 <string name="region_japan">Japan</string>
236 <string name="region_usa">USA</string>
237 <string name="region_europe">Europa</string>
238 <string name="region_australia">Australia</string>
239 <string name="region_china">Kina</string>
240 <string name="region_korea">Korea</string>
241 <string name="region_taiwan">Taiwan</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Japansk (日本語)</string>
245 <string name="language_english">Engelsk</string>
246 <string name="language_french">Fransk (Français)</string>
247 <string name="langauge_german">Tysk (Deutsch)</string>
248 <string name="language_italian">Italiensk (Italiano)</string>
249 <string name="language_spanish">Spansk (Español)</string>
250 <string name="language_chinese">Kinesisk (简体中文)</string>
251 <string name="language_korean">Koreansk (한국어)</string>
252 <string name="language_dutch">Nederlandsk (Nederlands)</string>
253 <string name="language_portuguese">Portugisisk (Português)</string>
254 <string name="language_russian">Russisk (Русский)</string>
255 <string name="language_taiwanese">Taiwansk (台湾)</string>
256 <string name="language_british_english">Britisk Engelsk</string>
257 <string name="language_canadian_french">Kanadisk fransk (Français canadien)</string>
258 <string name="language_latin_american_spanish">Latinamerikansk spansk (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">Forenklet kinesisk (简体中文)</string>
260 <string name="language_traditional_chinese">Tradisjonell Kinesisk (正體中文)</string>
261 <string name="language_brazilian_portuguese">Brasiliansk portugisisk (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Ingen</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normal</string>
269 <string name="renderer_accuracy_high">Høy</string>
270 <string name="renderer_accuracy_extreme">Ekstrem (Treg)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Slow)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Slow)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Slow)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Umiddelbar (av)</string>
282 <string name="renderer_vsync_mailbox">Postkasse</string>
283 <string name="renderer_vsync_fifo">FIFO (På)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO avslappet</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Nærmeste nabo</string>
288 <string name="scaling_filter_bilinear">Bilineær</string>
289 <string name="scaling_filter_bicubic">Bikubisk</string>
290 <string name="scaling_filter_gaussian">Gaussisk</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Ingen</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Standard (16:9)</string>
301 <string name="ratio_force_four_three">Tving 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Tving 21:9</string>
303 <string name="ratio_force_sixteen_ten">Tving 16:10</string>
304 <string name="ratio_stretch">Strekk til Vindu</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Nøyaktig</string>
308 <string name="cpu_accuracy_unsafe">Utrygt</string>
309 <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">D-Pad</string>
313 <string name="gamepad_left_stick">Venstre Pinne</string>
314 <string name="gamepad_right_stick">Høyre Pinne</string>
315 <string name="gamepad_home">Hjem</string>
316 <string name="gamepad_screenshot">Skjermbilde</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Forberedelse av shaders</string>
320 <string name="building_shaders">Bygging av shaders</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Endre appens tema</string>
324 <string name="theme_default">Standard</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Endre temamodus</string>
329 <string name="theme_mode_follow_system">Følg systemet</string>
330 <string name="theme_mode_light">Lys</string>
331 <string name="theme_mode_dark">Mørk</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Bruk svart bakgrunn</string>
335 <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string>
336
337</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
new file mode 100644
index 000000000..1cd1a8f87
--- /dev/null
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
5 <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string>
6 <string name="emulation_notification_channel_description">Pokaż trwałe powiadomienie gdy emulacja jest uruchomiona.</string>
7 <string name="emulation_notification_running">yuzu jest uruchomiony</string>
8 <string name="notice_notification_channel_name">Powiadomienia błędy</string>
9 <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string>
10 <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Witaj!</string>
14 <string name="welcome_description">Zobacz jak skonfigurować &lt;b>yuzu&lt;/b> i wskocz w świat emulacji.</string>
15 <string name="get_started">Zaczynamy</string>
16 <string name="keys">Klucze</string>
17 <string name="keys_description">Wybierz swoje klucze &lt;b>prod.keys&lt;/b> za pomocą przycisku poniżej.</string>
18 <string name="select_keys">Wybierz klucze</string>
19 <string name="games">Gry</string>
20 <string name="games_description">Wybierz katalog z grami &lt;b>Games&lt;/b> za pomocą przycisku poniżej.</string>
21 <string name="done">Gotowe</string>
22 <string name="done_description">Wszystko skonfigurowane.\n Miłego grania!</string>
23 <string name="text_continue">Kontynuuj</string>
24 <string name="next">Dalej</string>
25 <string name="back">Wstecz</string>
26 <string name="add_games">Dodaj gry</string>
27 <string name="add_games_description">Wybierz folder zawierający Twoje gry</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Gry</string>
31 <string name="home_search">Szukaj</string>
32 <string name="home_settings">Ustawienia</string>
33 <string name="empty_gamelist">Nie znaleziono plików, lub nie wybrano jeszcze katalogu zawierającego gry.</string>
34 <string name="search_and_filter_games">Szukaj i filtruj gry</string>
35 <string name="select_games_folder">Wybierz folder z grami</string>
36 <string name="select_games_folder_description">Pozwala yuzu wygenerować listę gier</string>
37 <string name="add_games_warning">Pominąć wybór folderu z grami?</string>
38 <string name="add_games_warning_description">Aby pokazać listę gier wybierz katalog zawierający gry.</string>
39 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Szukaj gier</string>
41 <string name="games_dir_selected">Wybrano katalog gier</string>
42 <string name="install_prod_keys">Instaluj klucze prod.keys</string>
43 <string name="install_prod_keys_description">Wymagane aby poprawnie odczytać sklepowe gry</string>
44 <string name="install_prod_keys_warning">Pominąć dodawanie kluczy?</string>
45 <string name="install_prod_keys_warning_description">Poprawne klucze są wymagane aby emulować sklepowe gry. Jeśli przejdziesz dalej, jedynie homebrew będą działać.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Powiadomienia</string>
48 <string name="notifications_description">Nadaj uprawnienia dostępu do powiadomień. </string>
49 <string name="give_permission">Nadaj uprawnienia</string>
50 <string name="notification_warning">Pominąć nadanie uprawnień powiadomień?</string>
51 <string name="notification_warning_description">yuzu nie będzie mógł powiadamiać Cię o ważnych informacjach.</string>
52 <string name="permission_denied">Odmowa dostępu</string>
53 <string name="permission_denied_description">Odmówiłeś dostępu do powiadomień zbyt wiele razy, teraz musisz przyznać je w ustawieniach systemowych Androida.</string>
54 <string name="about">O aplikacji</string>
55 <string name="about_description">Wersja, podziękowania i więcej</string>
56 <string name="warning_help">Pomoc</string>
57 <string name="warning_skip">Pomiń</string>
58 <string name="warning_cancel">Anuluj</string>
59 <string name="install_amiibo_keys">Zainstaluj klucze Amiibo</string>
60 <string name="install_amiibo_keys_description">Wymagane aby korzystać z Amiibo w grze</string>
61 <string name="invalid_keys_file">Wybrano niepoprawne klucze</string>
62 <string name="install_keys_success">Klucze zainstalowane pomyślnie</string>
63 <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string>
64 <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="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string>
67 <string name="install_gpu_driver">Zainstaluj sterownik GPU</string>
68 <string name="install_gpu_driver_description">Użyj alternatywnych sterowników aby potencjalnie zwiększyć wydajność i naprawić błędy</string>
69 <string name="advanced_settings">Ustawienia zaawansowane</string>
70 <string name="settings_description">Skonfiguruj ustawienia emulatora</string>
71 <string name="search_recently_played">Ostatnio grane</string>
72 <string name="search_recently_added">Ostatnio dodane</string>
73 <string name="search_retail">Sklepowe</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Otwórz folder yuzu</string>
76 <string name="open_user_folder_description">Zarządzaj plikami emulatora</string>
77 <string name="theme_and_color_description">Personalizuj wygląd aplikacji</string>
78 <string name="no_file_manager">Nie znaleziono menedżera plików</string>
79 <string name="notification_no_directory_link">Nie można otworzyć folderu emulatora</string>
80 <string name="notification_no_directory_link_description">Proszę wybrać ręcznie folder z pomocą panelu bocznego menedżera plików.</string>
81 <string name="manage_save_data">Zarządzaj plikami zapisów gier</string>
82 <string name="manage_save_data_description">Znaleziono pliki zapisów gier. Wybierz opcję poniżej.</string>
83 <string name="import_export_saves_description">Importuj lub wyeksportuj pliki zapisów</string>
84 <string name="import_export_saves_no_profile">Nie znaleziono plików zapisów. Uruchom grę i spróbuj ponownie.</string>
85 <string name="save_file_imported_success">Zaimportowano pomyślnie</string>
86 <string name="save_file_invalid_zip_structure">Niepoprawna struktura folderów</string>
87 <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string>
88 <string name="import_saves">Importuj</string>
89 <string name="export_saves">Eksportuj</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia isn\'t real</string>
93 <string name="copied_to_clipboard">Skopiowano do schowka</string>
94 <string name="about_app_description">Otwarto-źródłowy emulator konsoli Switch</string>
95 <string name="contributors">Współtwórcy</string>
96 <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Wersja</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Wczesny dostęp</string>
105 <string name="get_early_access">Uzyskaj wczesny dostęp</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Nowe funkcje, szybszy dostęp do aktualizacji i nie tylko</string>
108 <string name="early_access_benefits">Korzyści z wcześniejszego dostępu</string>
109 <string name="cutting_edge_features">Nowatorskie funkcje</string>
110 <string name="early_access_updates">Częste aktualizacje</string>
111 <string name="no_manual_installation">Automatyczne aktualizacje</string>
112 <string name="prioritized_support">Priorytetowe wsparcie</string>
113 <string name="helping_game_preservation">Pomoc w problemach z grami</string>
114 <string name="our_eternal_gratitude">Nasza wdzięczność</string>
115 <string name="are_you_interested">Jesteś zainteresowany?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Włącz limit szybkości emulacji</string>
119 <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string>
120 <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string>
121 <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>
122 <string name="cpu_accuracy">Dokładność procesora CPU</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Tryb zadokowany</string>
126 <string name="use_docked_mode_description">Emulacja w trybie stacji dokującej, zwiększa rozdzielczość kosztem wydajności.</string>
127 <string name="emulated_region">Region emulacji</string>
128 <string name="emulated_language">Język emulacji</string>
129 <string name="select_rtc_date">Ustaw datę RTC</string>
130 <string name="select_rtc_time">Ustaw czas RTC</string>
131 <string name="use_custom_rtc">Włącz niestandardowy zegar RTC</string>
132 <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>
133 <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">Interfejs graficzny</string>
137 <string name="renderer_accuracy">Poziom precyzji emulacji</string>
138 <string name="renderer_resolution">Rozdzielczość</string>
139 <string name="renderer_vsync">Synchronizacja pionowa VSync</string>
140 <string name="renderer_aspect_ratio">Proporcje ekranu</string>
141 <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string>
142 <string name="renderer_anti_aliasing">Metoda wygładzania krawędzi</string>
143 <string name="renderer_force_max_clock">Maksymalne taktowanie GPU (układy Adreno)</string>
144 <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string>
145 <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string>
146 <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string>
147 <string name="renderer_debug">Włącz debugowanie grafiki</string>
148 <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string>
149 <string name="use_disk_shader_cache">Użyj pamięci podręcznej shaderów na dysku</string>
150 <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Głośność</string>
154 <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Domyślne</string>
158 <string name="ini_saved">Ustawienia zapisane</string>
159 <string name="gameid_saved">Ustawienia zapisane w %1$s</string>
160 <string name="error_saving">Błąd zapisu %1$s.ini: %2$s</string>
161 <string name="loading">Wczytywanie...</string>
162 <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string>
163 <string name="reset_to_default">Przywróć ustawienia domyślne</string>
164 <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string>
165 <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>
166 <string name="settings_reset">Reset ustawień</string>
167 <string name="close">Zamknij</string>
168 <string name="learn_more">Dowiedz się więcej</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Wybierz sterownik GPU </string>
172 <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
173 <string name="select_gpu_driver_install">Zainstaluj</string>
174 <string name="select_gpu_driver_default">Domyślne</string>
175 <string name="select_gpu_driver_install_success">Zainstalowano %s</string>
176 <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
177 <string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
178 <string name="system_gpu_driver">Systemowy sterownik GPU</string>
179 <string name="installing_driver">Instalowanie sterownika...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Ustawienia</string>
183 <string name="preferences_general">Ogólne</string>
184 <string name="preferences_system">System</string>
185 <string name="preferences_graphics">Grafika</string>
186 <string name="preferences_audio">Dźwięk</string>
187 <string name="preferences_theme">Motyw i kolor</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">Twój ROM jest zakodowany</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string>
194 <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string>
195 <string name="loader_error_invalid_format">Nie można wczytać pliku ROM</string>
196 <string name="loader_error_file_not_found">Plik ROM nie istnieje</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Zakończ emulację</string>
200 <string name="emulation_done">Gotowe</string>
201 <string name="emulation_fps_counter">Licznik FPS</string>
202 <string name="emulation_toggle_controls">Wybierz przyciski</string>
203 <string name="emulation_rel_stick_center">Wycentruj gałki</string>
204 <string name="emulation_dpad_slide">Ruchomy DPad</string>
205 <string name="emulation_haptics">Wibracje haptyczne</string>
206 <string name="emulation_show_overlay">Pokaż przyciski</string>
207 <string name="emulation_toggle_all">Zaznacz wszystkie</string>
208 <string name="emulation_control_adjust">Dostosuj nakładkę</string>
209 <string name="emulation_control_scale">Skala</string>
210 <string name="emulation_control_opacity">Przeźroczystość</string>
211 <string name="emulation_touch_overlay_reset">Resetuj</string>
212 <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string>
213 <string name="emulation_pause">Wstrzymaj emulację</string>
214 <string name="emulation_unpause">Wznów emulację</string>
215 <string name="emulation_input_overlay">Opcje nakładki</string>
216 <string name="emulation_game_loading">Wczytywanie gry...</string>
217
218 <string name="load_settings">Wczytywanie ustawień...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Klawiatura systemowa</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Przerwij</string>
225 <string name="continue_button">Kontynuuj</string>
226 <string name="system_archive_not_found">Archiwum systemu nie znalezione.</string>
227 <string name="system_archive_not_found_message">%s nieznaleziony. Proszę wykonać zrzut archiwum systemu.\nKontynuowanie może powodować błędy lub przerwanie emulacji.</string>
228 <string name="system_archive_general">Archiwum systemu</string>
229 <string name="save_load_error">Błąd odczytu/zapisu</string>
230 <string name="fatal_error">Błąd krytyczny</string>
231 <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>
232 <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">Japonia</string>
236 <string name="region_usa">USA</string>
237 <string name="region_europe">Europa</string>
238 <string name="region_australia">Australia</string>
239 <string name="region_china">Chiny</string>
240 <string name="region_korea">Korea</string>
241 <string name="region_taiwan">Tajwan</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Japoński (日本語)</string>
245 <string name="language_english">Angielski</string>
246 <string name="language_french">Francuski (Francja)</string>
247 <string name="langauge_german">Niemiecki (Niemcy)</string>
248 <string name="language_italian">Włoski (Włochy)</string>
249 <string name="language_spanish">Hiszpański (Hiszpania)</string>
250 <string name="language_chinese">Chiński (简体中文)</string>
251 <string name="language_korean">Koreański (한국어)</string>
252 <string name="language_dutch">Duński (Holandia)</string>
253 <string name="language_portuguese">Portugalski (Portugalia)</string>
254 <string name="language_russian">Rosyjski (Русский)</string>
255 <string name="language_taiwanese">Tajwański (台湾)</string>
256 <string name="language_british_english">Angielski Brytyjski</string>
257 <string name="language_canadian_french">Francuski (Kanada)</string>
258 <string name="language_latin_american_spanish">Hiszpański (Ameryka Latynoska)</string>
259 <string name="language_simplified_chinese">Chiński uproszczony (简体中文)</string>
260 <string name="language_traditional_chinese">Chiński tradycyjny (正體中文)</string>
261 <string name="language_brazilian_portuguese">Portugalski (Brazylia)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Żadny</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normalny</string>
269 <string name="renderer_accuracy_high">Wysoki</string>
270 <string name="renderer_accuracy_extreme">Ekstremalny (Wolny)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Wolno)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Wolno)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Wolno)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Natychmiastowa (Wyłączona)</string>
282 <string name="renderer_vsync_mailbox">Skrzynka pocztowa</string>
283 <string name="renderer_vsync_fifo">FIFO (Włączona)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relaks</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Najbliższy sąsiadujący</string>
288 <string name="scaling_filter_bilinear">Bilinearny</string>
289 <string name="scaling_filter_bicubic">Bikubiczny</string>
290 <string name="scaling_filter_gaussian">Kulisty</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Żadna (wyłączony)</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Domyślne (16:9)</string>
301 <string name="ratio_force_four_three">Wymuś 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Wymuś 21:9</string>
303 <string name="ratio_force_sixteen_ten">Wymuś 16:10</string>
304 <string name="ratio_stretch">Rozciągnij do Okna</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Dokładny</string>
308 <string name="cpu_accuracy_unsafe">Niebezpieczny</string>
309 <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">D-Pad</string>
313 <string name="gamepad_left_stick">Lewa gałka</string>
314 <string name="gamepad_right_stick">Prawa gałka</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Zrzut ekranu</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Przygotowanie shaderów</string>
320 <string name="building_shaders">Budowanie shaderów</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Zmień motyw aplikacji</string>
324 <string name="theme_default">Domyślny</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Zmiana trybu motywu</string>
329 <string name="theme_mode_follow_system">Podążaj za systemowym</string>
330 <string name="theme_mode_light">Jasny</string>
331 <string name="theme_mode_dark">Ciemny</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Używaj czarnego tła</string>
335 <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string>
336
337</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
new file mode 100644
index 000000000..35197c280
--- /dev/null
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
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>
7 <string name="emulation_notification_running">Yuzu está em execução </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>
10 <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Bemvindo! </string>
14 <string name="welcome_description">Aprende como configurar &lt;b>yuzu&lt;/b> e arranca a emulação.</string>
15 <string name="get_started">Começa</string>
16 <string name="keys">Chaves</string>
17 <string name="keys_description">Seleciona o teu ficheiro &lt;b>prod.keys&lt;/b> com o botão abaixo.</string>
18 <string name="select_keys">Seleciona as Chaves</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>
21 <string name="done">Feito</string>
22 <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string>
23 <string name="text_continue">Continuar</string>
24 <string name="next">Próximo</string>
25 <string name="back">Voltar</string>
26 <string name="add_games">Adiciona Jogos</string>
27 <string name="add_games_description">Seleciona a tua pasta de Jogos</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Jogos</string>
31 <string name="home_search">Pesquisar</string>
32 <string name="home_settings">Configurações</string>
33 <string name="empty_gamelist">Não foram encontrados jogos ou a pasta de Jogos ainda não foi definida. </string>
34 <string name="search_and_filter_games">Procura e filtra jogos.</string>
35 <string name="select_games_folder">Seleciona a pasta de jogos.</string>
36 <string name="select_games_folder_description">Permite que o Yuzu preencha a lista de jogos</string>
37 <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_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Procurar Jogos</string>
41 <string name="games_dir_selected">Pasta de Jogos selecionada</string>
42 <string name="install_prod_keys">Instala prod.keys</string>
43 <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string>
44 <string name="install_prod_keys_warning">Ignorar a adição de chaves?</string>
45 <string name="install_prod_keys_warning_description">São necessárias chaves válidas para emular jogos comerciais. Somente aplicativos homebrew funcionarão se você continuar.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#Guia de introdução</string>
47 <string name="notifications">Notificações</string>
48 <string name="notifications_description">Conceda a permissão de notificação com o botão abaixo.</string>
49 <string name="give_permission">Conceda permissão</string>
50 <string name="notification_warning">Saltar a concessão da permissão de notificação?</string>
51 <string name="notification_warning_description">Yuzu não conseguirá te notificar de informações importantes. </string>
52 <string name="permission_denied">Permissão negada</string>
53 <string name="permission_denied_description">Você negou essa permissão muitas vezes e agora precisa concedê-la manualmente nas configurações do sistema.</string>
54 <string name="about">Sobre</string>
55 <string name="about_description">Versão de compilação, créditos e mais</string>
56 <string name="warning_help">Ajuda</string>
57 <string name="warning_skip">Saltar</string>
58 <string name="warning_cancel">Cancelar</string>
59 <string name="install_amiibo_keys">Instala chaves Amiibo</string>
60 <string name="install_amiibo_keys_description">Necessário para usares Amiibo no jogo</string>
61 <string name="invalid_keys_file">Ficheiro de chaves inválido</string>
62 <string name="install_keys_success">Chaves instaladas com sucesso</string>
63 <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string>
64 <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>
66 <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>
68 <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>
70 <string name="settings_description">Configura definições do emulador</string>
71 <string name="search_recently_played">Jogos recentes</string>
72 <string name="search_recently_added">Adicionados recentemente</string>
73 <string name="search_retail">Jogos comerciais</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Abre a pasta Yuzu</string>
76 <string name="open_user_folder_description">Gere os ficheiro internos do Yuzu</string>
77 <string name="theme_and_color_description">Modifica a aparência da App</string>
78 <string name="no_file_manager">Nenhum gestor de ficheiros encontrado</string>
79 <string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string>
80 <string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string>
81 <string name="manage_save_data">Gerir dados guardados</string>
82 <string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string>
83 <string name="import_export_saves_description">Importa ou exporta dados guardados</string>
84 <string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string>
85 <string name="save_file_imported_success">Importado com sucesso</string>
86 <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string>
87 <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>
88 <string name="import_saves">Importar</string>
89 <string name="export_saves">Exportar</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia não é real</string>
93 <string name="copied_to_clipboard">Copiado para a área de transferência</string>
94 <string name="about_app_description">Um emulador Switch de código aberto</string>
95 <string name="contributors">Contribuidores</string>
96 <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Versão</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Acesso antecipado</string>
105 <string name="get_early_access">Obtém Acesso Antecipado</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Recursos de ponta, acesso antecipado a atualizações e muito mais</string>
108 <string name="early_access_benefits">Benefícios do Acesso Antecipado</string>
109 <string name="cutting_edge_features">Recursos de ponta</string>
110 <string name="early_access_updates">Acesso antecipado a atualizações</string>
111 <string name="no_manual_installation">Sem instalação manual</string>
112 <string name="prioritized_support">Suporte prioritário</string>
113 <string name="helping_game_preservation">Ajuda na preservação dos jogos</string>
114 <string name="our_eternal_gratitude">A nossa eterna gratidão</string>
115 <string name="are_you_interested">Estás interessado?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Ativar limite de velocidade</string>
119 <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string>
120 <string name="frame_limit_slider">Percentagem do limite de velocidade</string>
121 <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>
122 <string name="cpu_accuracy">Precisão do CPU</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Modo ancorado</string>
126 <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string>
127 <string name="emulated_region">Região da emulação</string>
128 <string name="emulated_language">Idioma da emulação</string>
129 <string name="select_rtc_date">Seleciona a data RTC</string>
130 <string name="select_rtc_time">Seleciona a hora RTC</string>
131 <string name="use_custom_rtc">Ativa RTC personalizado</string>
132 <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string>
133 <string name="set_custom_rtc">Define RTC personalizado</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Nível de precisão</string>
138 <string name="renderer_resolution">Resolução</string>
139 <string name="renderer_vsync">Modo VSync</string>
140 <string name="renderer_aspect_ratio">Proporção do ecrã</string>
141 <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string>
142 <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string>
143 <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string>
144 <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string>
145 <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string>
146 <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string>
147 <string name="renderer_debug">Ativar depuração de gráficos</string>
148 <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string>
149 <string name="use_disk_shader_cache">Usar cache de shaders em disco</string>
150 <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Volume</string>
154 <string name="audio_volume_description">Especifica o volume de saída.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Padrão</string>
158 <string name="ini_saved">Definições guardadas</string>
159 <string name="gameid_saved">Definições guardadas para %1$s</string>
160 <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string>
161 <string name="loading">A carregar...</string>
162 <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string>
163 <string name="reset_to_default">Reverter para padrão</string>
164 <string name="reset_all_settings">Redefinir todas as definições?</string>
165 <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>
166 <string name="settings_reset">Redefinir definições</string>
167 <string name="close">Fechar</string>
168 <string name="learn_more">Saiba mais</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Seleciona a driver para o GPU</string>
172 <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
173 <string name="select_gpu_driver_install">Instalar</string>
174 <string name="select_gpu_driver_default">Padrão</string>
175 <string name="select_gpu_driver_install_success">Instalado%s</string>
176 <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
177 <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
178 <string name="system_gpu_driver">Driver do GPU padrão</string>
179 <string name="installing_driver">A instalar o Driver...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Configurações</string>
183 <string name="preferences_general">Geral</string>
184 <string name="preferences_system">Sistema</string>
185 <string name="preferences_graphics">Gráficos</string>
186 <string name="preferences_audio">Áudio</string>
187 <string name="preferences_theme">Cor e tema.</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">A tua ROM está encriptada</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string>
194 <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>
195 <string name="loader_error_invalid_format">Impossível carregar a tua ROM</string>
196 <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Sair da emulação</string>
200 <string name="emulation_done">Feito</string>
201 <string name="emulation_fps_counter">Contador de FPS</string>
202 <string name="emulation_toggle_controls">Alterar Controlos</string>
203 <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string>
204 <string name="emulation_dpad_slide">Deslizar do DPad</string>
205 <string name="emulation_haptics">Hápticos </string>
206 <string name="emulation_show_overlay">Mostrar sobreposição </string>
207 <string name="emulation_toggle_all">Alterar todos</string>
208 <string name="emulation_control_adjust">Ajustar a sobreposição </string>
209 <string name="emulation_control_scale">Escala</string>
210 <string name="emulation_control_opacity">Opacidade</string>
211 <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string>
212 <string name="emulation_touch_overlay_edit">Editar sobreposição </string>
213 <string name="emulation_pause">Pausa emulação</string>
214 <string name="emulation_unpause">Retomar emulação</string>
215 <string name="emulation_input_overlay">Opções de sobreposição </string>
216 <string name="emulation_game_loading">Jogo a carregar...</string>
217
218 <string name="load_settings">Configurações a carregar...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Teclado de software</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Abortar</string>
225 <string name="continue_button">Continuar</string>
226 <string name="system_archive_not_found">Arquivo do sistema não encontrado</string>
227 <string name="system_archive_not_found_message">%s está em falta. Por favor apaga os teus ficheiros de sistema.\nContinuar a emulação pode causar erros.</string>
228 <string name="system_archive_general">Um arquivo do sistema</string>
229 <string name="save_load_error">Erro Guardar/Carregar</string>
230 <string name="fatal_error">Erro fatal</string>
231 <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string>
232 <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>
233
234 <!-- Region Names -->
235 <string name="region_japan">Japão</string>
236 <string name="region_usa">EUA</string>
237 <string name="region_europe">Europa</string>
238 <string name="region_australia">Austrália</string>
239 <string name="region_china">China</string>
240 <string name="region_korea">Coréia</string>
241 <string name="region_taiwan">Taiwan</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Japônes (日本語)</string>
245 <string name="language_english">Português do Brasil</string>
246 <string name="language_french">Francês (Français)</string>
247 <string name="langauge_german">Alemão (Deutsch)</string>
248 <string name="language_italian">Italiano (Italiano)</string>
249 <string name="language_spanish">Espanhol (Español)</string>
250 <string name="language_chinese">Mandarim (简体中文)</string>
251 <string name="language_korean">Coreano (한국어)</string>
252 <string name="language_dutch">Holandês (Nederlands)</string>
253 <string name="language_portuguese">Português (Português)</string>
254 <string name="language_russian">Russo (Русский)</string>
255 <string name="language_taiwanese">Taiwanês (台湾)</string>
256 <string name="language_british_english">Inglês britânico (British English)</string>
257 <string name="language_canadian_french">Fracês Canadiano (Français canadien)</string>
258 <string name="language_latin_american_spanish">Espanhol da América Latina (Español latino-americano)</string>
259 <string name="language_simplified_chinese">Chinês Simplificado (简体中文)</string>
260 <string name="language_traditional_chinese">Chinês tradicional (正體中文)</string>
261 <string name="language_brazilian_portuguese">Português do Brasil (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulcano</string>
265 <string name="renderer_none">Nenhum</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normal</string>
269 <string name="renderer_accuracy_high">Alto</string>
270 <string name="renderer_accuracy_extreme">Estremo (Lento)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Slow)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Lento)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Lento)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Imediato (Desligado)</string>
282 <string name="renderer_vsync_mailbox">Caixa de entrada</string>
283 <string name="renderer_vsync_fifo">FIFO (Ligado)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxado </string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Vizinho mais próximo</string>
288 <string name="scaling_filter_bilinear">Bilinear</string>
289 <string name="scaling_filter_bicubic">Bicúbico</string>
290 <string name="scaling_filter_gaussian">Gaussiano</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Nenhum</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Padrão (16:9)</string>
301 <string name="ratio_force_four_three">Forçar 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Forçar 21:9</string>
303 <string name="ratio_force_sixteen_ten">Forçar 16:10</string>
304 <string name="ratio_stretch">Esticar para a janela</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Preciso</string>
308 <string name="cpu_accuracy_unsafe">Não seguro</string>
309 <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">D-pad</string>
313 <string name="gamepad_left_stick">Analógico esquerdo</string>
314 <string name="gamepad_right_stick">Analógico direito</string>
315 <string name="gamepad_home">Botão Home</string>
316 <string name="gamepad_screenshot">Captura de ecrã</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">A preparar shaders</string>
320 <string name="building_shaders">A criar shaders</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Muda o Tema da App</string>
324 <string name="theme_default">Padrão</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Altera o Modo do Tema</string>
329 <string name="theme_mode_follow_system">Igual ao Sistema</string>
330 <string name="theme_mode_light">Claro</string>
331 <string name="theme_mode_dark">Escuro</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Usa Fundos Negros</string>
335 <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string>
336
337</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
new file mode 100644
index 000000000..8761e2374
--- /dev/null
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>
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>
7 <string name="emulation_notification_running">Yuzu está em execução </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>
10 <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string>
11
12 <!-- Setup strings -->
13 <string name="welcome">Benvindo! </string>
14 <string name="welcome_description">Aprende como configurar &lt;b>yuzu&lt;/b> e arranca a emulação.</string>
15 <string name="get_started">Começa</string>
16 <string name="keys">Chaves</string>
17 <string name="keys_description">Seleciona o teu ficheiro &lt;b>prod.keys&lt;/b> com o botão abaixo.</string>
18 <string name="select_keys">Seleciona as Chaves</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>
21 <string name="done">Feito</string>
22 <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string>
23 <string name="text_continue">Continuar</string>
24 <string name="next">Próximo</string>
25 <string name="back">Voltar</string>
26 <string name="add_games">Adiciona Jogos</string>
27 <string name="add_games_description">Seleciona a tua pasta de Jogos</string>
28
29 <!-- Home strings -->
30 <string name="home_games">Jogos</string>
31 <string name="home_search">Pesquisar</string>
32 <string name="home_settings">Configurações</string>
33 <string name="empty_gamelist">Não foram encontrados jogos ou a pasta de Jogos ainda não foi definida. </string>
34 <string name="search_and_filter_games">Procura e filtra jogos.</string>
35 <string name="select_games_folder">Seleciona a pasta de jogos.</string>
36 <string name="select_games_folder_description">Permite que o Yuzu preencha a lista de jogos</string>
37 <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_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
40 <string name="home_search_games">Procurar Jogos</string>
41 <string name="games_dir_selected">Pasta de Jogos selecionada</string>
42 <string name="install_prod_keys">Instala prod.keys</string>
43 <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string>
44 <string name="install_prod_keys_warning">Ignorar a adição de chaves?</string>
45 <string name="install_prod_keys_warning_description">São necessárias chaves válidas para emular jogos comerciais. Somente aplicativos homebrew funcionarão se você continuar.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Notificações</string>
48 <string name="notifications_description">Conceda a permissão de notificação com o botão abaixo.</string>
49 <string name="give_permission">Conceda permissão</string>
50 <string name="notification_warning">Saltar a concessão da permissão de notificação?</string>
51 <string name="notification_warning_description">Yuzu não conseguirá te notificar de informações importantes. </string>
52 <string name="permission_denied">Permissão negada</string>
53 <string name="permission_denied_description">Você negou essa permissão muitas vezes e agora precisa concedê-la manualmente nas configurações do sistema.</string>
54 <string name="about">Sobre</string>
55 <string name="about_description">Versão de compilação, créditos e mais</string>
56 <string name="warning_help">Ajuda</string>
57 <string name="warning_skip">Saltar</string>
58 <string name="warning_cancel">Cancelar</string>
59 <string name="install_amiibo_keys">Instala chaves Amiibo</string>
60 <string name="install_amiibo_keys_description">Necessário para usares Amiibo no jogo</string>
61 <string name="invalid_keys_file">Ficheiro de chaves inválido</string>
62 <string name="install_keys_success">Chaves instaladas com sucesso</string>
63 <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string>
64 <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>
66 <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>
68 <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>
70 <string name="settings_description">Configura configurações do emulador</string>
71 <string name="search_recently_played">Jogos recentes</string>
72 <string name="search_recently_added">Adicionados recentemente</string>
73 <string name="search_retail">Jogos comerciais</string>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Abre a pasta Yuzu</string>
76 <string name="open_user_folder_description">Gere os ficheiro internos do Yuzu</string>
77 <string name="theme_and_color_description">Modifica a aparência da App</string>
78 <string name="no_file_manager">Nenhum gestor de ficheiros encontrado</string>
79 <string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string>
80 <string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string>
81 <string name="manage_save_data">Gerir dados guardados</string>
82 <string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string>
83 <string name="import_export_saves_description">Importa ou exporta dados guardados</string>
84 <string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string>
85 <string name="save_file_imported_success">Importado com sucesso</string>
86 <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string>
87 <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>
88 <string name="import_saves">Importar</string>
89 <string name="export_saves">Exportar</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia não é real</string>
93 <string name="copied_to_clipboard">Copiado para a área de transferência</string>
94 <string name="about_app_description">Um emulador Switch de código aberto</string>
95 <string name="contributors">Contribuidores</string>
96 <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Versão</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Acesso antecipado</string>
105 <string name="get_early_access">Obtém Acesso Antecipado</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Recursos de ponta, acesso antecipado a atualizações e muito mais</string>
108 <string name="early_access_benefits">Benefícios do Acesso Antecipado</string>
109 <string name="cutting_edge_features">Recursos de ponta</string>
110 <string name="early_access_updates">Acesso antecipado a atualizações</string>
111 <string name="no_manual_installation">Sem instalação manual</string>
112 <string name="prioritized_support">Suporte prioritário</string>
113 <string name="helping_game_preservation">Ajuda na preservação dos jogos</string>
114 <string name="our_eternal_gratitude">A nossa eterna gratidão</string>
115 <string name="are_you_interested">Estás interessado?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Ativar limite de velocidade</string>
119 <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string>
120 <string name="frame_limit_slider">Percentagem do limite de velocidade</string>
121 <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>
122 <string name="cpu_accuracy">Precisão do CPU</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Modo ancorado</string>
126 <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string>
127 <string name="emulated_region">Região da emulação</string>
128 <string name="emulated_language">Idioma da emulação</string>
129 <string name="select_rtc_date">Seleciona a data RTC</string>
130 <string name="select_rtc_time">Seleciona a hora RTC</string>
131 <string name="use_custom_rtc">Ativa RTC personalizado</string>
132 <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string>
133 <string name="set_custom_rtc">Define RTC personalizado</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Nível de precisão</string>
138 <string name="renderer_resolution">Resolução</string>
139 <string name="renderer_vsync">Modo VSync</string>
140 <string name="renderer_aspect_ratio">Proporção do ecrã</string>
141 <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string>
142 <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string>
143 <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string>
144 <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string>
145 <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string>
146 <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string>
147 <string name="renderer_debug">Ativar depuração de gráficos</string>
148 <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string>
149 <string name="use_disk_shader_cache">Usar cache do disk shader</string>
150 <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Volume</string>
154 <string name="audio_volume_description">Especifica o volume de saída.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">Padrão</string>
158 <string name="ini_saved">Configurações guardadas</string>
159 <string name="gameid_saved">Configurações guardadas para %1$s</string>
160 <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string>
161 <string name="loading">A carregar...</string>
162 <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string>
163 <string name="reset_to_default">Reverter para padrão</string>
164 <string name="reset_all_settings">Redefinir todas as configurações?</string>
165 <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>
166 <string name="settings_reset">Redefinir configurações </string>
167 <string name="close">Fechar</string>
168 <string name="learn_more">Saber Mais</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Seleciona a driver para o GPU</string>
172 <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
173 <string name="select_gpu_driver_install">Instalar</string>
174 <string name="select_gpu_driver_default">Padrão</string>
175 <string name="select_gpu_driver_install_success">Instalado%s</string>
176 <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
177 <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
178 <string name="system_gpu_driver">Driver do GPU padrão</string>
179 <string name="installing_driver">A instalar o Driver...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Configurações</string>
183 <string name="preferences_general">Geral</string>
184 <string name="preferences_system">Sistema</string>
185 <string name="preferences_graphics">Gráficos</string>
186 <string name="preferences_audio">Audio</string>
187 <string name="preferences_theme">Cor e tema.</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">A tua ROM está encriptada</string>
191 <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>
192 <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>
193 <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string>
194 <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>
195 <string name="loader_error_invalid_format">Impossível carregar a tua ROM</string>
196 <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Sair da emulação</string>
200 <string name="emulation_done">Feito</string>
201 <string name="emulation_fps_counter">Contador de FPS</string>
202 <string name="emulation_toggle_controls">Alterar Controlos</string>
203 <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string>
204 <string name="emulation_dpad_slide">Deslizar do DPad</string>
205 <string name="emulation_haptics">Hápticos </string>
206 <string name="emulation_show_overlay">Mostrar sobreposição </string>
207 <string name="emulation_toggle_all">Alterar todos</string>
208 <string name="emulation_control_adjust">Ajustar a sobreposição </string>
209 <string name="emulation_control_scale">Escala</string>
210 <string name="emulation_control_opacity">Opacidade</string>
211 <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string>
212 <string name="emulation_touch_overlay_edit">Editar sobreposição </string>
213 <string name="emulation_pause">Pausa emulação</string>
214 <string name="emulation_unpause">Retomar emulação</string>
215 <string name="emulation_input_overlay">Opções de sobreposição </string>
216 <string name="emulation_game_loading">Jogo a carregar...</string>
217
218 <string name="load_settings">Configurações a carregar...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Teclado de Software</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Abortar</string>
225 <string name="continue_button">Continuar</string>
226 <string name="system_archive_not_found">Arquivo do Sistema Não Encontrado</string>
227 <string name="system_archive_not_found_message">%s está em falta. Por favor apaga os teus ficheiros de sistema.\nContinuar a emulação pode causar erros.</string>
228 <string name="system_archive_general">Um arquivo do sistema</string>
229 <string name="save_load_error">Erro Guardar/Carregar</string>
230 <string name="fatal_error">Erro fatal</string>
231 <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string>
232 <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>
233
234 <!-- Region Names -->
235 <string name="region_japan">Japão</string>
236 <string name="region_usa">EUA</string>
237 <string name="region_europe">Europa</string>
238 <string name="region_australia">Austrália</string>
239 <string name="region_china">China</string>
240 <string name="region_korea">Coreia</string>
241 <string name="region_taiwan">Taiwan</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Japonês (日本語)</string>
245 <string name="language_english">Inglês</string>
246 <string name="language_french">Francês (Français)</string>
247 <string name="langauge_german">Alemão (Deutsch)</string>
248 <string name="language_italian">Italiano (Italiano)</string>
249 <string name="language_spanish">Espanhol (Español)</string>
250 <string name="language_chinese">Chinês simplificado (简体中文)</string>
251 <string name="language_korean">Coreano (한국어)</string>
252 <string name="language_dutch">Holandês (Nederlands)</string>
253 <string name="language_portuguese">Português (Português)</string>
254 <string name="language_russian">Russo (Русский)</string>
255 <string name="language_taiwanese">Taiwanês (台湾)</string>
256 <string name="language_british_english">Inglês Britânico</string>
257 <string name="language_canadian_french">Fracês Canadiano (Français canadien)</string>
258 <string name="language_latin_american_spanish">Espanhol da América Latina (Español latino-americano)</string>
259 <string name="language_simplified_chinese">Chinês Simplificado (简体中文)</string>
260 <string name="language_traditional_chinese">Chinês Tradicional (正 體 中文)</string>
261 <string name="language_brazilian_portuguese">Português do Brasil (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulcano</string>
265 <string name="renderer_none">Nenhum</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Normal</string>
269 <string name="renderer_accuracy_high">Alto</string>
270 <string name="renderer_accuracy_extreme">Estremo (Lento)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Lento)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Lento)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Lento)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Imediato (Desligado)</string>
282 <string name="renderer_vsync_mailbox">Caixa de entrada</string>
283 <string name="renderer_vsync_fifo">FIFO (Ligado)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxado </string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Vizinho mais próximo</string>
288 <string name="scaling_filter_bilinear">Bilinear</string>
289 <string name="scaling_filter_bicubic">Bicúbico</string>
290 <string name="scaling_filter_gaussian">Gaussiano</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Nenhum</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Padrão (16:9)</string>
301 <string name="ratio_force_four_three">Forçar 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Forçar 21:9</string>
303 <string name="ratio_force_sixteen_ten">Forçar 16:10</string>
304 <string name="ratio_stretch">Esticar à Janela</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Preciso</string>
308 <string name="cpu_accuracy_unsafe">Inseguro</string>
309 <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">D-Pad</string>
313 <string name="gamepad_left_stick">Analógico Esquerdo</string>
314 <string name="gamepad_right_stick">Analógico Direito</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Captura de ecrã</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">A preparar shaders</string>
320 <string name="building_shaders">A criar shaders</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Muda o Tema da App</string>
324 <string name="theme_default">Padrão</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Altera o Modo do Tema</string>
329 <string name="theme_mode_follow_system">Igual ao Sistema</string>
330 <string name="theme_mode_light">Claro</string>
331 <string name="theme_mode_dark">Escuro</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Usa Fundos Escuros</string>
335 <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string>
336
337</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
new file mode 100644
index 000000000..0fb4908f7
--- /dev/null
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>играми&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
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">Позволяет yuzu заполнить список игр</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="games_dir_selected">Выбрана папка с играми</string>
42 <string name="install_prod_keys">Установить prod.keys</string>
43 <string name="install_prod_keys_description">Требуется для расшифровки розничных игр</string>
44 <string name="install_prod_keys_warning">Пропустить добавление ключей?</string>
45 <string name="install_prod_keys_warning_description">Для эмуляции розничных игр требуются действительные ключи. Если вы продолжите, будут работать только homebrew приложения.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Уведомления</string>
48 <string name="notifications_description">Предоставьте разрешение уведомлений с помощью кнопки ниже.</string>
49 <string name="give_permission">Предоставить разрешение</string>
50 <string name="notification_warning">Пропустить предоставление разрешения уведомлений?</string>
51 <string name="notification_warning_description">yuzu не сможет уведомлять вас о важной информации.</string>
52 <string name="permission_denied">Разрешение отказано</string>
53 <string name="permission_denied_description">Вы слишком часто отклоняли это разрешение, и теперь вам нужно будет вручную предоставить его в настройках системы.</string>
54 <string name="about">О нас</string>
55 <string name="about_description">Версия сборки, титры и другое</string>
56 <string name="warning_help">Помощь</string>
57 <string name="warning_skip">Пропустить</string>
58 <string name="warning_cancel">Отмена</string>
59 <string name="install_amiibo_keys">Установить ключи Amiibo</string>
60 <string name="install_amiibo_keys_description">Необходимо для использования Amiibo в играх</string>
61 <string name="invalid_keys_file">Выбран неверный файл ключей</string>
62 <string name="install_keys_success">Ключи успешно установлены</string>
63 <string name="reading_keys_failure">Ошибка при чтении ключей шифрования</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>
66 <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. Пожалуйста, пере-дампите ваши ключи.</string>
67 <string name="install_gpu_driver">Установить драйвер ГП</string>
68 <string name="install_gpu_driver_description">Установите альтернативные драйверы для потенциально лучшей производительности и/или точности</string>
69 <string name="advanced_settings">Расширенные настройки</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>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Открыть папку yuzu</string>
76 <string name="open_user_folder_description">Управление внутренними файлами yuzu</string>
77 <string name="theme_and_color_description">Изменение внешнего вида приложения</string>
78 <string name="no_file_manager">Не найден файловый менеджер</string>
79 <string name="notification_no_directory_link">Не удалось открыть папку yuzu</string>
80 <string name="notification_no_directory_link_description">Пожалуйста, найдите папку пользователя с помощью боковой панели файлового менеджера вручную.</string>
81 <string name="manage_save_data">Управление данными сохранений</string>
82 <string name="manage_save_data_description">Найдено данные сохранений. Пожалуйста, выберите вариант ниже.</string>
83 <string name="import_export_saves_description">Импорт или экспорт файлов сохранения</string>
84 <string name="import_export_saves_no_profile">Данные сохранений не найдены. Пожалуйста, запустите игру и повторите попытку.</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
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia не существует</string>
93 <string name="copied_to_clipboard">Скопировано в буфер обмена</string>
94 <string name="about_app_description">Эмулятор Switch с открытым исходным кодом</string>
95 <string name="contributors">Контрибьюторы</string>
96 <string name="contributors_description">Сделано с \u2764 от команды yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Сборка</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Ранний доступ</string>
105 <string name="get_early_access">Получить ранний доступ</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Новейшие возможности, ранний доступ к обновлениям и другое</string>
108 <string name="early_access_benefits">Преимущества раннего доступа</string>
109 <string name="cutting_edge_features">Новейшие возможности</string>
110 <string name="early_access_updates">Ранний доступ к обновлениям</string>
111 <string name="no_manual_installation">Без ручной установки</string>
112 <string name="prioritized_support">Приоритетная поддержка</string>
113 <string name="helping_game_preservation">Помощь в презервации игр</string>
114 <string name="our_eternal_gratitude">Наша бесконечная благодарность</string>
115 <string name="are_you_interested">Вы заинтересованы?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Включить ограничение скорости</string>
119 <string name="frame_limit_enable_description">Если эта функция включена, скорость эмуляции будет ограничена указанным процентом от нормальной скорости.</string>
120 <string name="frame_limit_slider">Ограничение процента cкорости</string>
121 <string name="frame_limit_slider_description">Указывает процент для ограничения скорости эмуляции. При значении по умолчанию 100% эмуляция будет ограничена нормальной скоростью. Значения выше или ниже будут увеличивать или уменьшать ограничение скорости.</string>
122 <string name="cpu_accuracy">Точность ЦП</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Режим док-станции</string>
126 <string name="use_docked_mode_description">Эмуляция режима док-станции, что увеличивает разрешение за счет снижения производительности.</string>
127 <string name="emulated_region">Эмулируемый регион</string>
128 <string name="emulated_language">Эмулируемый язык</string>
129 <string name="select_rtc_date">Выберите дату RTC</string>
130 <string name="select_rtc_time">Выберите время RTC</string>
131 <string name="use_custom_rtc">Включить пользовательский RTC</string>
132 <string name="use_custom_rtc_description">Этот параметр позволяет установить пользовательские часы реального времени отдельно от текущего системного времени</string>
133 <string name="set_custom_rtc">Установить пользовательский RTC</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Уровень точности</string>
138 <string name="renderer_resolution">Разрешение</string>
139 <string name="renderer_vsync">Режим верт. синхронизации</string>
140 <string name="renderer_aspect_ratio">Соотношение сторон</string>
141 <string name="renderer_scaling_filter">Фильтр адаптации окна</string>
142 <string name="renderer_anti_aliasing">Метод сглаживания</string>
143 <string name="renderer_force_max_clock">Принудительно заставить максимальную тактовую частоту (только для Adreno)</string>
144 <string name="renderer_force_max_clock_description">Заставляет ГП работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
145 <string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string>
146 <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно, что уменьшает зависания, но может взамен предоставить визуальные баги.</string>
147 <string name="renderer_debug">Включить отладку графики</string>
148 <string name="renderer_debug_description">Если включено, графический API переходит в более медленный режим отладки</string>
149 <string name="use_disk_shader_cache">Использовать кэш шейдеров на диске</string>
150 <string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров на хранилище.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Громкость</string>
154 <string name="audio_volume_description">Задает громкость аудиовыхода.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">По умолчанию</string>
158 <string name="ini_saved">Сохраненные настройки</string>
159 <string name="gameid_saved">Настройки сохранены для %1$s</string>
160 <string name="error_saving">Ошибка сохранения %1$s.ini: %2$s</string>
161 <string name="loading">Загрузка...</string>
162 <string name="reset_setting_confirmation">Хотите ли вы вернуть этот параметр к значению по умолчанию?</string>
163 <string name="reset_to_default">Сброс к настройкам по умолчанию</string>
164 <string name="reset_all_settings">Сбросить все настройки?</string>
165 <string name="reset_all_settings_description">Все дополнительные настройки будут сброшены к настройке по умолчанию. Это невозможно отменить.</string>
166 <string name="settings_reset">Настройки сброшены</string>
167 <string name="close">Закрыть</string>
168 <string name="learn_more">Узнать больше</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Выбрать драйвер ГП</string>
172 <string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
173 <string name="select_gpu_driver_install">Установить</string>
174 <string name="select_gpu_driver_default">По умолчанию</string>
175 <string name="select_gpu_driver_install_success">Установлено %s</string>
176 <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
177 <string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
178 <string name="system_gpu_driver">Системный драйвер ГП</string>
179 <string name="installing_driver">Установка драйвера...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Настройки</string>
183 <string name="preferences_general">Общие</string>
184 <string name="preferences_system">Система</string>
185 <string name="preferences_graphics">Графика</string>
186 <string name="preferences_audio">Аудио</string>
187 <string name="preferences_theme">Тема и цвет</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">Ваш ROM зашифрованный</string>
191 <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>
192 <string name="loader_error_encrypted_keys_description"><![CDATA[Пожалуйста, убедитесь, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> установлен, чтобы игры можно было расшифровать.]]></string>
193 <string name="loader_error_video_core">Произошла ошибка при инициализации видеоядра.</string>
194 <string name="loader_error_video_core_description">Обычно это вызвано несовместимым драйвером ГП. Установка пользовательского драйвера ГП может решить эту проблему.</string>
195 <string name="loader_error_invalid_format">Не удалось запустить ROM</string>
196 <string name="loader_error_file_not_found">Файл ROM не существует</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Выход из эмуляции</string>
200 <string name="emulation_done">Готово</string>
201 <string name="emulation_fps_counter">Счётчик FPS</string>
202 <string name="emulation_toggle_controls">Переключение управления</string>
203 <string name="emulation_rel_stick_center">Относительный центр стика</string>
204 <string name="emulation_dpad_slide">Слайд крестовиной</string>
205 <string name="emulation_haptics">Тактильная обратная связь</string>
206 <string name="emulation_show_overlay">Показать оверлей</string>
207 <string name="emulation_toggle_all">Переключить всё</string>
208 <string name="emulation_control_adjust">Настроить оверлей</string>
209 <string name="emulation_control_scale">Масштаб</string>
210 <string name="emulation_control_opacity">Непрозрачность</string>
211 <string name="emulation_touch_overlay_reset">Сбросить оверлей</string>
212 <string name="emulation_touch_overlay_edit">Изменить оверлей</string>
213 <string name="emulation_pause">Пауза эмуляции</string>
214 <string name="emulation_unpause">Возобновление эмуляции</string>
215 <string name="emulation_input_overlay">Настройки оверлея</string>
216 <string name="emulation_game_loading">Загрузка игры...</string>
217
218 <string name="load_settings">Загрузка настроек...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Виртуальная клавиатура</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Прервать</string>
225 <string name="continue_button">Продолжить</string>
226 <string name="system_archive_not_found">Системный архив не найден</string>
227 <string name="system_archive_not_found_message">%s отсутствует. Пожалуйста, сдампите ваши системные архивы.\nПродолжение эмуляции может привести к сбоям и ошибкам.</string>
228 <string name="system_archive_general">Системный архив</string>
229 <string name="save_load_error">Ошибка сохранения/загрузки</string>
230 <string name="fatal_error">Фатальная ошибка</string>
231 <string name="fatal_error_message">Произошла фатальная ошибка. Проверьте журнал для получения подробной информации.\nПродолжение эмуляции может привести к сбоям и ошибкам.</string>
232 <string name="performance_warning">Отключение этой настройки значительно снизит производительность эмуляции! Для достижения наилучших результатов рекомендуется оставить эту настройку включенной.</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">Япония</string>
236 <string name="region_usa">США</string>
237 <string name="region_europe">Европа</string>
238 <string name="region_australia">Австралия</string>
239 <string name="region_china">Китай</string>
240 <string name="region_korea">Корея</string>
241 <string name="region_taiwan">Тайвань</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Японский (日本語)</string>
245 <string name="language_english">Английский (English)</string>
246 <string name="language_french">Французский (Français)</string>
247 <string name="langauge_german">Немецкий (Deutsch)</string>
248 <string name="language_italian">Итальянский (Italiano)</string>
249 <string name="language_spanish">Испанский (Español)</string>
250 <string name="language_chinese">Китайский (简体中文)</string>
251 <string name="language_korean">Корейский (한국어)</string>
252 <string name="language_dutch">Голландский (Nederlands)</string>
253 <string name="language_portuguese">Португальский (Português)</string>
254 <string name="language_russian">Русский</string>
255 <string name="language_taiwanese">Тайваньский (台湾)</string>
256 <string name="language_british_english">Британский английский</string>
257 <string name="language_canadian_french">Канадский французский (Français canadien)</string>
258 <string name="language_latin_american_spanish">Латиноамериканский испанский (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">Упрощенный китайский (简体中文)</string>
260 <string name="language_traditional_chinese">Традиционный китайский (正體中文)</string>
261 <string name="language_brazilian_portuguese">Бразильский португальский (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Никакой</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Нормальная</string>
269 <string name="renderer_accuracy_high">Высокая</string>
270 <string name="renderer_accuracy_extreme">Экстрим (медленный)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Медленно)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Медленно)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Медленно)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Моментальная (выключена) </string>
282 <string name="renderer_vsync_mailbox">Mailbox</string>
283 <string name="renderer_vsync_fifo">FIFO (Включена)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Ближайший сосед</string>
288 <string name="scaling_filter_bilinear">Билинейный</string>
289 <string name="scaling_filter_bicubic">Бикубический</string>
290 <string name="scaling_filter_gaussian">Гаусс</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™️ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Выкл.</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">Стандартное (16:9)</string>
301 <string name="ratio_force_four_three">Заставить 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Заставить 21:9</string>
303 <string name="ratio_force_sixteen_ten">Заставить 16:10</string>
304 <string name="ratio_stretch">Растянуть до окна</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Точно</string>
308 <string name="cpu_accuracy_unsafe">Небезопасно</string>
309 <string name="cpu_accuracy_paranoid">Параноик (медленно)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">Крестовина</string>
313 <string name="gamepad_left_stick">Левый мини-джойстик</string>
314 <string name="gamepad_right_stick">Правый мини-джойстик</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Скриншот</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Подготовка шейдеров</string>
320 <string name="building_shaders">Постройка шейдеров</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Изменить тему приложения</string>
324 <string name="theme_default">По умолчанию</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Изменить режим темы</string>
329 <string name="theme_mode_follow_system">Системная</string>
330 <string name="theme_mode_light">Светлая</string>
331 <string name="theme_mode_dark">Темная</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Использовать черный фон</string>
335 <string name="use_black_backgrounds_description">При использовании темной темы применяйте черный фон.</string>
336
337</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
new file mode 100644
index 000000000..0d11eb2d2
--- /dev/null
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>іграми&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
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">Дозволяє yuzu заповнити список ігор</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="games_dir_selected">Вибрано папку з іграми</string>
42 <string name="install_prod_keys">Встановити prod.keys</string>
43 <string name="install_prod_keys_description">Потрібно для розшифровки роздрібних ігор</string>
44 <string name="install_prod_keys_warning">Пропустити додавання ключів?</string>
45 <string name="install_prod_keys_warning_description">Для емуляції роздрібних ігор потрібні дійсні ключі. Якщо ви продовжите, працюватимуть тільки homebrew додатки.</string>
46 <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
47 <string name="notifications">Сповіщення</string>
48 <string name="notifications_description">Надайте дозвіл сповіщень за допомогою кнопки нижче.</string>
49 <string name="give_permission">Надати дозвіл</string>
50 <string name="notification_warning">Пропустити надання дозволу сповіщень?</string>
51 <string name="notification_warning_description">yuzu не зможе повідомляти вас про важливу інформацію.</string>
52 <string name="permission_denied">У дозволі відмовлено</string>
53 <string name="permission_denied_description">Ви занадто часто відхиляли цей дозвіл, тож тепер вам потрібно буде вручну надати його в системних налаштуваннях.</string>
54 <string name="about">Про нас</string>
55 <string name="about_description">Версія збірки, титри та інше</string>
56 <string name="warning_help">Допомога</string>
57 <string name="warning_skip">Пропустити</string>
58 <string name="warning_cancel">Відміна</string>
59 <string name="install_amiibo_keys">Встановити ключі Amiibo</string>
60 <string name="install_amiibo_keys_description">Необхідно для використання Amiibo в іграх</string>
61 <string name="invalid_keys_file">Вибрано неправильний файл ключів</string>
62 <string name="install_keys_success">Ключі успішно встановлено</string>
63 <string name="reading_keys_failure">Помилка під час зчитування ключів шифрування</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>
66 <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь ласка, пере-дампіть ваші ключі.</string>
67 <string name="install_gpu_driver">Встановити драйвер ГП</string>
68 <string name="install_gpu_driver_description">Встановіть альтернативні драйвери для потенційно кращої продуктивності та/або точності</string>
69 <string name="advanced_settings">Розширені налаштування</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>
74 <string name="search_homebrew">Homebrew</string>
75 <string name="open_user_folder">Відкрити папку yuzu</string>
76 <string name="open_user_folder_description">Керування внутрішніми файлами yuzu</string>
77 <string name="theme_and_color_description">Змінити зовнішній вигляд застосунку</string>
78 <string name="no_file_manager">Не знайдено файлового менеджера</string>
79 <string name="notification_no_directory_link">Не вдалося відкрити папку yuzu</string>
80 <string name="notification_no_directory_link_description">Будь ласка, знайдіть папку користувача за допомогою бічної панелі файлового менеджера вручну.</string>
81 <string name="manage_save_data">Керування даними збережень</string>
82 <string name="manage_save_data_description">Знайдено дані збережень. Будь ласка, виберіть варіант нижче.</string>
83 <string name="import_export_saves_description">Імпорт або експорт файлів збереження</string>
84 <string name="import_export_saves_no_profile">Дані збережень не знайдено. Будь ласка, запустіть гру та повторіть спробу.</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
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia не існує</string>
93 <string name="copied_to_clipboard">Скопійовано в буфер обміну</string>
94 <string name="about_app_description">Емулятор Switch із відкритим першокодом</string>
95 <string name="contributors">Вкладники</string>
96 <string name="contributors_description">Зроблено з \u2764 від команди yuzu</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">Збірка</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">Ранній доступ</string>
105 <string name="get_early_access">Отримати ранній доступ</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">Новітні можливості, ранній доступ до оновлень та інше</string>
108 <string name="early_access_benefits">Переваги раннього доступу</string>
109 <string name="cutting_edge_features">Новітні можливості</string>
110 <string name="early_access_updates">Ранній доступ до оновлень</string>
111 <string name="no_manual_installation">Без ручного встановлення</string>
112 <string name="prioritized_support">Пріоритетна підтримка</string>
113 <string name="helping_game_preservation">Допомога в презервації ігор</string>
114 <string name="our_eternal_gratitude">Наша нескінченна вдячність</string>
115 <string name="are_you_interested">Ви зацікавлені?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">Увімкнути обмеження швидкості</string>
119 <string name="frame_limit_enable_description">Якщо цю функцію ввімкнено, швидкість емуляції буде обмежена зазначеним відсотком від нормальної швидкості.</string>
120 <string name="frame_limit_slider">Обмеження відсотка швидкості</string>
121 <string name="frame_limit_slider_description">Вказує відсоток для обмеження швидкості емуляції. При значенні за замовчуванням 100% емуляція буде обмежена нормальною швидкістю. Значення вище або нижче збільшуватимуть або зменшуватимуть обмеження швидкості.</string>
122 <string name="cpu_accuracy">Точність ЦП</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">Режим док-станції</string>
126 <string name="use_docked_mode_description">Емуляція режиму док-станції, що збільшує роздільну здатність за рахунок зниження продуктивності.</string>
127 <string name="emulated_region">Емульований регіон</string>
128 <string name="emulated_language">Емульована мова</string>
129 <string name="select_rtc_date">Оберіть дату RTC</string>
130 <string name="select_rtc_time">Оберіть час RTC</string>
131 <string name="use_custom_rtc">Увімкнути користувацький RTC</string>
132 <string name="use_custom_rtc_description">Цей параметр дає змогу встановити користувацький годинник реального часу окремо від поточного системного часу</string>
133 <string name="set_custom_rtc">Встановити користувацький RTC</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">Рівень точності</string>
138 <string name="renderer_resolution">Роздільна здатність</string>
139 <string name="renderer_vsync">Режим верт. синхронізації</string>
140 <string name="renderer_aspect_ratio">Співвідношення сторін</string>
141 <string name="renderer_scaling_filter">Фільтр адаптації вікна</string>
142 <string name="renderer_anti_aliasing">Метод згладжування</string>
143 <string name="renderer_force_max_clock">Примусово змусити максимальну тактову частоту (тільки для Adreno)</string>
144 <string name="renderer_force_max_clock_description">Змушує ГП працювати на максимально можливих тактових частотах (теплові обмеження все одно будуть застосовуватися).</string>
145 <string name="renderer_asynchronous_shaders">Використовувати асинхронні шейдери</string>
146 <string name="renderer_asynchronous_shaders_description">Компілює шейдери асинхронно, що зменшує зависання, але може натомість надати візуальні баги.</string>
147 <string name="renderer_debug">Увімкнути налагодження графіки</string>
148 <string name="renderer_debug_description">Якщо увімкнено, графічний API переходить у повільніший режим налагодження</string>
149 <string name="use_disk_shader_cache">Використовувати кеш шейдерів на диску</string>
150 <string name="use_disk_shader_cache_description">Зменшення зависань завдяки зберіганню та завантаженню згенерованих шейдерів на сховище.</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">Гучність</string>
154 <string name="audio_volume_description">Вказує гучність аудіовиходу.</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">За замовчуванням</string>
158 <string name="ini_saved">Збережені налаштування</string>
159 <string name="gameid_saved">Налаштування збережені для %1$s</string>
160 <string name="error_saving">Помилка збереження %1$s.ini: %2$s</string>
161 <string name="loading">Завантаження...</string>
162 <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до значення за замовчуванням?</string>
163 <string name="reset_to_default">Скидання до налаштувань за замовчуванням</string>
164 <string name="reset_all_settings">Скинути всі налаштування</string>
165 <string name="reset_all_settings_description">Усі додаткові налаштування буде скинуто до налаштування за замовчуванням. Це неможливо скасувати.</string>
166 <string name="settings_reset">Налаштування скинуто</string>
167 <string name="close">Закрити</string>
168 <string name="learn_more">Дізнатися більше</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">Вибрати драйвер ГП</string>
172 <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
173 <string name="select_gpu_driver_install">Встановити</string>
174 <string name="select_gpu_driver_default">За замовчуванням</string>
175 <string name="select_gpu_driver_install_success">Встановлено %s</string>
176 <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
177 <string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
178 <string name="system_gpu_driver">Системний драйвер ГП</string>
179 <string name="installing_driver">Встановлення драйвера...</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">Налаштування</string>
183 <string name="preferences_general">Загальні</string>
184 <string name="preferences_system">Система</string>
185 <string name="preferences_graphics">Графіка</string>
186 <string name="preferences_audio">Аудіо</string>
187 <string name="preferences_theme">Тема і колір</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">Ваш ROM зашифрований</string>
191 <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>
192 <string name="loader_error_encrypted_keys_description"><![CDATA[Будь ласка, переконайтеся, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> встановлено, щоб ігри можна було розшифрувати.]]></string>
193 <string name="loader_error_video_core">Сталася помилка під час ініціалізації відеоядра.</string>
194 <string name="loader_error_video_core_description">Зазвичай це спричинено несумісним драйвером ГП. Встановлення користувацького драйвера ГП може вирішити цю проблему.</string>
195 <string name="loader_error_invalid_format">Не вдалося запустити ROM</string>
196 <string name="loader_error_file_not_found">Файл ROM не існує</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">Вихід з емуляції</string>
200 <string name="emulation_done">Готово</string>
201 <string name="emulation_fps_counter">Лічильник FPS</string>
202 <string name="emulation_toggle_controls">Перемикання керування</string>
203 <string name="emulation_rel_stick_center">Відносний центр стіка</string>
204 <string name="emulation_dpad_slide">Слайд хрестовиною</string>
205 <string name="emulation_haptics">Тактильний зворотний зв\'язок</string>
206 <string name="emulation_show_overlay">Показати оверлей</string>
207 <string name="emulation_toggle_all">Перемкнути все</string>
208 <string name="emulation_control_adjust">Налаштувати оверлей</string>
209 <string name="emulation_control_scale">Масштаб</string>
210 <string name="emulation_control_opacity">Непрозорість</string>
211 <string name="emulation_touch_overlay_reset">Скинути оверлей</string>
212 <string name="emulation_touch_overlay_edit">Змінити оверлей</string>
213 <string name="emulation_pause">Пауза емуляції</string>
214 <string name="emulation_unpause">Відновлення емуляції</string>
215 <string name="emulation_input_overlay">Налаштування оверлея</string>
216 <string name="emulation_game_loading">Завантаження гри...</string>
217
218 <string name="load_settings">Завантаження налаштувань...</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">Віртуальна клавіатура</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">Перервати</string>
225 <string name="continue_button">Продовжити</string>
226 <string name="system_archive_not_found">Системний архів не знайдено</string>
227 <string name="system_archive_not_found_message">%s відсутній. Будь ласка, здампіть ваші системні архіви.\nПродовження емуляції може призвести до збоїв і помилок.</string>
228 <string name="system_archive_general">Системний архів</string>
229 <string name="save_load_error">Помилка збереження/завантаження</string>
230 <string name="fatal_error">Фатальна помилка</string>
231 <string name="fatal_error_message">Сталася фатальна помилка. Перевірте журнал для отримання докладної інформації.\nПродовження емуляції може призвести до збоїв і помилок.</string>
232 <string name="performance_warning">Вимкнення цього налаштування значно знизить продуктивність емуляції! Для досягнення найкращих результатів рекомендується залишити це налаштування увімкненим.</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">Японія</string>
236 <string name="region_usa">США</string>
237 <string name="region_europe">Європа</string>
238 <string name="region_australia">Австралія</string>
239 <string name="region_china">Китай</string>
240 <string name="region_korea">Корея</string>
241 <string name="region_taiwan">Тайвань</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">Японська (日本語)</string>
245 <string name="language_english">Англійська (English)</string>
246 <string name="language_french">Французька (Français)</string>
247 <string name="langauge_german">Німецька (Deutsch)</string>
248 <string name="language_italian">Італійська (Italiano)</string>
249 <string name="language_spanish">Іспанська (Español)</string>
250 <string name="language_chinese">Китайскька (简体中文)</string>
251 <string name="language_korean">Корейська (한국어)</string>
252 <string name="language_dutch">Голландська (Nederlands)</string>
253 <string name="language_portuguese">Португальська (Português)</string>
254 <string name="language_russian">Російська (Русский)</string>
255 <string name="language_taiwanese">Тайванська (台湾)</string>
256 <string name="language_british_english">Британська англійська</string>
257 <string name="language_canadian_french">Канадська французька (Français canadien)</string>
258 <string name="language_latin_american_spanish">Латиноамериканська іспанська (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">Спрощена китайська (简体中文)</string>
260 <string name="language_traditional_chinese">Традиційна китайська (正體中文)</string>
261 <string name="language_brazilian_portuguese">Бразильська португальська (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">Вимкнено</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">Нормальна</string>
269 <string name="renderer_accuracy_high">Висока</string>
270 <string name="renderer_accuracy_extreme">Екстрим (повільно)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (Повільно)</string>
277 <string name="resolution_three">3X (2160p/3240p) (Повільно)</string>
278 <string name="resolution_four">4X (2880p/4320p) (Повільно)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">Моментальна (вимкнена)</string>
282 <string name="renderer_vsync_mailbox">Mailbox</string>
283 <string name="renderer_vsync_fifo">FIFO (ввімкнута)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">Найближчий сусід</string>
288 <string name="scaling_filter_bilinear">Білінійне</string>
289 <string name="scaling_filter_bicubic">Бікубічне</string>
290 <string name="scaling_filter_gaussian">Гауса</string>
291 <string name="scaling_filter_scale_force">ScaleForce</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">Вимкнено</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">За замовчуванням (16:9)</string>
301 <string name="ratio_force_four_three">Змусити 4:3</string>
302 <string name="ratio_force_twenty_one_nine">Змусити 21:9</string>
303 <string name="ratio_force_sixteen_ten">Змусити 16:10</string>
304 <string name="ratio_stretch">Розтягнути до вікна</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">Точно</string>
308 <string name="cpu_accuracy_unsafe">Небезпечно</string>
309 <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">Кнопки напрямків</string>
313 <string name="gamepad_left_stick">Лівий міні-джойстик</string>
314 <string name="gamepad_right_stick">Правий міні-джойстик</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">Знімок екрану</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">Підготовка шейдерів</string>
320 <string name="building_shaders">Побудова шейдерів</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">Змінити тему застосунку</string>
324 <string name="theme_default">За замовчуванням</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">Змінити режим теми</string>
329 <string name="theme_mode_follow_system">Системна</string>
330 <string name="theme_mode_light">Світла</string>
331 <string name="theme_mode_dark">Темна</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">Використовувати чорне тло</string>
335 <string name="use_black_backgrounds_description">У разі використання темної теми застосовуйте чорне тло.</string>
336
337</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
new file mode 100644
index 000000000..e00bbaa2e
--- /dev/null
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -0,0 +1,337 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>游戏&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
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">允许 yuzu 填充游戏列表</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="games_dir_selected">已选择游戏文件夹</string>
42 <string name="install_prod_keys">安装 prod.keys 文件</string>
43 <string name="install_prod_keys_description">需要密钥文件来解密游戏</string>
44 <string name="install_prod_keys_warning">跳过添加密钥文件?</string>
45 <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>
47 <string name="notifications">通知</string>
48 <string name="notifications_description">使用下方的按钮授予通知权限。</string>
49 <string name="give_permission">授予权限</string>
50 <string name="notification_warning">跳过授予通知权限?</string>
51 <string name="notification_warning_description">yuzu 将无法通知您重要信息。</string>
52 <string name="permission_denied">授权遭拒</string>
53 <string name="permission_denied_description">您曾多次拒绝权限请求,现在您需要在系统设置中手动授予权限。</string>
54 <string name="about">关于</string>
55 <string name="about_description">开发版本、贡献者、以及更多</string>
56 <string name="warning_help">帮助</string>
57 <string name="warning_skip">跳过</string>
58 <string name="warning_cancel">取消</string>
59 <string name="install_amiibo_keys">安装 Amiibo 密钥文件</string>
60 <string name="install_amiibo_keys_description">在遊戏中使用 Amiibo 时必需</string>
61 <string name="invalid_keys_file">选择的密钥文件无效</string>
62 <string name="install_keys_success">密钥文件已成功安装</string>
63 <string name="reading_keys_failure">读取加密密钥时出错</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>
66 <string name="install_keys_failure_description">选择的密钥文件不正确或已损坏。请重新转储密钥文件。</string>
67 <string name="install_gpu_driver">安装 GPU 驱动</string>
68 <string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
69 <string name="advanced_settings">高级选项</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>
74 <string name="search_homebrew">自制游戏</string>
75 <string name="open_user_folder">打开 yuzu 文件夹</string>
76 <string name="open_user_folder_description">管理 yuzu 内部文件</string>
77 <string name="theme_and_color_description">更改外观</string>
78 <string name="no_file_manager">找不到可用的文件管理器</string>
79 <string name="notification_no_directory_link">无法打开 yuzu 文件夹</string>
80 <string name="notification_no_directory_link_description">请使用文件管理器的侧部面板手动定位用户文件夹。</string>
81 <string name="manage_save_data">管理存档数据</string>
82 <string name="manage_save_data_description">已找到存档数据,请选择下方的选项。</string>
83 <string name="import_export_saves_description">导入或导出存档</string>
84 <string name="import_export_saves_no_profile">找不到存档数据,请启动游戏并重试。</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">第一个子文件夹名称必须为当前游戏的 ID。</string>
88 <string name="import_saves">导入</string>
89 <string name="export_saves">导出</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia 不真实</string>
93 <string name="copied_to_clipboard">已复制到剪贴板</string>
94 <string name="about_app_description">一款开放源代码的 Switch 模拟器</string>
95 <string name="contributors">贡献者</string>
96 <string name="contributors_description">使用来自 yuzu 团队的 \u2764 制作</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">构建版本</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">抢先体验</string>
105 <string name="get_early_access">取得抢先体验</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">最新的功能、抢先更新、以及更多</string>
108 <string name="early_access_benefits">抢先体验的权益</string>
109 <string name="cutting_edge_features">最新功能</string>
110 <string name="early_access_updates">抢先更新</string>
111 <string name="no_manual_installation">无需手动安装</string>
112 <string name="prioritized_support">优先支持</string>
113 <string name="helping_game_preservation">帮助保留游戏</string>
114 <string name="our_eternal_gratitude">我们真诚的感激</string>
115 <string name="are_you_interested">您对此感兴趣吗?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">启用运行速度限制</string>
119 <string name="frame_limit_enable_description">启用后,模拟速度将限制在正常运行速度的指定百分比。</string>
120 <string name="frame_limit_slider">限制速度百分比</string>
121 <string name="frame_limit_slider_description">指定限制模拟速度的百分比。预设为 100%,此时模拟速度将被限制为标准速度。更高或更低的值将增加或降低速度限制上限。</string>
122 <string name="cpu_accuracy">CPU 精度</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">主机模式</string>
126 <string name="use_docked_mode_description">以主机模式进行模拟,牺牲性能并提高画面分辨率。</string>
127 <string name="emulated_region">模拟区域</string>
128 <string name="emulated_language">模拟语言</string>
129 <string name="select_rtc_date">选择日期</string>
130 <string name="select_rtc_time">选择时间</string>
131 <string name="use_custom_rtc">启用自定义系统时钟</string>
132 <string name="use_custom_rtc_description">此选项允许您设置与目前系统时间相独立的自定义系统时钟</string>
133 <string name="set_custom_rtc">设置自定义系统时钟</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">精度等级</string>
138 <string name="renderer_resolution">分辨率</string>
139 <string name="renderer_vsync">垂直同步模式</string>
140 <string name="renderer_aspect_ratio">屏幕纵横比</string>
141 <string name="renderer_scaling_filter">窗口滤镜</string>
142 <string name="renderer_anti_aliasing">抗锯齿方式</string>
143 <string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string>
144 <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string>
145 <string name="renderer_asynchronous_shaders">使用异步着色器</string>
146 <string name="renderer_asynchronous_shaders_description">异步编译着色器,减少卡顿,但可能引入故障。</string>
147 <string name="renderer_debug">启用图形调试</string>
148 <string name="renderer_debug_description">启用时,图形 API 将进入较慢的调试模式。</string>
149 <string name="use_disk_shader_cache">使用磁盘着色器缓存</string>
150 <string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取以减少卡顿。</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">音量</string>
154 <string name="audio_volume_description">指定输出的音量。</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">系统默认</string>
158 <string name="ini_saved">已保存设置</string>
159 <string name="gameid_saved">已保存 %1$s 的设置</string>
160 <string name="error_saving">保存 %1$s.ini 时出错: %2$s</string>
161 <string name="loading">加载中…</string>
162 <string name="reset_setting_confirmation">您要将此设定重设为默认值吗?</string>
163 <string name="reset_to_default">恢复默认</string>
164 <string name="reset_all_settings">重置所有设置项?</string>
165 <string name="reset_all_settings_description">所有高级选项都将被重设,此动作无法还原。</string>
166 <string name="settings_reset">重设设置项</string>
167 <string name="close">关闭</string>
168 <string name="learn_more">了解更多</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">选择 GPU 驱动程序</string>
172 <string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
173 <string name="select_gpu_driver_install">安装</string>
174 <string name="select_gpu_driver_default">系统默认</string>
175 <string name="select_gpu_driver_install_success">已安装 %s</string>
176 <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
177 <string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
178 <string name="system_gpu_driver">系统 GPU 驱动程序</string>
179 <string name="installing_driver">正在安装驱动程序…</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">设置</string>
183 <string name="preferences_general">通用</string>
184 <string name="preferences_system">系统</string>
185 <string name="preferences_graphics">图形</string>
186 <string name="preferences_audio">声音</string>
187 <string name="preferences_theme">主题和色彩</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">您的 ROM 已加密</string>
191 <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>
192 <string name="loader_error_encrypted_keys_description"><![CDATA[请确保 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游戏可以被解密。]]></string>
193 <string name="loader_error_video_core">初始化视频核心时发生错误</string>
194 <string name="loader_error_video_core_description">这通常由不兼容的 GPU 驱动程序造成,安装自定义 GPU 驱动程序可能解决此问题。</string>
195 <string name="loader_error_invalid_format">无法载入 ROM</string>
196 <string name="loader_error_file_not_found">ROM 文件不存在</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">退出模拟</string>
200 <string name="emulation_done">完成</string>
201 <string name="emulation_fps_counter">FPS 计数器</string>
202 <string name="emulation_toggle_controls">按键切换</string>
203 <string name="emulation_rel_stick_center">相对摇杆中心</string>
204 <string name="emulation_dpad_slide">十字方向键滑动</string>
205 <string name="emulation_haptics">触觉反馈</string>
206 <string name="emulation_show_overlay">显示虚拟按键</string>
207 <string name="emulation_toggle_all">全部切换</string>
208 <string name="emulation_control_adjust">调整虚拟按键</string>
209 <string name="emulation_control_scale">缩放</string>
210 <string name="emulation_control_opacity">不透明度</string>
211 <string name="emulation_touch_overlay_reset">重设虚拟按键</string>
212 <string name="emulation_touch_overlay_edit">编辑虚拟按键</string>
213 <string name="emulation_pause">暂停模拟</string>
214 <string name="emulation_unpause">继续模拟</string>
215 <string name="emulation_input_overlay">虚拟按键选项</string>
216 <string name="emulation_game_loading">载入游戏中…</string>
217
218 <string name="load_settings">正在载入设定…</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">软件键盘</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">中止</string>
225 <string name="continue_button">继续</string>
226 <string name="system_archive_not_found">未找到系统档案</string>
227 <string name="system_archive_not_found_message">%s 丢失,请转储您的系统档案。\n继续模拟可能造成崩溃和错误。</string>
228 <string name="system_archive_general">系统档案</string>
229 <string name="save_load_error">保存/载入发生错误</string>
230 <string name="fatal_error">致命错误</string>
231 <string name="fatal_error_message">发生致命错误,请查阅日志获取详细信息。\n继续模拟可能会造成崩溃和错误。</string>
232 <string name="performance_warning">关闭此项会显著降低模拟性能!建议您将此项保持为启用状态。</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">日本</string>
236 <string name="region_usa">美国</string>
237 <string name="region_europe">欧洲</string>
238 <string name="region_australia">澳大利亚</string>
239 <string name="region_china">中国</string>
240 <string name="region_korea">韩国</string>
241 <string name="region_taiwan">中国台湾</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">日语 (日本語)</string>
245 <string name="language_english">英语 (English)</string>
246 <string name="language_french">法语 (Français)</string>
247 <string name="langauge_german">德语 (Deutsch)</string>
248 <string name="language_italian">意大利语 (Italiano)</string>
249 <string name="language_spanish">西班牙语 (Español)</string>
250 <string name="language_chinese">中文 (简体中文)</string>
251 <string name="language_korean">韩语 (한국어)</string>
252 <string name="language_dutch">荷兰语 (Nederlands)</string>
253 <string name="language_portuguese">葡萄牙语 (Português)</string>
254 <string name="language_russian">俄语 (Русский)</string>
255 <string name="language_taiwanese">台湾中文 (台灣)</string>
256 <string name="language_british_english">英式英语</string>
257 <string name="language_canadian_french">加拿大法语 (Français canadien)</string>
258 <string name="language_latin_american_spanish">拉丁美洲西班牙语 (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">简体中文 (简体中文)</string>
260 <string name="language_traditional_chinese">繁体中文 (正體中文)</string>
261 <string name="language_brazilian_portuguese">巴西葡萄牙语 (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">无</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">正常</string>
269 <string name="renderer_accuracy_high">高</string>
270 <string name="renderer_accuracy_extreme">极高 (慢速)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (慢速)</string>
277 <string name="resolution_three">3X (2160p/3240p) (慢速)</string>
278 <string name="resolution_four">4X (2880p/4320p) (慢速)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">即时 (关闭)</string>
282 <string name="renderer_vsync_mailbox">Mailbox</string>
283 <string name="renderer_vsync_fifo">FIFO (开启)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">近邻取样</string>
288 <string name="scaling_filter_bilinear">双线性过滤</string>
289 <string name="scaling_filter_bicubic">双三线过滤</string>
290 <string name="scaling_filter_gaussian">高斯模糊</string>
291 <string name="scaling_filter_scale_force">强制缩放</string>
292 <string name="scaling_filter_fsr">AMD FidelityFX™️ 超级分辨率锐画技术</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">无</string>
296 <string name="anti_aliasing_fxaa">快速近似抗锯齿</string>
297 <string name="anti_aliasing_smaa">子像素形态学抗锯齿</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">默认 (16:9)</string>
301 <string name="ratio_force_four_three">强制 4:3</string>
302 <string name="ratio_force_twenty_one_nine">强制 21:9</string>
303 <string name="ratio_force_sixteen_ten">强制 16:10</string>
304 <string name="ratio_stretch">拉伸窗口</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_accurate">高精度</string>
308 <string name="cpu_accuracy_unsafe">低精度</string>
309 <string name="cpu_accuracy_paranoid">偏执模式 (慢速)</string>
310
311 <!-- Gamepad Buttons -->
312 <string name="gamepad_d_pad">十字方向键</string>
313 <string name="gamepad_left_stick">左摇杆</string>
314 <string name="gamepad_right_stick">右摇杆</string>
315 <string name="gamepad_home">Home</string>
316 <string name="gamepad_screenshot">截图</string>
317
318 <!-- Disk shader cache -->
319 <string name="preparing_shaders">正在准备着色器</string>
320 <string name="building_shaders">正在编译着色器</string>
321
322 <!-- Theme options -->
323 <string name="change_app_theme">切换主题</string>
324 <string name="theme_default">系统默认</string>
325 <string name="theme_material_you">Material You</string>
326
327 <!-- Theme Modes -->
328 <string name="change_theme_mode">主题模式</string>
329 <string name="theme_mode_follow_system">跟随系统</string>
330 <string name="theme_mode_light">浅色</string>
331 <string name="theme_mode_dark">深色</string>
332
333 <!-- Black backgrounds theme -->
334 <string name="use_black_backgrounds">使用黑色背景</string>
335 <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string>
336
337</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
new file mode 100644
index 000000000..a54d04248
--- /dev/null
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,336 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
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>遊戲&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
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">一律允許 yuzu 填入遊戲清單</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="games_dir_selected">遊戲目錄已選取</string>
42 <string name="install_prod_keys">安裝 prod.keys</string>
43 <string name="install_prod_keys_description">需要解密零售遊戲</string>
44 <string name="install_prod_keys_warning">跳過新增金鑰?</string>
45 <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>
47 <string name="notifications">通知</string>
48 <string name="notifications_description">使用下方的按鈕授予通知權限。</string>
49 <string name="give_permission">授予權限</string>
50 <string name="notification_warning">跳過授予通知權限?</string>
51 <string name="notification_warning_description">yuzu 將無法通知您重要資訊。</string>
52 <string name="permission_denied">權限遭拒</string>
53 <string name="permission_denied_description">您曾多次拒絕了權限要求,現在您需要在系統設定中手動授予權限。</string>
54 <string name="about">關於</string>
55 <string name="about_description">組建版本、製作群、以及更多</string>
56 <string name="warning_help">說明</string>
57 <string name="warning_skip">跳過</string>
58 <string name="warning_cancel">取消</string>
59 <string name="install_amiibo_keys">安裝 Amiibo 金鑰</string>
60 <string name="install_amiibo_keys_description">需要在遊戲中使用 Amiibo</string>
61 <string name="invalid_keys_file">無效的金鑰檔案已選取</string>
62 <string name="install_keys_success">金鑰已成功安裝</string>
63 <string name="reading_keys_failure">讀取加密金鑰時出現錯誤</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>
66 <string name="install_keys_failure_description">選取的檔案不正確或已損毀,請重新傾印您的金鑰。</string>
67 <string name="install_gpu_driver">安裝 GPU 驅動程式</string>
68 <string name="install_gpu_driver_description">安裝替代驅動程式以取得潛在的更佳效能或準確度</string>
69 <string name="advanced_settings">進階設定</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>
74 <string name="search_homebrew">自製遊戲</string>
75 <string name="open_user_folder">開啟 yuzu 資料夾</string>
76 <string name="open_user_folder_description">管理 yuzu 的內部檔案</string>
77 <string name="theme_and_color_description">修改應用程式外觀</string>
78 <string name="no_file_manager">找不到檔案管理員</string>
79 <string name="notification_no_directory_link">無法開啟 yuzu 目錄</string>
80 <string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾。</string>
81 <string name="manage_save_data">管理儲存資料</string>
82 <string name="manage_save_data_description">已找到儲存資料,請選取下方的選項。</string>
83 <string name="import_export_saves_description">匯入或匯出儲存檔案</string>
84 <string name="import_export_saves_no_profile">找不到儲存資料,請啟動遊戲並重試。</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">首個子資料夾名稱必須為遊戲標題 ID。</string>
88 <string name="import_saves">匯入</string>
89 <string name="export_saves">匯出</string>
90
91 <!-- About screen strings -->
92 <string name="gaia_is_not_real">Gaia 不真實</string>
93 <string name="copied_to_clipboard">已複製到剪貼簿</string>
94 <string name="about_app_description">一個開放原始碼的 Switch 模擬器</string>
95 <string name="contributors">參與者</string>
96 <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string>
97 <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
98 <string name="build">組建</string>
99 <string name="support_link">https://discord.gg/u77vRWY</string>
100 <string name="website_link">https://yuzu-emu.org/</string>
101 <string name="github_link">https://github.com/yuzu-emu</string>
102
103 <!-- Early access upgrade strings -->
104 <string name="early_access">搶先體驗</string>
105 <string name="get_early_access">搶先體驗新功能</string>
106 <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
107 <string name="get_early_access_description">最新的功能、搶先版更新、以及更多</string>
108 <string name="early_access_benefits">搶先體驗權益</string>
109 <string name="cutting_edge_features">最新功能</string>
110 <string name="early_access_updates">搶先版更新</string>
111 <string name="no_manual_installation">無需手動安裝</string>
112 <string name="prioritized_support">優先支援</string>
113 <string name="helping_game_preservation">協助遊戲保留</string>
114 <string name="our_eternal_gratitude">我們永遠的感激</string>
115 <string name="are_you_interested">您仍感興趣嗎?</string>
116
117 <!-- General settings strings -->
118 <string name="frame_limit_enable">啟用限制速度</string>
119 <string name="frame_limit_enable_description">若啟用,模擬速度將會限制在標準速度的指定百分比。</string>
120 <string name="frame_limit_slider">限制速度百分比</string>
121 <string name="frame_limit_slider_description">指定限制模擬速度的百分比。預設為 100%,模擬速度將被限制為標準速度。更高或更低的值將會增加或減少速度限制。</string>
122 <string name="cpu_accuracy">CPU 準確度</string>
123
124 <!-- System settings strings -->
125 <string name="use_docked_mode">底座模式</string>
126 <string name="use_docked_mode_description">以底座模式模擬,以犧牲效能的代價提高解析度。</string>
127 <string name="emulated_region">模擬區域</string>
128 <string name="emulated_language">模擬語言</string>
129 <string name="select_rtc_date">選取 RTC 日期</string>
130 <string name="select_rtc_time">選取 RTC 時間</string>
131 <string name="use_custom_rtc">啟用自訂 RTC</string>
132 <string name="use_custom_rtc_description">此設定允許您設定與您的目前系統時間相互獨立的自訂即時時鐘</string>
133 <string name="set_custom_rtc">設定自訂 RTC</string>
134
135 <!-- Graphics settings strings -->
136 <string name="renderer_api">API</string>
137 <string name="renderer_accuracy">準確度層級</string>
138 <string name="renderer_resolution">解析度</string>
139 <string name="renderer_vsync">VSync 模式</string>
140 <string name="renderer_aspect_ratio">長寬比</string>
141 <string name="renderer_scaling_filter">視窗適應過濾器</string>
142 <string name="renderer_anti_aliasing">消除鋸齒方法</string>
143 <string name="renderer_force_max_clock">強制最大時脈 (僅 Adreno)</string>
144 <string name="renderer_force_max_clock_description">強制 GPU 以最大可能時脈執行 (熱溫限制仍被套用)。</string>
145 <string name="renderer_asynchronous_shaders">使用非同步著色器</string>
146 <string name="renderer_asynchronous_shaders_description">非同步編譯著色器,將會減少間斷,但可能會引入故障。</string>
147 <string name="renderer_debug">啟用圖形偵錯</string>
148 <string name="renderer_debug_description">核取時,圖形 API 將會進入慢速偵錯模式。</string>
149 <string name="use_disk_shader_cache">使用磁碟著色器快取</string>
150 <string name="use_disk_shader_cache_description">透過將產生的著色器儲存並載入至磁碟,減少中斷。</string>
151
152 <!-- Audio settings strings -->
153 <string name="audio_volume">音量</string>
154 <string name="audio_volume_description">指定音訊輸出音量。</string>
155
156 <!-- Miscellaneous -->
157 <string name="slider_default">預設</string>
158 <string name="ini_saved">已儲存設定</string>
159 <string name="gameid_saved">已儲存 %1$s 設定</string>
160 <string name="error_saving">儲存 %1$s 時發生錯誤 ini: %2$s</string>
161 <string name="loading">正在載入…</string>
162 <string name="reset_setting_confirmation">要將此設定重設回預設值嗎?</string>
163 <string name="reset_to_default">重設為預設值</string>
164 <string name="reset_all_settings">重設所有設定?</string>
165 <string name="reset_all_settings_description">所有進階設定將被重設為預設組態,此動作無法復原。</string>
166 <string name="settings_reset">設定已重設</string>
167 <string name="close">關閉</string>
168 <string name="learn_more">深入瞭解</string>
169
170 <!-- GPU driver installation -->
171 <string name="select_gpu_driver">選取 GPU 驅動程式</string>
172 <string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
173 <string name="select_gpu_driver_install">安裝</string>
174 <string name="select_gpu_driver_default">預設</string>
175 <string name="select_gpu_driver_install_success">已安裝 %s</string>
176 <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
177 <string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
178 <string name="system_gpu_driver">系統 GPU 驅動程式</string>
179 <string name="installing_driver">正在安裝驅動程式…</string>
180
181 <!-- Preferences Screen -->
182 <string name="preferences_settings">設定</string>
183 <string name="preferences_general">一般</string>
184 <string name="preferences_system">系統</string>
185 <string name="preferences_graphics">圖形</string>
186 <string name="preferences_audio">音訊</string>
187 <string name="preferences_theme">主題和色彩</string>
188
189 <!-- ROM loading errors -->
190 <string name="loader_error_encrypted">您的 ROM 已加密</string>
191 <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>
192 <string name="loader_error_encrypted_keys_description"><![CDATA[請確保您的 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安裝,讓遊戲可以解密。]]></string>
193 <string name="loader_error_video_core">初始化視訊核心時發生錯誤</string>
194 <string name="loader_error_video_core_description">這經常由不相容的 GPU 驅動程式造成,安裝自訂 GPU 驅動程式可能會解決此問題。</string>
195 <string name="loader_error_invalid_format">無法載入 ROM</string>
196 <string name="loader_error_file_not_found">ROM 檔案不存在</string>
197
198 <!-- Emulation Menu -->
199 <string name="emulation_exit">結束模擬</string>
200 <string name="emulation_done">完成</string>
201 <string name="emulation_fps_counter">FPS 計數器</string>
202 <string name="emulation_toggle_controls">切換控制</string>
203 <string name="emulation_rel_stick_center">相對搖桿中心</string>
204 <string name="emulation_dpad_slide">方向鍵滑動</string>
205 <string name="emulation_haptics">觸覺回饋技術</string>
206 <string name="emulation_show_overlay">顯示覆疊</string>
207 <string name="emulation_toggle_all">全部切換</string>
208 <string name="emulation_control_adjust">調整覆疊</string>
209 <string name="emulation_control_scale">縮放</string>
210 <string name="emulation_control_opacity">不透明度</string>
211 <string name="emulation_touch_overlay_reset">重設覆疊</string>
212 <string name="emulation_touch_overlay_edit">編輯覆疊</string>
213 <string name="emulation_pause">暫停模擬</string>
214 <string name="emulation_unpause">取消暫停模擬</string>
215 <string name="emulation_input_overlay">覆疊選項</string>
216 <string name="emulation_game_loading">遊戲正在載入…</string>
217
218 <string name="load_settings">正在載入設定…</string>
219
220 <!-- Software keyboard -->
221 <string name="software_keyboard">軟體鍵盤</string>
222
223 <!-- Errors and warnings -->
224 <string name="abort_button">中止</string>
225 <string name="continue_button">繼續</string>
226 <string name="system_archive_not_found">找不到系統檔案</string>
227 <string name="system_archive_not_found_message">%s 遺失,請傾印您的系統封存。\n繼續模擬可能會造成當機和錯誤。</string>
228 <string name="system_archive_general">系統封存</string>
229 <string name="save_load_error">儲存/載入發生錯誤</string>
230 <string name="fatal_error">嚴重錯誤</string>
231 <string name="fatal_error_message">發生嚴重錯誤,檢查記錄以取得詳細資訊。\n繼續模擬可能會造成當機和錯誤。</string>
232 <string name="performance_warning">關閉此設定會顯著降低模擬效能!如需最佳體驗,建議您將此設定保持為啟用狀態。</string>
233
234 <!-- Region Names -->
235 <string name="region_japan">日本</string>
236 <string name="region_usa">美國</string>
237 <string name="region_europe">歐洲</string>
238 <string name="region_australia">澳洲</string>
239 <string name="region_china">中國</string>
240 <string name="region_korea">南韓</string>
241 <string name="region_taiwan">台灣</string>
242
243 <!-- Language Names -->
244 <string name="language_japanese">日文 (日本語)</string>
245 <string name="language_english">英文</string>
246 <string name="language_french">法文 (Français)</string>
247 <string name="langauge_german">德文 (Deutsch)</string>
248 <string name="language_italian">義大利文 (Italiano)</string>
249 <string name="language_spanish">西班牙文 (Español)</string>
250 <string name="language_chinese">中文 (简体中文)</string>
251 <string name="language_korean">韓文 (한국어)</string>
252 <string name="language_dutch">荷蘭文 (Nederlands)</string>
253 <string name="language_portuguese">葡萄牙文 (Português)</string>
254 <string name="language_russian">俄文 (Русский)</string>
255 <string name="language_taiwanese">台文 (台灣)</string>
256 <string name="language_british_english">英式英文</string>
257 <string name="language_canadian_french">加拿大法文 (Français canadien)</string>
258 <string name="language_latin_american_spanish">拉丁美洲西班牙文 (Español latinoamericano)</string>
259 <string name="language_simplified_chinese">簡體中文 (简体中文)</string>
260 <string name="language_traditional_chinese">正體中文 (正體中文)</string>
261 <string name="language_brazilian_portuguese">巴西葡萄牙文 (Português do Brasil)</string>
262
263 <!-- Renderer APIs -->
264 <string name="renderer_vulkan">Vulkan</string>
265 <string name="renderer_none">無</string>
266
267 <!-- Renderer Accuracy -->
268 <string name="renderer_accuracy_normal">標準</string>
269 <string name="renderer_accuracy_high">高</string>
270 <string name="renderer_accuracy_extreme">極高 (慢)</string>
271
272 <!-- Resolutions -->
273 <string name="resolution_half">0.5X (360p/540p)</string>
274 <string name="resolution_three_quarter">0.75X (540p/810p)</string>
275 <string name="resolution_one">1X (720p/1080p)</string>
276 <string name="resolution_two">2X (1440p/2160p) (慢)</string>
277 <string name="resolution_three">3X (2160p/3240p) (慢)</string>
278 <string name="resolution_four">4X (2880p/4320p) (慢)</string>
279
280 <!-- Renderer VSync -->
281 <string name="renderer_vsync_immediate">即時 (關閉)</string>
282 <string name="renderer_vsync_mailbox">信箱</string>
283 <string name="renderer_vsync_fifo">FIFO (開啟)</string>
284 <string name="renderer_vsync_fifo_relaxed">FIFO 寬鬆</string>
285
286 <!-- Scaling Filters -->
287 <string name="scaling_filter_nearest_neighbor">最近鄰</string>
288 <string name="scaling_filter_bilinear">雙線性</string>
289 <string name="scaling_filter_bicubic">雙立方</string>
290 <string name="scaling_filter_gaussian">高斯</string>
291 <string name="scaling_filter_scale_force">強制縮放</string>
292 <string name="scaling_filter_fsr">AMD Radeon™ 超級解析度</string>
293
294 <!-- Anti-Aliasing -->
295 <string name="anti_aliasing_none">無</string>
296 <string name="anti_aliasing_fxaa">FXAA</string>
297 <string name="anti_aliasing_smaa">SMAA</string>
298
299 <!-- Aspect Ratios -->
300 <string name="ratio_default">預設 (16:9)</string>
301 <string name="ratio_force_four_three">強制 4:3</string>
302 <string name="ratio_force_twenty_one_nine">強制 21:9</string>
303 <string name="ratio_force_sixteen_ten">強制 16:10</string>
304 <string name="ratio_stretch">延伸視窗</string>
305
306 <!-- CPU Accuracy -->
307 <string name="cpu_accuracy_unsafe">低精度</string>
308 <string name="cpu_accuracy_paranoid">不合理 (慢)</string>
309
310 <!-- Gamepad Buttons -->
311 <string name="gamepad_d_pad">方向鍵</string>
312 <string name="gamepad_left_stick">左搖桿</string>
313 <string name="gamepad_right_stick">右搖桿</string>
314 <string name="gamepad_home">HOME</string>
315 <string name="gamepad_screenshot">螢幕截圖</string>
316
317 <!-- Disk shader cache -->
318 <string name="preparing_shaders">正在準備著色器</string>
319 <string name="building_shaders">正在建置著色器</string>
320
321 <!-- Theme options -->
322 <string name="change_app_theme">變更應用程式主題</string>
323 <string name="theme_default">預設</string>
324 <string name="theme_material_you">Material You</string>
325
326 <!-- Theme Modes -->
327 <string name="change_theme_mode">變更主題模式</string>
328 <string name="theme_mode_follow_system">跟隨系統</string>
329 <string name="theme_mode_light">淺色</string>
330 <string name="theme_mode_dark">深色</string>
331
332 <!-- Black backgrounds theme -->
333 <string name="use_black_backgrounds">使用黑色背景</string>
334 <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string>
335
336</resources>
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index ea20cb17c..6d092f7a9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -119,6 +119,18 @@
119 <item>3</item> 119 <item>3</item>
120 </integer-array> 120 </integer-array>
121 121
122 <string-array name="rendererScreenLayoutNames">
123 <item>@string/screen_layout_landscape</item>
124 <item>@string/screen_layout_portrait</item>
125 <item>@string/screen_layout_auto</item>
126 </string-array>
127
128 <integer-array name="rendererScreenLayoutValues">
129 <item>5</item>
130 <item>4</item>
131 <item>0</item>
132 </integer-array>
133
122 <string-array name="rendererAspectRatioNames"> 134 <string-array name="rendererAspectRatioNames">
123 <item>@string/ratio_default</item> 135 <item>@string/ratio_default</item>
124 <item>@string/ratio_force_four_three</item> 136 <item>@string/ratio_force_four_three</item>
@@ -224,4 +236,15 @@
224 <item>2</item> 236 <item>2</item>
225 </integer-array> 237 </integer-array>
226 238
239 <string-array name="outputEngineEntries">
240 <item>@string/auto</item>
241 <item>@string/cubeb</item>
242 <item>@string/string_null</item>
243 </string-array>
244 <string-array name="outputEngineValues">
245 <item>auto</item>
246 <item>cubeb</item>
247 <item>null</item>
248 </string-array>
249
227</resources> 250</resources>
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index bc614b81d..2e93b408c 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -34,4 +34,68 @@
34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer> 34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer>
35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> 35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
36 36
37 <!-- Default SWITCH portrait layout -->
38 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
39 <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer>
40 <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer>
41 <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer>
42 <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer>
43 <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer>
44 <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer>
45 <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer>
46 <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer>
47 <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer>
48 <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer>
49 <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer>
50 <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer>
51 <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer>
52 <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer>
53 <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer>
54 <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer>
55 <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer>
56 <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer>
57 <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer>
58 <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer>
59 <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer>
60 <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer>
61 <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer>
62 <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer>
63 <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer>
64 <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer>
65 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
66 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
67 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
68
69 <!-- Default SWITCH foldable layout -->
70 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
71 <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer>
72 <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer>
73 <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer>
74 <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer>
75 <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer>
76 <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer>
77 <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer>
78 <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer>
79 <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer>
80 <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer>
81 <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer>
82 <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer>
83 <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer>
84 <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer>
85 <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer>
86 <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer>
87 <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer>
88 <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer>
89 <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer>
90 <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer>
91 <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer>
92 <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer>
93 <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer>
94 <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer>
95 <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer>
96 <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer>
97 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
98 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
99 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
100
37</resources> 101</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 6e9d47557..af7450619 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/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 <!-- General application strings --> 4 <!-- General application strings -->
5 <string name="app_name" translatable="false">yuzu</string> 5 <string name="app_name" translatable="false">yuzu</string>
@@ -102,6 +102,17 @@
102 <string name="share_log">Share debug logs</string> 102 <string name="share_log">Share debug logs</string>
103 <string name="share_log_description">Share yuzu\'s log file to debug issues</string> 103 <string name="share_log_description">Share yuzu\'s log file to debug issues</string>
104 <string name="share_log_missing">No log file found</string> 104 <string name="share_log_missing">No log file found</string>
105 <string name="install_game_content">Install game content</string>
106 <string name="install_game_content_description">Install game updates or DLC</string>
107 <string name="install_game_content_failure">Error installing file(s) to NAND</string>
108 <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
109 <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
110 <string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string>
111 <string name="install_game_content_failed_count">%1$d installation error(s)</string>
112 <string name="install_game_content_success">Game content(s) installed successfully</string>
113 <string name="install_game_content_success_install">%1$d installed successfully</string>
114 <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
115 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
105 116
106 <!-- About screen strings --> 117 <!-- About screen strings -->
107 <string name="gaia_is_not_real">Gaia isn\'t real</string> 118 <string name="gaia_is_not_real">Gaia isn\'t real</string>
@@ -149,10 +160,10 @@
149 <string name="set_custom_rtc">Set custom RTC</string> 160 <string name="set_custom_rtc">Set custom RTC</string>
150 161
151 <!-- Graphics settings strings --> 162 <!-- Graphics settings strings -->
152 <string name="renderer_api">API</string>
153 <string name="renderer_accuracy">Accuracy level</string> 163 <string name="renderer_accuracy">Accuracy level</string>
154 <string name="renderer_resolution">Resolution (Handheld/Docked)</string> 164 <string name="renderer_resolution">Resolution (Handheld/Docked)</string>
155 <string name="renderer_vsync">VSync mode</string> 165 <string name="renderer_vsync">VSync mode</string>
166 <string name="renderer_screen_layout">Orientation</string>
156 <string name="renderer_aspect_ratio">Aspect ratio</string> 167 <string name="renderer_aspect_ratio">Aspect ratio</string>
157 <string name="renderer_scaling_filter">Window adapting filter</string> 168 <string name="renderer_scaling_filter">Window adapting filter</string>
158 <string name="renderer_anti_aliasing">Anti-aliasing method</string> 169 <string name="renderer_anti_aliasing">Anti-aliasing method</string>
@@ -160,12 +171,23 @@
160 <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> 171 <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
161 <string name="renderer_asynchronous_shaders">Use asynchronous shaders</string> 172 <string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
162 <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string> 173 <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string>
163 <string name="renderer_debug">Graphics debugging</string> 174 <string name="renderer_reactive_flushing">Use reactive flushing</string>
164 <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string> 175 <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
165 <string name="use_disk_shader_cache">Disk shader cache</string> 176 <string name="use_disk_shader_cache">Disk shader cache</string>
166 <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> 177 <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string>
167 178
179 <!-- Debug settings strings -->
180 <string name="cpu">CPU</string>
181 <string name="cpu_debug_mode">CPU Debugging</string>
182 <string name="cpu_debug_mode_description">Puts the CPU in a slow debugging mode.</string>
183 <string name="gpu">GPU</string>
184 <string name="renderer_api">API</string>
185 <string name="renderer_debug">Graphics debugging</string>
186 <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string>
187 <string name="fastmem">Fastmem</string>
188
168 <!-- Audio settings strings --> 189 <!-- Audio settings strings -->
190 <string name="audio_output_engine">Output engine</string>
169 <string name="audio_volume">Volume</string> 191 <string name="audio_volume">Volume</string>
170 <string name="audio_volume_description">Specifies the volume of audio output.</string> 192 <string name="audio_volume_description">Specifies the volume of audio output.</string>
171 193
@@ -184,6 +206,7 @@
184 <string name="learn_more">Learn more</string> 206 <string name="learn_more">Learn more</string>
185 <string name="auto">Auto</string> 207 <string name="auto">Auto</string>
186 <string name="submit">Submit</string> 208 <string name="submit">Submit</string>
209 <string name="string_null">Null</string>
187 210
188 <!-- GPU driver installation --> 211 <!-- GPU driver installation -->
189 <string name="select_gpu_driver">Select GPU driver</string> 212 <string name="select_gpu_driver">Select GPU driver</string>
@@ -249,6 +272,7 @@
249 <string name="fatal_error">Fatal Error</string> 272 <string name="fatal_error">Fatal Error</string>
250 <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> 273 <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
251 <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> 274 <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
275 <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
252 276
253 <!-- Region Names --> 277 <!-- Region Names -->
254 <string name="region_japan">Japan</string> 278 <string name="region_japan">Japan</string>
@@ -279,6 +303,15 @@
279 <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string> 303 <string name="language_traditional_chinese">Traditional Chinese (正體中文)</string>
280 <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string> 304 <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string>
281 305
306 <!-- Memory Sizes -->
307 <string name="memory_byte">Byte</string>
308 <string name="memory_kilobyte">KB</string>
309 <string name="memory_megabyte">MB</string>
310 <string name="memory_gigabyte">GB</string>
311 <string name="memory_terabyte">TB</string>
312 <string name="memory_petabyte">PB</string>
313 <string name="memory_exabyte">EB</string>
314
282 <!-- Renderer APIs --> 315 <!-- Renderer APIs -->
283 <string name="renderer_vulkan">Vulkan</string> 316 <string name="renderer_vulkan">Vulkan</string>
284 <string name="renderer_none">None</string> 317 <string name="renderer_none">None</string>
@@ -315,6 +348,11 @@
315 <string name="anti_aliasing_fxaa">FXAA</string> 348 <string name="anti_aliasing_fxaa">FXAA</string>
316 <string name="anti_aliasing_smaa">SMAA</string> 349 <string name="anti_aliasing_smaa">SMAA</string>
317 350
351 <!-- Screen Layouts -->
352 <string name="screen_layout_landscape">Landscape</string>
353 <string name="screen_layout_portrait">Portrait</string>
354 <string name="screen_layout_auto">Auto</string>
355
318 <!-- Aspect Ratios --> 356 <!-- Aspect Ratios -->
319 <string name="ratio_default">Default (16:9)</string> 357 <string name="ratio_default">Default (16:9)</string>
320 <string name="ratio_force_four_three">Force 4:3</string> 358 <string name="ratio_force_four_three">Force 4:3</string>
@@ -349,10 +387,21 @@
349 <string name="theme_mode_light">Light</string> 387 <string name="theme_mode_light">Light</string>
350 <string name="theme_mode_dark">Dark</string> 388 <string name="theme_mode_dark">Dark</string>
351 389
390 <!-- Audio output engines -->
391 <string name="cubeb">cubeb</string>
392
352 <!-- Black backgrounds theme --> 393 <!-- Black backgrounds theme -->
353 <string name="use_black_backgrounds">Black backgrounds</string> 394 <string name="use_black_backgrounds">Black backgrounds</string>
354 <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> 395 <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
355 396
397 <!-- Picture-In-Picture -->
398 <string name="picture_in_picture">Picture in Picture</string>
399 <string name="picture_in_picture_description">Minimize window when placed in the background</string>
400 <string name="pause">Pause</string>
401 <string name="play">Play</string>
402 <string name="mute">Mute</string>
403 <string name="unmute">Unmute</string>
404
356 <!-- Licenses screen strings --> 405 <!-- Licenses screen strings -->
357 <string name="licenses">Licenses</string> 406 <string name="licenses">Licenses</string>
358 <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> 407 <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</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
new file mode 100644
index 000000000..51b88d9dc
--- /dev/null
+++ b/src/android/app/src/main/res/xml/locales_config.xml
@@ -0,0 +1,17 @@
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/android/build.gradle.kts b/src/android/build.gradle.kts
index e19e8ce58..80f370c16 100644
--- a/src/android/build.gradle.kts
+++ b/src/android/build.gradle.kts
@@ -11,3 +11,12 @@ plugins {
11tasks.register("clean").configure { 11tasks.register("clean").configure {
12 delete(rootProject.buildDir) 12 delete(rootProject.buildDir)
13} 13}
14
15buildscript {
16 repositories {
17 google()
18 }
19 dependencies {
20 classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.6.0")
21 }
22}
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h
index 15082f6c6..5d8ed0ef7 100644
--- a/src/audio_core/device/audio_buffers.h
+++ b/src/audio_core/device/audio_buffers.h
@@ -7,6 +7,7 @@
7#include <mutex> 7#include <mutex>
8#include <span> 8#include <span>
9#include <vector> 9#include <vector>
10#include <boost/container/static_vector.hpp>
10 11
11#include "audio_buffer.h" 12#include "audio_buffer.h"
12#include "audio_core/device/device_session.h" 13#include "audio_core/device/device_session.h"
@@ -48,7 +49,7 @@ public:
48 * 49 *
49 * @param out_buffers - The buffers which were registered. 50 * @param out_buffers - The buffers which were registered.
50 */ 51 */
51 void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) { 52 void RegisterBuffers(boost::container::static_vector<AudioBuffer, N>& out_buffers) {
52 std::scoped_lock l{lock}; 53 std::scoped_lock l{lock};
53 const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit), 54 const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),
54 BufferAppendLimit - registered_count)}; 55 BufferAppendLimit - registered_count)};
@@ -162,7 +163,8 @@ public:
162 * @param max_buffers - Maximum number of buffers to released. 163 * @param max_buffers - Maximum number of buffers to released.
163 * @return The number of buffers released. 164 * @return The number of buffers released.
164 */ 165 */
165 u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) { 166 u32 GetRegisteredAppendedBuffers(
167 boost::container::static_vector<AudioBuffer, N>& buffers_flushed, u32 max_buffers) {
166 std::scoped_lock l{lock}; 168 std::scoped_lock l{lock};
167 if (registered_count + appended_count == 0) { 169 if (registered_count + appended_count == 0) {
168 return 0; 170 return 0;
@@ -270,7 +272,7 @@ public:
270 */ 272 */
271 bool FlushBuffers(u32& buffers_released) { 273 bool FlushBuffers(u32& buffers_released) {
272 std::scoped_lock l{lock}; 274 std::scoped_lock l{lock};
273 std::vector<AudioBuffer> buffers_flushed{}; 275 boost::container::static_vector<AudioBuffer, N> buffers_flushed{};
274 276
275 buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit); 277 buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit);
276 278
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index b5c0ef0e6..86811fcb8 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -79,7 +79,7 @@ void DeviceSession::ClearBuffers() {
79 } 79 }
80} 80}
81 81
82void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { 82void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
83 for (const auto& buffer : buffers) { 83 for (const auto& buffer : buffers) {
84 Sink::SinkBuffer new_buffer{ 84 Sink::SinkBuffer new_buffer{
85 .frames = buffer.size / (channel_count * sizeof(s16)), 85 .frames = buffer.size / (channel_count * sizeof(s16)),
@@ -88,13 +88,13 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
88 .consumed = false, 88 .consumed = false,
89 }; 89 };
90 90
91 tmp_samples.resize_destructive(buffer.size / sizeof(s16));
91 if (type == Sink::StreamType::In) { 92 if (type == Sink::StreamType::In) {
92 std::vector<s16> samples{}; 93 stream->AppendBuffer(new_buffer, tmp_samples);
93 stream->AppendBuffer(new_buffer, samples);
94 } else { 94 } else {
95 std::vector<s16> samples(buffer.size / sizeof(s16)); 95 system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(),
96 system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size); 96 buffer.size);
97 stream->AppendBuffer(new_buffer, samples); 97 stream->AppendBuffer(new_buffer, tmp_samples);
98 } 98 }
99 } 99 }
100} 100}
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h
index 75f766c68..7d52f362d 100644
--- a/src/audio_core/device/device_session.h
+++ b/src/audio_core/device/device_session.h
@@ -10,6 +10,7 @@
10 10
11#include "audio_core/common/common.h" 11#include "audio_core/common/common.h"
12#include "audio_core/sink/sink.h" 12#include "audio_core/sink/sink.h"
13#include "common/scratch_buffer.h"
13#include "core/hle/service/audio/errors.h" 14#include "core/hle/service/audio/errors.h"
14 15
15namespace Core { 16namespace Core {
@@ -62,7 +63,7 @@ public:
62 * 63 *
63 * @param buffers - The buffers to play. 64 * @param buffers - The buffers to play.
64 */ 65 */
65 void AppendBuffers(std::span<const AudioBuffer> buffers) const; 66 void AppendBuffers(std::span<const AudioBuffer> buffers);
66 67
67 /** 68 /**
68 * (Audio In only) Pop samples from the backend, and write them back to this buffer's address. 69 * (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
@@ -146,8 +147,8 @@ private:
146 std::shared_ptr<Core::Timing::EventType> thread_event; 147 std::shared_ptr<Core::Timing::EventType> thread_event;
147 /// Is this session initialised? 148 /// Is this session initialised?
148 bool initialized{}; 149 bool initialized{};
149 /// Buffer queue 150 /// Temporary sample buffer
150 std::vector<AudioBuffer> buffer_queue{}; 151 Common::ScratchBuffer<s16> tmp_samples{};
151}; 152};
152 153
153} // namespace AudioCore 154} // namespace AudioCore
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index e23e51758..579129121 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <mutex> 4#include <mutex>
5
5#include "audio_core/audio_event.h" 6#include "audio_core/audio_event.h"
6#include "audio_core/audio_manager.h" 7#include "audio_core/audio_manager.h"
7#include "audio_core/in/audio_in_system.h" 8#include "audio_core/in/audio_in_system.h"
@@ -89,7 +90,7 @@ Result System::Start() {
89 session->Start(); 90 session->Start();
90 state = State::Started; 91 state = State::Started;
91 92
92 std::vector<AudioBuffer> buffers_to_flush{}; 93 boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
93 buffers.RegisterBuffers(buffers_to_flush); 94 buffers.RegisterBuffers(buffers_to_flush);
94 session->AppendBuffers(buffers_to_flush); 95 session->AppendBuffers(buffers_to_flush);
95 session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); 96 session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
@@ -134,7 +135,7 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {
134 135
135void System::RegisterBuffers() { 136void System::RegisterBuffers() {
136 if (state == State::Started) { 137 if (state == State::Started) {
137 std::vector<AudioBuffer> registered_buffers{}; 138 boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
138 buffers.RegisterBuffers(registered_buffers); 139 buffers.RegisterBuffers(registered_buffers);
139 session->AppendBuffers(registered_buffers); 140 session->AppendBuffers(registered_buffers);
140 } 141 }
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index bd13f7219..0adf64bd3 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -89,7 +89,7 @@ Result System::Start() {
89 session->Start(); 89 session->Start();
90 state = State::Started; 90 state = State::Started;
91 91
92 std::vector<AudioBuffer> buffers_to_flush{}; 92 boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
93 buffers.RegisterBuffers(buffers_to_flush); 93 buffers.RegisterBuffers(buffers_to_flush);
94 session->AppendBuffers(buffers_to_flush); 94 session->AppendBuffers(buffers_to_flush);
95 session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); 95 session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
@@ -134,7 +134,7 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
134 134
135void System::RegisterBuffers() { 135void System::RegisterBuffers() {
136 if (state == State::Started) { 136 if (state == State::Started) {
137 std::vector<AudioBuffer> registered_buffers{}; 137 boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
138 buffers.RegisterBuffers(registered_buffers); 138 buffers.RegisterBuffers(registered_buffers);
139 session->AppendBuffers(registered_buffers); 139 session->AppendBuffers(registered_buffers);
140 } 140 }
diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp
index 74772fc50..b1db31e93 100644
--- a/src/audio_core/renderer/adsp/adsp.cpp
+++ b/src/audio_core/renderer/adsp/adsp.cpp
@@ -7,7 +7,6 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "core/core_timing_util.h"
11#include "core/memory.h" 10#include "core/memory.h"
12 11
13namespace AudioCore::AudioRenderer::ADSP { 12namespace AudioCore::AudioRenderer::ADSP {
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
index 8bc39f9f9..9ca716b60 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.cpp
+++ b/src/audio_core/renderer/adsp/audio_renderer.cpp
@@ -13,7 +13,6 @@
13#include "common/thread.h" 13#include "common/thread.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/core_timing_util.h"
17 16
18MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); 17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
19 18
@@ -144,6 +143,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
144 143
145 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK); 144 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
146 145
146 // 0.12 seconds (2304000 / 19200000)
147 constexpr u64 max_process_time{2'304'000ULL}; 147 constexpr u64 max_process_time{2'304'000ULL};
148 148
149 while (!stop_token.stop_requested()) { 149 while (!stop_token.stop_requested()) {
@@ -184,8 +184,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
184 u64 max_time{max_process_time}; 184 u64 max_time{max_process_time};
185 if (index == 1 && command_buffer.applet_resource_user_id == 185 if (index == 1 && command_buffer.applet_resource_user_id ==
186 mailbox->GetCommandBuffer(0).applet_resource_user_id) { 186 mailbox->GetCommandBuffer(0).applet_resource_user_id) {
187 max_time = max_process_time - 187 max_time = max_process_time - render_times_taken[0];
188 Core::Timing::CyclesToNs(render_times_taken[0]).count();
189 if (render_times_taken[0] > max_process_time) { 188 if (render_times_taken[0] > max_process_time) {
190 max_time = 0; 189 max_time = 0;
191 } 190 }
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/renderer/adsp/command_list_processor.cpp
index 7a300d216..3a0f1ae38 100644
--- a/src/audio_core/renderer/adsp/command_list_processor.cpp
+++ b/src/audio_core/renderer/adsp/command_list_processor.cpp
@@ -9,7 +9,6 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/core_timing_util.h"
13#include "core/memory.h" 12#include "core/memory.h"
14 13
15namespace AudioCore::AudioRenderer::ADSP { 14namespace AudioCore::AudioRenderer::ADSP {
diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp
index ff5d31bd6..f45933203 100644
--- a/src/audio_core/renderer/command/data_source/decode.cpp
+++ b/src/audio_core/renderer/command/data_source/decode.cpp
@@ -8,6 +8,7 @@
8#include "audio_core/renderer/command/resample/resample.h" 8#include "audio_core/renderer/command/resample/resample.h"
9#include "common/fixed_point.h" 9#include "common/fixed_point.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/scratch_buffer.h"
11#include "core/memory.h" 12#include "core/memory.h"
12 13
13namespace AudioCore::AudioRenderer { 14namespace AudioCore::AudioRenderer {
@@ -27,6 +28,7 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
27template <typename T> 28template <typename T>
28static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, 29static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
29 const DecodeArg& req) { 30 const DecodeArg& req) {
31 std::array<T, TempBufferSize> tmp_samples{};
30 constexpr s32 min{std::numeric_limits<s16>::min()}; 32 constexpr s32 min{std::numeric_limits<s16>::min()};
31 constexpr s32 max{std::numeric_limits<s16>::max()}; 33 constexpr s32 max{std::numeric_limits<s16>::max()};
32 34
@@ -49,18 +51,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
49 const u64 size{channel_count * samples_to_decode}; 51 const u64 size{channel_count * samples_to_decode};
50 const u64 size_bytes{size * sizeof(T)}; 52 const u64 size_bytes{size * sizeof(T)};
51 53
52 std::vector<T> samples(size); 54 memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);
53 memory.ReadBlockUnsafe(source, samples.data(), size_bytes);
54 55
55 if constexpr (std::is_floating_point_v<T>) { 56 if constexpr (std::is_floating_point_v<T>) {
56 for (u32 i = 0; i < samples_to_decode; i++) { 57 for (u32 i = 0; i < samples_to_decode; i++) {
57 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * 58 auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
58 std::numeric_limits<s16>::max())}; 59 std::numeric_limits<s16>::max())};
59 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); 60 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
60 } 61 }
61 } else { 62 } else {
62 for (u32 i = 0; i < samples_to_decode; i++) { 63 for (u32 i = 0; i < samples_to_decode; i++) {
63 out_buffer[i] = samples[i * channel_count + req.target_channel]; 64 out_buffer[i] = tmp_samples[i * channel_count + req.target_channel];
64 } 65 }
65 } 66 }
66 } break; 67 } break;
@@ -73,17 +74,16 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
73 } 74 }
74 75
75 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))}; 76 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
76 std::vector<T> samples(samples_to_decode); 77 memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T));
77 memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T));
78 78
79 if constexpr (std::is_floating_point_v<T>) { 79 if constexpr (std::is_floating_point_v<T>) {
80 for (u32 i = 0; i < samples_to_decode; i++) { 80 for (u32 i = 0; i < samples_to_decode; i++) {
81 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * 81 auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
82 std::numeric_limits<s16>::max())}; 82 std::numeric_limits<s16>::max())};
83 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); 83 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
84 } 84 }
85 } else { 85 } else {
86 std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16)); 86 std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16));
87 } 87 }
88 break; 88 break;
89 } 89 }
@@ -101,6 +101,7 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
101 */ 101 */
102static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, 102static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
103 const DecodeArg& req) { 103 const DecodeArg& req) {
104 std::array<u8, TempBufferSize> wavebuffer{};
104 constexpr u32 SamplesPerFrame{14}; 105 constexpr u32 SamplesPerFrame{14};
105 constexpr u32 NibblesPerFrame{16}; 106 constexpr u32 NibblesPerFrame{16};
106 107
@@ -138,9 +139,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
138 } 139 }
139 140
140 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)}; 141 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
141 std::vector<u8> wavebuffer(size); 142 memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size);
142 memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(),
143 wavebuffer.size());
144 143
145 auto context{req.adpcm_context}; 144 auto context{req.adpcm_context};
146 auto header{context->header}; 145 auto header{context->header};
@@ -258,7 +257,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
258 u32 offset{voice_state.offset}; 257 u32 offset{voice_state.offset};
259 258
260 auto output_buffer{args.output}; 259 auto output_buffer{args.output};
261 std::vector<s16> temp_buffer(TempBufferSize, 0); 260 std::array<s16, TempBufferSize> temp_buffer{};
262 261
263 while (remaining_sample_count > 0) { 262 while (remaining_sample_count > 0) {
264 const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)}; 263 const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)};
diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp
index 7229618e8..ee9b68d5b 100644
--- a/src/audio_core/renderer/command/effect/compressor.cpp
+++ b/src/audio_core/renderer/command/effect/compressor.cpp
@@ -44,8 +44,8 @@ static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2&
44 44
45static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params, 45static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params,
46 CompressorInfo::State& state, bool enabled, 46 CompressorInfo::State& state, bool enabled,
47 std::vector<std::span<const s32>> input_buffers, 47 std::span<std::span<const s32>> input_buffers,
48 std::vector<std::span<s32>> output_buffers, u32 sample_count) { 48 std::span<std::span<s32>> output_buffers, u32 sample_count) {
49 if (enabled) { 49 if (enabled) {
50 auto state_00{state.unk_00}; 50 auto state_00{state.unk_00};
51 auto state_04{state.unk_04}; 51 auto state_04{state.unk_04};
@@ -124,8 +124,8 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
124} 124}
125 125
126void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { 126void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
127 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 127 std::array<std::span<const s32>, MaxChannels> input_buffers{};
128 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 128 std::array<std::span<s32>, MaxChannels> output_buffers{};
129 129
130 for (s16 i = 0; i < parameter.channel_count; i++) { 130 for (s16 i = 0; i < parameter.channel_count; i++) {
131 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 131 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp
index a4e408d40..e536cbb1e 100644
--- a/src/audio_core/renderer/command/effect/delay.cpp
+++ b/src/audio_core/renderer/command/effect/delay.cpp
@@ -51,7 +51,7 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
51 state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor(); 51 state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();
52 state.delay_lines[channel].sample_count = sample_count.to_int_floor(); 52 state.delay_lines[channel].sample_count = sample_count.to_int_floor();
53 state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0); 53 state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0);
54 if (state.delay_lines[channel].buffer.size() == 0) { 54 if (state.delay_lines[channel].sample_count == 0) {
55 state.delay_lines[channel].buffer.push_back(0); 55 state.delay_lines[channel].buffer.push_back(0);
56 } 56 }
57 state.delay_lines[channel].buffer_pos = 0; 57 state.delay_lines[channel].buffer_pos = 0;
@@ -74,8 +74,8 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
74 */ 74 */
75template <size_t NumChannels> 75template <size_t NumChannels>
76static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, 76static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
77 std::vector<std::span<const s32>>& inputs, 77 std::span<std::span<const s32>> inputs, std::span<std::span<s32>> outputs,
78 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 78 const u32 sample_count) {
79 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { 79 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
80 std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{}; 80 std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
81 for (u32 channel = 0; channel < NumChannels; channel++) { 81 for (u32 channel = 0; channel < NumChannels; channel++) {
@@ -153,8 +153,8 @@ static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::St
153 * @param sample_count - Number of samples to process. 153 * @param sample_count - Number of samples to process.
154 */ 154 */
155static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, 155static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
156 const bool enabled, std::vector<std::span<const s32>>& inputs, 156 const bool enabled, std::span<std::span<const s32>> inputs,
157 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 157 std::span<std::span<s32>> outputs, const u32 sample_count) {
158 158
159 if (!IsChannelCountValid(params.channel_count)) { 159 if (!IsChannelCountValid(params.channel_count)) {
160 LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count); 160 LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count);
@@ -208,8 +208,8 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce
208} 208}
209 209
210void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { 210void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
211 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 211 std::array<std::span<const s32>, MaxChannels> input_buffers{};
212 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 212 std::array<std::span<s32>, MaxChannels> output_buffers{};
213 213
214 for (s16 i = 0; i < parameter.channel_count; i++) { 214 for (s16 i = 0; i < parameter.channel_count; i++) {
215 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 215 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
index 27d8b9844..d2bfb67cc 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -408,8 +408,8 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
408} 408}
409 409
410void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { 410void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
411 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 411 std::array<std::span<const s32>, MaxChannels> input_buffers{};
412 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 412 std::array<std::span<s32>, MaxChannels> output_buffers{};
413 413
414 for (u32 i = 0; i < parameter.channel_count; i++) { 414 for (u32 i = 0; i < parameter.channel_count; i++) {
415 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 415 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp
index e8fb0e2fc..4161a9821 100644
--- a/src/audio_core/renderer/command/effect/light_limiter.cpp
+++ b/src/audio_core/renderer/command/effect/light_limiter.cpp
@@ -47,8 +47,8 @@ static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersio
47 */ 47 */
48static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params, 48static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
49 LightLimiterInfo::State& state, const bool enabled, 49 LightLimiterInfo::State& state, const bool enabled,
50 std::vector<std::span<const s32>>& inputs, 50 std::span<std::span<const s32>> inputs,
51 std::vector<std::span<s32>>& outputs, const u32 sample_count, 51 std::span<std::span<s32>> outputs, const u32 sample_count,
52 LightLimiterInfo::StatisticsInternal* statistics) { 52 LightLimiterInfo::StatisticsInternal* statistics) {
53 constexpr s64 min{std::numeric_limits<s32>::min()}; 53 constexpr s64 min{std::numeric_limits<s32>::min()};
54 constexpr s64 max{std::numeric_limits<s32>::max()}; 54 constexpr s64 max{std::numeric_limits<s32>::max()};
@@ -147,8 +147,8 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP
147} 147}
148 148
149void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { 149void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
150 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 150 std::array<std::span<const s32>, MaxChannels> input_buffers{};
151 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 151 std::array<std::span<s32>, MaxChannels> output_buffers{};
152 152
153 for (u32 i = 0; i < parameter.channel_count; i++) { 153 for (u32 i = 0; i < parameter.channel_count; i++) {
154 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 154 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
@@ -190,8 +190,8 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP
190} 190}
191 191
192void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { 192void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
193 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 193 std::array<std::span<const s32>, MaxChannels> input_buffers{};
194 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 194 std::array<std::span<s32>, MaxChannels> output_buffers{};
195 195
196 for (u32 i = 0; i < parameter.channel_count; i++) { 196 for (u32 i = 0; i < parameter.channel_count; i++) {
197 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 197 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
index 8b9b65214..fc2f15a5e 100644
--- a/src/audio_core/renderer/command/effect/reverb.cpp
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -250,8 +250,8 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&
250 */ 250 */
251template <size_t NumChannels> 251template <size_t NumChannels>
252static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, 252static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
253 std::vector<std::span<const s32>>& inputs, 253 std::span<std::span<const s32>> inputs,
254 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 254 std::span<std::span<s32>> outputs, const u32 sample_count) {
255 static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{ 255 static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
256 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 256 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
257 }; 257 };
@@ -369,8 +369,8 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
369 * @param sample_count - Number of samples to process. 369 * @param sample_count - Number of samples to process.
370 */ 370 */
371static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, 371static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
372 const bool enabled, std::vector<std::span<const s32>>& inputs, 372 const bool enabled, std::span<std::span<const s32>> inputs,
373 std::vector<std::span<s32>>& outputs, const u32 sample_count) { 373 std::span<std::span<s32>> outputs, const u32 sample_count) {
374 if (enabled) { 374 if (enabled) {
375 switch (params.channel_count) { 375 switch (params.channel_count) {
376 case 0: 376 case 0:
@@ -412,8 +412,8 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc
412} 412}
413 413
414void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { 414void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
415 std::vector<std::span<const s32>> input_buffers(parameter.channel_count); 415 std::array<std::span<const s32>, MaxChannels> input_buffers{};
416 std::vector<std::span<s32>> output_buffers(parameter.channel_count); 416 std::array<std::span<s32>, MaxChannels> output_buffers{};
417 417
418 for (u32 i = 0; i < parameter.channel_count; i++) { 418 for (u32 i = 0; i < parameter.channel_count; i++) {
419 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, 419 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp
index 985958b03..4a881547f 100644
--- a/src/audio_core/renderer/command/performance/performance.cpp
+++ b/src/audio_core/renderer/command/performance/performance.cpp
@@ -5,7 +5,6 @@
5#include "audio_core/renderer/command/performance/performance.h" 5#include "audio_core/renderer/command/performance/performance.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9 8
10namespace AudioCore::AudioRenderer { 9namespace AudioCore::AudioRenderer {
11 10
@@ -18,20 +17,18 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) {
18 auto base{entry_address.translated_address}; 17 auto base{entry_address.translated_address};
19 if (state == PerformanceState::Start) { 18 if (state == PerformanceState::Start) {
20 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; 19 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
21 *start_time_ptr = static_cast<u32>( 20 *start_time_ptr =
22 Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() - 21 static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
23 processor.start_time - processor.current_processing_time) 22 processor.current_processing_time);
24 .count());
25 } else if (state == PerformanceState::Stop) { 23 } else if (state == PerformanceState::Stop) {
26 auto processed_time_ptr{ 24 auto processed_time_ptr{
27 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; 25 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
28 auto entry_count_ptr{ 26 auto entry_count_ptr{
29 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; 27 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
30 28
31 *processed_time_ptr = static_cast<u32>( 29 *processed_time_ptr =
32 Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() - 30 static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
33 processor.start_time - processor.current_processing_time) 31 processor.current_processing_time);
34 .count());
35 (*entry_count_ptr)++; 32 (*entry_count_ptr)++;
36 } 33 }
37} 34}
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp
index ded5afc94..e2ce59792 100644
--- a/src/audio_core/renderer/command/sink/circular_buffer.cpp
+++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp
@@ -24,7 +24,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
24 constexpr s32 min{std::numeric_limits<s16>::min()}; 24 constexpr s32 min{std::numeric_limits<s16>::min()};
25 constexpr s32 max{std::numeric_limits<s16>::max()}; 25 constexpr s32 max{std::numeric_limits<s16>::max()};
26 26
27 std::vector<s16> output(processor.sample_count); 27 std::array<s16, TargetSampleCount * MaxChannels> output{};
28 for (u32 channel = 0; channel < input_count; channel++) { 28 for (u32 channel = 0; channel < input_count; channel++) {
29 auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count, 29 auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,
30 processor.sample_count)}; 30 processor.sample_count)};
@@ -33,7 +33,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
33 } 33 }
34 34
35 processor.memory->WriteBlockUnsafe(address + pos, output.data(), 35 processor.memory->WriteBlockUnsafe(address + pos, output.data(),
36 output.size() * sizeof(s16)); 36 processor.sample_count * sizeof(s16));
37 pos += static_cast<u32>(processor.sample_count * sizeof(s16)); 37 pos += static_cast<u32>(processor.sample_count * sizeof(s16));
38 if (pos >= size) { 38 if (pos >= size) {
39 pos = 0; 39 pos = 0;
diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp
index e88372a75..5f74dd7ad 100644
--- a/src/audio_core/renderer/command/sink/device.cpp
+++ b/src/audio_core/renderer/command/sink/device.cpp
@@ -33,8 +33,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
33 .consumed{false}, 33 .consumed{false},
34 }; 34 };
35 35
36 std::vector<s16> samples(out_buffer.frames * input_count); 36 std::array<s16, TargetSampleCount * MaxChannels> samples{};
37
38 for (u32 channel = 0; channel < input_count; channel++) { 37 for (u32 channel = 0; channel < input_count; channel++) {
39 const auto offset{inputs[channel] * out_buffer.frames}; 38 const auto offset{inputs[channel] * out_buffer.frames};
40 39
@@ -45,7 +44,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
45 } 44 }
46 45
47 out_buffer.tag = reinterpret_cast<u64>(samples.data()); 46 out_buffer.tag = reinterpret_cast<u64>(samples.data());
48 stream->AppendBuffer(out_buffer, samples); 47 stream->AppendBuffer(out_buffer, {samples.data(), out_buffer.frames * input_count});
49 48
50 if (stream->IsPaused()) { 49 if (stream->IsPaused()) {
51 stream->Start(); 50 stream->Start();
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp
index 35b748ede..3a18ae7c2 100644
--- a/src/audio_core/renderer/mix/mix_context.cpp
+++ b/src/audio_core/renderer/mix/mix_context.cpp
@@ -125,10 +125,10 @@ bool MixContext::TSortInfo(const SplitterContext& splitter_context) {
125 return false; 125 return false;
126 } 126 }
127 127
128 std::vector<s32> sorted_results{node_states.GetSortedResuls()}; 128 auto sorted_results{node_states.GetSortedResuls()};
129 const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))}; 129 const auto result_size{std::min(count, static_cast<s32>(sorted_results.second))};
130 for (s32 i = 0; i < result_size; i++) { 130 for (s32 i = 0; i < result_size; i++) {
131 sorted_mix_infos[i] = &mix_infos[sorted_results[i]]; 131 sorted_mix_infos[i] = &mix_infos[sorted_results.first[i]];
132 } 132 }
133 133
134 CalcMixBufferOffset(); 134 CalcMixBufferOffset();
diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp
index 1821a51e6..b7a44a54c 100644
--- a/src/audio_core/renderer/nodes/node_states.cpp
+++ b/src/audio_core/renderer/nodes/node_states.cpp
@@ -134,8 +134,8 @@ u32 NodeStates::GetNodeCount() const {
134 return node_count; 134 return node_count;
135} 135}
136 136
137std::vector<s32> NodeStates::GetSortedResuls() const { 137std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls() const {
138 return {results.rbegin(), results.rbegin() + result_pos}; 138 return {results.rbegin(), result_pos};
139} 139}
140 140
141} // namespace AudioCore::AudioRenderer 141} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h
index 94b1d1254..e768cd4b5 100644
--- a/src/audio_core/renderer/nodes/node_states.h
+++ b/src/audio_core/renderer/nodes/node_states.h
@@ -175,7 +175,7 @@ public:
175 * 175 *
176 * @return Vector of nodes in reverse order. 176 * @return Vector of nodes in reverse order.
177 */ 177 */
178 std::vector<s32> GetSortedResuls() const; 178 std::pair<std::span<u32>::reverse_iterator, size_t> GetSortedResuls() const;
179 179
180private: 180private:
181 /// Number of nodes in the graph 181 /// Number of nodes in the graph
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index 53b258c4f..a23627472 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -444,6 +444,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
444 std::scoped_lock l{lock}; 444 std::scoped_lock l{lock};
445 445
446 const auto start_time{core.CoreTiming().GetClockTicks()}; 446 const auto start_time{core.CoreTiming().GetClockTicks()};
447 std::memset(output.data(), 0, output.size());
447 448
448 InfoUpdater info_updater(input, output, process_handle, behavior); 449 InfoUpdater info_updater(input, output, process_handle, behavior);
449 450
diff --git a/src/audio_core/sink/null_sink.h b/src/audio_core/sink/null_sink.h
index 1215d3cd2..b6b43c93e 100644
--- a/src/audio_core/sink/null_sink.h
+++ b/src/audio_core/sink/null_sink.h
@@ -20,7 +20,7 @@ public:
20 explicit NullSinkStreamImpl(Core::System& system_, StreamType type_) 20 explicit NullSinkStreamImpl(Core::System& system_, StreamType type_)
21 : SinkStream{system_, type_} {} 21 : SinkStream{system_, type_} {}
22 ~NullSinkStreamImpl() override {} 22 ~NullSinkStreamImpl() override {}
23 void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {} 23 void AppendBuffer(SinkBuffer&, std::span<s16>) override {}
24 std::vector<s16> ReleaseBuffer(u64) override { 24 std::vector<s16> ReleaseBuffer(u64) override {
25 return {}; 25 return {};
26 } 26 }
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index f44fedfd5..404dcd0e9 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -15,11 +15,10 @@
15#include "common/settings.h" 15#include "common/settings.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/core_timing_util.h"
19 18
20namespace AudioCore::Sink { 19namespace AudioCore::Sink {
21 20
22void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { 21void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
23 if (type == StreamType::In) { 22 if (type == StreamType::In) {
24 queue.enqueue(buffer); 23 queue.enqueue(buffer);
25 queued_buffers++; 24 queued_buffers++;
@@ -67,15 +66,16 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
67 static_cast<s16>(std::clamp(right_sample, min, max)); 66 static_cast<s16>(std::clamp(right_sample, min, max));
68 } 67 }
69 68
70 samples.resize(samples.size() / system_channels * device_channels); 69 samples = samples.subspan(0, samples.size() / system_channels * device_channels);
71 70
72 } else if (system_channels == 2 && device_channels == 6) { 71 } else if (system_channels == 2 && device_channels == 6) {
73 // We need moar samples! Not all games will provide 6 channel audio. 72 // We need moar samples! Not all games will provide 6 channel audio.
74 // TODO: Implement some upmixing here. Currently just passthrough, with other 73 // TODO: Implement some upmixing here. Currently just passthrough, with other
75 // channels left as silence. 74 // channels left as silence.
76 std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); 75 auto new_size = samples.size() / system_channels * device_channels;
76 tmp_samples.resize_destructive(new_size);
77 77
78 for (u32 read_index = 0, write_index = 0; read_index < samples.size(); 78 for (u32 read_index = 0, write_index = 0; read_index < new_size;
79 read_index += system_channels, write_index += device_channels) { 79 read_index += system_channels, write_index += device_channels) {
80 const auto left_sample{static_cast<s16>(std::clamp( 80 const auto left_sample{static_cast<s16>(std::clamp(
81 static_cast<s32>( 81 static_cast<s32>(
@@ -83,7 +83,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
83 volume), 83 volume),
84 min, max))}; 84 min, max))};
85 85
86 new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; 86 tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
87 87
88 const auto right_sample{static_cast<s16>(std::clamp( 88 const auto right_sample{static_cast<s16>(std::clamp(
89 static_cast<s32>( 89 static_cast<s32>(
@@ -91,9 +91,9 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
91 volume), 91 volume),
92 min, max))}; 92 min, max))};
93 93
94 new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; 94 tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
95 } 95 }
96 samples = std::move(new_samples); 96 samples = std::span<s16>(tmp_samples);
97 97
98 } else if (volume != 1.0f) { 98 } else if (volume != 1.0f) {
99 for (u32 i = 0; i < samples.size(); i++) { 99 for (u32 i = 0; i < samples.size(); i++) {
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 41cbadc9c..98d72ace1 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -16,6 +16,7 @@
16#include "common/polyfill_thread.h" 16#include "common/polyfill_thread.h"
17#include "common/reader_writer_queue.h" 17#include "common/reader_writer_queue.h"
18#include "common/ring_buffer.h" 18#include "common/ring_buffer.h"
19#include "common/scratch_buffer.h"
19#include "common/thread.h" 20#include "common/thread.h"
20 21
21namespace Core { 22namespace Core {
@@ -170,7 +171,7 @@ public:
170 * @param buffer - Audio buffer information to be queued. 171 * @param buffer - Audio buffer information to be queued.
171 * @param samples - The s16 samples to be queue for playback. 172 * @param samples - The s16 samples to be queue for playback.
172 */ 173 */
173 virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples); 174 virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples);
174 175
175 /** 176 /**
176 * Release a buffer. Audio In only, will fill a buffer with recorded samples. 177 * Release a buffer. Audio In only, will fill a buffer with recorded samples.
@@ -255,6 +256,8 @@ private:
255 /// Signalled when ring buffer entries are consumed 256 /// Signalled when ring buffer entries are consumed
256 std::condition_variable_any release_cv; 257 std::condition_variable_any release_cv;
257 std::mutex release_mutex; 258 std::mutex release_mutex;
259 /// Temporary buffer for appending samples when upmixing
260 Common::ScratchBuffer<s16> tmp_samples{};
258}; 261};
259 262
260using SinkStreamPtr = std::unique_ptr<SinkStream>; 263using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index efc4a9fe9..3adf13a3f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -172,6 +172,8 @@ if(ARCHITECTURE_x86_64)
172 x64/cpu_wait.h 172 x64/cpu_wait.h
173 x64/native_clock.cpp 173 x64/native_clock.cpp
174 x64/native_clock.h 174 x64/native_clock.h
175 x64/rdtsc.cpp
176 x64/rdtsc.h
175 x64/xbyak_abi.h 177 x64/xbyak_abi.h
176 x64/xbyak_util.h 178 x64/xbyak_util.h
177 ) 179 )
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index e1716c62d..36e67c145 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -3,6 +3,9 @@
3 3
4#include "common/fs/file.h" 4#include "common/fs/file.h"
5#include "common/fs/fs.h" 5#include "common/fs/fs.h"
6#ifdef ANDROID
7#include "common/fs/fs_android.h"
8#endif
6#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
7#include "common/logging/log.h" 10#include "common/logging/log.h"
8 11
@@ -433,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
433 436
434 if (True(filter & DirEntryFilter::File) && 437 if (True(filter & DirEntryFilter::File) &&
435 entry.status().type() == fs::file_type::regular) { 438 entry.status().type() == fs::file_type::regular) {
436 if (!callback(entry.path())) { 439 if (!callback(entry)) {
437 callback_error = true; 440 callback_error = true;
438 break; 441 break;
439 } 442 }
@@ -441,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
441 444
442 if (True(filter & DirEntryFilter::Directory) && 445 if (True(filter & DirEntryFilter::Directory) &&
443 entry.status().type() == fs::file_type::directory) { 446 entry.status().type() == fs::file_type::directory) {
444 if (!callback(entry.path())) { 447 if (!callback(entry)) {
445 callback_error = true; 448 callback_error = true;
446 break; 449 break;
447 } 450 }
@@ -490,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
490 493
491 if (True(filter & DirEntryFilter::File) && 494 if (True(filter & DirEntryFilter::File) &&
492 entry.status().type() == fs::file_type::regular) { 495 entry.status().type() == fs::file_type::regular) {
493 if (!callback(entry.path())) { 496 if (!callback(entry)) {
494 callback_error = true; 497 callback_error = true;
495 break; 498 break;
496 } 499 }
@@ -498,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
498 501
499 if (True(filter & DirEntryFilter::Directory) && 502 if (True(filter & DirEntryFilter::Directory) &&
500 entry.status().type() == fs::file_type::directory) { 503 entry.status().type() == fs::file_type::directory) {
501 if (!callback(entry.path())) { 504 if (!callback(entry)) {
502 callback_error = true; 505 callback_error = true;
503 break; 506 break;
504 } 507 }
@@ -525,15 +528,39 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
525// Generic Filesystem Operations 528// Generic Filesystem Operations
526 529
527bool Exists(const fs::path& path) { 530bool Exists(const fs::path& path) {
531#ifdef ANDROID
532 if (Android::IsContentUri(path)) {
533 return Android::Exists(path);
534 } else {
535 return fs::exists(path);
536 }
537#else
528 return fs::exists(path); 538 return fs::exists(path);
539#endif
529} 540}
530 541
531bool IsFile(const fs::path& path) { 542bool IsFile(const fs::path& path) {
543#ifdef ANDROID
544 if (Android::IsContentUri(path)) {
545 return !Android::IsDirectory(path);
546 } else {
547 return fs::is_regular_file(path);
548 }
549#else
532 return fs::is_regular_file(path); 550 return fs::is_regular_file(path);
551#endif
533} 552}
534 553
535bool IsDir(const fs::path& path) { 554bool IsDir(const fs::path& path) {
555#ifdef ANDROID
556 if (Android::IsContentUri(path)) {
557 return Android::IsDirectory(path);
558 } else {
559 return fs::is_directory(path);
560 }
561#else
536 return fs::is_directory(path); 562 return fs::is_directory(path);
563#endif
537} 564}
538 565
539fs::path GetCurrentDir() { 566fs::path GetCurrentDir() {
@@ -578,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) {
578} 605}
579 606
580u64 GetSize(const fs::path& path) { 607u64 GetSize(const fs::path& path) {
608#ifdef ANDROID
609 if (Android::IsContentUri(path)) {
610 return Android::GetSize(path);
611 }
612#endif
613
581 std::error_code ec; 614 std::error_code ec;
582 615
583 const auto file_size = fs::file_size(path, ec); 616 const auto file_size = fs::file_size(path, ec);
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h
index bb8a52648..b441c2a12 100644
--- a/src/common/fs/fs_android.h
+++ b/src/common/fs/fs_android.h
@@ -12,7 +12,10 @@
12 "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") 12 "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
13 13
14#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \ 14#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
15 V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") 15 V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \
16 V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
17 "(Ljava/lang/String;)Z") \
18 V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
16 19
17namespace Common::FS::Android { 20namespace Common::FS::Android {
18 21
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h
index 5a4090c19..900f85d24 100644
--- a/src/common/fs/fs_types.h
+++ b/src/common/fs/fs_types.h
@@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
66 * @returns A boolean value. 66 * @returns A boolean value.
67 * Return true to indicate whether the callback is successful, false otherwise. 67 * Return true to indicate whether the callback is successful, false otherwise.
68 */ 68 */
69using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; 69using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>;
70 70
71} // namespace Common::FS 71} // namespace Common::FS
diff --git a/src/common/input.h b/src/common/input.h
index 66fb15f0a..ea30770ae 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -86,7 +86,7 @@ enum class NfcState {
86 NewAmiibo, 86 NewAmiibo,
87 WaitingForAmiibo, 87 WaitingForAmiibo,
88 AmiiboRemoved, 88 AmiiboRemoved,
89 NotAnAmiibo, 89 InvalidTagType,
90 NotSupported, 90 NotSupported,
91 WrongDeviceState, 91 WrongDeviceState,
92 WriteFailed, 92 WriteFailed,
@@ -218,8 +218,22 @@ struct CameraStatus {
218}; 218};
219 219
220struct NfcStatus { 220struct NfcStatus {
221 NfcState state{}; 221 NfcState state{NfcState::Unknown};
222 std::vector<u8> data{}; 222 u8 uuid_length;
223 u8 protocol;
224 u8 tag_type;
225 std::array<u8, 10> uuid;
226};
227
228struct MifareData {
229 u8 command;
230 u8 sector;
231 std::array<u8, 0x6> key;
232 std::array<u8, 0x10> data;
233};
234
235struct MifareRequest {
236 std::array<MifareData, 0x10> data;
223}; 237};
224 238
225// List of buttons to be passed to Qt that can be translated 239// List of buttons to be passed to Qt that can be translated
@@ -294,7 +308,7 @@ struct CallbackStatus {
294 BatteryStatus battery_status{}; 308 BatteryStatus battery_status{};
295 VibrationStatus vibration_status{}; 309 VibrationStatus vibration_status{};
296 CameraFormat camera_status{CameraFormat::None}; 310 CameraFormat camera_status{CameraFormat::None};
297 NfcState nfc_status{NfcState::Unknown}; 311 NfcStatus nfc_status{};
298 std::vector<u8> raw_data{}; 312 std::vector<u8> raw_data{};
299}; 313};
300 314
@@ -356,9 +370,30 @@ public:
356 return NfcState::NotSupported; 370 return NfcState::NotSupported;
357 } 371 }
358 372
373 virtual NfcState StartNfcPolling() {
374 return NfcState::NotSupported;
375 }
376
377 virtual NfcState StopNfcPolling() {
378 return NfcState::NotSupported;
379 }
380
381 virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) {
382 return NfcState::NotSupported;
383 }
384
359 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { 385 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
360 return NfcState::NotSupported; 386 return NfcState::NotSupported;
361 } 387 }
388
389 virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request,
390 [[maybe_unused]] MifareRequest& out_data) {
391 return NfcState::NotSupported;
392 }
393
394 virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) {
395 return NfcState::NotSupported;
396 }
362}; 397};
363 398
364/// An abstract class template for a factory that can create input devices. 399/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 4c328ab44..416680d44 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -9,6 +9,7 @@
9#include <cstddef> 9#include <cstddef>
10#include <cstring> 10#include <cstring>
11#include <new> 11#include <new>
12#include <span>
12#include <type_traits> 13#include <type_traits>
13#include <vector> 14#include <vector>
14 15
@@ -53,7 +54,7 @@ public:
53 return push_count; 54 return push_count;
54 } 55 }
55 56
56 std::size_t Push(const std::vector<T>& input) { 57 std::size_t Push(const std::span<T> input) {
57 return Push(input.data(), input.size()); 58 return Push(input.data(), input.size());
58 } 59 }
59 60
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h
index a69a5a7af..6fe907953 100644
--- a/src/common/scratch_buffer.h
+++ b/src/common/scratch_buffer.h
@@ -3,6 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <iterator>
7
8#include "common/concepts.h"
6#include "common/make_unique_for_overwrite.h" 9#include "common/make_unique_for_overwrite.h"
7 10
8namespace Common { 11namespace Common {
@@ -16,6 +19,12 @@ namespace Common {
16template <typename T> 19template <typename T>
17class ScratchBuffer { 20class ScratchBuffer {
18public: 21public:
22 using iterator = T*;
23 using const_iterator = const T*;
24 using value_type = T;
25 using element_type = T;
26 using iterator_category = std::contiguous_iterator_tag;
27
19 ScratchBuffer() = default; 28 ScratchBuffer() = default;
20 29
21 explicit ScratchBuffer(size_t initial_capacity) 30 explicit ScratchBuffer(size_t initial_capacity)
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 9ff3edabb..66dffc9bf 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -1,12 +1,16 @@
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#if __cpp_lib_chrono >= 201907L
5#include <chrono>
6#endif
4#include <string_view> 7#include <string_view>
5 8
6#include "common/assert.h" 9#include "common/assert.h"
7#include "common/fs/path_util.h" 10#include "common/fs/path_util.h"
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9#include "common/settings.h" 12#include "common/settings.h"
13#include "common/time_zone.h"
10 14
11namespace Settings { 15namespace Settings {
12 16
@@ -14,18 +18,23 @@ Values values;
14static bool configuring_global = true; 18static bool configuring_global = true;
15 19
16std::string GetTimeZoneString() { 20std::string GetTimeZoneString() {
17 static constexpr std::array timezones{
18 "auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
19 "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
20 "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
21 "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
22 "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
23 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
24 };
25
26 const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue()); 21 const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue());
27 ASSERT(time_zone_index < timezones.size()); 22 ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size());
28 return timezones[time_zone_index]; 23
24 std::string location_name;
25 if (time_zone_index == 0) { // Auto
26#if __cpp_lib_chrono >= 201907L
27 const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
28 const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
29 std::string_view current_zone_name = current_zone->name();
30 location_name = current_zone_name;
31#else
32 location_name = Common::TimeZone::FindSystemTimeZone();
33#endif
34 } else {
35 location_name = Common::TimeZone::GetTimeZoneStrings()[time_zone_index];
36 }
37 return location_name;
29} 38}
30 39
31void LogSettings() { 40void LogSettings() {
diff --git a/src/common/settings.h b/src/common/settings.h
index 3c775d3d2..ae5ed93d8 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -483,6 +483,7 @@ struct Values {
483 AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, 483 AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
484 "astc_recompression"}; 484 "astc_recompression"};
485 SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"}; 485 SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
486 SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"};
486 487
487 SwitchableSetting<u8> bg_red{0, "bg_red"}; 488 SwitchableSetting<u8> bg_red{0, "bg_red"};
488 SwitchableSetting<u8> bg_green{0, "bg_green"}; 489 SwitchableSetting<u8> bg_green{0, "bg_green"};
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp
index 782859196..9415eed29 100644
--- a/src/common/steady_clock.cpp
+++ b/src/common/steady_clock.cpp
@@ -28,13 +28,12 @@ static s64 GetSystemTimeNS() {
28 // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. 28 // GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
29 static constexpr s64 Multiplier = 100; 29 static constexpr s64 Multiplier = 100;
30 // Convert Windows epoch to Unix epoch. 30 // Convert Windows epoch to Unix epoch.
31 static constexpr s64 WindowsEpochToUnixEpochNS = 0x19DB1DED53E8000LL; 31 static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
32 32
33 FILETIME filetime; 33 FILETIME filetime;
34 GetSystemTimePreciseAsFileTime(&filetime); 34 GetSystemTimePreciseAsFileTime(&filetime);
35 return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) + 35 return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
36 static_cast<s64>(filetime.dwLowDateTime)) - 36 static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
37 WindowsEpochToUnixEpochNS;
38} 37}
39#endif 38#endif
40 39
diff --git a/src/common/thread.h b/src/common/thread.h
index 8ae169b4e..c6976fb6c 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -55,7 +55,7 @@ public:
55 is_set = false; 55 is_set = false;
56 } 56 }
57 57
58 [[nodiscard]] bool IsSet() { 58 [[nodiscard]] bool IsSet() const {
59 return is_set; 59 return is_set;
60 } 60 }
61 61
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
index 126836b01..d8d7896c6 100644
--- a/src/common/time_zone.cpp
+++ b/src/common/time_zone.cpp
@@ -2,14 +2,33 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <chrono> 4#include <chrono>
5#include <exception>
5#include <iomanip> 6#include <iomanip>
6#include <sstream> 7#include <sstream>
8#include <stdexcept>
9#include <fmt/chrono.h>
10#include <fmt/core.h>
7 11
8#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/settings.h"
9#include "common/time_zone.h" 14#include "common/time_zone.h"
10 15
11namespace Common::TimeZone { 16namespace Common::TimeZone {
12 17
18// Time zone strings
19constexpr std::array timezones{
20 "GMT", "GMT", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
21 "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
22 "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
23 "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
24 "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
25 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
26};
27
28const std::array<const char*, 46>& GetTimeZoneStrings() {
29 return timezones;
30}
31
13std::string GetDefaultTimeZone() { 32std::string GetDefaultTimeZone() {
14 return "GMT"; 33 return "GMT";
15} 34}
@@ -18,10 +37,7 @@ static std::string GetOsTimeZoneOffset() {
18 const std::time_t t{std::time(nullptr)}; 37 const std::time_t t{std::time(nullptr)};
19 const std::tm tm{*std::localtime(&t)}; 38 const std::tm tm{*std::localtime(&t)};
20 39
21 std::stringstream ss; 40 return fmt::format("{:%z}", tm);
22 ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
23
24 return ss.str();
25} 41}
26 42
27static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { 43static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
@@ -45,4 +61,43 @@ std::chrono::seconds GetCurrentOffsetSeconds() {
45 return std::chrono::seconds{seconds}; 61 return std::chrono::seconds{seconds};
46} 62}
47 63
64// Key is [Hours * 100 + Minutes], multiplied by 100 if DST
65const static std::map<s64, const char*> off_timezones = {
66 {530, "Asia/Calcutta"}, {930, "Australia/Darwin"}, {845, "Australia/Eucla"},
67 {103000, "Australia/Adelaide"}, {1030, "Australia/Lord_Howe"}, {630, "Indian/Cocos"},
68 {1245, "Pacific/Chatham"}, {134500, "Pacific/Chatham"}, {-330, "Canada/Newfoundland"},
69 {-23000, "Canada/Newfoundland"}, {430, "Asia/Kabul"}, {330, "Asia/Tehran"},
70 {43000, "Asia/Tehran"}, {545, "Asia/Kathmandu"}, {-930, "Asia/Marquesas"},
71};
72
73std::string FindSystemTimeZone() {
74#if defined(MINGW)
75 // MinGW has broken strftime -- https://sourceforge.net/p/mingw-w64/bugs/793/
76 // e.g. fmt::format("{:%z}") -- returns "Eastern Daylight Time" when it should be "-0400"
77 return timezones[0];
78#else
79 const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count());
80
81 const s64 minutes = seconds / 60;
82 const s64 hours = minutes / 60;
83
84 const s64 minutes_off = minutes - hours * 60;
85
86 if (minutes_off != 0) {
87 const auto the_time = std::time(nullptr);
88 const struct std::tm& local = *std::localtime(&the_time);
89 const bool is_dst = local.tm_isdst != 0;
90
91 const s64 tz_index = (hours * 100 + minutes_off) * (is_dst ? 100 : 1);
92
93 try {
94 return off_timezones.at(tz_index);
95 } catch (std::out_of_range&) {
96 LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index);
97 }
98 }
99 return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours));
100#endif
101}
102
48} // namespace Common::TimeZone 103} // namespace Common::TimeZone
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
index 99cae6ef2..f574d5c04 100644
--- a/src/common/time_zone.h
+++ b/src/common/time_zone.h
@@ -3,15 +3,21 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <array>
6#include <chrono> 7#include <chrono>
7#include <string> 8#include <string>
8 9
9namespace Common::TimeZone { 10namespace Common::TimeZone {
10 11
12[[nodiscard]] const std::array<const char*, 46>& GetTimeZoneStrings();
13
11/// Gets the default timezone, i.e. "GMT" 14/// Gets the default timezone, i.e. "GMT"
12[[nodiscard]] std::string GetDefaultTimeZone(); 15[[nodiscard]] std::string GetDefaultTimeZone();
13 16
14/// Gets the offset of the current timezone (from the default), in seconds 17/// Gets the offset of the current timezone (from the default), in seconds
15[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds(); 18[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
16 19
20/// Searches time zone offsets for the closest offset to the system time zone
21[[nodiscard]] std::string FindSystemTimeZone();
22
17} // namespace Common::TimeZone 23} // namespace Common::TimeZone
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 817e71d52..dc0dcbd68 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -2,88 +2,75 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/steady_clock.h" 4#include "common/steady_clock.h"
5#include "common/uint128.h"
6#include "common/wall_clock.h" 5#include "common/wall_clock.h"
7 6
8#ifdef ARCHITECTURE_x86_64 7#ifdef ARCHITECTURE_x86_64
9#include "common/x64/cpu_detect.h" 8#include "common/x64/cpu_detect.h"
10#include "common/x64/native_clock.h" 9#include "common/x64/native_clock.h"
10#include "common/x64/rdtsc.h"
11#endif 11#endif
12 12
13namespace Common { 13namespace Common {
14 14
15class StandardWallClock final : public WallClock { 15class StandardWallClock final : public WallClock {
16public: 16public:
17 explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) 17 explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
18 : WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
19 start_time{SteadyClock::Now()} {}
20 18
21 std::chrono::nanoseconds GetTimeNS() override { 19 std::chrono::nanoseconds GetTimeNS() const override {
22 return SteadyClock::Now() - start_time; 20 return SteadyClock::Now() - start_time;
23 } 21 }
24 22
25 std::chrono::microseconds GetTimeUS() override { 23 std::chrono::microseconds GetTimeUS() const override {
26 return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS()); 24 return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
27 } 25 }
28 26
29 std::chrono::milliseconds GetTimeMS() override { 27 std::chrono::milliseconds GetTimeMS() const override {
30 return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS()); 28 return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
31 } 29 }
32 30
33 u64 GetClockCycles() override { 31 u64 GetCNTPCT() const override {
34 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency); 32 return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
35 return Common::Divide128On32(temp, NS_RATIO).first;
36 } 33 }
37 34
38 u64 GetCPUCycles() override { 35 u64 GetGPUTick() const override {
39 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency); 36 return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
40 return Common::Divide128On32(temp, NS_RATIO).first;
41 } 37 }
42 38
43 void Pause([[maybe_unused]] bool is_paused) override { 39 u64 GetHostTicksNow() const override {
44 // Do nothing in this clock type. 40 return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
41 }
42
43 u64 GetHostTicksElapsed() const override {
44 return static_cast<u64>(GetTimeNS().count());
45 }
46
47 bool IsNative() const override {
48 return false;
45 } 49 }
46 50
47private: 51private:
48 SteadyClock::time_point start_time; 52 SteadyClock::time_point start_time;
49}; 53};
50 54
55std::unique_ptr<WallClock> CreateOptimalClock() {
51#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
52
53std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
54 u64 emulated_clock_frequency) {
55 const auto& caps = GetCPUCaps(); 57 const auto& caps = GetCPUCaps();
56 u64 rtsc_frequency = 0;
57 if (caps.invariant_tsc) {
58 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
59 }
60 58
61 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 59 if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::GPUTickFreq) {
62 // - A nanosecond 60 return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
63 // - The emulated CPU frequency
64 // - The emulated clock counter frequency (CNTFRQ)
65 if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
66 rtsc_frequency <= emulated_clock_frequency) {
67 return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
68 emulated_clock_frequency);
69 } else { 61 } else {
70 return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, 62 // Fallback to StandardWallClock if the hardware TSC
71 rtsc_frequency); 63 // - Is not invariant
64 // - Is not more precise than GPUTickFreq
65 return std::make_unique<StandardWallClock>();
72 } 66 }
73}
74
75#else 67#else
76 68 return std::make_unique<StandardWallClock>();
77std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
78 u64 emulated_clock_frequency) {
79 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
80}
81
82#endif 69#endif
70}
83 71
84std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, 72std::unique_ptr<WallClock> CreateStandardWallClock() {
85 u64 emulated_clock_frequency) { 73 return std::make_unique<StandardWallClock>();
86 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
87} 74}
88 75
89} // namespace Common 76} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 157ec5eae..f45d3d8c5 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -5,6 +5,7 @@
5 5
6#include <chrono> 6#include <chrono>
7#include <memory> 7#include <memory>
8#include <ratio>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -12,50 +13,82 @@ namespace Common {
12 13
13class WallClock { 14class WallClock {
14public: 15public:
15 static constexpr u64 NS_RATIO = 1'000'000'000; 16 static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
16 static constexpr u64 US_RATIO = 1'000'000; 17 static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
17 static constexpr u64 MS_RATIO = 1'000; 18 static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
18 19
19 virtual ~WallClock() = default; 20 virtual ~WallClock() = default;
20 21
21 /// Returns current wall time in nanoseconds 22 /// @returns The time in nanoseconds since the construction of this clock.
22 [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0; 23 virtual std::chrono::nanoseconds GetTimeNS() const = 0;
23 24
24 /// Returns current wall time in microseconds 25 /// @returns The time in microseconds since the construction of this clock.
25 [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0; 26 virtual std::chrono::microseconds GetTimeUS() const = 0;
26 27
27 /// Returns current wall time in milliseconds 28 /// @returns The time in milliseconds since the construction of this clock.
28 [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0; 29 virtual std::chrono::milliseconds GetTimeMS() const = 0;
29 30
30 /// Returns current wall time in emulated clock cycles 31 /// @returns The guest CNTPCT ticks since the construction of this clock.
31 [[nodiscard]] virtual u64 GetClockCycles() = 0; 32 virtual u64 GetCNTPCT() const = 0;
32 33
33 /// Returns current wall time in emulated cpu cycles 34 /// @returns The guest GPU ticks since the construction of this clock.
34 [[nodiscard]] virtual u64 GetCPUCycles() = 0; 35 virtual u64 GetGPUTick() const = 0;
35 36
36 virtual void Pause(bool is_paused) = 0; 37 /// @returns The raw host timer ticks since an indeterminate epoch.
38 virtual u64 GetHostTicksNow() const = 0;
37 39
38 /// Tells if the wall clock, uses the host CPU's hardware clock 40 /// @returns The raw host timer ticks since the construction of this clock.
39 [[nodiscard]] bool IsNative() const { 41 virtual u64 GetHostTicksElapsed() const = 0;
40 return is_native; 42
43 /// @returns Whether the clock directly uses the host's hardware clock.
44 virtual bool IsNative() const = 0;
45
46 static inline u64 NSToCNTPCT(u64 ns) {
47 return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
48 }
49
50 static inline u64 NSToGPUTick(u64 ns) {
51 return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
52 }
53
54 // Cycle Timing
55
56 static inline u64 CPUTickToNS(u64 cpu_tick) {
57 return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
58 }
59
60 static inline u64 CPUTickToUS(u64 cpu_tick) {
61 return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
62 }
63
64 static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
65 return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
66 }
67
68 static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
69 return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
41 } 70 }
42 71
43protected: 72protected:
44 explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_) 73 using NsRatio = std::nano;
45 : emulated_cpu_frequency{emulated_cpu_frequency_}, 74 using UsRatio = std::micro;
46 emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {} 75 using MsRatio = std::milli;
76
77 using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
78 using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
79 using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
80 using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
47 81
48 u64 emulated_cpu_frequency; 82 // Cycle Timing
49 u64 emulated_clock_frequency;
50 83
51private: 84 using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
52 bool is_native; 85 using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
86 using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
87 using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
53}; 88};
54 89
55[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, 90std::unique_ptr<WallClock> CreateOptimalClock();
56 u64 emulated_clock_frequency);
57 91
58[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, 92std::unique_ptr<WallClock> CreateStandardWallClock();
59 u64 emulated_clock_frequency);
60 93
61} // namespace Common 94} // namespace Common
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 72ed6e96c..c998b1197 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -14,6 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/x64/cpu_detect.h" 16#include "common/x64/cpu_detect.h"
17#include "common/x64/rdtsc.h"
17 18
18#ifdef _WIN32 19#ifdef _WIN32
19#include <windows.h> 20#include <windows.h>
@@ -187,6 +188,8 @@ static CPUCaps Detect() {
187 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * 188 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
188 caps.tsc_crystal_ratio_numerator / 189 caps.tsc_crystal_ratio_numerator /
189 caps.tsc_crystal_ratio_denominator; 190 caps.tsc_crystal_ratio_denominator;
191 } else {
192 caps.tsc_frequency = X64::EstimateRDTSCFrequency();
190 } 193 }
191 } 194 }
192 195
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp
index cfeef6a3d..c53dd4945 100644
--- a/src/common/x64/cpu_wait.cpp
+++ b/src/common/x64/cpu_wait.cpp
@@ -9,19 +9,11 @@
9 9
10#include "common/x64/cpu_detect.h" 10#include "common/x64/cpu_detect.h"
11#include "common/x64/cpu_wait.h" 11#include "common/x64/cpu_wait.h"
12#include "common/x64/rdtsc.h"
12 13
13namespace Common::X64 { 14namespace Common::X64 {
14 15
15#ifdef _MSC_VER 16#ifdef _MSC_VER
16__forceinline static u64 FencedRDTSC() {
17 _mm_lfence();
18 _ReadWriteBarrier();
19 const u64 result = __rdtsc();
20 _mm_lfence();
21 _ReadWriteBarrier();
22 return result;
23}
24
25__forceinline static void TPAUSE() { 17__forceinline static void TPAUSE() {
26 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 18 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
27 // For reference: 19 // For reference:
@@ -32,16 +24,6 @@ __forceinline static void TPAUSE() {
32 _tpause(0, FencedRDTSC() + PauseCycles); 24 _tpause(0, FencedRDTSC() + PauseCycles);
33} 25}
34#else 26#else
35static u64 FencedRDTSC() {
36 u64 eax;
37 u64 edx;
38 asm volatile("lfence\n\t"
39 "rdtsc\n\t"
40 "lfence\n\t"
41 : "=a"(eax), "=d"(edx));
42 return (edx << 32) | eax;
43}
44
45static void TPAUSE() { 27static void TPAUSE() {
46 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. 28 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
47 // For reference: 29 // For reference:
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 277b00662..7d2a26bd9 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -1,164 +1,50 @@
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 <array>
5#include <chrono>
6#include <thread>
7
8#include "common/atomic_ops.h"
9#include "common/steady_clock.h"
10#include "common/uint128.h" 4#include "common/uint128.h"
11#include "common/x64/native_clock.h" 5#include "common/x64/native_clock.h"
6#include "common/x64/rdtsc.h"
12 7
13#ifdef _MSC_VER 8namespace Common::X64 {
14#include <intrin.h>
15#endif
16
17namespace Common {
18 9
19#ifdef _MSC_VER 10NativeClock::NativeClock(u64 rdtsc_frequency_)
20__forceinline static u64 FencedRDTSC() { 11 : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
21 _mm_lfence(); 12 ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
22 _ReadWriteBarrier(); 13 us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
23 const u64 result = __rdtsc(); 14 ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
24 _mm_lfence(); 15 cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
25 _ReadWriteBarrier(); 16 gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
26 return result;
27}
28#else
29static u64 FencedRDTSC() {
30 u64 eax;
31 u64 edx;
32 asm volatile("lfence\n\t"
33 "rdtsc\n\t"
34 "lfence\n\t"
35 : "=a"(eax), "=d"(edx));
36 return (edx << 32) | eax;
37}
38#endif
39 17
40template <u64 Nearest> 18std::chrono::nanoseconds NativeClock::GetTimeNS() const {
41static u64 RoundToNearest(u64 value) { 19 return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
42 const auto mod = value % Nearest;
43 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
44} 20}
45 21
46u64 EstimateRDTSCFrequency() { 22std::chrono::microseconds NativeClock::GetTimeUS() const {
47 // Discard the first result measuring the rdtsc. 23 return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
48 FencedRDTSC();
49 std::this_thread::sleep_for(std::chrono::milliseconds{1});
50 FencedRDTSC();
51
52 // Get the current time.
53 const auto start_time = Common::RealTimeClock::Now();
54 const u64 tsc_start = FencedRDTSC();
55 // Wait for 250 milliseconds.
56 std::this_thread::sleep_for(std::chrono::milliseconds{250});
57 const auto end_time = Common::RealTimeClock::Now();
58 const u64 tsc_end = FencedRDTSC();
59 // Calculate differences.
60 const u64 timer_diff = static_cast<u64>(
61 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
62 const u64 tsc_diff = tsc_end - tsc_start;
63 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
64 return RoundToNearest<1000>(tsc_freq);
65} 24}
66 25
67namespace X64 { 26std::chrono::milliseconds NativeClock::GetTimeMS() const {
68NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, 27 return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
69 u64 rtsc_frequency_)
70 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
71 rtsc_frequency_} {
72 // Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed.
73 time_sync_thread = std::jthread{[this](std::stop_token token) {
74 // Get the current time.
75 const auto start_time = Common::RealTimeClock::Now();
76 const u64 tsc_start = FencedRDTSC();
77 // Wait for 10 seconds.
78 if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) {
79 return;
80 }
81 const auto end_time = Common::RealTimeClock::Now();
82 const u64 tsc_end = FencedRDTSC();
83 // Calculate differences.
84 const u64 timer_diff = static_cast<u64>(
85 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
86 const u64 tsc_diff = tsc_end - tsc_start;
87 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
88 rtsc_frequency = tsc_freq;
89 CalculateAndSetFactors();
90 }};
91
92 time_point.inner.last_measure = FencedRDTSC();
93 time_point.inner.accumulated_ticks = 0U;
94 CalculateAndSetFactors();
95} 28}
96 29
97u64 NativeClock::GetRTSC() { 30u64 NativeClock::GetCNTPCT() const {
98 TimePoint new_time_point{}; 31 return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
99 TimePoint current_time_point{};
100
101 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
102 do {
103 const u64 current_measure = FencedRDTSC();
104 u64 diff = current_measure - current_time_point.inner.last_measure;
105 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
106 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
107 ? current_measure
108 : current_time_point.inner.last_measure;
109 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
110 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
111 current_time_point.pack, current_time_point.pack));
112 return new_time_point.inner.accumulated_ticks;
113} 32}
114 33
115void NativeClock::Pause(bool is_paused) { 34u64 NativeClock::GetGPUTick() const {
116 if (!is_paused) { 35 return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor);
117 TimePoint current_time_point{};
118 TimePoint new_time_point{};
119
120 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
121 do {
122 new_time_point.pack = current_time_point.pack;
123 new_time_point.inner.last_measure = FencedRDTSC();
124 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
125 current_time_point.pack, current_time_point.pack));
126 }
127} 36}
128 37
129std::chrono::nanoseconds NativeClock::GetTimeNS() { 38u64 NativeClock::GetHostTicksNow() const {
130 const u64 rtsc_value = GetRTSC(); 39 return FencedRDTSC();
131 return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
132} 40}
133 41
134std::chrono::microseconds NativeClock::GetTimeUS() { 42u64 NativeClock::GetHostTicksElapsed() const {
135 const u64 rtsc_value = GetRTSC(); 43 return FencedRDTSC() - start_ticks;
136 return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
137} 44}
138 45
139std::chrono::milliseconds NativeClock::GetTimeMS() { 46bool NativeClock::IsNative() const {
140 const u64 rtsc_value = GetRTSC(); 47 return true;
141 return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
142} 48}
143 49
144u64 NativeClock::GetClockCycles() { 50} // namespace Common::X64
145 const u64 rtsc_value = GetRTSC();
146 return MultiplyHigh(rtsc_value, clock_rtsc_factor);
147}
148
149u64 NativeClock::GetCPUCycles() {
150 const u64 rtsc_value = GetRTSC();
151 return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
152}
153
154void NativeClock::CalculateAndSetFactors() {
155 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
156 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
157 ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
158 clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
159 cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
160}
161
162} // namespace X64
163
164} // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 03ca291d8..334415eff 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -3,58 +3,39 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/polyfill_thread.h"
7#include "common/wall_clock.h" 6#include "common/wall_clock.h"
8 7
9namespace Common { 8namespace Common::X64 {
10 9
11namespace X64 {
12class NativeClock final : public WallClock { 10class NativeClock final : public WallClock {
13public: 11public:
14 explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, 12 explicit NativeClock(u64 rdtsc_frequency_);
15 u64 rtsc_frequency_);
16 13
17 std::chrono::nanoseconds GetTimeNS() override; 14 std::chrono::nanoseconds GetTimeNS() const override;
18 15
19 std::chrono::microseconds GetTimeUS() override; 16 std::chrono::microseconds GetTimeUS() const override;
20 17
21 std::chrono::milliseconds GetTimeMS() override; 18 std::chrono::milliseconds GetTimeMS() const override;
22 19
23 u64 GetClockCycles() override; 20 u64 GetCNTPCT() const override;
24 21
25 u64 GetCPUCycles() override; 22 u64 GetGPUTick() const override;
26 23
27 void Pause(bool is_paused) override; 24 u64 GetHostTicksNow() const override;
28 25
29private: 26 u64 GetHostTicksElapsed() const override;
30 u64 GetRTSC();
31
32 void CalculateAndSetFactors();
33
34 union alignas(16) TimePoint {
35 TimePoint() : pack{} {}
36 u128 pack{};
37 struct Inner {
38 u64 last_measure{};
39 u64 accumulated_ticks{};
40 } inner;
41 };
42
43 TimePoint time_point;
44 27
45 // factors 28 bool IsNative() const override;
46 u64 clock_rtsc_factor{};
47 u64 cpu_rtsc_factor{};
48 u64 ns_rtsc_factor{};
49 u64 us_rtsc_factor{};
50 u64 ms_rtsc_factor{};
51 29
52 u64 rtsc_frequency; 30private:
53 31 u64 start_ticks;
54 std::jthread time_sync_thread; 32 u64 rdtsc_frequency;
33
34 u64 ns_rdtsc_factor;
35 u64 us_rdtsc_factor;
36 u64 ms_rdtsc_factor;
37 u64 cntpct_rdtsc_factor;
38 u64 gputick_rdtsc_factor;
55}; 39};
56} // namespace X64
57
58u64 EstimateRDTSCFrequency();
59 40
60} // namespace Common 41} // namespace Common::X64
diff --git a/src/common/x64/rdtsc.cpp b/src/common/x64/rdtsc.cpp
new file mode 100644
index 000000000..9273274a3
--- /dev/null
+++ b/src/common/x64/rdtsc.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5
6#include "common/steady_clock.h"
7#include "common/uint128.h"
8#include "common/x64/rdtsc.h"
9
10namespace Common::X64 {
11
12template <u64 Nearest>
13static u64 RoundToNearest(u64 value) {
14 const auto mod = value % Nearest;
15 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
16}
17
18u64 EstimateRDTSCFrequency() {
19 // Discard the first result measuring the rdtsc.
20 FencedRDTSC();
21 std::this_thread::sleep_for(std::chrono::milliseconds{1});
22 FencedRDTSC();
23
24 // Get the current time.
25 const auto start_time = RealTimeClock::Now();
26 const u64 tsc_start = FencedRDTSC();
27 // Wait for 100 milliseconds.
28 std::this_thread::sleep_for(std::chrono::milliseconds{100});
29 const auto end_time = RealTimeClock::Now();
30 const u64 tsc_end = FencedRDTSC();
31 // Calculate differences.
32 const u64 timer_diff = static_cast<u64>(
33 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
34 const u64 tsc_diff = tsc_end - tsc_start;
35 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
36 return RoundToNearest<100'000>(tsc_freq);
37}
38
39} // namespace Common::X64
diff --git a/src/common/x64/rdtsc.h b/src/common/x64/rdtsc.h
new file mode 100644
index 000000000..0ec4f52f9
--- /dev/null
+++ b/src/common/x64/rdtsc.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#ifdef _MSC_VER
7#include <intrin.h>
8#endif
9
10#include "common/common_types.h"
11
12namespace Common::X64 {
13
14#ifdef _MSC_VER
15__forceinline static u64 FencedRDTSC() {
16 _mm_lfence();
17 _ReadWriteBarrier();
18 const u64 result = __rdtsc();
19 _mm_lfence();
20 _ReadWriteBarrier();
21 return result;
22}
23#else
24static inline u64 FencedRDTSC() {
25 u64 eax;
26 u64 edx;
27 asm volatile("lfence\n\t"
28 "rdtsc\n\t"
29 "lfence\n\t"
30 : "=a"(eax), "=d"(edx));
31 return (edx << 32) | eax;
32}
33#endif
34
35u64 EstimateRDTSCFrequency();
36
37} // namespace Common::X64
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 99602699a..3655b8478 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -4,8 +4,6 @@
4add_library(core STATIC 4add_library(core STATIC
5 arm/arm_interface.h 5 arm/arm_interface.h
6 arm/arm_interface.cpp 6 arm/arm_interface.cpp
7 arm/dynarmic/arm_exclusive_monitor.cpp
8 arm/dynarmic/arm_exclusive_monitor.h
9 arm/exclusive_monitor.cpp 7 arm/exclusive_monitor.cpp
10 arm/exclusive_monitor.h 8 arm/exclusive_monitor.h
11 arm/symbols.cpp 9 arm/symbols.cpp
@@ -16,7 +14,6 @@ add_library(core STATIC
16 core.h 14 core.h
17 core_timing.cpp 15 core_timing.cpp
18 core_timing.h 16 core_timing.h
19 core_timing_util.h
20 cpu_manager.cpp 17 cpu_manager.cpp
21 cpu_manager.h 18 cpu_manager.h
22 crypto/aes_util.cpp 19 crypto/aes_util.cpp
@@ -836,7 +833,7 @@ endif()
836 833
837create_target_directory_groups(core) 834create_target_directory_groups(core)
838 835
839target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) 836target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb)
840target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) 837target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
841if (MINGW) 838if (MINGW)
842 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 839 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
@@ -849,12 +846,15 @@ endif()
849 846
850if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 847if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
851 target_sources(core PRIVATE 848 target_sources(core PRIVATE
849 arm/dynarmic/arm_dynarmic.h
852 arm/dynarmic/arm_dynarmic_64.cpp 850 arm/dynarmic/arm_dynarmic_64.cpp
853 arm/dynarmic/arm_dynarmic_64.h 851 arm/dynarmic/arm_dynarmic_64.h
854 arm/dynarmic/arm_dynarmic_32.cpp 852 arm/dynarmic/arm_dynarmic_32.cpp
855 arm/dynarmic/arm_dynarmic_32.h 853 arm/dynarmic/arm_dynarmic_32.h
856 arm/dynarmic/arm_dynarmic_cp15.cpp 854 arm/dynarmic/dynarmic_cp15.cpp
857 arm/dynarmic/arm_dynarmic_cp15.h 855 arm/dynarmic/dynarmic_cp15.h
856 arm/dynarmic/dynarmic_exclusive_monitor.cpp
857 arm/dynarmic/dynarmic_exclusive_monitor.h
858 hle/service/jit/jit_context.cpp 858 hle/service/jit/jit_context.cpp
859 hle/service/jit/jit_context.h 859 hle/service/jit/jit_context.h
860 hle/service/jit/jit.cpp 860 hle/service/jit/jit.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index d30914b7a..beaea64b3 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -13,25 +13,68 @@
13#include "core/core.h" 13#include "core/core.h"
14#include "core/debugger/debugger.h" 14#include "core/debugger/debugger.h"
15#include "core/hle/kernel/k_process.h" 15#include "core/hle/kernel/k_process.h"
16#include "core/hle/kernel/k_thread.h"
16#include "core/hle/kernel/svc.h" 17#include "core/hle/kernel/svc.h"
17#include "core/loader/loader.h" 18#include "core/loader/loader.h"
18#include "core/memory.h" 19#include "core/memory.h"
19 20
20#include "core/arm/dynarmic/arm_dynarmic_32.h"
21#include "core/arm/dynarmic/arm_dynarmic_64.h"
22
23namespace Core { 21namespace Core {
24 22
25constexpr u64 SEGMENT_BASE = 0x7100000000ull; 23constexpr u64 SEGMENT_BASE = 0x7100000000ull;
26 24
27std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( 25std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
28 Core::System& system, const ARM_Interface::ThreadContext32& ctx) { 26 Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
29 return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx); 27 std::vector<BacktraceEntry> out;
28 auto& memory = system.ApplicationMemory();
29
30 const auto& reg = ctx.cpu_registers;
31 u32 pc = reg[15], lr = reg[14], fp = reg[11];
32 out.push_back({"", 0, pc, 0, ""});
33
34 // fp (= r11) points to the last frame record.
35 // Frame records are two words long:
36 // fp+0 : pointer to previous frame record
37 // fp+4 : value of lr for frame
38 for (size_t i = 0; i < 256; i++) {
39 out.push_back({"", 0, lr, 0, ""});
40 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
41 break;
42 }
43 lr = memory.Read32(fp + 4);
44 fp = memory.Read32(fp);
45 }
46
47 SymbolicateBacktrace(system, out);
48
49 return out;
30} 50}
31 51
32std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( 52std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
33 Core::System& system, const ARM_Interface::ThreadContext64& ctx) { 53 Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
34 return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx); 54 std::vector<BacktraceEntry> out;
55 auto& memory = system.ApplicationMemory();
56
57 const auto& reg = ctx.cpu_registers;
58 u64 pc = ctx.pc, lr = reg[30], fp = reg[29];
59
60 out.push_back({"", 0, pc, 0, ""});
61
62 // fp (= x29) points to the previous frame record.
63 // Frame records are two words long:
64 // fp+0 : pointer to previous frame record
65 // fp+8 : value of lr for frame
66 for (size_t i = 0; i < 256; i++) {
67 out.push_back({"", 0, lr, 0, ""});
68 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
69 break;
70 }
71 lr = memory.Read64(fp + 8);
72 fp = memory.Read64(fp);
73 }
74
75 SymbolicateBacktrace(system, out);
76
77 return out;
35} 78}
36 79
37void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) { 80void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
@@ -76,6 +119,18 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
76 } 119 }
77} 120}
78 121
122std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
123 if (GetArchitecture() == Architecture::Aarch64) {
124 ThreadContext64 ctx;
125 SaveContext(ctx);
126 return GetBacktraceFromContext(system, ctx);
127 } else {
128 ThreadContext32 ctx;
129 SaveContext(ctx);
130 return GetBacktraceFromContext(system, ctx);
131 }
132}
133
79void ARM_Interface::LogBacktrace() const { 134void ARM_Interface::LogBacktrace() const {
80 const VAddr sp = GetSP(); 135 const VAddr sp = GetSP();
81 const VAddr pc = GetPC(); 136 const VAddr pc = GetPC();
@@ -83,7 +138,6 @@ void ARM_Interface::LogBacktrace() const {
83 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", 138 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
84 "Offset", "Symbol"); 139 "Offset", "Symbol");
85 LOG_ERROR(Core_ARM, ""); 140 LOG_ERROR(Core_ARM, "");
86
87 const auto backtrace = GetBacktrace(); 141 const auto backtrace = GetBacktrace();
88 for (const auto& entry : backtrace) { 142 for (const auto& entry : backtrace) {
89 LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, 143 LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
@@ -97,7 +151,7 @@ void ARM_Interface::Run() {
97 151
98 while (true) { 152 while (true) {
99 Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; 153 Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
100 Dynarmic::HaltReason hr{}; 154 HaltReason hr{};
101 155
102 // Notify the debugger and go to sleep if a step was performed 156 // Notify the debugger and go to sleep if a step was performed
103 // and this thread has been scheduled again. 157 // and this thread has been scheduled again.
@@ -108,17 +162,17 @@ void ARM_Interface::Run() {
108 } 162 }
109 163
110 // Otherwise, run the thread. 164 // Otherwise, run the thread.
111 system.EnterDynarmicProfile(); 165 system.EnterCPUProfile();
112 if (current_thread->GetStepState() == StepState::StepPending) { 166 if (current_thread->GetStepState() == StepState::StepPending) {
113 hr = StepJit(); 167 hr = StepJit();
114 168
115 if (Has(hr, step_thread)) { 169 if (True(hr & HaltReason::StepThread)) {
116 current_thread->SetStepState(StepState::StepPerformed); 170 current_thread->SetStepState(StepState::StepPerformed);
117 } 171 }
118 } else { 172 } else {
119 hr = RunJit(); 173 hr = RunJit();
120 } 174 }
121 system.ExitDynarmicProfile(); 175 system.ExitCPUProfile();
122 176
123 // If the thread is scheduled for termination, exit the thread. 177 // If the thread is scheduled for termination, exit the thread.
124 if (current_thread->HasDpc()) { 178 if (current_thread->HasDpc()) {
@@ -130,8 +184,8 @@ void ARM_Interface::Run() {
130 184
131 // 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,
132 // or if the thread is unable to continue for any reason. 186 // or if the thread is unable to continue for any reason.
133 if (Has(hr, breakpoint) || Has(hr, no_execute)) { 187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
134 if (!Has(hr, no_execute)) { 188 if (!True(hr & HaltReason::InstructionBreakpoint)) {
135 RewindBreakpointInstruction(); 189 RewindBreakpointInstruction();
136 } 190 }
137 if (system.DebuggerEnabled()) { 191 if (system.DebuggerEnabled()) {
@@ -144,7 +198,7 @@ void ARM_Interface::Run() {
144 } 198 }
145 199
146 // Notify the debugger and go to sleep if a watchpoint was hit. 200 // Notify the debugger and go to sleep if a watchpoint was hit.
147 if (Has(hr, watchpoint)) { 201 if (True(hr & HaltReason::DataAbort)) {
148 if (system.DebuggerEnabled()) { 202 if (system.DebuggerEnabled()) {
149 system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); 203 system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
150 } 204 }
@@ -153,11 +207,11 @@ void ARM_Interface::Run() {
153 } 207 }
154 208
155 // Handle syscalls and scheduling (this may change the current thread/core) 209 // Handle syscalls and scheduling (this may change the current thread/core)
156 if (Has(hr, svc_call)) { 210 if (True(hr & HaltReason::SupervisorCall)) {
157 Kernel::Svc::Call(system, GetSvcNumber()); 211 Kernel::Svc::Call(system, GetSvcNumber());
158 break; 212 break;
159 } 213 }
160 if (Has(hr, break_loop) || !uses_wall_clock) { 214 if (True(hr & HaltReason::BreakLoop) || !uses_wall_clock) {
161 break; 215 break;
162 } 216 }
163 } 217 }
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8e40702cc..d5f2fa09a 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -8,8 +8,6 @@
8#include <string> 8#include <string>
9#include <vector> 9#include <vector>
10 10
11#include <dynarmic/interface/halt_reason.h>
12
13#include "common/common_funcs.h" 11#include "common/common_funcs.h"
14#include "common/common_types.h" 12#include "common/common_types.h"
15#include "core/hardware_properties.h" 13#include "core/hardware_properties.h"
@@ -30,6 +28,22 @@ class CPUInterruptHandler;
30 28
31using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; 29using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
32 30
31// NOTE: these values match the HaltReason enum in Dynarmic
32enum class HaltReason : u64 {
33 StepThread = 0x00000001,
34 DataAbort = 0x00000004,
35 BreakLoop = 0x02000000,
36 SupervisorCall = 0x04000000,
37 InstructionBreakpoint = 0x08000000,
38 PrefetchAbort = 0x20000000,
39};
40DECLARE_ENUM_FLAG_OPERATORS(HaltReason);
41
42enum class Architecture {
43 Aarch32,
44 Aarch64,
45};
46
33/// Generic ARMv8 CPU interface 47/// Generic ARMv8 CPU interface
34class ARM_Interface { 48class ARM_Interface {
35public: 49public:
@@ -167,8 +181,9 @@ public:
167 */ 181 */
168 virtual void SetTPIDR_EL0(u64 value) = 0; 182 virtual void SetTPIDR_EL0(u64 value) = 0;
169 183
170 virtual void SaveContext(ThreadContext32& ctx) = 0; 184 virtual Architecture GetArchitecture() const = 0;
171 virtual void SaveContext(ThreadContext64& ctx) = 0; 185 virtual void SaveContext(ThreadContext32& ctx) const = 0;
186 virtual void SaveContext(ThreadContext64& ctx) const = 0;
172 virtual void LoadContext(const ThreadContext32& ctx) = 0; 187 virtual void LoadContext(const ThreadContext32& ctx) = 0;
173 virtual void LoadContext(const ThreadContext64& ctx) = 0; 188 virtual void LoadContext(const ThreadContext64& ctx) = 0;
174 void LoadWatchpointArray(const WatchpointArray& wp); 189 void LoadWatchpointArray(const WatchpointArray& wp);
@@ -195,17 +210,9 @@ public:
195 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, 210 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
196 const ThreadContext64& ctx); 211 const ThreadContext64& ctx);
197 212
198 virtual std::vector<BacktraceEntry> GetBacktrace() const = 0; 213 std::vector<BacktraceEntry> GetBacktrace() const;
199
200 void LogBacktrace() const; 214 void LogBacktrace() const;
201 215
202 static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
203 static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
204 static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
205 static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
206 static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort;
207 static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
208
209protected: 216protected:
210 /// System context that this ARM interface is running under. 217 /// System context that this ARM interface is running under.
211 System& system; 218 System& system;
@@ -216,8 +223,8 @@ protected:
216 const Kernel::DebugWatchpoint* MatchingWatchpoint( 223 const Kernel::DebugWatchpoint* MatchingWatchpoint(
217 u64 addr, u64 size, Kernel::DebugWatchpointType access_type) const; 224 u64 addr, u64 size, Kernel::DebugWatchpointType access_type) const;
218 225
219 virtual Dynarmic::HaltReason RunJit() = 0; 226 virtual HaltReason RunJit() = 0;
220 virtual Dynarmic::HaltReason StepJit() = 0; 227 virtual HaltReason StepJit() = 0;
221 virtual u32 GetSvcNumber() const = 0; 228 virtual u32 GetSvcNumber() const = 0;
222 virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; 229 virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
223 virtual void RewindBreakpointInstruction() = 0; 230 virtual void RewindBreakpointInstruction() = 0;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
new file mode 100644
index 000000000..eef7c3116
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <dynarmic/interface/halt_reason.h>
5
6#include "core/arm/arm_interface.h"
7
8namespace Core {
9
10constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
11constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
12constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
13constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
14constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
15constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
16
17constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
18 static_assert(static_cast<u64>(HaltReason::StepThread) == static_cast<u64>(StepThread));
19 static_assert(static_cast<u64>(HaltReason::DataAbort) == static_cast<u64>(DataAbort));
20 static_assert(static_cast<u64>(HaltReason::BreakLoop) == static_cast<u64>(BreakLoop));
21 static_assert(static_cast<u64>(HaltReason::SupervisorCall) == static_cast<u64>(SupervisorCall));
22 static_assert(static_cast<u64>(HaltReason::InstructionBreakpoint) ==
23 static_cast<u64>(InstructionBreakpoint));
24 static_assert(static_cast<u64>(HaltReason::PrefetchAbort) == static_cast<u64>(PrefetchAbort));
25
26 return static_cast<HaltReason>(hr);
27}
28
29} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index dfdcbe35a..5acf9008d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -10,9 +10,10 @@
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/arm/dynarmic/arm_dynarmic.h"
13#include "core/arm/dynarmic/arm_dynarmic_32.h" 14#include "core/arm/dynarmic/arm_dynarmic_32.h"
14#include "core/arm/dynarmic/arm_dynarmic_cp15.h" 15#include "core/arm/dynarmic/dynarmic_cp15.h"
15#include "core/arm/dynarmic/arm_exclusive_monitor.h" 16#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
16#include "core/core.h" 17#include "core/core.h"
17#include "core/core_timing.h" 18#include "core/core_timing.h"
18#include "core/debugger/debugger.h" 19#include "core/debugger/debugger.h"
@@ -104,11 +105,11 @@ public:
104 switch (exception) { 105 switch (exception) {
105 case Dynarmic::A32::Exception::NoExecuteFault: 106 case Dynarmic::A32::Exception::NoExecuteFault:
106 LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); 107 LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
107 ReturnException(pc, ARM_Interface::no_execute); 108 ReturnException(pc, PrefetchAbort);
108 return; 109 return;
109 default: 110 default:
110 if (debugger_enabled) { 111 if (debugger_enabled) {
111 ReturnException(pc, ARM_Interface::breakpoint); 112 ReturnException(pc, InstructionBreakpoint);
112 return; 113 return;
113 } 114 }
114 115
@@ -121,7 +122,7 @@ public:
121 122
122 void CallSVC(u32 swi) override { 123 void CallSVC(u32 swi) override {
123 parent.svc_swi = swi; 124 parent.svc_swi = swi;
124 parent.jit.load()->HaltExecution(ARM_Interface::svc_call); 125 parent.jit.load()->HaltExecution(SupervisorCall);
125 } 126 }
126 127
127 void AddTicks(u64 ticks) override { 128 void AddTicks(u64 ticks) override {
@@ -162,7 +163,7 @@ public:
162 if (!memory.IsValidVirtualAddressRange(addr, size)) { 163 if (!memory.IsValidVirtualAddressRange(addr, size)) {
163 LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", 164 LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
164 addr); 165 addr);
165 parent.jit.load()->HaltExecution(ARM_Interface::no_execute); 166 parent.jit.load()->HaltExecution(PrefetchAbort);
166 return false; 167 return false;
167 } 168 }
168 169
@@ -173,7 +174,7 @@ public:
173 const auto match{parent.MatchingWatchpoint(addr, size, type)}; 174 const auto match{parent.MatchingWatchpoint(addr, size, type)};
174 if (match) { 175 if (match) {
175 parent.halted_watchpoint = match; 176 parent.halted_watchpoint = match;
176 parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); 177 parent.jit.load()->HaltExecution(DataAbort);
177 return false; 178 return false;
178 } 179 }
179 180
@@ -329,12 +330,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
329 return std::make_unique<Dynarmic::A32::Jit>(config); 330 return std::make_unique<Dynarmic::A32::Jit>(config);
330} 331}
331 332
332Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() { 333HaltReason ARM_Dynarmic_32::RunJit() {
333 return jit.load()->Run(); 334 return TranslateHaltReason(jit.load()->Run());
334} 335}
335 336
336Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() { 337HaltReason ARM_Dynarmic_32::StepJit() {
337 return jit.load()->Step(); 338 return TranslateHaltReason(jit.load()->Step());
338} 339}
339 340
340u32 ARM_Dynarmic_32::GetSvcNumber() const { 341u32 ARM_Dynarmic_32::GetSvcNumber() const {
@@ -408,7 +409,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
408 cp15->uprw = static_cast<u32>(value); 409 cp15->uprw = static_cast<u32>(value);
409} 410}
410 411
411void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 412void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) const {
412 Dynarmic::A32::Jit* j = jit.load(); 413 Dynarmic::A32::Jit* j = jit.load();
413 ctx.cpu_registers = j->Regs(); 414 ctx.cpu_registers = j->Regs();
414 ctx.extension_registers = j->ExtRegs(); 415 ctx.extension_registers = j->ExtRegs();
@@ -425,11 +426,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
425} 426}
426 427
427void ARM_Dynarmic_32::SignalInterrupt() { 428void ARM_Dynarmic_32::SignalInterrupt() {
428 jit.load()->HaltExecution(break_loop); 429 jit.load()->HaltExecution(BreakLoop);
429} 430}
430 431
431void ARM_Dynarmic_32::ClearInterrupt() { 432void ARM_Dynarmic_32::ClearInterrupt() {
432 jit.load()->ClearHalt(break_loop); 433 jit.load()->ClearHalt(BreakLoop);
433} 434}
434 435
435void ARM_Dynarmic_32::ClearInstructionCache() { 436void ARM_Dynarmic_32::ClearInstructionCache() {
@@ -462,39 +463,4 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
462 jit_cache.emplace(key, std::move(new_jit)); 463 jit_cache.emplace(key, std::move(new_jit));
463} 464}
464 465
465std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
466 u64 fp, u64 lr, u64 pc) {
467 std::vector<BacktraceEntry> out;
468 auto& memory = system.ApplicationMemory();
469
470 out.push_back({"", 0, pc, 0, ""});
471
472 // fp (= r11) points to the last frame record.
473 // Frame records are two words long:
474 // fp+0 : pointer to previous frame record
475 // fp+4 : value of lr for frame
476 for (size_t i = 0; i < 256; i++) {
477 out.push_back({"", 0, lr, 0, ""});
478 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
479 break;
480 }
481 lr = memory.Read32(fp + 4);
482 fp = memory.Read32(fp);
483 }
484
485 SymbolicateBacktrace(system, out);
486
487 return out;
488}
489
490std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
491 System& system, const ThreadContext32& ctx) {
492 const auto& reg = ctx.cpu_registers;
493 return GetBacktrace(system, reg[11], reg[14], reg[15]);
494}
495
496std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
497 return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
498}
499
500} // namespace Core 466} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index bce695daf..a990845cb 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -50,8 +50,11 @@ public:
50 return (GetPSTATE() & 0x20) != 0; 50 return (GetPSTATE() & 0x20) != 0;
51 } 51 }
52 52
53 void SaveContext(ThreadContext32& ctx) override; 53 Architecture GetArchitecture() const override {
54 void SaveContext(ThreadContext64& ctx) override {} 54 return Architecture::Aarch32;
55 }
56 void SaveContext(ThreadContext32& ctx) const override;
57 void SaveContext(ThreadContext64& ctx) const override {}
55 void LoadContext(const ThreadContext32& ctx) override; 58 void LoadContext(const ThreadContext32& ctx) override;
56 void LoadContext(const ThreadContext64& ctx) override {} 59 void LoadContext(const ThreadContext64& ctx) override {}
57 60
@@ -64,14 +67,9 @@ public:
64 void PageTableChanged(Common::PageTable& new_page_table, 67 void PageTableChanged(Common::PageTable& new_page_table,
65 std::size_t new_address_space_size_in_bits) override; 68 std::size_t new_address_space_size_in_bits) override;
66 69
67 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
68 const ThreadContext32& ctx);
69
70 std::vector<BacktraceEntry> GetBacktrace() const override;
71
72protected: 70protected:
73 Dynarmic::HaltReason RunJit() override; 71 HaltReason RunJit() override;
74 Dynarmic::HaltReason StepJit() override; 72 HaltReason StepJit() override;
75 u32 GetSvcNumber() const override; 73 u32 GetSvcNumber() const override;
76 const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; 74 const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
77 void RewindBreakpointInstruction() override; 75 void RewindBreakpointInstruction() override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index bbbcb4f9d..bb97ed5bc 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -10,8 +10,9 @@
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/arm/dynarmic/arm_dynarmic.h"
13#include "core/arm/dynarmic/arm_dynarmic_64.h" 14#include "core/arm/dynarmic/arm_dynarmic_64.h"
14#include "core/arm/dynarmic/arm_exclusive_monitor.h" 15#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
15#include "core/core.h" 16#include "core/core.h"
16#include "core/core_timing.h" 17#include "core/core_timing.h"
17#include "core/debugger/debugger.h" 18#include "core/debugger/debugger.h"
@@ -113,7 +114,7 @@ public:
113 LOG_ERROR(Core_ARM, 114 LOG_ERROR(Core_ARM,
114 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 115 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
115 num_instructions, memory.Read32(pc)); 116 num_instructions, memory.Read32(pc));
116 ReturnException(pc, ARM_Interface::no_execute); 117 ReturnException(pc, PrefetchAbort);
117 } 118 }
118 119
119 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, 120 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -148,11 +149,11 @@ public:
148 return; 149 return;
149 case Dynarmic::A64::Exception::NoExecuteFault: 150 case Dynarmic::A64::Exception::NoExecuteFault:
150 LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); 151 LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
151 ReturnException(pc, ARM_Interface::no_execute); 152 ReturnException(pc, PrefetchAbort);
152 return; 153 return;
153 default: 154 default:
154 if (debugger_enabled) { 155 if (debugger_enabled) {
155 ReturnException(pc, ARM_Interface::breakpoint); 156 ReturnException(pc, InstructionBreakpoint);
156 return; 157 return;
157 } 158 }
158 159
@@ -164,7 +165,7 @@ public:
164 165
165 void CallSVC(u32 swi) override { 166 void CallSVC(u32 swi) override {
166 parent.svc_swi = swi; 167 parent.svc_swi = swi;
167 parent.jit.load()->HaltExecution(ARM_Interface::svc_call); 168 parent.jit.load()->HaltExecution(SupervisorCall);
168 } 169 }
169 170
170 void AddTicks(u64 ticks) override { 171 void AddTicks(u64 ticks) override {
@@ -207,7 +208,7 @@ public:
207 if (!memory.IsValidVirtualAddressRange(addr, size)) { 208 if (!memory.IsValidVirtualAddressRange(addr, size)) {
208 LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", 209 LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
209 addr); 210 addr);
210 parent.jit.load()->HaltExecution(ARM_Interface::no_execute); 211 parent.jit.load()->HaltExecution(PrefetchAbort);
211 return false; 212 return false;
212 } 213 }
213 214
@@ -218,7 +219,7 @@ public:
218 const auto match{parent.MatchingWatchpoint(addr, size, type)}; 219 const auto match{parent.MatchingWatchpoint(addr, size, type)};
219 if (match) { 220 if (match) {
220 parent.halted_watchpoint = match; 221 parent.halted_watchpoint = match;
221 parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); 222 parent.jit.load()->HaltExecution(DataAbort);
222 return false; 223 return false;
223 } 224 }
224 225
@@ -383,12 +384,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
383 return std::make_shared<Dynarmic::A64::Jit>(config); 384 return std::make_shared<Dynarmic::A64::Jit>(config);
384} 385}
385 386
386Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() { 387HaltReason ARM_Dynarmic_64::RunJit() {
387 return jit.load()->Run(); 388 return TranslateHaltReason(jit.load()->Run());
388} 389}
389 390
390Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() { 391HaltReason ARM_Dynarmic_64::StepJit() {
391 return jit.load()->Step(); 392 return TranslateHaltReason(jit.load()->Step());
392} 393}
393 394
394u32 ARM_Dynarmic_64::GetSvcNumber() const { 395u32 ARM_Dynarmic_64::GetSvcNumber() const {
@@ -464,7 +465,7 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
464 cb->tpidr_el0 = value; 465 cb->tpidr_el0 = value;
465} 466}
466 467
467void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 468void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) const {
468 Dynarmic::A64::Jit* j = jit.load(); 469 Dynarmic::A64::Jit* j = jit.load();
469 ctx.cpu_registers = j->GetRegisters(); 470 ctx.cpu_registers = j->GetRegisters();
470 ctx.sp = j->GetSP(); 471 ctx.sp = j->GetSP();
@@ -489,11 +490,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
489} 490}
490 491
491void ARM_Dynarmic_64::SignalInterrupt() { 492void ARM_Dynarmic_64::SignalInterrupt() {
492 jit.load()->HaltExecution(break_loop); 493 jit.load()->HaltExecution(BreakLoop);
493} 494}
494 495
495void ARM_Dynarmic_64::ClearInterrupt() { 496void ARM_Dynarmic_64::ClearInterrupt() {
496 jit.load()->ClearHalt(break_loop); 497 jit.load()->ClearHalt(BreakLoop);
497} 498}
498 499
499void ARM_Dynarmic_64::ClearInstructionCache() { 500void ARM_Dynarmic_64::ClearInstructionCache() {
@@ -526,39 +527,4 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
526 jit_cache.emplace(key, std::move(new_jit)); 527 jit_cache.emplace(key, std::move(new_jit));
527} 528}
528 529
529std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
530 u64 fp, u64 lr, u64 pc) {
531 std::vector<BacktraceEntry> out;
532 auto& memory = system.ApplicationMemory();
533
534 out.push_back({"", 0, pc, 0, ""});
535
536 // fp (= x29) points to the previous frame record.
537 // Frame records are two words long:
538 // fp+0 : pointer to previous frame record
539 // fp+8 : value of lr for frame
540 for (size_t i = 0; i < 256; i++) {
541 out.push_back({"", 0, lr, 0, ""});
542 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
543 break;
544 }
545 lr = memory.Read64(fp + 8);
546 fp = memory.Read64(fp);
547 }
548
549 SymbolicateBacktrace(system, out);
550
551 return out;
552}
553
554std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
555 System& system, const ThreadContext64& ctx) {
556 const auto& reg = ctx.cpu_registers;
557 return GetBacktrace(system, reg[29], reg[30], ctx.pc);
558}
559
560std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
561 return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
562}
563
564} // namespace Core 530} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index e83599e82..af2aa1f1c 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -43,8 +43,11 @@ public:
43 void SetTPIDR_EL0(u64 value) override; 43 void SetTPIDR_EL0(u64 value) override;
44 u64 GetTPIDR_EL0() const override; 44 u64 GetTPIDR_EL0() const override;
45 45
46 void SaveContext(ThreadContext32& ctx) override {} 46 Architecture GetArchitecture() const override {
47 void SaveContext(ThreadContext64& ctx) override; 47 return Architecture::Aarch64;
48 }
49 void SaveContext(ThreadContext32& ctx) const override {}
50 void SaveContext(ThreadContext64& ctx) const override;
48 void LoadContext(const ThreadContext32& ctx) override {} 51 void LoadContext(const ThreadContext32& ctx) override {}
49 void LoadContext(const ThreadContext64& ctx) override; 52 void LoadContext(const ThreadContext64& ctx) override;
50 53
@@ -57,14 +60,9 @@ public:
57 void PageTableChanged(Common::PageTable& new_page_table, 60 void PageTableChanged(Common::PageTable& new_page_table,
58 std::size_t new_address_space_size_in_bits) override; 61 std::size_t new_address_space_size_in_bits) override;
59 62
60 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
61 const ThreadContext64& ctx);
62
63 std::vector<BacktraceEntry> GetBacktrace() const override;
64
65protected: 63protected:
66 Dynarmic::HaltReason RunJit() override; 64 HaltReason RunJit() override;
67 Dynarmic::HaltReason StepJit() override; 65 HaltReason StepJit() override;
68 u32 GetSvcNumber() const override; 66 u32 GetSvcNumber() const override;
69 const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; 67 const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
70 void RewindBreakpointInstruction() override; 68 void RewindBreakpointInstruction() override;
@@ -73,8 +71,6 @@ private:
73 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 71 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
74 std::size_t address_space_bits) const; 72 std::size_t address_space_bits) const;
75 73
76 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
77
78 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 74 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
79 using JitCacheType = 75 using JitCacheType =
80 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; 76 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/dynarmic_cp15.cpp
index 5a4eba3eb..92c548db0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/dynarmic_cp15.cpp
@@ -4,7 +4,7 @@
4#include <fmt/format.h> 4#include <fmt/format.h>
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/arm/dynarmic/arm_dynarmic_32.h" 6#include "core/arm/dynarmic/arm_dynarmic_32.h"
7#include "core/arm/dynarmic/arm_dynarmic_cp15.h" 7#include "core/arm/dynarmic/dynarmic_cp15.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10 10
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/dynarmic_cp15.h
index d90b3e568..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/dynarmic_cp15.h
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.cpp
index fa0c48b25..b5c9c43c4 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
+++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.cpp
@@ -1,7 +1,7 @@
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 "core/arm/dynarmic/arm_exclusive_monitor.h" 4#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
5#include "core/memory.h" 5#include "core/memory.h"
6 6
7namespace Core { 7namespace Core {
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h
index 57e6dd0d0..57e6dd0d0 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index 20550faeb..6d9a862e1 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -2,7 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) 4#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
5#include "core/arm/dynarmic/arm_exclusive_monitor.h" 5#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
6#endif 6#endif
7#include "core/arm/exclusive_monitor.h" 7#include "core/arm/exclusive_monitor.h"
8#include "core/memory.h" 8#include "core/memory.h"
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7ba704f18..b74fd0a58 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -54,10 +54,10 @@
54#include "video_core/renderer_base.h" 54#include "video_core/renderer_base.h"
55#include "video_core/video_core.h" 55#include "video_core/video_core.h"
56 56
57MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU0, "ARM JIT", "Dynarmic CPU 0", MP_RGB(255, 64, 64)); 57MICROPROFILE_DEFINE(ARM_CPU0, "ARM", "CPU 0", MP_RGB(255, 64, 64));
58MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU1, "ARM JIT", "Dynarmic CPU 1", MP_RGB(255, 64, 64)); 58MICROPROFILE_DEFINE(ARM_CPU1, "ARM", "CPU 1", MP_RGB(255, 64, 64));
59MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU2, "ARM JIT", "Dynarmic CPU 2", MP_RGB(255, 64, 64)); 59MICROPROFILE_DEFINE(ARM_CPU2, "ARM", "CPU 2", MP_RGB(255, 64, 64));
60MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU3, "ARM JIT", "Dynarmic CPU 3", MP_RGB(255, 64, 64)); 60MICROPROFILE_DEFINE(ARM_CPU3, "ARM", "CPU 3", MP_RGB(255, 64, 64));
61 61
62namespace Core { 62namespace Core {
63 63
@@ -259,10 +259,10 @@ struct System::Impl {
259 is_powered_on = true; 259 is_powered_on = true;
260 exit_lock = false; 260 exit_lock = false;
261 261
262 microprofile_dynarmic[0] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU0); 262 microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0);
263 microprofile_dynarmic[1] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU1); 263 microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1);
264 microprofile_dynarmic[2] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU2); 264 microprofile_cpu[2] = MICROPROFILE_TOKEN(ARM_CPU2);
265 microprofile_dynarmic[3] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU3); 265 microprofile_cpu[3] = MICROPROFILE_TOKEN(ARM_CPU3);
266 266
267 LOG_DEBUG(Core, "Initialized OK"); 267 LOG_DEBUG(Core, "Initialized OK");
268 268
@@ -539,7 +539,7 @@ struct System::Impl {
539 ExitCallback exit_callback; 539 ExitCallback exit_callback;
540 540
541 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 541 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
542 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; 542 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
543}; 543};
544 544
545System::System() : impl{std::make_unique<Impl>(*this)} {} 545System::System() : impl{std::make_unique<Impl>(*this)} {}
@@ -927,14 +927,14 @@ void System::RegisterHostThread() {
927 impl->kernel.RegisterHostThread(); 927 impl->kernel.RegisterHostThread();
928} 928}
929 929
930void System::EnterDynarmicProfile() { 930void System::EnterCPUProfile() {
931 std::size_t core = impl->kernel.GetCurrentHostThreadID(); 931 std::size_t core = impl->kernel.GetCurrentHostThreadID();
932 impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_dynarmic[core]); 932 impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_cpu[core]);
933} 933}
934 934
935void System::ExitDynarmicProfile() { 935void System::ExitCPUProfile() {
936 std::size_t core = impl->kernel.GetCurrentHostThreadID(); 936 std::size_t core = impl->kernel.GetCurrentHostThreadID();
937 MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]); 937 MicroProfileLeave(impl->microprofile_cpu[core], impl->dynarmic_ticks[core]);
938} 938}
939 939
940bool System::IsMulticore() const { 940bool System::IsMulticore() const {
diff --git a/src/core/core.h b/src/core/core.h
index ff2e4bd30..93afc9303 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -412,11 +412,11 @@ public:
412 /// Register a host thread as an auxiliary thread. 412 /// Register a host thread as an auxiliary thread.
413 void RegisterHostThread(); 413 void RegisterHostThread();
414 414
415 /// Enter Dynarmic Microprofile 415 /// Enter CPU Microprofile
416 void EnterDynarmicProfile(); 416 void EnterCPUProfile();
417 417
418 /// Exit Dynarmic Microprofile 418 /// Exit CPU Microprofile
419 void ExitDynarmicProfile(); 419 void ExitCPUProfile();
420 420
421 /// Tells if system is running on multicore. 421 /// Tells if system is running on multicore.
422 [[nodiscard]] bool IsMulticore() const; 422 [[nodiscard]] bool IsMulticore() const;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 4f2692b05..4f0a3f8ea 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -16,12 +16,11 @@
16 16
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "core/core_timing.h" 18#include "core/core_timing.h"
19#include "core/core_timing_util.h"
20#include "core/hardware_properties.h" 19#include "core/hardware_properties.h"
21 20
22namespace Core::Timing { 21namespace Core::Timing {
23 22
24constexpr s64 MAX_SLICE_LENGTH = 4000; 23constexpr s64 MAX_SLICE_LENGTH = 10000;
25 24
26std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 25std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
27 return std::make_shared<EventType>(std::move(callback), std::move(name)); 26 return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -45,9 +44,7 @@ struct CoreTiming::Event {
45 } 44 }
46}; 45};
47 46
48CoreTiming::CoreTiming() 47CoreTiming::CoreTiming() : clock{Common::CreateOptimalClock()} {}
49 : cpu_clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)},
50 event_clock{Common::CreateStandardWallClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
51 48
52CoreTiming::~CoreTiming() { 49CoreTiming::~CoreTiming() {
53 Reset(); 50 Reset();
@@ -68,7 +65,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
68 on_thread_init = std::move(on_thread_init_); 65 on_thread_init = std::move(on_thread_init_);
69 event_fifo_id = 0; 66 event_fifo_id = 0;
70 shutting_down = false; 67 shutting_down = false;
71 ticks = 0; 68 cpu_ticks = 0;
72 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) 69 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
73 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; 70 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
74 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 71 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
@@ -173,38 +170,30 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
173} 170}
174 171
175void CoreTiming::AddTicks(u64 ticks_to_add) { 172void CoreTiming::AddTicks(u64 ticks_to_add) {
176 ticks += ticks_to_add; 173 cpu_ticks += ticks_to_add;
177 downcount -= static_cast<s64>(ticks); 174 downcount -= static_cast<s64>(cpu_ticks);
178} 175}
179 176
180void CoreTiming::Idle() { 177void CoreTiming::Idle() {
181 if (!event_queue.empty()) { 178 cpu_ticks += 1000U;
182 const u64 next_event_time = event_queue.front().time;
183 const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U;
184 if (next_ticks > ticks) {
185 ticks = next_ticks;
186 }
187 return;
188 }
189 ticks += 1000U;
190} 179}
191 180
192void CoreTiming::ResetTicks() { 181void CoreTiming::ResetTicks() {
193 downcount = MAX_SLICE_LENGTH; 182 downcount = MAX_SLICE_LENGTH;
194} 183}
195 184
196u64 CoreTiming::GetCPUTicks() const { 185u64 CoreTiming::GetClockTicks() const {
197 if (is_multicore) [[likely]] { 186 if (is_multicore) [[likely]] {
198 return cpu_clock->GetCPUCycles(); 187 return clock->GetCNTPCT();
199 } 188 }
200 return ticks; 189 return Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
201} 190}
202 191
203u64 CoreTiming::GetClockTicks() const { 192u64 CoreTiming::GetGPUTicks() const {
204 if (is_multicore) [[likely]] { 193 if (is_multicore) [[likely]] {
205 return cpu_clock->GetClockCycles(); 194 return clock->GetGPUTick();
206 } 195 }
207 return CpuCyclesToClockCycles(ticks); 196 return Common::WallClock::CPUTickToGPUTick(cpu_ticks);
208} 197}
209 198
210std::optional<s64> CoreTiming::Advance() { 199std::optional<s64> CoreTiming::Advance() {
@@ -297,9 +286,7 @@ void CoreTiming::ThreadLoop() {
297 } 286 }
298 287
299 paused_set = true; 288 paused_set = true;
300 event_clock->Pause(true);
301 pause_event.Wait(); 289 pause_event.Wait();
302 event_clock->Pause(false);
303 } 290 }
304} 291}
305 292
@@ -315,25 +302,18 @@ void CoreTiming::Reset() {
315 has_started = false; 302 has_started = false;
316} 303}
317 304
318std::chrono::nanoseconds CoreTiming::GetCPUTimeNs() const {
319 if (is_multicore) [[likely]] {
320 return cpu_clock->GetTimeNS();
321 }
322 return CyclesToNs(ticks);
323}
324
325std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { 305std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
326 if (is_multicore) [[likely]] { 306 if (is_multicore) [[likely]] {
327 return event_clock->GetTimeNS(); 307 return clock->GetTimeNS();
328 } 308 }
329 return CyclesToNs(ticks); 309 return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
330} 310}
331 311
332std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { 312std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
333 if (is_multicore) [[likely]] { 313 if (is_multicore) [[likely]] {
334 return event_clock->GetTimeUS(); 314 return clock->GetTimeUS();
335 } 315 }
336 return CyclesToUs(ticks); 316 return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
337} 317}
338 318
339} // namespace Core::Timing 319} // namespace Core::Timing
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index e7c4a949f..10db1de55 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -116,14 +116,11 @@ public:
116 return downcount; 116 return downcount;
117 } 117 }
118 118
119 /// Returns current time in emulated CPU cycles 119 /// Returns the current CNTPCT tick value.
120 u64 GetCPUTicks() const;
121
122 /// Returns current time in emulated in Clock cycles
123 u64 GetClockTicks() const; 120 u64 GetClockTicks() const;
124 121
125 /// Returns current time in nanoseconds. 122 /// Returns the current GPU tick value.
126 std::chrono::nanoseconds GetCPUTimeNs() const; 123 u64 GetGPUTicks() const;
127 124
128 /// Returns current time in microseconds. 125 /// Returns current time in microseconds.
129 std::chrono::microseconds GetGlobalTimeUs() const; 126 std::chrono::microseconds GetGlobalTimeUs() const;
@@ -142,8 +139,7 @@ private:
142 139
143 void Reset(); 140 void Reset();
144 141
145 std::unique_ptr<Common::WallClock> cpu_clock; 142 std::unique_ptr<Common::WallClock> clock;
146 std::unique_ptr<Common::WallClock> event_clock;
147 143
148 s64 global_timer = 0; 144 s64 global_timer = 0;
149 145
@@ -171,7 +167,7 @@ private:
171 s64 pause_end_time{}; 167 s64 pause_end_time{};
172 168
173 /// Cycle timing 169 /// Cycle timing
174 u64 ticks{}; 170 u64 cpu_ticks{};
175 s64 downcount{}; 171 s64 downcount{};
176}; 172};
177 173
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
deleted file mode 100644
index fe5aaefc7..000000000
--- a/src/core/core_timing_util.h
+++ /dev/null
@@ -1,58 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "common/common_types.h"
9#include "core/hardware_properties.h"
10
11namespace Core::Timing {
12
13namespace detail {
14constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000;
15constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000;
16} // namespace detail
17
18[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) {
19 return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
20}
21
22[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
23 return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
24}
25
26[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
27 return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
28}
29
30[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
31 return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
32}
33
34[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
35 return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
36}
37
38[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
39 return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
40}
41
42[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
43 return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
44}
45
46[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
47 return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
48}
49
50[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
51 return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
52}
53
54[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
55 return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
56}
57
58} // namespace Core::Timing
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 4e61d4335..d3286b352 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -153,7 +153,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
153 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 153 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
154 154
155 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; 155 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
156 if (load_dir != nullptr && load_dir->GetSize() > 0) { 156 if (load_dir != nullptr) {
157 const auto load_patch_dirs = load_dir->GetSubdirectories(); 157 const auto load_patch_dirs = load_dir->GetSubdirectories();
158 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); 158 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
159 } 159 }
@@ -354,8 +354,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
354 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 354 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
355 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 355 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
356 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 356 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
357 ((load_dir == nullptr || load_dir->GetSize() <= 0) && 357 (load_dir == nullptr && sdmc_load_dir == nullptr)) {
358 (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) {
359 return; 358 return;
360 } 359 }
361 360
@@ -496,7 +495,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
496 495
497 // General Mods (LayeredFS and IPS) 496 // General Mods (LayeredFS and IPS)
498 const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id); 497 const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
499 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 498 if (mod_dir != nullptr) {
500 for (const auto& mod : mod_dir->GetSubdirectories()) { 499 for (const auto& mod : mod_dir->GetSubdirectories()) {
501 std::string types; 500 std::string types;
502 501
@@ -540,7 +539,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
540 539
541 // SDMC mod directory (RomFS LayeredFS) 540 // SDMC mod directory (RomFS LayeredFS)
542 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 541 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
543 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { 542 if (sdmc_mod_dir != nullptr) {
544 std::string types; 543 std::string types;
545 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { 544 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
546 AppendCommaIfNotEmpty(types, "LayeredExeFS"); 545 AppendCommaIfNotEmpty(types, "LayeredExeFS");
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 3226b884a..27f97c725 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -8,6 +8,7 @@
8#include <set> 8#include <set>
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
12 13
13namespace Core::Crypto { 14namespace Core::Crypto {
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index 85383998d..7c17bbefa 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -1,7 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 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 <vector> 4#include <vector>
6 5
7#include "common/swap.h" 6#include "common/swap.h"
@@ -9,656 +8,79 @@
9#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs_vector.h"
10#include "core/hle/service/time/time_zone_types.h" 9#include "core/hle/service/time/time_zone_types.h"
11 10
12namespace FileSys::SystemArchive { 11#include "nx_tzdb.h"
13
14static constexpr std::array<u8, 9633> LOCATION_NAMES{
15 0x43, 0x45, 0x54, 0x0d, 0x0a, 0x43, 0x53, 0x54, 0x36, 0x43, 0x44, 0x54, 0x0d, 0x0a, 0x43, 0x75,
16 0x62, 0x61, 0x0d, 0x0a, 0x45, 0x45, 0x54, 0x0d, 0x0a, 0x45, 0x67, 0x79, 0x70, 0x74, 0x0d, 0x0a,
17 0x45, 0x69, 0x72, 0x65, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45,
18 0x44, 0x54, 0x0d, 0x0a, 0x47, 0x42, 0x0d, 0x0a, 0x47, 0x42, 0x2d, 0x45, 0x69, 0x72, 0x65, 0x0d,
19 0x0a, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54,
20 0x2d, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77,
21 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x48, 0x6f, 0x6e, 0x67, 0x6b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x48,
22 0x53, 0x54, 0x0d, 0x0a, 0x49, 0x63, 0x65, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x49, 0x72, 0x61,
23 0x6e, 0x0d, 0x0a, 0x49, 0x73, 0x72, 0x61, 0x65, 0x6c, 0x0d, 0x0a, 0x4a, 0x61, 0x6d, 0x61, 0x69,
24 0x63, 0x61, 0x0d, 0x0a, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x4b, 0x77, 0x61, 0x6a, 0x61,
25 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x4c, 0x69, 0x62, 0x79, 0x61, 0x0d, 0x0a, 0x4d, 0x45, 0x54,
26 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x37, 0x4d, 0x44, 0x54, 0x0d, 0x0a,
27 0x4e, 0x61, 0x76, 0x61, 0x6a, 0x6f, 0x0d, 0x0a, 0x4e, 0x5a, 0x0d, 0x0a, 0x4e, 0x5a, 0x2d, 0x43,
28 0x48, 0x41, 0x54, 0x0d, 0x0a, 0x50, 0x6f, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x50, 0x6f, 0x72,
29 0x74, 0x75, 0x67, 0x61, 0x6c, 0x0d, 0x0a, 0x50, 0x52, 0x43, 0x0d, 0x0a, 0x50, 0x53, 0x54, 0x38,
30 0x50, 0x44, 0x54, 0x0d, 0x0a, 0x52, 0x4f, 0x43, 0x0d, 0x0a, 0x52, 0x4f, 0x4b, 0x0d, 0x0a, 0x53,
31 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x54, 0x75, 0x72, 0x6b, 0x65, 0x79,
32 0x0d, 0x0a, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c,
33 0x0d, 0x0a, 0x55, 0x54, 0x43, 0x0d, 0x0a, 0x57, 0x2d, 0x53, 0x55, 0x0d, 0x0a, 0x57, 0x45, 0x54,
34 0x0d, 0x0a, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
35 0x62, 0x69, 0x64, 0x6a, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
36 0x63, 0x63, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x64,
37 0x69, 0x73, 0x5f, 0x41, 0x62, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
38 0x2f, 0x41, 0x6c, 0x67, 0x69, 0x65, 0x72, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
39 0x2f, 0x41, 0x73, 0x6d, 0x61, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
40 0x41, 0x73, 0x6d, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42,
41 0x61, 0x6d, 0x61, 0x6b, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61,
42 0x6e, 0x67, 0x75, 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x6e,
43 0x6a, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x73,
44 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x74,
45 0x79, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x72, 0x61, 0x7a,
46 0x7a, 0x61, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
47 0x42, 0x75, 0x6a, 0x75, 0x6d, 0x62, 0x75, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63,
48 0x61, 0x2f, 0x43, 0x61, 0x69, 0x72, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
49 0x43, 0x61, 0x73, 0x61, 0x62, 0x6c, 0x61, 0x6e, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
50 0x63, 0x61, 0x2f, 0x43, 0x65, 0x75, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
51 0x2f, 0x43, 0x6f, 0x6e, 0x61, 0x6b, 0x72, 0x79, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
52 0x2f, 0x44, 0x61, 0x6b, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44,
53 0x61, 0x72, 0x5f, 0x65, 0x73, 0x5f, 0x53, 0x61, 0x6c, 0x61, 0x61, 0x6d, 0x0d, 0x0a, 0x41, 0x66,
54 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6a, 0x69, 0x62, 0x6f, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x41,
55 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x75, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x66,
56 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x41, 0x61, 0x69, 0x75, 0x6e, 0x0d, 0x0a, 0x41,
57 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x72, 0x65, 0x65, 0x74, 0x6f, 0x77, 0x6e, 0x0d, 0x0a,
58 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x61, 0x62, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x0d,
59 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x61, 0x72, 0x65, 0x0d, 0x0a,
60 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x6f, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x62,
61 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x75, 0x62, 0x61,
62 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x70, 0x61, 0x6c, 0x61,
63 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x68, 0x61, 0x72, 0x74, 0x6f, 0x75,
64 0x6d, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x67, 0x61, 0x6c, 0x69,
65 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x6e, 0x73, 0x68, 0x61, 0x73,
66 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x67, 0x6f, 0x73, 0x0d,
67 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x76, 0x69, 0x6c,
68 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x6d, 0x65, 0x0d,
69 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x61, 0x6e, 0x64, 0x61, 0x0d, 0x0a,
70 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x62, 0x75, 0x6d, 0x62, 0x61, 0x73, 0x68,
71 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x73, 0x61, 0x6b, 0x61,
72 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6c, 0x61, 0x62, 0x6f, 0x0d,
73 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x70, 0x75, 0x74, 0x6f, 0x0d, 0x0a,
74 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x73, 0x65, 0x72, 0x75, 0x0d, 0x0a, 0x41,
75 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x62, 0x61, 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41,
76 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x67, 0x61, 0x64, 0x69, 0x73, 0x68, 0x75, 0x0d,
77 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x72, 0x6f, 0x76, 0x69, 0x61,
78 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x69, 0x72, 0x6f, 0x62, 0x69,
79 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x64, 0x6a, 0x61, 0x6d, 0x65, 0x6e,
80 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x61, 0x6d, 0x65, 0x79,
81 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x75, 0x61, 0x6b, 0x63, 0x68,
82 0x6f, 0x74, 0x74, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x75, 0x61, 0x67,
83 0x61, 0x64, 0x6f, 0x75, 0x67, 0x6f, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
84 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x2d, 0x4e, 0x6f, 0x76, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
85 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f, 0x5f, 0x54, 0x6f, 0x6d, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72,
86 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6d, 0x62, 0x75, 0x6b, 0x74, 0x75, 0x0d, 0x0a, 0x41, 0x66,
87 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x72, 0x69, 0x70, 0x6f, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x66,
88 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x75, 0x6e, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
89 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x64, 0x68, 0x6f, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
90 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x61, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
91 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0d, 0x0a, 0x41, 0x6d,
92 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x67, 0x75, 0x69, 0x6c, 0x6c, 0x61, 0x0d, 0x0a,
93 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x74, 0x69, 0x67, 0x75, 0x61, 0x0d,
94 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x61, 0x67, 0x75, 0x61, 0x69,
95 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x75, 0x62,
96 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x73, 0x75, 0x6e, 0x63,
97 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x74, 0x69,
98 0x6b, 0x6f, 0x6b, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
99 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68,
100 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x69,
101 0x61, 0x5f, 0x42, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
102 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x72, 0x62, 0x61, 0x64, 0x6f, 0x73, 0x0d, 0x0a, 0x41, 0x6d,
103 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
104 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x69, 0x7a, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
105 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x63, 0x2d, 0x53, 0x61, 0x62, 0x6c, 0x6f,
106 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f, 0x61, 0x5f, 0x56,
107 0x69, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f,
108 0x67, 0x6f, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f,
109 0x69, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x75, 0x65,
110 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
111 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x42, 0x61, 0x79,
112 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x70, 0x6f, 0x5f,
113 0x47, 0x72, 0x61, 0x6e, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
114 0x43, 0x61, 0x6e, 0x63, 0x75, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
115 0x43, 0x61, 0x72, 0x61, 0x63, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
116 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
117 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x65, 0x6e, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
118 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
119 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x63, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
120 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x68, 0x75, 0x61, 0x68, 0x75, 0x61, 0x0d,
121 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x61, 0x6c, 0x5f, 0x48,
122 0x61, 0x72, 0x62, 0x6f, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
123 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
124 0x2f, 0x43, 0x6f, 0x73, 0x74, 0x61, 0x5f, 0x52, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
125 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d,
126 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x69, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d,
127 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x61, 0x63, 0x61, 0x6f, 0x0d, 0x0a, 0x41,
128 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x6e, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x68,
129 0x61, 0x76, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77,
130 0x73, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77,
131 0x73, 0x6f, 0x6e, 0x5f, 0x43, 0x72, 0x65, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
132 0x63, 0x61, 0x2f, 0x44, 0x65, 0x6e, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
133 0x63, 0x61, 0x2f, 0x44, 0x65, 0x74, 0x72, 0x6f, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
134 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x6d, 0x69, 0x6e, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d,
135 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x64, 0x6d, 0x6f, 0x6e, 0x74, 0x6f, 0x6e, 0x0d, 0x0a,
136 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x69, 0x72, 0x75, 0x6e, 0x65, 0x70, 0x65,
137 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x53, 0x61, 0x6c,
138 0x76, 0x61, 0x64, 0x6f, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45,
139 0x6e, 0x73, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
140 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x65, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
141 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x4e, 0x65, 0x6c, 0x73, 0x6f, 0x6e, 0x0d,
142 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x57, 0x61,
143 0x79, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x6c, 0x61,
144 0x63, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
145 0x47, 0x6f, 0x64, 0x74, 0x68, 0x61, 0x62, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
146 0x2f, 0x47, 0x6f, 0x6f, 0x73, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
147 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x54, 0x75, 0x72, 0x6b, 0x0d, 0x0a,
148 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d,
149 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x65, 0x6c, 0x6f,
150 0x75, 0x70, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61,
151 0x74, 0x65, 0x6d, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
152 0x47, 0x75, 0x61, 0x79, 0x61, 0x71, 0x75, 0x69, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
153 0x63, 0x61, 0x2f, 0x47, 0x75, 0x79, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
154 0x63, 0x61, 0x2f, 0x48, 0x61, 0x6c, 0x69, 0x66, 0x61, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
155 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x76, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
156 0x69, 0x63, 0x61, 0x2f, 0x48, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x69, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a,
157 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70,
158 0x6f, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e,
159 0x75, 0x76, 0x69, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x71,
160 0x61, 0x6c, 0x75, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a,
161 0x61, 0x6d, 0x61, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
162 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a,
163 0x75, 0x6e, 0x65, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b,
164 0x6e, 0x6f, 0x78, 0x5f, 0x49, 0x4e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
165 0x4b, 0x72, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6a, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
166 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x50, 0x61, 0x7a, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
167 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
168 0x61, 0x2f, 0x4c, 0x6f, 0x73, 0x5f, 0x41, 0x6e, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x0d, 0x0a, 0x41,
169 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c,
170 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x77, 0x65, 0x72,
171 0x5f, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
172 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
173 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x75, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
174 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
175 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x67, 0x6f, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
176 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x0d, 0x0a,
177 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x74, 0x61, 0x6d, 0x6f, 0x72, 0x6f,
178 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x7a, 0x61, 0x74,
179 0x6c, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x6e,
180 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65,
181 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
182 0x2f, 0x4d, 0x65, 0x72, 0x69, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
183 0x2f, 0x4d, 0x65, 0x74, 0x6c, 0x61, 0x6b, 0x61, 0x74, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
184 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x5f, 0x43, 0x69, 0x74, 0x79,
185 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x69, 0x71, 0x75, 0x65, 0x6c,
186 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x63,
187 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e,
188 0x74, 0x65, 0x72, 0x72, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
189 0x4d, 0x6f, 0x6e, 0x74, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
190 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x72, 0x65, 0x61, 0x6c, 0x0d, 0x0a, 0x41, 0x6d,
191 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x73, 0x65, 0x72, 0x72, 0x61, 0x74,
192 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x73, 0x73, 0x61, 0x75,
193 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x5f, 0x59, 0x6f,
194 0x72, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x70, 0x69,
195 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x6d,
196 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x6f, 0x6e,
197 0x68, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x6a, 0x69, 0x6e,
198 0x61, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e,
199 0x61, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e,
200 0x67, 0x6e, 0x69, 0x72, 0x74, 0x75, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
201 0x61, 0x2f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x61, 0x72, 0x69, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
202 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x68, 0x6f, 0x65, 0x6e, 0x69, 0x78, 0x0d, 0x0a, 0x41,
203 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x2d, 0x61, 0x75, 0x2d, 0x50,
204 0x72, 0x69, 0x6e, 0x63, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50,
205 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
206 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x56, 0x65, 0x6c, 0x68, 0x6f, 0x0d, 0x0a,
207 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x6f, 0x66, 0x5f,
208 0x53, 0x70, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50,
209 0x75, 0x65, 0x72, 0x74, 0x6f, 0x5f, 0x52, 0x69, 0x63, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
210 0x69, 0x63, 0x61, 0x2f, 0x50, 0x75, 0x6e, 0x74, 0x61, 0x5f, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73,
211 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x61, 0x69, 0x6e, 0x79, 0x5f,
212 0x52, 0x69, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52,
213 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x5f, 0x49, 0x6e, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
214 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x63, 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
215 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
216 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x0d, 0x0a, 0x41,
217 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x42, 0x72, 0x61, 0x6e, 0x63,
218 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x73, 0x61, 0x72,
219 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74,
220 0x61, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61,
221 0x6e, 0x74, 0x61, 0x5f, 0x49, 0x73, 0x61, 0x62, 0x65, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
222 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
223 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x6f, 0x5f, 0x44, 0x6f, 0x6d, 0x69,
224 0x6e, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f,
225 0x5f, 0x50, 0x61, 0x75, 0x6c, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
226 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x73, 0x75, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x6d,
227 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x68, 0x69, 0x70, 0x72, 0x6f, 0x63, 0x6b, 0x0d, 0x0a,
228 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x69, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41,
229 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x42, 0x61, 0x72, 0x74, 0x68, 0x65,
230 0x6c, 0x65, 0x6d, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74,
231 0x5f, 0x4a, 0x6f, 0x68, 0x6e, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
232 0x53, 0x74, 0x5f, 0x4b, 0x69, 0x74, 0x74, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
233 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x4c, 0x75, 0x63, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
234 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x54, 0x68, 0x6f, 0x6d, 0x61, 0x73, 0x0d, 0x0a, 0x41,
235 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e,
236 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x77, 0x69, 0x66, 0x74,
237 0x5f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
238 0x61, 0x2f, 0x54, 0x65, 0x67, 0x75, 0x63, 0x69, 0x67, 0x61, 0x6c, 0x70, 0x61, 0x0d, 0x0a, 0x41,
239 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6d,
240 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x42, 0x61,
241 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6a, 0x75, 0x61,
242 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72, 0x6f,
243 0x6e, 0x74, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72,
244 0x74, 0x6f, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x56, 0x61,
245 0x6e, 0x63, 0x6f, 0x75, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
246 0x2f, 0x56, 0x69, 0x72, 0x67, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
247 0x2f, 0x57, 0x68, 0x69, 0x74, 0x65, 0x68, 0x6f, 0x72, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
248 0x72, 0x69, 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x6e, 0x69, 0x70, 0x65, 0x67, 0x0d, 0x0a, 0x41,
249 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x61, 0x74, 0x0d, 0x0a,
250 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6b, 0x6e,
251 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67,
252 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x42, 0x75, 0x65, 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69,
253 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67,
254 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61,
255 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74,
256 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64, 0x52, 0x69, 0x76, 0x61, 0x64, 0x61, 0x76,
257 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
258 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41,
259 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
260 0x2f, 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
261 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x52, 0x69, 0x6f,
262 0x6a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
263 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4d, 0x65, 0x6e, 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41,
264 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
265 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x47, 0x61, 0x6c, 0x6c, 0x65, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x41,
266 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
267 0x2f, 0x53, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
268 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4a, 0x75,
269 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
270 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4c, 0x75, 0x69, 0x73, 0x0d, 0x0a,
271 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e,
272 0x61, 0x2f, 0x54, 0x75, 0x63, 0x75, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
273 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x55, 0x73, 0x68,
274 0x75, 0x61, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e,
275 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70, 0x6f, 0x6c,
276 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69,
277 0x61, 0x6e, 0x61, 0x2f, 0x4b, 0x6e, 0x6f, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
278 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x65, 0x6e, 0x67,
279 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61,
280 0x6e, 0x61, 0x2f, 0x50, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41,
281 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x54,
282 0x65, 0x6c, 0x6c, 0x5f, 0x43, 0x69, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
283 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x56, 0x65, 0x76, 0x61, 0x79, 0x0d,
284 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61,
285 0x2f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
286 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x61,
287 0x6d, 0x61, 0x63, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e,
288 0x74, 0x75, 0x63, 0x6b, 0x79, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c, 0x65,
289 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e, 0x74, 0x75, 0x63,
290 0x6b, 0x79, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x69, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a, 0x41,
291 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b,
292 0x6f, 0x74, 0x61, 0x2f, 0x42, 0x65, 0x75, 0x6c, 0x61, 0x68, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
293 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61,
294 0x2f, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
295 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61, 0x2f, 0x4e, 0x65,
296 0x77, 0x5f, 0x53, 0x61, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74,
297 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72,
298 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x76, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6e, 0x74,
299 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x75, 0x6d, 0x6f, 0x6e, 0x74, 0x44, 0x55,
300 0x72, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69,
301 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x71, 0x75, 0x61, 0x72, 0x69, 0x65, 0x0d, 0x0a, 0x41, 0x6e,
302 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x77, 0x73, 0x6f, 0x6e, 0x0d,
303 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x63, 0x4d, 0x75,
304 0x72, 0x64, 0x6f, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f,
305 0x50, 0x61, 0x6c, 0x6d, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69,
306 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61,
307 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f, 0x50, 0x6f, 0x6c,
308 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x79,
309 0x6f, 0x77, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f,
310 0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63,
311 0x61, 0x2f, 0x56, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x72, 0x63, 0x74, 0x69, 0x63,
312 0x2f, 0x4c, 0x6f, 0x6e, 0x67, 0x79, 0x65, 0x61, 0x72, 0x62, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41,
313 0x73, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41,
314 0x6c, 0x6d, 0x61, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6d, 0x6d, 0x61,
315 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6e, 0x61, 0x64, 0x79, 0x72, 0x0d, 0x0a,
316 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x71, 0x74, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
317 0x2f, 0x41, 0x71, 0x74, 0x6f, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73,
318 0x68, 0x67, 0x61, 0x62, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73, 0x68,
319 0x6b, 0x68, 0x61, 0x62, 0x61, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x74, 0x79,
320 0x72, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x67, 0x68, 0x64, 0x61,
321 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x72, 0x61, 0x69, 0x6e, 0x0d,
322 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x6b, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
323 0x2f, 0x42, 0x61, 0x6e, 0x67, 0x6b, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42,
324 0x61, 0x72, 0x6e, 0x61, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x65, 0x69,
325 0x72, 0x75, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x68, 0x6b, 0x65,
326 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x75, 0x6e, 0x65, 0x69, 0x0d, 0x0a,
327 0x41, 0x73, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6c, 0x63, 0x75, 0x74, 0x74, 0x61, 0x0d, 0x0a, 0x41,
328 0x73, 0x69, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
329 0x43, 0x68, 0x6f, 0x69, 0x62, 0x61, 0x6c, 0x73, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
330 0x2f, 0x43, 0x68, 0x6f, 0x6e, 0x67, 0x71, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
331 0x2f, 0x43, 0x68, 0x75, 0x6e, 0x67, 0x6b, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
332 0x2f, 0x43, 0x6f, 0x6c, 0x6f, 0x6d, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44,
333 0x61, 0x63, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x6d, 0x61, 0x73,
334 0x63, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x68, 0x61, 0x6b, 0x61, 0x0d,
335 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x69, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
336 0x2f, 0x44, 0x75, 0x62, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x75, 0x73,
337 0x68, 0x61, 0x6e, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x46, 0x61, 0x6d, 0x61,
338 0x67, 0x75, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x47, 0x61, 0x7a, 0x61,
339 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x62, 0x69, 0x6e, 0x0d, 0x0a, 0x41,
340 0x73, 0x69, 0x61, 0x2f, 0x48, 0x65, 0x62, 0x72, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
341 0x2f, 0x48, 0x6f, 0x6e, 0x67, 0x5f, 0x4b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
342 0x2f, 0x48, 0x6f, 0x76, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x6f, 0x5f, 0x43,
343 0x68, 0x69, 0x5f, 0x4d, 0x69, 0x6e, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x72,
344 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x73, 0x74, 0x61,
345 0x6e, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x6b, 0x61, 0x72,
346 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x79, 0x61, 0x70, 0x75, 0x72,
347 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x65, 0x72, 0x75, 0x73, 0x61, 0x6c, 0x65,
348 0x6d, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41,
349 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x63, 0x68, 0x61, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41,
350 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x72, 0x61, 0x63, 0x68, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69,
351 0x61, 0x2f, 0x4b, 0x61, 0x73, 0x68, 0x67, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
352 0x4b, 0x61, 0x74, 0x68, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
353 0x4b, 0x61, 0x74, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b,
354 0x68, 0x61, 0x6e, 0x64, 0x79, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x6f,
355 0x6c, 0x6b, 0x61, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x72, 0x61, 0x73,
356 0x6e, 0x6f, 0x79, 0x61, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x75,
357 0x61, 0x6c, 0x61, 0x5f, 0x4c, 0x75, 0x6d, 0x70, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
358 0x2f, 0x4b, 0x75, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b,
359 0x75, 0x77, 0x61, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61,
360 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61, 0x75, 0x0d, 0x0a, 0x41,
361 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x67, 0x61, 0x64, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69,
362 0x61, 0x2f, 0x4d, 0x61, 0x6b, 0x61, 0x73, 0x73, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
363 0x2f, 0x4d, 0x61, 0x6e, 0x69, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x75,
364 0x73, 0x63, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73,
365 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f, 0x6b, 0x75, 0x7a,
366 0x6e, 0x65, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f,
367 0x73, 0x69, 0x62, 0x69, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x6d,
368 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x41,
369 0x73, 0x69, 0x61, 0x2f, 0x50, 0x68, 0x6e, 0x6f, 0x6d, 0x5f, 0x50, 0x65, 0x6e, 0x68, 0x0d, 0x0a,
370 0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x6f, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x61, 0x6b, 0x0d, 0x0a,
371 0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x79, 0x6f, 0x6e, 0x67, 0x79, 0x61, 0x6e, 0x67, 0x0d, 0x0a,
372 0x41, 0x73, 0x69, 0x61, 0x2f, 0x51, 0x61, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
373 0x2f, 0x51, 0x79, 0x7a, 0x79, 0x6c, 0x6f, 0x72, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
374 0x2f, 0x52, 0x61, 0x6e, 0x67, 0x6f, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x52,
375 0x69, 0x79, 0x61, 0x64, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x69, 0x67,
376 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6b, 0x68, 0x61, 0x6c, 0x69,
377 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x6b, 0x61, 0x6e,
378 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x65, 0x6f, 0x75, 0x6c, 0x0d, 0x0a, 0x41,
379 0x73, 0x69, 0x61, 0x2f, 0x53, 0x68, 0x61, 0x6e, 0x67, 0x68, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73,
380 0x69, 0x61, 0x2f, 0x53, 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x73,
381 0x69, 0x61, 0x2f, 0x53, 0x72, 0x65, 0x64, 0x6e, 0x65, 0x6b, 0x6f, 0x6c, 0x79, 0x6d, 0x73, 0x6b,
382 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x69, 0x70, 0x65, 0x69, 0x0d, 0x0a, 0x41,
383 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x73,
384 0x69, 0x61, 0x2f, 0x54, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
385 0x2f, 0x54, 0x65, 0x68, 0x72, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x65,
386 0x6c, 0x5f, 0x41, 0x76, 0x69, 0x76, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69,
387 0x6d, 0x62, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69, 0x6d, 0x70, 0x68,
388 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x0d, 0x0a, 0x41,
389 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6d, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
390 0x55, 0x6a, 0x75, 0x6e, 0x67, 0x5f, 0x50, 0x61, 0x6e, 0x64, 0x61, 0x6e, 0x67, 0x0d, 0x0a, 0x41,
391 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x61, 0x6e, 0x62, 0x61, 0x61, 0x74, 0x61, 0x72, 0x0d,
392 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x6e, 0x5f, 0x42, 0x61, 0x74, 0x6f, 0x72,
393 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x72, 0x75, 0x6d, 0x71, 0x69, 0x0d, 0x0a, 0x41,
394 0x73, 0x69, 0x61, 0x2f, 0x55, 0x73, 0x74, 0x2d, 0x4e, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x73,
395 0x69, 0x61, 0x2f, 0x56, 0x69, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x73,
396 0x69, 0x61, 0x2f, 0x56, 0x6c, 0x61, 0x64, 0x69, 0x76, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a,
397 0x41, 0x73, 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73,
398 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6e, 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
399 0x59, 0x65, 0x6b, 0x61, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41,
400 0x73, 0x69, 0x61, 0x2f, 0x59, 0x65, 0x72, 0x65, 0x76, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c,
401 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x41, 0x7a, 0x6f, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x74,
402 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x42, 0x65, 0x72, 0x6d, 0x75, 0x64, 0x61, 0x0d, 0x0a,
403 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x0d,
404 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x70, 0x65, 0x5f, 0x56,
405 0x65, 0x72, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x46,
406 0x61, 0x65, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f,
407 0x46, 0x61, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f,
408 0x4a, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e,
409 0x74, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x64, 0x65, 0x69, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c,
410 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x52, 0x65, 0x79, 0x6b, 0x6a, 0x61, 0x76, 0x69, 0x6b, 0x0d,
411 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f,
412 0x47, 0x65, 0x6f, 0x72, 0x67, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69,
413 0x63, 0x2f, 0x53, 0x74, 0x61, 0x6e, 0x6c, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e,
414 0x74, 0x69, 0x63, 0x2f, 0x53, 0x74, 0x5f, 0x48, 0x65, 0x6c, 0x65, 0x6e, 0x61, 0x0d, 0x0a, 0x41,
415 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x43, 0x54, 0x0d, 0x0a, 0x41, 0x75,
416 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6c, 0x61, 0x69, 0x64, 0x65,
417 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x69, 0x73,
418 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
419 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x48, 0x69, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x75, 0x73,
420 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6e, 0x62, 0x65, 0x72, 0x72, 0x61, 0x0d,
421 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x72, 0x69,
422 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x72,
423 0x77, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x45,
424 0x75, 0x63, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
425 0x48, 0x6f, 0x62, 0x61, 0x72, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69,
426 0x61, 0x2f, 0x4c, 0x48, 0x49, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
427 0x2f, 0x4c, 0x69, 0x6e, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72,
428 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4c, 0x6f, 0x72, 0x64, 0x5f, 0x48, 0x6f, 0x77, 0x65, 0x0d, 0x0a,
429 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4d, 0x65, 0x6c, 0x62, 0x6f, 0x75,
430 0x72, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4e,
431 0x6f, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
432 0x4e, 0x53, 0x57, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x50,
433 0x65, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
434 0x51, 0x75, 0x65, 0x65, 0x6e, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74,
435 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73,
436 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x79, 0x64, 0x6e, 0x65, 0x79, 0x0d, 0x0a, 0x41,
437 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x6d, 0x61, 0x6e, 0x69,
438 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x56, 0x69, 0x63,
439 0x74, 0x6f, 0x72, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
440 0x2f, 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
441 0x2f, 0x59, 0x61, 0x6e, 0x63, 0x6f, 0x77, 0x69, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61,
442 0x7a, 0x69, 0x6c, 0x2f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c,
443 0x2f, 0x44, 0x65, 0x4e, 0x6f, 0x72, 0x6f, 0x6e, 0x68, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a,
444 0x69, 0x6c, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c, 0x2f,
445 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x41, 0x74, 0x6c,
446 0x61, 0x6e, 0x74, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x43, 0x65,
447 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61,
448 0x73, 0x74, 0x2d, 0x53, 0x61, 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d,
449 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x0d,
450 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e,
451 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x66, 0x6f, 0x75, 0x6e,
452 0x64, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x50, 0x61,
453 0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x53, 0x61,
454 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61,
455 0x64, 0x61, 0x2f, 0x59, 0x75, 0x6b, 0x6f, 0x6e, 0x0d, 0x0a, 0x43, 0x68, 0x69, 0x6c, 0x65, 0x2f,
456 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x68, 0x69,
457 0x6c, 0x65, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x49, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d,
458 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
459 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x0d, 0x0a,
460 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f,
461 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b,
462 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x32, 0x0d, 0x0a, 0x45,
463 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
464 0x54, 0x2b, 0x34, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x35, 0x0d, 0x0a,
465 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47,
466 0x4d, 0x54, 0x2b, 0x37, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x38, 0x0d,
467 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f,
468 0x47, 0x4d, 0x54, 0x2d, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31,
469 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74,
470 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
471 0x54, 0x2d, 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x33,
472 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x34, 0x0d, 0x0a, 0x45, 0x74,
473 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54,
474 0x2d, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x34, 0x0d, 0x0a, 0x45,
475 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x35, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
476 0x54, 0x2d, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x37, 0x0d, 0x0a,
477 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x38, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47,
478 0x4d, 0x54, 0x2d, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a,
479 0x45, 0x74, 0x63, 0x2f, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x45,
480 0x74, 0x63, 0x2f, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x6e, 0x69, 0x76,
481 0x65, 0x72, 0x73, 0x61, 0x6c, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x54, 0x43, 0x0d, 0x0a,
482 0x45, 0x74, 0x63, 0x2f, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
483 0x2f, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64, 0x61, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
484 0x70, 0x65, 0x2f, 0x41, 0x6e, 0x64, 0x6f, 0x72, 0x72, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
485 0x70, 0x65, 0x2f, 0x41, 0x73, 0x74, 0x72, 0x61, 0x6b, 0x68, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75,
486 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x41, 0x74, 0x68, 0x65, 0x6e, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72,
487 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x66, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72,
488 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x45, 0x75,
489 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72,
490 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x61, 0x74, 0x69, 0x73, 0x6c, 0x61, 0x76, 0x61, 0x0d, 0x0a,
491 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x75, 0x73, 0x73, 0x65, 0x6c, 0x73, 0x0d,
492 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x63, 0x68, 0x61, 0x72, 0x65, 0x73,
493 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x64, 0x61, 0x70, 0x65,
494 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x73, 0x69, 0x6e,
495 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x68, 0x69, 0x73,
496 0x69, 0x6e, 0x61, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x6f, 0x70,
497 0x65, 0x6e, 0x68, 0x61, 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
498 0x44, 0x75, 0x62, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x47,
499 0x69, 0x62, 0x72, 0x61, 0x6c, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
500 0x2f, 0x47, 0x75, 0x65, 0x72, 0x6e, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
501 0x65, 0x2f, 0x48, 0x65, 0x6c, 0x73, 0x69, 0x6e, 0x6b, 0x69, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
502 0x70, 0x65, 0x2f, 0x49, 0x73, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x4d, 0x61, 0x6e, 0x0d, 0x0a,
503 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x49, 0x73, 0x74, 0x61, 0x6e, 0x62, 0x75, 0x6c, 0x0d,
504 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4a, 0x65, 0x72, 0x73, 0x65, 0x79, 0x0d, 0x0a,
505 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x61, 0x6c, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x72,
506 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x65, 0x76, 0x0d,
507 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x72, 0x6f, 0x76, 0x0d, 0x0a, 0x45,
508 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x69, 0x73, 0x62, 0x6f, 0x6e, 0x0d, 0x0a, 0x45, 0x75,
509 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6a, 0x75, 0x62, 0x6c, 0x6a, 0x61, 0x6e, 0x61, 0x0d, 0x0a,
510 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x0d, 0x0a, 0x45,
511 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x75, 0x78, 0x65, 0x6d, 0x62, 0x6f, 0x75, 0x72, 0x67,
512 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x64, 0x72, 0x69, 0x64, 0x0d,
513 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x45,
514 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x65, 0x68, 0x61, 0x6d, 0x6e, 0x0d,
515 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x73, 0x6b, 0x0d, 0x0a, 0x45,
516 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x6e, 0x61, 0x63, 0x6f, 0x0d, 0x0a, 0x45, 0x75,
517 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x73, 0x63, 0x6f, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72,
518 0x6f, 0x70, 0x65, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72,
519 0x6f, 0x70, 0x65, 0x2f, 0x4f, 0x73, 0x6c, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
520 0x2f, 0x50, 0x61, 0x72, 0x69, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x50,
521 0x6f, 0x64, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
522 0x2f, 0x50, 0x72, 0x61, 0x67, 0x75, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
523 0x52, 0x69, 0x67, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x52, 0x6f, 0x6d,
524 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x61,
525 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x72,
526 0x69, 0x6e, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72, 0x61,
527 0x6a, 0x65, 0x76, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72,
528 0x61, 0x74, 0x6f, 0x76, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x69, 0x6d,
529 0x66, 0x65, 0x72, 0x6f, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
530 0x53, 0x6b, 0x6f, 0x70, 0x6a, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53,
531 0x6f, 0x66, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x74, 0x6f,
532 0x63, 0x6b, 0x68, 0x6f, 0x6c, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54,
533 0x61, 0x6c, 0x6c, 0x69, 0x6e, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54,
534 0x69, 0x72, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54, 0x69,
535 0x72, 0x61, 0x73, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x55,
536 0x6c, 0x79, 0x61, 0x6e, 0x6f, 0x76, 0x73, 0x6b, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
537 0x2f, 0x55, 0x7a, 0x68, 0x67, 0x6f, 0x72, 0x6f, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
538 0x65, 0x2f, 0x56, 0x61, 0x64, 0x75, 0x7a, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
539 0x56, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
540 0x56, 0x69, 0x65, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56,
541 0x69, 0x6c, 0x6e, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56,
542 0x6f, 0x6c, 0x67, 0x6f, 0x67, 0x72, 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
543 0x2f, 0x57, 0x61, 0x72, 0x73, 0x61, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
544 0x5a, 0x61, 0x67, 0x72, 0x65, 0x62, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x5a,
545 0x61, 0x70, 0x6f, 0x72, 0x6f, 0x7a, 0x68, 0x79, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
546 0x65, 0x2f, 0x5a, 0x75, 0x72, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e,
547 0x2f, 0x41, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x76, 0x6f, 0x0d, 0x0a, 0x49,
548 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e,
549 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x6d, 0x61, 0x73, 0x0d, 0x0a,
550 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x63, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e,
551 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x72, 0x6f, 0x0d, 0x0a, 0x49, 0x6e, 0x64,
552 0x69, 0x61, 0x6e, 0x2f, 0x4b, 0x65, 0x72, 0x67, 0x75, 0x65, 0x6c, 0x65, 0x6e, 0x0d, 0x0a, 0x49,
553 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x68, 0x65, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69,
554 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x6c, 0x64, 0x69, 0x76, 0x65, 0x73, 0x0d, 0x0a, 0x49, 0x6e, 0x64,
555 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x75, 0x72, 0x69, 0x74, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x49,
556 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x79, 0x6f, 0x74, 0x74, 0x65, 0x0d, 0x0a, 0x49,
557 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x52, 0x65, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x4d,
558 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x4e, 0x6f, 0x72, 0x74, 0x65, 0x0d,
559 0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x53, 0x75, 0x72, 0x0d,
560 0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x0d,
561 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x70, 0x69, 0x61, 0x0d, 0x0a, 0x50,
562 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x75, 0x63, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x0d,
563 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x42, 0x6f, 0x75, 0x67, 0x61, 0x69, 0x6e,
564 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x43,
565 0x68, 0x61, 0x74, 0x68, 0x61, 0x6d, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
566 0x43, 0x68, 0x75, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45,
567 0x61, 0x73, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45,
568 0x66, 0x61, 0x74, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45, 0x6e,
569 0x64, 0x65, 0x72, 0x62, 0x75, 0x72, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
570 0x2f, 0x46, 0x61, 0x6b, 0x61, 0x6f, 0x66, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
571 0x63, 0x2f, 0x46, 0x69, 0x6a, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
572 0x46, 0x75, 0x6e, 0x61, 0x66, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
573 0x63, 0x2f, 0x47, 0x61, 0x6c, 0x61, 0x70, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63,
574 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61,
575 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x61, 0x6c, 0x63, 0x61, 0x6e, 0x61,
576 0x6c, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x6d, 0x0d,
577 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x48, 0x6f, 0x6e, 0x6f, 0x6c, 0x75, 0x6c,
578 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4a, 0x6f, 0x68, 0x6e, 0x73,
579 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4b, 0x69, 0x72,
580 0x69, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
581 0x2f, 0x4b, 0x6f, 0x73, 0x72, 0x61, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
582 0x2f, 0x4b, 0x77, 0x61, 0x6a, 0x61, 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
583 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x6a, 0x75, 0x72, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
584 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x72, 0x71, 0x75, 0x65, 0x73, 0x61, 0x73, 0x0d, 0x0a, 0x50,
585 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x69, 0x64, 0x77, 0x61, 0x79, 0x0d, 0x0a, 0x50,
586 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x61, 0x75, 0x72, 0x75, 0x0d, 0x0a, 0x50, 0x61,
587 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x69, 0x75, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
588 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x72, 0x66, 0x6f, 0x6c, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63,
589 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x75, 0x6d, 0x65, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63,
590 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x67, 0x6f, 0x5f, 0x50, 0x61, 0x67, 0x6f, 0x0d, 0x0a,
591 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x6c, 0x61, 0x75, 0x0d, 0x0a, 0x50,
592 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x69, 0x74, 0x63, 0x61, 0x69, 0x72, 0x6e, 0x0d,
593 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x68, 0x6e, 0x70, 0x65, 0x69,
594 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x6e, 0x61, 0x70, 0x65,
595 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x4d,
596 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
597 0x52, 0x61, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x67, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
598 0x69, 0x63, 0x2f, 0x53, 0x61, 0x69, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
599 0x69, 0x63, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
600 0x63, 0x2f, 0x54, 0x61, 0x68, 0x69, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
601 0x63, 0x2f, 0x54, 0x61, 0x72, 0x61, 0x77, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
602 0x63, 0x2f, 0x54, 0x6f, 0x6e, 0x67, 0x61, 0x74, 0x61, 0x70, 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63,
603 0x69, 0x66, 0x69, 0x63, 0x2f, 0x54, 0x72, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
604 0x69, 0x63, 0x2f, 0x57, 0x61, 0x6b, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
605 0x2f, 0x57, 0x61, 0x6c, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
606 0x2f, 0x59, 0x61, 0x70, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x61, 0x73, 0x6b, 0x61, 0x0d,
607 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x65, 0x75, 0x74, 0x69, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53,
608 0x2f, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x43, 0x65, 0x6e,
609 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x2d, 0x49, 0x6e,
610 0x64, 0x69, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72,
611 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x48, 0x61, 0x77, 0x61, 0x69, 0x69, 0x0d, 0x0a, 0x55, 0x53,
612 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2d, 0x53, 0x74, 0x61, 0x72, 0x6b, 0x65, 0x0d,
613 0x0a, 0x55, 0x53, 0x2f, 0x4d, 0x69, 0x63, 0x68, 0x69, 0x67, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53,
614 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61,
615 0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
616 0x63, 0x2d, 0x4e, 0x65, 0x77, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d,
617 0x0a};
618 12
619static VirtualFile GenerateDefaultTimeZoneFile() { 13namespace FileSys::SystemArchive {
620 struct TimeZoneInfo {
621 s64_be at;
622 std::array<u8, 7> padding1;
623 std::array<char, 4> time_zone_chars;
624 std::array<u8, 2> padding2;
625 std::array<char, 6> time_zone_name;
626 };
627 14
628 VirtualFile file{std::make_shared<VectorVfsFile>( 15const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
629 std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)), 16 tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa},
630 "GMT")}; 17 {"America", NxTzdb::america},
18 {"Antarctica", NxTzdb::antarctica},
19 {"Arctic", NxTzdb::arctic},
20 {"Asia", NxTzdb::asia},
21 {"Atlantic", NxTzdb::atlantic},
22 {"Australia", NxTzdb::australia},
23 {"Brazil", NxTzdb::brazil},
24 {"Canada", NxTzdb::canada},
25 {"Chile", NxTzdb::chile},
26 {"Etc", NxTzdb::etc},
27 {"Europe", NxTzdb::europe},
28 {"Indian", NxTzdb::indian},
29 {"Mexico", NxTzdb::mexico},
30 {"Pacific", NxTzdb::pacific},
31 {"US", NxTzdb::us}};
631 32
632 const Service::Time::TimeZone::TzifHeader header{ 33const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
633 .magic = 0x545a6966, 34 tzdb_america_dirs = {{"Argentina", NxTzdb::america_argentina},
634 .version = 0x32, 35 {"Indiana", NxTzdb::america_indiana},
635 .ttis_gmt_count = 1, 36 {"Kentucky", NxTzdb::america_kentucky},
636 .ttis_std_count = 1, 37 {"North_Dakota", NxTzdb::america_north_dakota}};
637 .time_count = 1,
638 .type_count = 1,
639 .char_count = 4,
640 };
641 file->WriteObject(header, 0);
642 38
643 const TimeZoneInfo time_zone_info{ 39static void GenerateFiles(std::vector<VirtualFile>& directory,
644 .at = 0xf8, 40 const std::map<const char*, const std::vector<u8>>& files) {
645 .padding1 = {}, 41 for (const auto& [filename, data] : files) {
646 .time_zone_chars = {'G', 'M', 'T', '\0'}, 42 const auto data_copy{data};
647 .padding2 = {}, 43 const std::string filename_copy{filename};
648 .time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}, 44 VirtualFile file{
649 }; 45 std::make_shared<VectorVfsFile>(std::move(data_copy), std::move(filename_copy))};
650 file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); 46 directory.push_back(file);
47 }
48}
651 49
652 return file; 50static std::vector<VirtualFile> GenerateZoneinfoFiles() {
51 std::vector<VirtualFile> zoneinfo_files;
52 GenerateFiles(zoneinfo_files, NxTzdb::zoneinfo);
53 return zoneinfo_files;
653} 54}
654 55
655VirtualDir TimeZoneBinary() { 56VirtualDir TimeZoneBinary() {
656 std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( 57 std::vector<VirtualDir> america_sub_dirs;
657 std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, 58 for (const auto& [dir_name, files] : tzdb_america_dirs) {
658 "zoneinfo")}; 59 std::vector<VirtualFile> vfs_files;
659 std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")}; 60 GenerateFiles(vfs_files, files);
61 america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
62 std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
63 }
64
65 std::vector<VirtualDir> zoneinfo_sub_dirs;
66 for (const auto& [dir_name, files] : tzdb_zoneinfo_dirs) {
67 std::vector<VirtualFile> vfs_files;
68 GenerateFiles(vfs_files, files);
69 if (dir_name == "America") {
70 zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
71 std::move(vfs_files), std::move(america_sub_dirs), dir_name));
72 } else {
73 zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
74 std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
75 }
76 }
77
78 std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(
79 GenerateZoneinfoFiles(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
80 std::vector<VirtualFile> root_files;
81 GenerateFiles(root_files, NxTzdb::base);
660 82
661 return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs), 83 return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(zoneinfo_dir),
662 "data"); 84 "data");
663} 85}
664 86
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 853b893a1..311a59e5f 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -150,23 +150,29 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t
150 while (cur_length > 0 && it != concatenation_map.end()) { 150 while (cur_length > 0 && it != concatenation_map.end()) {
151 // Check if we can read the file at this position. 151 // Check if we can read the file at this position.
152 const auto& file = it->file; 152 const auto& file = it->file;
153 const u64 file_offset = it->offset; 153 const u64 map_offset = it->offset;
154 const u64 file_size = file->GetSize(); 154 const u64 file_size = file->GetSize();
155 155
156 if (cur_offset >= file_offset + file_size) { 156 if (cur_offset > map_offset + file_size) {
157 // Entirely out of bounds read. 157 // Entirely out of bounds read.
158 break; 158 break;
159 } 159 }
160 160
161 // Read the file at this position. 161 // Read the file at this position.
162 const u64 intended_read_size = std::min<u64>(cur_length, file_size); 162 const u64 file_seek = cur_offset - map_offset;
163 const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek);
163 const u64 actual_read_size = 164 const u64 actual_read_size =
164 file->Read(data + (cur_offset - offset), intended_read_size, cur_offset - file_offset); 165 file->Read(data + (cur_offset - offset), intended_read_size, file_seek);
165 166
166 // Update tracking. 167 // Update tracking.
167 cur_offset += actual_read_size; 168 cur_offset += actual_read_size;
168 cur_length -= actual_read_size; 169 cur_length -= actual_read_size;
169 it++; 170 it++;
171
172 // If we encountered a short read, we're done.
173 if (actual_read_size < intended_read_size) {
174 break;
175 }
170 } 176 }
171 177
172 return cur_offset - offset; 178 return cur_offset - offset;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index cc0076238..b0515ec05 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -10,6 +10,7 @@
10#include "common/fs/fs.h" 10#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/file_sys/vfs.h"
13#include "core/file_sys/vfs_real.h" 14#include "core/file_sys/vfs_real.h"
14 15
15// For FileTimeStampRaw 16// For FileTimeStampRaw
@@ -25,6 +26,8 @@ namespace FS = Common::FS;
25 26
26namespace { 27namespace {
27 28
29constexpr size_t MaxOpenFiles = 512;
30
28constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { 31constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
29 switch (mode) { 32 switch (mode) {
30 case Mode::Read: 33 case Mode::Read:
@@ -70,31 +73,42 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
70 return VfsEntryType::File; 73 return VfsEntryType::File;
71} 74}
72 75
73VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 76VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size,
77 Mode perms) {
74 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 78 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
79 std::scoped_lock lk{list_lock};
75 80
76 if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) { 81 if (auto it = cache.find(path); it != cache.end()) {
77 const auto& weak = weak_iter->second; 82 if (auto file = it->second.lock(); file) {
78 83 return file;
79 if (!weak.expired()) {
80 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
81 } 84 }
82 } 85 }
83 86
84 auto backing = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); 87 if (!size && !FS::IsFile(path)) {
85
86 if (!backing) {
87 return nullptr; 88 return nullptr;
88 } 89 }
89 90
90 cache.insert_or_assign(path, std::move(backing)); 91 auto reference = std::make_unique<FileReference>();
92 this->InsertReferenceIntoListLocked(*reference);
91 93
92 // Cannot use make_shared as RealVfsFile constructor is private 94 auto file = std::shared_ptr<RealVfsFile>(
93 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); 95 new RealVfsFile(*this, std::move(reference), path, perms, size));
96 cache[path] = file;
97
98 return file;
99}
100
101VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
102 return OpenFileFromEntry(path_, {}, perms);
94} 103}
95 104
96VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 105VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
97 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 106 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
107 {
108 std::scoped_lock lk{list_lock};
109 cache.erase(path);
110 }
111
98 // Current usages of CreateFile expect to delete the contents of an existing file. 112 // Current usages of CreateFile expect to delete the contents of an existing file.
99 if (FS::IsFile(path)) { 113 if (FS::IsFile(path)) {
100 FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; 114 FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile};
@@ -123,51 +137,28 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_
123VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 137VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
124 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); 138 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
125 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); 139 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
126 const auto cached_file_iter = cache.find(old_path); 140 {
127 141 std::scoped_lock lk{list_lock};
128 if (cached_file_iter != cache.cend()) {
129 auto file = cached_file_iter->second.lock();
130
131 if (!cached_file_iter->second.expired()) {
132 file->Close();
133 }
134
135 if (!FS::RenameFile(old_path, new_path)) {
136 return nullptr;
137 }
138
139 cache.erase(old_path); 142 cache.erase(old_path);
140 file->Open(new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); 143 cache.erase(new_path);
141 if (file->IsOpen()) { 144 }
142 cache.insert_or_assign(new_path, std::move(file)); 145 if (!FS::RenameFile(old_path, new_path)) {
143 } else {
144 LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
145 }
146 } else {
147 ASSERT(false);
148 return nullptr; 146 return nullptr;
149 } 147 }
150
151 return OpenFile(new_path, Mode::ReadWrite); 148 return OpenFile(new_path, Mode::ReadWrite);
152} 149}
153 150
154bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 151bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
155 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 152 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
156 const auto cached_iter = cache.find(path); 153 {
157 154 std::scoped_lock lk{list_lock};
158 if (cached_iter != cache.cend()) {
159 if (!cached_iter->second.expired()) {
160 cached_iter->second.lock()->Close();
161 }
162 cache.erase(path); 155 cache.erase(path);
163 } 156 }
164
165 return FS::RemoveFile(path); 157 return FS::RemoveFile(path);
166} 158}
167 159
168VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 160VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
169 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 161 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
170 // Cannot use make_shared as RealVfsDirectory constructor is private
171 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 162 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
172} 163}
173 164
@@ -176,7 +167,6 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
176 if (!FS::CreateDirs(path)) { 167 if (!FS::CreateDirs(path)) {
177 return nullptr; 168 return nullptr;
178 } 169 }
179 // Cannot use make_shared as RealVfsDirectory constructor is private
180 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 170 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
181} 171}
182 172
@@ -194,73 +184,112 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
194 if (!FS::RenameDir(old_path, new_path)) { 184 if (!FS::RenameDir(old_path, new_path)) {
195 return nullptr; 185 return nullptr;
196 } 186 }
187 return OpenDirectory(new_path, Mode::ReadWrite);
188}
197 189
198 for (auto& kv : cache) { 190bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
199 // If the path in the cache doesn't start with old_path, then bail on this file. 191 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
200 if (kv.first.rfind(old_path, 0) != 0) { 192 return FS::RemoveDirRecursively(path);
201 continue; 193}
202 }
203 194
204 const auto file_old_path = 195std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path,
205 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); 196 Mode perms,
206 auto file_new_path = FS::SanitizePath(new_path + '/' + kv.first.substr(old_path.size()), 197 FileReference& reference) {
207 FS::DirectorySeparator::PlatformDefault); 198 std::unique_lock lk{list_lock};
208 const auto& cached = cache[file_old_path];
209 199
210 if (cached.expired()) { 200 // Temporarily remove from list.
211 continue; 201 this->RemoveReferenceFromListLocked(reference);
212 } 202
203 // Restore file if needed.
204 if (!reference.file) {
205 this->EvictSingleReferenceLocked();
213 206
214 auto file = cached.lock(); 207 reference.file =
215 cache.erase(file_old_path); 208 FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
216 file->Open(file_new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); 209 if (reference.file) {
217 if (file->IsOpen()) { 210 num_open_files++;
218 cache.insert_or_assign(std::move(file_new_path), std::move(file));
219 } else {
220 LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
221 } 211 }
222 } 212 }
223 213
224 return OpenDirectory(new_path, Mode::ReadWrite); 214 // Reinsert into list.
215 this->InsertReferenceIntoListLocked(reference);
216
217 return lk;
225} 218}
226 219
227bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 220void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) {
228 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 221 std::scoped_lock lk{list_lock};
229 222
230 for (auto& kv : cache) { 223 // Remove from list.
231 // If the path in the cache doesn't start with path, then bail on this file. 224 this->RemoveReferenceFromListLocked(*reference);
232 if (kv.first.rfind(path, 0) != 0) {
233 continue;
234 }
235 225
236 const auto& entry = cache[kv.first]; 226 // Close the file.
237 if (!entry.expired()) { 227 if (reference->file) {
238 entry.lock()->Close(); 228 reference->file.reset();
239 } 229 num_open_files--;
230 }
231}
240 232
241 cache.erase(kv.first); 233void RealVfsFilesystem::EvictSingleReferenceLocked() {
234 if (num_open_files < MaxOpenFiles || open_references.empty()) {
235 return;
242 } 236 }
243 237
244 return FS::RemoveDirRecursively(path); 238 // Get and remove from list.
239 auto& reference = open_references.back();
240 this->RemoveReferenceFromListLocked(reference);
241
242 // Close the file.
243 if (reference.file) {
244 reference.file.reset();
245 num_open_files--;
246 }
247
248 // Reinsert into closed list.
249 this->InsertReferenceIntoListLocked(reference);
250}
251
252void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) {
253 if (reference.file) {
254 open_references.push_front(reference);
255 } else {
256 closed_references.push_front(reference);
257 }
245} 258}
246 259
247RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_, 260void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) {
248 const std::string& path_, Mode perms_) 261 if (reference.file) {
249 : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)), 262 open_references.erase(open_references.iterator_to(reference));
250 path_components(FS::SplitPathComponents(path_)), perms(perms_) {} 263 } else {
264 closed_references.erase(closed_references.iterator_to(reference));
265 }
266}
251 267
252RealVfsFile::~RealVfsFile() = default; 268RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
269 const std::string& path_, Mode perms_, std::optional<u64> size_)
270 : base(base_), reference(std::move(reference_)), path(path_),
271 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)),
272 size(size_), perms(perms_) {}
273
274RealVfsFile::~RealVfsFile() {
275 base.DropReference(std::move(reference));
276}
253 277
254std::string RealVfsFile::GetName() const { 278std::string RealVfsFile::GetName() const {
255 return path_components.back(); 279 return path_components.back();
256} 280}
257 281
258std::size_t RealVfsFile::GetSize() const { 282std::size_t RealVfsFile::GetSize() const {
259 return backing->GetSize(); 283 if (size) {
284 return *size;
285 }
286 return FS::GetSize(path);
260} 287}
261 288
262bool RealVfsFile::Resize(std::size_t new_size) { 289bool RealVfsFile::Resize(std::size_t new_size) {
263 return backing->SetSize(new_size); 290 size.reset();
291 auto lk = base.RefreshReference(path, perms, *reference);
292 return reference->file ? reference->file->SetSize(new_size) : false;
264} 293}
265 294
266VirtualDir RealVfsFile::GetContainingDirectory() const { 295VirtualDir RealVfsFile::GetContainingDirectory() const {
@@ -276,27 +305,26 @@ bool RealVfsFile::IsReadable() const {
276} 305}
277 306
278std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 307std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
279 if (!backing->Seek(static_cast<s64>(offset))) { 308 auto lk = base.RefreshReference(path, perms, *reference);
309 if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
280 return 0; 310 return 0;
281 } 311 }
282 return backing->ReadSpan(std::span{data, length}); 312 return reference->file->ReadSpan(std::span{data, length});
283} 313}
284 314
285std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 315std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
286 if (!backing->Seek(static_cast<s64>(offset))) { 316 size.reset();
317 auto lk = base.RefreshReference(path, perms, *reference);
318 if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
287 return 0; 319 return 0;
288 } 320 }
289 return backing->WriteSpan(std::span{data, length}); 321 return reference->file->WriteSpan(std::span{data, length});
290} 322}
291 323
292bool RealVfsFile::Rename(std::string_view name) { 324bool RealVfsFile::Rename(std::string_view name) {
293 return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; 325 return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr;
294} 326}
295 327
296void RealVfsFile::Close() {
297 backing->Close();
298}
299
300// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if 328// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
301// constexpr' because there is a compile error in the branch not used. 329// constexpr' because there is a compile error in the branch not used.
302 330
@@ -308,10 +336,11 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
308 336
309 std::vector<VirtualFile> out; 337 std::vector<VirtualFile> out;
310 338
311 const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { 339 const FS::DirEntryCallable callback = [this,
312 const auto full_path_string = FS::PathToUTF8String(full_path); 340 &out](const std::filesystem::directory_entry& entry) {
341 const auto full_path_string = FS::PathToUTF8String(entry.path());
313 342
314 out.emplace_back(base.OpenFile(full_path_string, perms)); 343 out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms));
315 344
316 return true; 345 return true;
317 }; 346 };
@@ -329,8 +358,9 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
329 358
330 std::vector<VirtualDir> out; 359 std::vector<VirtualDir> out;
331 360
332 const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { 361 const FS::DirEntryCallable callback = [this,
333 const auto full_path_string = FS::PathToUTF8String(full_path); 362 &out](const std::filesystem::directory_entry& entry) {
363 const auto full_path_string = FS::PathToUTF8String(entry.path());
334 364
335 out.emplace_back(base.OpenDirectory(full_path_string, perms)); 365 out.emplace_back(base.OpenDirectory(full_path_string, perms));
336 366
@@ -482,12 +512,10 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
482 512
483 std::map<std::string, VfsEntryType, std::less<>> out; 513 std::map<std::string, VfsEntryType, std::less<>> out;
484 514
485 const FS::DirEntryCallable callback = [&out](const std::filesystem::path& full_path) { 515 const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) {
486 const auto filename = FS::PathToUTF8String(full_path.filename()); 516 const auto filename = FS::PathToUTF8String(entry.path().filename());
487
488 out.insert_or_assign(filename, 517 out.insert_or_assign(filename,
489 FS::IsDir(full_path) ? VfsEntryType::Directory : VfsEntryType::File); 518 entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File);
490
491 return true; 519 return true;
492 }; 520 };
493 521
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index b92c84316..26ea7df62 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -3,8 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <map>
7#include <mutex>
8#include <optional>
6#include <string_view> 9#include <string_view>
7#include <boost/container/flat_map.hpp> 10#include "common/intrusive_list.h"
8#include "core/file_sys/mode.h" 11#include "core/file_sys/mode.h"
9#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
10 13
@@ -14,6 +17,13 @@ class IOFile;
14 17
15namespace FileSys { 18namespace FileSys {
16 19
20struct FileReference : public Common::IntrusiveListBaseNode<FileReference> {
21 std::shared_ptr<Common::FS::IOFile> file{};
22};
23
24class RealVfsFile;
25class RealVfsDirectory;
26
17class RealVfsFilesystem : public VfsFilesystem { 27class RealVfsFilesystem : public VfsFilesystem {
18public: 28public:
19 RealVfsFilesystem(); 29 RealVfsFilesystem();
@@ -35,7 +45,28 @@ public:
35 bool DeleteDirectory(std::string_view path) override; 45 bool DeleteDirectory(std::string_view path) override;
36 46
37private: 47private:
38 boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache; 48 using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType;
49 std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
50 ReferenceListType open_references;
51 ReferenceListType closed_references;
52 std::mutex list_lock;
53 size_t num_open_files{};
54
55private:
56 friend class RealVfsFile;
57 std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms,
58 FileReference& reference);
59 void DropReference(std::unique_ptr<FileReference>&& reference);
60
61private:
62 friend class RealVfsDirectory;
63 VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size,
64 Mode perms = Mode::Read);
65
66private:
67 void EvictSingleReferenceLocked();
68 void InsertReferenceIntoListLocked(FileReference& reference);
69 void RemoveReferenceFromListLocked(FileReference& reference);
39}; 70};
40 71
41// An implementation of VfsFile that represents a file on the user's computer. 72// An implementation of VfsFile that represents a file on the user's computer.
@@ -57,16 +88,15 @@ public:
57 bool Rename(std::string_view name) override; 88 bool Rename(std::string_view name) override;
58 89
59private: 90private:
60 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing, 91 RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
61 const std::string& path, Mode perms = Mode::Read); 92 const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {});
62
63 void Close();
64 93
65 RealVfsFilesystem& base; 94 RealVfsFilesystem& base;
66 std::shared_ptr<Common::FS::IOFile> backing; 95 std::unique_ptr<FileReference> reference;
67 std::string path; 96 std::string path;
68 std::string parent_path; 97 std::string parent_path;
69 std::vector<std::string> path_components; 98 std::vector<std::string> path_components;
99 std::optional<u64> size;
70 Mode perms; 100 Mode perms;
71}; 101};
72 102
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 0a7777732..1ebc32c1e 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -149,12 +149,16 @@ void EmulatedController::LoadDevices() {
149 149
150 camera_params[0] = right_joycon; 150 camera_params[0] = right_joycon;
151 camera_params[0].Set("camera", true); 151 camera_params[0].Set("camera", true);
152 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
153 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
154 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
155 nfc_params[1] = right_joycon; 152 nfc_params[1] = right_joycon;
156 nfc_params[1].Set("nfc", true); 153 nfc_params[1].Set("nfc", true);
157 154
155 // Only map virtual devices to the first controller
156 if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
157 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
158 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
159 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
160 }
161
158 output_params[LeftIndex] = left_joycon; 162 output_params[LeftIndex] = left_joycon;
159 output_params[RightIndex] = right_joycon; 163 output_params[RightIndex] = right_joycon;
160 output_params[2] = camera_params[1]; 164 output_params[2] = camera_params[1];
@@ -1176,10 +1180,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1176 return; 1180 return;
1177 } 1181 }
1178 1182
1179 controller.nfc_state = { 1183 controller.nfc_state = controller.nfc_values;
1180 controller.nfc_values.state,
1181 controller.nfc_values.data,
1182 };
1183} 1184}
1184 1185
1185bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1186bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1249,6 +1250,11 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
1249 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); 1250 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
1250 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); 1251 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
1251 1252
1253 // Restore previous state
1254 if (mapped_nfc_result != Common::Input::DriverResult::Success) {
1255 right_output_device->SetPollingMode(Common::Input::PollingMode::Active);
1256 }
1257
1252 if (virtual_nfc_result == Common::Input::DriverResult::Success) { 1258 if (virtual_nfc_result == Common::Input::DriverResult::Success) {
1253 return virtual_nfc_result; 1259 return virtual_nfc_result;
1254 } 1260 }
@@ -1308,6 +1314,79 @@ bool EmulatedController::HasNfc() const {
1308 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); 1314 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
1309} 1315}
1310 1316
1317bool EmulatedController::AddNfcHandle() {
1318 nfc_handles++;
1319 return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) ==
1320 Common::Input::DriverResult::Success;
1321}
1322
1323bool EmulatedController::RemoveNfcHandle() {
1324 nfc_handles--;
1325 if (nfc_handles <= 0) {
1326 return SetPollingMode(EmulatedDeviceIndex::RightIndex,
1327 Common::Input::PollingMode::Active) ==
1328 Common::Input::DriverResult::Success;
1329 }
1330 return true;
1331}
1332
1333bool EmulatedController::StartNfcPolling() {
1334 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1335 auto& nfc_virtual_output_device = output_devices[3];
1336
1337 const auto device_result = nfc_output_device->StartNfcPolling();
1338 const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling();
1339
1340 return device_result == Common::Input::NfcState::Success ||
1341 virtual_device_result == Common::Input::NfcState::Success;
1342}
1343
1344bool EmulatedController::StopNfcPolling() {
1345 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1346 auto& nfc_virtual_output_device = output_devices[3];
1347
1348 const auto device_result = nfc_output_device->StopNfcPolling();
1349 const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling();
1350
1351 return device_result == Common::Input::NfcState::Success ||
1352 virtual_device_result == Common::Input::NfcState::Success;
1353}
1354
1355bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1356 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1357 auto& nfc_virtual_output_device = output_devices[3];
1358
1359 if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) {
1360 return true;
1361 }
1362
1363 return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success;
1364}
1365
1366bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1367 Common::Input::MifareRequest& out_data) {
1368 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1369 auto& nfc_virtual_output_device = output_devices[3];
1370
1371 if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) {
1372 return true;
1373 }
1374
1375 return nfc_virtual_output_device->ReadMifareData(request, out_data) ==
1376 Common::Input::NfcState::Success;
1377}
1378
1379bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1380 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1381 auto& nfc_virtual_output_device = output_devices[3];
1382
1383 if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) {
1384 return true;
1385 }
1386
1387 return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success;
1388}
1389
1311bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1390bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1312 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1391 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1313 auto& nfc_virtual_output_device = output_devices[3]; 1392 auto& nfc_virtual_output_device = output_devices[3];
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 09fe1a0ab..d511e5fac 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -97,10 +97,7 @@ struct RingSensorForce {
97 f32 force; 97 f32 force;
98}; 98};
99 99
100struct NfcState { 100using NfcState = Common::Input::NfcStatus;
101 Common::Input::NfcState state{};
102 std::vector<u8> data{};
103};
104 101
105struct ControllerMotion { 102struct ControllerMotion {
106 Common::Vec3f accel{}; 103 Common::Vec3f accel{};
@@ -393,9 +390,31 @@ public:
393 /// Returns true if the device has nfc support 390 /// Returns true if the device has nfc support
394 bool HasNfc() const; 391 bool HasNfc() const;
395 392
393 /// Sets the joycon in nfc mode and increments the handle count
394 bool AddNfcHandle();
395
396 /// Decrements the handle count if zero sets the joycon in active mode
397 bool RemoveNfcHandle();
398
399 /// Start searching for nfc tags
400 bool StartNfcPolling();
401
402 /// Stop searching for nfc tags
403 bool StopNfcPolling();
404
405 /// Returns true if the nfc tag was readable
406 bool ReadAmiiboData(std::vector<u8>& data);
407
396 /// Returns true if the nfc tag was written 408 /// Returns true if the nfc tag was written
397 bool WriteNfc(const std::vector<u8>& data); 409 bool WriteNfc(const std::vector<u8>& data);
398 410
411 /// Returns true if the nfc tag was readable
412 bool ReadMifareData(const Common::Input::MifareRequest& request,
413 Common::Input::MifareRequest& out_data);
414
415 /// Returns true if the nfc tag was written
416 bool WriteMifareData(const Common::Input::MifareRequest& request);
417
399 /// Returns the led pattern corresponding to this emulated controller 418 /// Returns the led pattern corresponding to this emulated controller
400 LedPattern GetLedPattern() const; 419 LedPattern GetLedPattern() const;
401 420
@@ -532,6 +551,7 @@ private:
532 bool system_buttons_enabled{true}; 551 bool system_buttons_enabled{true};
533 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 552 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
534 u32 turbo_button_state{0}; 553 u32 turbo_button_state{0};
554 std::size_t nfc_handles{0};
535 555
536 // Temporary values to avoid doing changes while the controller is in configuring mode 556 // Temporary values to avoid doing changes while the controller is in configuring mode
537 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 557 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 4ccb1c596..a05716fd8 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -299,11 +299,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
299 Common::Input::NfcStatus nfc{}; 299 Common::Input::NfcStatus nfc{};
300 switch (callback.type) { 300 switch (callback.type) {
301 case Common::Input::InputType::Nfc: 301 case Common::Input::InputType::Nfc:
302 nfc = { 302 return callback.nfc_status;
303 .state = callback.nfc_status,
304 .data = callback.raw_data,
305 };
306 break;
307 default: 303 default:
308 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); 304 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
309 break; 305 break;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index faa12b4f0..75ce5a23c 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -184,7 +184,8 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
184 prev_highest_thread != highest_thread) [[likely]] { 184 prev_highest_thread != highest_thread) [[likely]] {
185 if (prev_highest_thread != nullptr) [[likely]] { 185 if (prev_highest_thread != nullptr) [[likely]] {
186 IncrementScheduledCount(prev_highest_thread); 186 IncrementScheduledCount(prev_highest_thread);
187 prev_highest_thread->SetLastScheduledTick(m_kernel.System().CoreTiming().GetCPUTicks()); 187 prev_highest_thread->SetLastScheduledTick(
188 m_kernel.System().CoreTiming().GetClockTicks());
188 } 189 }
189 if (m_state.should_count_idle) { 190 if (m_state.should_count_idle) {
190 if (highest_thread != nullptr) [[likely]] { 191 if (highest_thread != nullptr) [[likely]] {
@@ -351,7 +352,7 @@ void KScheduler::SwitchThread(KThread* next_thread) {
351 352
352 // Update the CPU time tracking variables. 353 // Update the CPU time tracking variables.
353 const s64 prev_tick = m_last_context_switch_time; 354 const s64 prev_tick = m_last_context_switch_time;
354 const s64 cur_tick = m_kernel.System().CoreTiming().GetCPUTicks(); 355 const s64 cur_tick = m_kernel.System().CoreTiming().GetClockTicks();
355 const s64 tick_diff = cur_tick - prev_tick; 356 const s64 tick_diff = cur_tick - prev_tick;
356 cur_thread->AddCpuTime(m_core_id, tick_diff); 357 cur_thread->AddCpuTime(m_core_id, tick_diff);
357 if (cur_process != nullptr) { 358 if (cur_process != nullptr) {
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index b7da3eee7..3e5b735b1 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/scratch_buffer.h"
6#include "core/hle/kernel/k_scheduler.h" 7#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
8#include "core/hle/kernel/k_synchronization_object.h" 9#include "core/hle/kernel/k_synchronization_object.h"
@@ -75,7 +76,7 @@ Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
75 KSynchronizationObject** objects, const s32 num_objects, 76 KSynchronizationObject** objects, const s32 num_objects,
76 s64 timeout) { 77 s64 timeout) {
77 // Allocate space on stack for thread nodes. 78 // Allocate space on stack for thread nodes.
78 std::vector<ThreadListNode> thread_nodes(num_objects); 79 std::array<ThreadListNode, Svc::ArgumentHandleCountMax> thread_nodes;
79 80
80 // Prepare for wait. 81 // Prepare for wait.
81 KThread* thread = GetCurrentThreadPointer(kernel); 82 KThread* thread = GetCurrentThreadPointer(kernel);
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 70480b725..adb6ec581 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -4,6 +4,8 @@
4#include <algorithm> 4#include <algorithm>
5#include <atomic> 5#include <atomic>
6#include <cinttypes> 6#include <cinttypes>
7#include <condition_variable>
8#include <mutex>
7#include <optional> 9#include <optional>
8#include <vector> 10#include <vector>
9 11
@@ -907,7 +909,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) {
907 R_SUCCEED(); 909 R_SUCCEED();
908} 910}
909 911
910Result KThread::GetThreadContext3(std::vector<u8>& out) { 912Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) {
911 // Lock ourselves. 913 // Lock ourselves.
912 KScopedLightLock lk{m_activity_pause_lock}; 914 KScopedLightLock lk{m_activity_pause_lock};
913 915
@@ -925,15 +927,13 @@ Result KThread::GetThreadContext3(std::vector<u8>& out) {
925 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. 927 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
926 auto context = GetContext64(); 928 auto context = GetContext64();
927 context.pstate &= 0xFF0FFE20; 929 context.pstate &= 0xFF0FFE20;
928 930 out.resize_destructive(sizeof(context));
929 out.resize(sizeof(context));
930 std::memcpy(out.data(), std::addressof(context), sizeof(context)); 931 std::memcpy(out.data(), std::addressof(context), sizeof(context));
931 } else { 932 } else {
932 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. 933 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
933 auto context = GetContext32(); 934 auto context = GetContext32();
934 context.cpsr &= 0xFF0FFE20; 935 context.cpsr &= 0xFF0FFE20;
935 936 out.resize_destructive(sizeof(context));
936 out.resize(sizeof(context));
937 std::memcpy(out.data(), std::addressof(context), sizeof(context)); 937 std::memcpy(out.data(), std::addressof(context), sizeof(context));
938 } 938 }
939 } 939 }
@@ -1313,7 +1313,8 @@ void KThread::RequestDummyThreadWait() {
1313 ASSERT(this->IsDummyThread()); 1313 ASSERT(this->IsDummyThread());
1314 1314
1315 // We will block when the scheduler lock is released. 1315 // We will block when the scheduler lock is released.
1316 m_dummy_thread_runnable.store(false); 1316 std::scoped_lock lock{m_dummy_thread_mutex};
1317 m_dummy_thread_runnable = false;
1317} 1318}
1318 1319
1319void KThread::DummyThreadBeginWait() { 1320void KThread::DummyThreadBeginWait() {
@@ -1323,7 +1324,8 @@ void KThread::DummyThreadBeginWait() {
1323 } 1324 }
1324 1325
1325 // Block until runnable is no longer false. 1326 // Block until runnable is no longer false.
1326 m_dummy_thread_runnable.wait(false); 1327 std::unique_lock lock{m_dummy_thread_mutex};
1328 m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; });
1327} 1329}
1328 1330
1329void KThread::DummyThreadEndWait() { 1331void KThread::DummyThreadEndWait() {
@@ -1331,8 +1333,11 @@ void KThread::DummyThreadEndWait() {
1331 ASSERT(this->IsDummyThread()); 1333 ASSERT(this->IsDummyThread());
1332 1334
1333 // Wake up the waiting thread. 1335 // Wake up the waiting thread.
1334 m_dummy_thread_runnable.store(true); 1336 {
1335 m_dummy_thread_runnable.notify_one(); 1337 std::scoped_lock lock{m_dummy_thread_mutex};
1338 m_dummy_thread_runnable = true;
1339 }
1340 m_dummy_thread_cv.notify_one();
1336} 1341}
1337 1342
1338void KThread::BeginWait(KThreadQueue* queue) { 1343void KThread::BeginWait(KThreadQueue* queue) {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f9814ac8f..dd662b3f8 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -15,6 +15,7 @@
15#include "common/intrusive_list.h" 15#include "common/intrusive_list.h"
16 16
17#include "common/intrusive_red_black_tree.h" 17#include "common/intrusive_red_black_tree.h"
18#include "common/scratch_buffer.h"
18#include "common/spin_lock.h" 19#include "common/spin_lock.h"
19#include "core/arm/arm_interface.h" 20#include "core/arm/arm_interface.h"
20#include "core/hle/kernel/k_affinity_mask.h" 21#include "core/hle/kernel/k_affinity_mask.h"
@@ -567,7 +568,7 @@ public:
567 568
568 void RemoveWaiter(KThread* thread); 569 void RemoveWaiter(KThread* thread);
569 570
570 Result GetThreadContext3(std::vector<u8>& out); 571 Result GetThreadContext3(Common::ScratchBuffer<u8>& out);
571 572
572 KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) { 573 KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) {
573 return this->RemoveWaiterByKey(out_has_waiters, key, false); 574 return this->RemoveWaiterByKey(out_has_waiters, key, false);
@@ -892,7 +893,9 @@ private:
892 std::shared_ptr<Common::Fiber> m_host_context{}; 893 std::shared_ptr<Common::Fiber> m_host_context{};
893 ThreadType m_thread_type{}; 894 ThreadType m_thread_type{};
894 StepState m_step_state{}; 895 StepState m_step_state{};
895 std::atomic<bool> m_dummy_thread_runnable{true}; 896 bool m_dummy_thread_runnable{true};
897 std::mutex m_dummy_thread_mutex{};
898 std::condition_variable m_dummy_thread_cv{};
896 899
897 // For debugging 900 // For debugging
898 std::vector<KSynchronizationObject*> m_wait_objects_for_debugging{}; 901 std::vector<KSynchronizationObject*> m_wait_objects_for_debugging{};
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index 2b2c878b5..445cdd87b 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -199,9 +199,9 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
199 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { 199 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
200 const u64 thread_ticks = current_thread->GetCpuTime(); 200 const u64 thread_ticks = current_thread->GetCpuTime();
201 201
202 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); 202 out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
203 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { 203 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
204 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; 204 out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
205 } 205 }
206 206
207 *result = out_ticks; 207 *result = out_ticks;
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index ea03068aa..60247df2e 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "common/scratch_buffer.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/hle/kernel/k_client_session.h" 7#include "core/hle/kernel/k_client_session.h"
7#include "core/hle/kernel/k_process.h" 8#include "core/hle/kernel/k_process.h"
@@ -45,11 +46,11 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
45 handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), 46 handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)),
46 ResultInvalidPointer); 47 ResultInvalidPointer);
47 48
48 std::vector<Handle> handles(num_handles); 49 std::array<Handle, Svc::ArgumentHandleCountMax> handles;
49 GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles); 50 GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles);
50 51
51 // Convert handle list to object table. 52 // Convert handle list to object table.
52 std::vector<KSynchronizationObject*> objs(num_handles); 53 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs;
53 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(), 54 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(),
54 num_handles), 55 num_handles),
55 ResultInvalidHandle); 56 ResultInvalidHandle);
@@ -80,7 +81,7 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
80 // Wait for an object. 81 // Wait for an object.
81 s32 index; 82 s32 index;
82 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), 83 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
83 static_cast<s32>(objs.size()), timeout_ns); 84 num_handles, timeout_ns);
84 if (result == ResultTimedOut) { 85 if (result == ResultTimedOut) {
85 R_RETURN(result); 86 R_RETURN(result);
86 } 87 }
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
index 04d65f0bd..53df5bcd8 100644
--- a/src/core/hle/kernel/svc/svc_synchronization.cpp
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "common/scratch_buffer.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/hle/kernel/k_process.h" 7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_readable_event.h" 8#include "core/hle/kernel/k_readable_event.h"
@@ -54,7 +55,7 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons
54 // Get the synchronization context. 55 // Get the synchronization context.
55 auto& kernel = system.Kernel(); 56 auto& kernel = system.Kernel();
56 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); 57 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
57 std::vector<KSynchronizationObject*> objs(num_handles); 58 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs;
58 59
59 // Copy user handles. 60 // Copy user handles.
60 if (num_handles > 0) { 61 if (num_handles > 0) {
@@ -72,8 +73,8 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons
72 }); 73 });
73 74
74 // Wait on the objects. 75 // Wait on the objects.
75 Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), 76 Result res =
76 static_cast<s32>(objs.size()), timeout_ns); 77 KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout_ns);
77 78
78 R_SUCCEED_IF(res == ResultSessionClosed); 79 R_SUCCEED_IF(res == ResultSessionClosed);
79 R_RETURN(res); 80 R_RETURN(res);
@@ -87,8 +88,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
87 88
88 // Ensure number of handles is valid. 89 // Ensure number of handles is valid.
89 R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); 90 R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
90 91 std::array<Handle, Svc::ArgumentHandleCountMax> handles;
91 std::vector<Handle> handles(num_handles);
92 if (num_handles > 0) { 92 if (num_handles > 0) {
93 GetCurrentMemory(system.Kernel()) 93 GetCurrentMemory(system.Kernel())
94 .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle)); 94 .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle));
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index 37b54079c..36b94e6bf 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -174,7 +174,7 @@ Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_ha
174 } 174 }
175 175
176 // Get the thread context. 176 // Get the thread context.
177 std::vector<u8> context; 177 static thread_local Common::ScratchBuffer<u8> context;
178 R_TRY(thread->GetThreadContext3(context)); 178 R_TRY(thread->GetThreadContext3(context));
179 179
180 // Copy the thread context to user space. 180 // Copy the thread context to user space.
diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp
index 561336482..7dd7c6e51 100644
--- a/src/core/hle/kernel/svc/svc_tick.cpp
+++ b/src/core/hle/kernel/svc/svc_tick.cpp
@@ -12,16 +12,8 @@ namespace Kernel::Svc {
12int64_t GetSystemTick(Core::System& system) { 12int64_t GetSystemTick(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called"); 13 LOG_TRACE(Kernel_SVC, "called");
14 14
15 auto& core_timing = system.CoreTiming();
16
17 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) 15 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
18 const u64 result{core_timing.GetClockTicks()}; 16 return static_cast<int64_t>(system.CoreTiming().GetClockTicks());
19
20 if (!system.Kernel().IsMulticore()) {
21 core_timing.AddTicks(400U);
22 }
23
24 return static_cast<int64_t>(result);
25} 17}
26 18
27int64_t GetSystemTick64(Core::System& system) { 19int64_t GetSystemTick64(Core::System& system) {
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index 8b754e9d4..19ed184e8 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -141,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
141 applet_output.device_handle = applet_input_common.device_handle; 141 applet_output.device_handle = applet_input_common.device_handle;
142 applet_output.result = CabinetResult::Cancel; 142 applet_output.result = CabinetResult::Cancel;
143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); 143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false); 144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
145 nfp_device->Finalize(); 145 nfp_device->Finalize();
146 146
147 if (reg_result.IsSuccess()) { 147 if (reg_result.IsSuccess()) {
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index f0640c64f..c8d574993 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -5,6 +5,7 @@
5#include "audio_core/renderer/audio_device.h" 5#include "audio_core/renderer/audio_device.h"
6#include "common/common_funcs.h" 6#include "common/common_funcs.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/settings.h"
8#include "common/string_util.h" 9#include "common/string_util.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
@@ -123,19 +124,13 @@ private:
123 124
124 void GetReleasedAudioInBuffer(HLERequestContext& ctx) { 125 void GetReleasedAudioInBuffer(HLERequestContext& ctx) {
125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); 126 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
126 std::vector<u64> released_buffers(write_buffer_size); 127 tmp_buffer.resize_destructive(write_buffer_size);
128 tmp_buffer[0] = 0;
127 129
128 const auto count = impl->GetReleasedBuffers(released_buffers); 130 const auto count = impl->GetReleasedBuffers(tmp_buffer);
129 131
130 [[maybe_unused]] std::string tags{}; 132 ctx.WriteBuffer(tmp_buffer);
131 for (u32 i = 0; i < count; i++) {
132 tags += fmt::format("{:08X}, ", released_buffers[i]);
133 }
134 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
135 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
136 tags);
137 133
138 ctx.WriteBuffer(released_buffers);
139 IPC::ResponseBuilder rb{ctx, 3}; 134 IPC::ResponseBuilder rb{ctx, 3};
140 rb.Push(ResultSuccess); 135 rb.Push(ResultSuccess);
141 rb.Push(count); 136 rb.Push(count);
@@ -200,6 +195,7 @@ private:
200 KernelHelpers::ServiceContext service_context; 195 KernelHelpers::ServiceContext service_context;
201 Kernel::KEvent* event; 196 Kernel::KEvent* event;
202 std::shared_ptr<AudioCore::AudioIn::In> impl; 197 std::shared_ptr<AudioCore::AudioIn::In> impl;
198 Common::ScratchBuffer<u64> tmp_buffer;
203}; 199};
204 200
205AudInU::AudInU(Core::System& system_) 201AudInU::AudInU(Core::System& system_)
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 3e62fa4fc..032c8c11f 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -123,19 +123,13 @@ private:
123 123
124 void GetReleasedAudioOutBuffers(HLERequestContext& ctx) { 124 void GetReleasedAudioOutBuffers(HLERequestContext& ctx) {
125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); 125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
126 std::vector<u64> released_buffers(write_buffer_size); 126 tmp_buffer.resize_destructive(write_buffer_size);
127 tmp_buffer[0] = 0;
127 128
128 const auto count = impl->GetReleasedBuffers(released_buffers); 129 const auto count = impl->GetReleasedBuffers(tmp_buffer);
129 130
130 [[maybe_unused]] std::string tags{}; 131 ctx.WriteBuffer(tmp_buffer);
131 for (u32 i = 0; i < count; i++) {
132 tags += fmt::format("{:08X}, ", released_buffers[i]);
133 }
134 [[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()};
135 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
136 tags);
137 132
138 ctx.WriteBuffer(released_buffers);
139 IPC::ResponseBuilder rb{ctx, 3}; 133 IPC::ResponseBuilder rb{ctx, 3};
140 rb.Push(ResultSuccess); 134 rb.Push(ResultSuccess);
141 rb.Push(count); 135 rb.Push(count);
@@ -211,6 +205,7 @@ private:
211 KernelHelpers::ServiceContext service_context; 205 KernelHelpers::ServiceContext service_context;
212 Kernel::KEvent* event; 206 Kernel::KEvent* event;
213 std::shared_ptr<AudioCore::AudioOut::Out> impl; 207 std::shared_ptr<AudioCore::AudioOut::Out> impl;
208 Common::ScratchBuffer<u64> tmp_buffer;
214}; 209};
215 210
216AudOutU::AudOutU(Core::System& system_) 211AudOutU::AudOutU(Core::System& system_)
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 7086d4750..12845c23a 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -116,28 +116,26 @@ private:
116 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for 116 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
117 // checking size 0. Performance size is 0 for most games. 117 // checking size 0. Performance size is 0 for most games.
118 118
119 std::vector<u8> output{};
120 std::vector<u8> performance{};
121 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; 119 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
122 if (is_buffer_b) { 120 if (is_buffer_b) {
123 const auto buffersB{ctx.BufferDescriptorB()}; 121 const auto buffersB{ctx.BufferDescriptorB()};
124 output.resize(buffersB[0].Size(), 0); 122 tmp_output.resize_destructive(buffersB[0].Size());
125 performance.resize(buffersB[1].Size(), 0); 123 tmp_performance.resize_destructive(buffersB[1].Size());
126 } else { 124 } else {
127 const auto buffersC{ctx.BufferDescriptorC()}; 125 const auto buffersC{ctx.BufferDescriptorC()};
128 output.resize(buffersC[0].Size(), 0); 126 tmp_output.resize_destructive(buffersC[0].Size());
129 performance.resize(buffersC[1].Size(), 0); 127 tmp_performance.resize_destructive(buffersC[1].Size());
130 } 128 }
131 129
132 auto result = impl->RequestUpdate(input, performance, output); 130 auto result = impl->RequestUpdate(input, tmp_performance, tmp_output);
133 131
134 if (result.IsSuccess()) { 132 if (result.IsSuccess()) {
135 if (is_buffer_b) { 133 if (is_buffer_b) {
136 ctx.WriteBufferB(output.data(), output.size(), 0); 134 ctx.WriteBufferB(tmp_output.data(), tmp_output.size(), 0);
137 ctx.WriteBufferB(performance.data(), performance.size(), 1); 135 ctx.WriteBufferB(tmp_performance.data(), tmp_performance.size(), 1);
138 } else { 136 } else {
139 ctx.WriteBufferC(output.data(), output.size(), 0); 137 ctx.WriteBufferC(tmp_output.data(), tmp_output.size(), 0);
140 ctx.WriteBufferC(performance.data(), performance.size(), 1); 138 ctx.WriteBufferC(tmp_performance.data(), tmp_performance.size(), 1);
141 } 139 }
142 } else { 140 } else {
143 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); 141 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
@@ -235,6 +233,8 @@ private:
235 Kernel::KEvent* rendered_event; 233 Kernel::KEvent* rendered_event;
236 Manager& manager; 234 Manager& manager;
237 std::unique_ptr<Renderer> impl; 235 std::unique_ptr<Renderer> impl;
236 Common::ScratchBuffer<u8> tmp_output;
237 Common::ScratchBuffer<u8> tmp_performance;
238}; 238};
239 239
240class IAudioDevice final : public ServiceFramework<IAudioDevice> { 240class IAudioDevice final : public ServiceFramework<IAudioDevice> {
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 24ce37e87..d8e9c8719 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "audio_core/audio_render_manager.h" 6#include "audio_core/audio_render_manager.h"
7#include "common/scratch_buffer.h"
7#include "core/hle/service/kernel_helpers.h" 8#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 451ac224a..c835f6cb7 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -68,13 +68,13 @@ private:
68 ExtraBehavior extra_behavior) { 68 ExtraBehavior extra_behavior) {
69 u32 consumed = 0; 69 u32 consumed = 0;
70 u32 sample_count = 0; 70 u32 sample_count = 0;
71 std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>()); 71 tmp_samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>());
72 72
73 if (extra_behavior == ExtraBehavior::ResetContext) { 73 if (extra_behavior == ExtraBehavior::ResetContext) {
74 ResetDecoderContext(); 74 ResetDecoderContext();
75 } 75 }
76 76
77 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { 77 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), tmp_samples, performance)) {
78 LOG_ERROR(Audio, "Failed to decode opus data"); 78 LOG_ERROR(Audio, "Failed to decode opus data");
79 IPC::ResponseBuilder rb{ctx, 2}; 79 IPC::ResponseBuilder rb{ctx, 2};
80 // TODO(ogniK): Use correct error code 80 // TODO(ogniK): Use correct error code
@@ -90,11 +90,11 @@ private:
90 if (performance) { 90 if (performance) {
91 rb.Push<u64>(*performance); 91 rb.Push<u64>(*performance);
92 } 92 }
93 ctx.WriteBuffer(samples); 93 ctx.WriteBuffer(tmp_samples);
94 } 94 }
95 95
96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, 96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input,
97 std::vector<opus_int16>& output, u64* out_performance_time) const { 97 std::span<opus_int16> output, u64* out_performance_time) const {
98 const auto start_time = std::chrono::steady_clock::now(); 98 const auto start_time = std::chrono::steady_clock::now();
99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
100 if (sizeof(OpusPacketHeader) > input.size()) { 100 if (sizeof(OpusPacketHeader) > input.size()) {
@@ -154,6 +154,7 @@ private:
154 OpusDecoderPtr decoder; 154 OpusDecoderPtr decoder;
155 u32 sample_rate; 155 u32 sample_rate;
156 u32 channel_count; 156 u32 channel_count;
157 Common::ScratchBuffer<opus_int16> tmp_samples;
157}; 158};
158 159
159class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 160class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 5604a6fda..80aac221b 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -5,7 +5,6 @@
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
10#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
11#include "core/hle/kernel/k_readable_event.h" 10#include "core/hle/kernel/k_readable_event.h"
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index b2bcb68c3..bc232c334 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
@@ -36,12 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
36 36
37 // Validate UUID 37 // Validate UUID
38 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` 38 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
39 if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != 39 if ((CT ^ ntag_file.uuid.part1[0] ^ ntag_file.uuid.part1[1] ^ ntag_file.uuid.part1[2]) !=
40 ntag_file.uuid.uid[3]) { 40 ntag_file.uuid.crc_check1) {
41 return false; 41 return false;
42 } 42 }
43 if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ 43 if ((ntag_file.uuid.part2[0] ^ ntag_file.uuid.part2[1] ^ ntag_file.uuid.part2[2] ^
44 ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { 44 ntag_file.uuid.nintendo_id) != ntag_file.uuid_crc_check2) {
45 return false; 45 return false;
46 } 46 }
47 47
@@ -74,8 +74,9 @@ bool IsAmiiboValid(const NTAG215File& ntag_file) {
74NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { 74NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
75 NTAG215File encoded_data{}; 75 NTAG215File encoded_data{};
76 76
77 encoded_data.uid = nfc_data.uuid.uid; 77 encoded_data.uid = nfc_data.uuid;
78 encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; 78 encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2;
79 encoded_data.internal_number = nfc_data.internal_number;
79 encoded_data.static_lock = nfc_data.static_lock; 80 encoded_data.static_lock = nfc_data.static_lock;
80 encoded_data.compability_container = nfc_data.compability_container; 81 encoded_data.compability_container = nfc_data.compability_container;
81 encoded_data.hmac_data = nfc_data.user_memory.hmac_data; 82 encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
@@ -94,7 +95,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
94 encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; 95 encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc;
95 encoded_data.application_area = nfc_data.user_memory.application_area; 96 encoded_data.application_area = nfc_data.user_memory.application_area;
96 encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; 97 encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
97 encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
98 encoded_data.model_info = nfc_data.user_memory.model_info; 98 encoded_data.model_info = nfc_data.user_memory.model_info;
99 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; 99 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
100 encoded_data.dynamic_lock = nfc_data.dynamic_lock; 100 encoded_data.dynamic_lock = nfc_data.dynamic_lock;
@@ -108,9 +108,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
108EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { 108EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
109 EncryptedNTAG215File nfc_data{}; 109 EncryptedNTAG215File nfc_data{};
110 110
111 nfc_data.uuid.uid = encoded_data.uid; 111 nfc_data.uuid = encoded_data.uid;
112 nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; 112 nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2;
113 nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; 113 nfc_data.internal_number = encoded_data.internal_number;
114 nfc_data.static_lock = encoded_data.static_lock; 114 nfc_data.static_lock = encoded_data.static_lock;
115 nfc_data.compability_container = encoded_data.compability_container; 115 nfc_data.compability_container = encoded_data.compability_container;
116 nfc_data.user_memory.hmac_data = encoded_data.hmac_data; 116 nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
@@ -139,23 +139,12 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
139 return nfc_data; 139 return nfc_data;
140} 140}
141 141
142u32 GetTagPassword(const TagUuid& uuid) {
143 // Verify that the generated password is correct
144 u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
145 password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
146 password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
147 password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
148 return password;
149}
150
151HashSeed GetSeed(const NTAG215File& data) { 142HashSeed GetSeed(const NTAG215File& data) {
152 HashSeed seed{ 143 HashSeed seed{
153 .magic = data.write_counter, 144 .magic = data.write_counter,
154 .padding = {}, 145 .padding = {},
155 .uid_1 = data.uid, 146 .uid_1 = data.uid,
156 .nintendo_id_1 = data.nintendo_id,
157 .uid_2 = data.uid, 147 .uid_2 = data.uid,
158 .nintendo_id_2 = data.nintendo_id,
159 .keygen_salt = data.keygen_salt, 148 .keygen_salt = data.keygen_salt,
160 }; 149 };
161 150
@@ -177,10 +166,11 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
177 output.insert(output.end(), key.magic_bytes.begin(), 166 output.insert(output.end(), key.magic_bytes.begin(),
178 key.magic_bytes.begin() + key.magic_length); 167 key.magic_bytes.begin() + key.magic_length);
179 168
180 output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); 169 std::array<u8, sizeof(NFP::TagUuid)> seed_uuid{};
181 output.emplace_back(seed.nintendo_id_1); 170 memcpy(seed_uuid.data(), &seed.uid_1, sizeof(NFP::TagUuid));
182 output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); 171 output.insert(output.end(), seed_uuid.begin(), seed_uuid.end());
183 output.emplace_back(seed.nintendo_id_2); 172 memcpy(seed_uuid.data(), &seed.uid_2, sizeof(NFP::TagUuid));
173 output.insert(output.end(), seed_uuid.begin(), seed_uuid.end());
184 174
185 for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { 175 for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
186 output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); 176 output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
@@ -264,8 +254,8 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
264 254
265 // Copy the rest of the data directly 255 // Copy the rest of the data directly
266 out_data.uid = in_data.uid; 256 out_data.uid = in_data.uid;
267 out_data.nintendo_id = in_data.nintendo_id; 257 out_data.uid_crc_check2 = in_data.uid_crc_check2;
268 out_data.lock_bytes = in_data.lock_bytes; 258 out_data.internal_number = in_data.internal_number;
269 out_data.static_lock = in_data.static_lock; 259 out_data.static_lock = in_data.static_lock;
270 out_data.compability_container = in_data.compability_container; 260 out_data.compability_container = in_data.compability_container;
271 261
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h
index bf3044ed9..6a3e0841e 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.h
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.h
@@ -24,10 +24,8 @@ using DrgbOutput = std::array<u8, 0x20>;
24struct HashSeed { 24struct HashSeed {
25 u16_be magic; 25 u16_be magic;
26 std::array<u8, 0xE> padding; 26 std::array<u8, 0xE> padding;
27 NFC::UniqueSerialNumber uid_1; 27 TagUuid uid_1;
28 u8 nintendo_id_1; 28 TagUuid uid_2;
29 NFC::UniqueSerialNumber uid_2;
30 u8 nintendo_id_2;
31 std::array<u8, 0x20> keygen_salt; 29 std::array<u8, 0x20> keygen_salt;
32}; 30};
33static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); 31static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
@@ -69,9 +67,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data);
69/// Converts from encoded file format to encrypted file format 67/// Converts from encoded file format to encrypted file format
70EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); 68EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
71 69
72/// Returns password needed to allow write access to protected memory
73u32 GetTagPassword(const TagUuid& uuid);
74
75// Generates Seed needed for key derivation 70// Generates Seed needed for key derivation
76HashSeed GetSeed(const NTAG215File& data); 71HashSeed GetSeed(const NTAG215File& data);
77 72
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index b14f682b5..5bf289818 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -93,7 +93,8 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
93 const auto nfc_status = npad_device->GetNfc(); 93 const auto nfc_status = npad_device->GetNfc();
94 switch (nfc_status.state) { 94 switch (nfc_status.state) {
95 case Common::Input::NfcState::NewAmiibo: 95 case Common::Input::NfcState::NewAmiibo:
96 LoadNfcTag(nfc_status.data); 96 LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length,
97 nfc_status.uuid);
97 break; 98 break;
98 case Common::Input::NfcState::AmiiboRemoved: 99 case Common::Input::NfcState::AmiiboRemoved:
99 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 100 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
@@ -108,28 +109,46 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
108 } 109 }
109} 110}
110 111
111bool NfcDevice::LoadNfcTag(std::span<const u8> data) { 112bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) {
112 if (device_state != DeviceState::SearchingForTag) { 113 if (device_state != DeviceState::SearchingForTag) {
113 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); 114 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
114 return false; 115 return false;
115 } 116 }
116 117
118 if ((protocol & static_cast<u8>(allowed_protocols)) == 0) {
119 LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol);
120 return false;
121 }
122
123 real_tag_info = {
124 .uuid = uuid,
125 .uuid_length = uuid_length,
126 .protocol = static_cast<NfcProtocol>(protocol),
127 .tag_type = static_cast<TagType>(tag_type),
128 };
129
130 device_state = DeviceState::TagFound;
131 deactivate_event->GetReadableEvent().Clear();
132 activate_event->Signal();
133 return true;
134}
135
136bool NfcDevice::LoadAmiiboData() {
137 std::vector<u8> data{};
138
139 if (!npad_device->ReadAmiiboData(data)) {
140 return false;
141 }
142
117 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { 143 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
118 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); 144 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
119 return false; 145 return false;
120 } 146 }
121 147
122 mifare_data.resize(data.size());
123 memcpy(mifare_data.data(), data.data(), data.size());
124
125 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 148 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
126 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data); 149 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data);
127 is_write_protected = false; 150 is_write_protected = false;
128 151
129 device_state = DeviceState::TagFound;
130 deactivate_event->GetReadableEvent().Clear();
131 activate_event->Signal();
132
133 // Fallback for plain amiibos 152 // Fallback for plain amiibos
134 if (is_plain_amiibo) { 153 if (is_plain_amiibo) {
135 LOG_INFO(Service_NFP, "Using plain amiibo"); 154 LOG_INFO(Service_NFP, "Using plain amiibo");
@@ -147,6 +166,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
147 return true; 166 return true;
148 } 167 }
149 168
169 LOG_INFO(Service_NFP, "Using encrypted amiibo");
150 tag_data = {}; 170 tag_data = {};
151 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 171 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
152 return true; 172 return true;
@@ -162,7 +182,6 @@ void NfcDevice::CloseNfcTag() {
162 device_state = DeviceState::TagRemoved; 182 device_state = DeviceState::TagRemoved;
163 encrypted_tag_data = {}; 183 encrypted_tag_data = {};
164 tag_data = {}; 184 tag_data = {};
165 mifare_data = {};
166 activate_event->GetReadableEvent().Clear(); 185 activate_event->GetReadableEvent().Clear();
167 deactivate_event->Signal(); 186 deactivate_event->Signal();
168} 187}
@@ -179,8 +198,12 @@ void NfcDevice::Initialize() {
179 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; 198 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
180 encrypted_tag_data = {}; 199 encrypted_tag_data = {};
181 tag_data = {}; 200 tag_data = {};
182 mifare_data = {}; 201
183 is_initalized = true; 202 if (device_state != DeviceState::Initialized) {
203 return;
204 }
205
206 is_initalized = npad_device->AddNfcHandle();
184} 207}
185 208
186void NfcDevice::Finalize() { 209void NfcDevice::Finalize() {
@@ -190,6 +213,11 @@ void NfcDevice::Finalize() {
190 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 213 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
191 StopDetection(); 214 StopDetection();
192 } 215 }
216
217 if (device_state != DeviceState::Unavailable) {
218 npad_device->RemoveNfcHandle();
219 }
220
193 device_state = DeviceState::Unavailable; 221 device_state = DeviceState::Unavailable;
194 is_initalized = false; 222 is_initalized = false;
195} 223}
@@ -200,10 +228,8 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
200 return ResultWrongDeviceState; 228 return ResultWrongDeviceState;
201 } 229 }
202 230
203 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 231 if (!npad_device->StartNfcPolling()) {
204 Common::Input::PollingMode::NFC) != 232 LOG_ERROR(Service_NFC, "Nfc polling not supported");
205 Common::Input::DriverResult::Success) {
206 LOG_ERROR(Service_NFC, "Nfc not supported");
207 return ResultNfcDisabled; 233 return ResultNfcDisabled;
208 } 234 }
209 235
@@ -213,9 +239,6 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
213} 239}
214 240
215Result NfcDevice::StopDetection() { 241Result NfcDevice::StopDetection() {
216 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
217 Common::Input::PollingMode::Active);
218
219 if (device_state == DeviceState::Initialized) { 242 if (device_state == DeviceState::Initialized) {
220 return ResultSuccess; 243 return ResultSuccess;
221 } 244 }
@@ -225,6 +248,7 @@ Result NfcDevice::StopDetection() {
225 } 248 }
226 249
227 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 250 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
251 npad_device->StopNfcPolling();
228 device_state = DeviceState::Initialized; 252 device_state = DeviceState::Initialized;
229 return ResultSuccess; 253 return ResultSuccess;
230 } 254 }
@@ -233,7 +257,7 @@ Result NfcDevice::StopDetection() {
233 return ResultWrongDeviceState; 257 return ResultWrongDeviceState;
234} 258}
235 259
236Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { 260Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
237 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { 261 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
238 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 262 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
239 if (device_state == DeviceState::TagRemoved) { 263 if (device_state == DeviceState::TagRemoved) {
@@ -242,72 +266,78 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
242 return ResultWrongDeviceState; 266 return ResultWrongDeviceState;
243 } 267 }
244 268
245 UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid; 269 tag_info = real_tag_info;
246 270
247 // Generate random UUID to bypass amiibo load limits 271 // Generate random UUID to bypass amiibo load limits
248 if (Settings::values.random_amiibo_id) { 272 if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) {
249 Common::TinyMT rng{}; 273 Common::TinyMT rng{};
250 rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); 274 rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
251 rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber)); 275 rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length);
252 uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2];
253 } 276 }
254 277
255 if (is_mifare) {
256 tag_info = {
257 .uuid = uuid,
258 .uuid_extension = {},
259 .uuid_length = static_cast<u8>(uuid.size()),
260 .protocol = NfcProtocol::TypeA,
261 .tag_type = TagType::Type4,
262 };
263 return ResultSuccess;
264 }
265
266 // Protocol and tag type may change here
267 tag_info = {
268 .uuid = uuid,
269 .uuid_extension = {},
270 .uuid_length = static_cast<u8>(uuid.size()),
271 .protocol = NfcProtocol::TypeA,
272 .tag_type = TagType::Type2,
273 };
274
275 return ResultSuccess; 278 return ResultSuccess;
276} 279}
277 280
278Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters, 281Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters,
279 std::span<MifareReadBlockData> read_block_data) const { 282 std::span<MifareReadBlockData> read_block_data) const {
283 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
284 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
285 if (device_state == DeviceState::TagRemoved) {
286 return ResultTagRemoved;
287 }
288 return ResultWrongDeviceState;
289 }
290
280 Result result = ResultSuccess; 291 Result result = ResultSuccess;
281 292
282 for (std::size_t i = 0; i < parameters.size(); i++) { 293 TagInfo tag_info{};
283 result = ReadMifare(parameters[i], read_block_data[i]); 294 result = GetTagInfo(tag_info);
284 if (result.IsError()) { 295
285 break; 296 if (result.IsError()) {
286 } 297 return result;
287 } 298 }
288 299
289 return result; 300 if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
290} 301 return ResultInvalidTagType;
302 }
291 303
292Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, 304 if (parameters.size() == 0) {
293 MifareReadBlockData& read_block_data) const { 305 return ResultInvalidArgument;
294 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); 306 }
295 read_block_data.sector_number = parameter.sector_number;
296 307
297 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { 308 Common::Input::MifareRequest request{};
298 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 309 Common::Input::MifareRequest out_data{};
299 if (device_state == DeviceState::TagRemoved) { 310 const auto unknown = parameters[0].sector_key.unknown;
300 return ResultTagRemoved; 311 for (std::size_t i = 0; i < parameters.size(); i++) {
312 if (unknown != parameters[i].sector_key.unknown) {
313 return ResultInvalidArgument;
301 } 314 }
302 return ResultWrongDeviceState;
303 } 315 }
304 316
305 if (mifare_data.size() < sector_index + sizeof(DataBlock)) { 317 for (std::size_t i = 0; i < parameters.size(); i++) {
306 return Mifare::ResultReadError; 318 if (parameters[i].sector_key.command == MifareCmd::None) {
319 continue;
320 }
321 request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
322 request.data[i].sector = parameters[i].sector_number;
323 memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
324 sizeof(KeyData));
307 } 325 }
308 326
309 // TODO: Use parameter.sector_key to read encrypted data 327 if (!npad_device->ReadMifareData(request, out_data)) {
310 memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); 328 return ResultMifareError288;
329 }
330
331 for (std::size_t i = 0; i < read_block_data.size(); i++) {
332 if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) {
333 continue;
334 }
335
336 read_block_data[i] = {
337 .data = out_data.data[i].data,
338 .sector_number = out_data.data[i].sector,
339 };
340 }
311 341
312 return ResultSuccess; 342 return ResultSuccess;
313} 343}
@@ -315,40 +345,45 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
315Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) { 345Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
316 Result result = ResultSuccess; 346 Result result = ResultSuccess;
317 347
318 for (std::size_t i = 0; i < parameters.size(); i++) { 348 TagInfo tag_info{};
319 result = WriteMifare(parameters[i]); 349 result = GetTagInfo(tag_info);
320 if (result.IsError()) {
321 break;
322 }
323 }
324 350
325 if (!npad_device->WriteNfc(mifare_data)) { 351 if (result.IsError()) {
326 LOG_ERROR(Service_NFP, "Error writing to file"); 352 return result;
327 return Mifare::ResultReadError;
328 } 353 }
329 354
330 return result; 355 if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
331} 356 return ResultInvalidTagType;
357 }
332 358
333Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) { 359 if (parameters.size() == 0) {
334 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); 360 return ResultInvalidArgument;
361 }
335 362
336 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { 363 const auto unknown = parameters[0].sector_key.unknown;
337 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 364 for (std::size_t i = 0; i < parameters.size(); i++) {
338 if (device_state == DeviceState::TagRemoved) { 365 if (unknown != parameters[i].sector_key.unknown) {
339 return ResultTagRemoved; 366 return ResultInvalidArgument;
340 } 367 }
341 return ResultWrongDeviceState;
342 } 368 }
343 369
344 if (mifare_data.size() < sector_index + sizeof(DataBlock)) { 370 Common::Input::MifareRequest request{};
345 return Mifare::ResultReadError; 371 for (std::size_t i = 0; i < parameters.size(); i++) {
372 if (parameters[i].sector_key.command == MifareCmd::None) {
373 continue;
374 }
375 request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
376 request.data[i].sector = parameters[i].sector_number;
377 memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
378 sizeof(KeyData));
379 memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData));
346 } 380 }
347 381
348 // TODO: Use parameter.sector_key to encrypt the data 382 if (!npad_device->WriteMifareData(request)) {
349 memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock)); 383 return ResultMifareError288;
384 }
350 385
351 return ResultSuccess; 386 return result;
352} 387}
353 388
354Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 389Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
@@ -364,9 +399,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
364 return ResultWrongDeviceState; 399 return ResultWrongDeviceState;
365 } 400 }
366 401
402 if (!LoadAmiiboData()) {
403 LOG_ERROR(Service_NFP, "Not an amiibo");
404 return ResultInvalidTagType;
405 }
406
367 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { 407 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
368 LOG_ERROR(Service_NFP, "Not an amiibo"); 408 LOG_ERROR(Service_NFP, "Not an amiibo");
369 return ResultNotAnAmiibo; 409 return ResultInvalidTagType;
370 } 410 }
371 411
372 // The loaded amiibo is not encrypted 412 // The loaded amiibo is not encrypted
@@ -381,14 +421,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
381 } 421 }
382 422
383 if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { 423 if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
384 bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess(); 424 bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
385 LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); 425 LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
386 return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; 426 return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
387 } 427 }
388 428
389 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); 429 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
390 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); 430 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
391 WriteBackupData(encrypted_tag_data.uuid.uid, data); 431 WriteBackupData(encrypted_tag_data.uuid, data);
392 432
393 device_state = DeviceState::TagMounted; 433 device_state = DeviceState::TagMounted;
394 mount_target = mount_target_; 434 mount_target = mount_target_;
@@ -492,7 +532,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
492 } 532 }
493 533
494 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); 534 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
495 WriteBackupData(encrypted_tag_data.uuid.uid, data); 535 WriteBackupData(encrypted_tag_data.uuid, data);
496 } 536 }
497 537
498 if (!npad_device->WriteNfc(data)) { 538 if (!npad_device->WriteNfc(data)) {
@@ -514,13 +554,13 @@ Result NfcDevice::Restore() {
514 554
515 NFC::TagInfo tag_info{}; 555 NFC::TagInfo tag_info{};
516 std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; 556 std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
517 Result result = GetTagInfo(tag_info, false); 557 Result result = GetTagInfo(tag_info);
518 558
519 if (result.IsError()) { 559 if (result.IsError()) {
520 return result; 560 return result;
521 } 561 }
522 562
523 result = ReadBackupData(tag_info.uuid, data); 563 result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
524 564
525 if (result.IsError()) { 565 if (result.IsError()) {
526 return result; 566 return result;
@@ -548,7 +588,7 @@ Result NfcDevice::Restore() {
548 } 588 }
549 589
550 if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) { 590 if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
551 return ResultNotAnAmiibo; 591 return ResultInvalidTagType;
552 } 592 }
553 593
554 if (!is_plain_amiibo) { 594 if (!is_plain_amiibo) {
@@ -587,7 +627,7 @@ Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {
587 // TODO: Validate this data 627 // TODO: Validate this data
588 common_info = { 628 common_info = {
589 .last_write_date = settings.write_date.GetWriteDate(), 629 .last_write_date = settings.write_date.GetWriteDate(),
590 .write_counter = tag_data.write_counter, 630 .write_counter = tag_data.application_write_counter,
591 .version = tag_data.amiibo_version, 631 .version = tag_data.amiibo_version,
592 .application_area_size = sizeof(NFP::ApplicationArea), 632 .application_area_size = sizeof(NFP::ApplicationArea),
593 }; 633 };
@@ -1194,10 +1234,12 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) {
1194 return FlushWithBreak(break_type); 1234 return FlushWithBreak(break_type);
1195} 1235}
1196 1236
1197Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { 1237Result NfcDevice::HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const {
1238 ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
1198 constexpr auto backup_dir = "backup"; 1239 constexpr auto backup_dir = "backup";
1199 const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); 1240 const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
1200 const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); 1241 const auto file_name =
1242 fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
1201 1243
1202 if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { 1244 if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
1203 return ResultUnableToAccessBackupFile; 1245 return ResultUnableToAccessBackupFile;
@@ -1206,10 +1248,19 @@ Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
1206 return ResultSuccess; 1248 return ResultSuccess;
1207} 1249}
1208 1250
1209Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const { 1251Result NfcDevice::HasBackup(const NFP::TagUuid& tag_uid) const {
1252 UniqueSerialNumber uuid{};
1253 memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
1254 return HasBackup(uuid, sizeof(NFP::TagUuid));
1255}
1256
1257Result NfcDevice::ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
1258 std::span<u8> data) const {
1259 ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
1210 constexpr auto backup_dir = "backup"; 1260 constexpr auto backup_dir = "backup";
1211 const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); 1261 const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
1212 const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); 1262 const auto file_name =
1263 fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
1213 1264
1214 const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, 1265 const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
1215 Common::FS::FileAccessMode::Read, 1266 Common::FS::FileAccessMode::Read,
@@ -1228,12 +1279,21 @@ Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u
1228 return ResultSuccess; 1279 return ResultSuccess;
1229} 1280}
1230 1281
1231Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) { 1282Result NfcDevice::ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const {
1283 UniqueSerialNumber uuid{};
1284 memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
1285 return ReadBackupData(uuid, sizeof(NFP::TagUuid), data);
1286}
1287
1288Result NfcDevice::WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
1289 std::span<const u8> data) {
1290 ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
1232 constexpr auto backup_dir = "backup"; 1291 constexpr auto backup_dir = "backup";
1233 const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); 1292 const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
1234 const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); 1293 const auto file_name =
1294 fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
1235 1295
1236 if (HasBackup(uid).IsError()) { 1296 if (HasBackup(uid, uuid_size).IsError()) {
1237 if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) { 1297 if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
1238 return ResultBackupPathAlreadyExist; 1298 return ResultBackupPathAlreadyExist;
1239 } 1299 }
@@ -1260,6 +1320,12 @@ Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<
1260 return ResultSuccess; 1320 return ResultSuccess;
1261} 1321}
1262 1322
1323Result NfcDevice::WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data) {
1324 UniqueSerialNumber uuid{};
1325 memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
1326 return WriteBackupData(uuid, sizeof(NFP::TagUuid), data);
1327}
1328
1263Result NfcDevice::WriteNtf(std::span<const u8> data) { 1329Result NfcDevice::WriteNtf(std::span<const u8> data) {
1264 if (device_state != DeviceState::TagMounted) { 1330 if (device_state != DeviceState::TagMounted) {
1265 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 1331 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 6f049b687..0ed1ff34c 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -42,15 +42,12 @@ public:
42 Result StartDetection(NfcProtocol allowed_protocol); 42 Result StartDetection(NfcProtocol allowed_protocol);
43 Result StopDetection(); 43 Result StopDetection();
44 44
45 Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; 45 Result GetTagInfo(TagInfo& tag_info) const;
46 46
47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters, 47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
48 std::span<MifareReadBlockData> read_block_data) const; 48 std::span<MifareReadBlockData> read_block_data) const;
49 Result ReadMifare(const MifareReadBlockParameter& parameter,
50 MifareReadBlockData& read_block_data) const;
51 49
52 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); 50 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
53 Result WriteMifare(const MifareWriteBlockParameter& parameter);
54 51
55 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 52 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
56 std::span<const u8> command_data, std::span<u8> out_data); 53 std::span<const u8> command_data, std::span<u8> out_data);
@@ -86,9 +83,14 @@ public:
86 Result GetAll(NFP::NfpData& data) const; 83 Result GetAll(NFP::NfpData& data) const;
87 Result SetAll(const NFP::NfpData& data); 84 Result SetAll(const NFP::NfpData& data);
88 Result BreakTag(NFP::BreakType break_type); 85 Result BreakTag(NFP::BreakType break_type);
89 Result HasBackup(const NFC::UniqueSerialNumber& uid) const; 86 Result HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const;
90 Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const; 87 Result HasBackup(const NFP::TagUuid& tag_uid) const;
91 Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data); 88 Result ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
89 std::span<u8> data) const;
90 Result ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const;
91 Result WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
92 std::span<const u8> data);
93 Result WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data);
92 Result WriteNtf(std::span<const u8> data); 94 Result WriteNtf(std::span<const u8> data);
93 95
94 u64 GetHandle() const; 96 u64 GetHandle() const;
@@ -100,7 +102,8 @@ public:
100 102
101private: 103private:
102 void NpadUpdate(Core::HID::ControllerTriggerType type); 104 void NpadUpdate(Core::HID::ControllerTriggerType type);
103 bool LoadNfcTag(std::span<const u8> data); 105 bool LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid);
106 bool LoadAmiiboData();
104 void CloseNfcTag(); 107 void CloseNfcTag();
105 108
106 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; 109 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
@@ -135,8 +138,8 @@ private:
135 bool is_write_protected{}; 138 bool is_write_protected{};
136 NFP::MountTarget mount_target{NFP::MountTarget::None}; 139 NFP::MountTarget mount_target{NFP::MountTarget::None};
137 140
141 TagInfo real_tag_info{};
138 NFP::NTAG215File tag_data{}; 142 NFP::NTAG215File tag_data{};
139 std::vector<u8> mifare_data{};
140 NFP::EncryptedNTAG215File encrypted_tag_data{}; 143 NFP::EncryptedNTAG215File encrypted_tag_data{};
141}; 144};
142 145
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index cffd602df..562f3a28e 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -29,6 +29,9 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex
29} 29}
30 30
31DeviceManager ::~DeviceManager() { 31DeviceManager ::~DeviceManager() {
32 if (is_initialized) {
33 Finalize();
34 }
32 service_context.CloseEvent(availability_change_event); 35 service_context.CloseEvent(availability_change_event);
33} 36}
34 37
@@ -125,14 +128,14 @@ Result DeviceManager::StopDetection(u64 device_handle) {
125 return result; 128 return result;
126} 129}
127 130
128Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { 131Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const {
129 std::scoped_lock lock{mutex}; 132 std::scoped_lock lock{mutex};
130 133
131 std::shared_ptr<NfcDevice> device = nullptr; 134 std::shared_ptr<NfcDevice> device = nullptr;
132 auto result = GetDeviceHandle(device_handle, device); 135 auto result = GetDeviceHandle(device_handle, device);
133 136
134 if (result.IsSuccess()) { 137 if (result.IsSuccess()) {
135 result = device->GetTagInfo(tag_info, is_mifare); 138 result = device->GetTagInfo(tag_info);
136 result = VerifyDeviceResult(device, result); 139 result = VerifyDeviceResult(device, result);
137 } 140 }
138 141
@@ -546,11 +549,11 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
546 NFC::TagInfo tag_info{}; 549 NFC::TagInfo tag_info{};
547 550
548 if (result.IsSuccess()) { 551 if (result.IsSuccess()) {
549 result = device->GetTagInfo(tag_info, false); 552 result = device->GetTagInfo(tag_info);
550 } 553 }
551 554
552 if (result.IsSuccess()) { 555 if (result.IsSuccess()) {
553 result = device->ReadBackupData(tag_info.uuid, data); 556 result = device->ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
554 result = VerifyDeviceResult(device, result); 557 result = VerifyDeviceResult(device, result);
555 } 558 }
556 559
@@ -565,11 +568,11 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
565 NFC::TagInfo tag_info{}; 568 NFC::TagInfo tag_info{};
566 569
567 if (result.IsSuccess()) { 570 if (result.IsSuccess()) {
568 result = device->GetTagInfo(tag_info, false); 571 result = device->GetTagInfo(tag_info);
569 } 572 }
570 573
571 if (result.IsSuccess()) { 574 if (result.IsSuccess()) {
572 result = device->WriteBackupData(tag_info.uuid, data); 575 result = device->WriteBackupData(tag_info.uuid, tag_info.uuid_length, data);
573 result = VerifyDeviceResult(device, result); 576 result = VerifyDeviceResult(device, result);
574 } 577 }
575 578
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
index 2971e280f..c61ba0cf3 100644
--- a/src/core/hle/service/nfc/common/device_manager.h
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -33,7 +33,7 @@ public:
33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; 33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); 34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
35 Result StopDetection(u64 device_handle); 35 Result StopDetection(u64 device_handle);
36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const; 36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const;
37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; 37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; 38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
39 Result ReadMifare(u64 device_handle, 39 Result ReadMifare(u64 device_handle,
diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h
index 4b60048a5..16a9171e6 100644
--- a/src/core/hle/service/nfc/mifare_result.h
+++ b/src/core/hle/service/nfc/mifare_result.h
@@ -12,6 +12,6 @@ constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
12constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73); 12constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
13constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80); 13constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
14constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97); 14constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
15constexpr Result ResultReadError(ErrorModule::NFCMifare, 288); 15constexpr Result ResultNotAMifare(ErrorModule::NFCMifare, 288);
16 16
17} // namespace Service::NFC::Mifare 17} // namespace Service::NFC::Mifare
diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h
index 75b59f021..467937399 100644
--- a/src/core/hle/service/nfc/mifare_types.h
+++ b/src/core/hle/service/nfc/mifare_types.h
@@ -11,9 +11,10 @@
11namespace Service::NFC { 11namespace Service::NFC {
12 12
13enum class MifareCmd : u8 { 13enum class MifareCmd : u8 {
14 None = 0x00,
15 Read = 0x30,
14 AuthA = 0x60, 16 AuthA = 0x60,
15 AuthB = 0x61, 17 AuthB = 0x61,
16 Read = 0x30,
17 Write = 0xA0, 18 Write = 0xA0,
18 Transfer = 0xB0, 19 Transfer = 0xB0,
19 Decrement = 0xC0, 20 Decrement = 0xC0,
@@ -35,17 +36,17 @@ static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
35 36
36// This is nn::nfc::MifareReadBlockParameter 37// This is nn::nfc::MifareReadBlockParameter
37struct MifareReadBlockParameter { 38struct MifareReadBlockParameter {
38 u8 sector_number; 39 u8 sector_number{};
39 INSERT_PADDING_BYTES(0x7); 40 INSERT_PADDING_BYTES(0x7);
40 SectorKey sector_key; 41 SectorKey sector_key{};
41}; 42};
42static_assert(sizeof(MifareReadBlockParameter) == 0x18, 43static_assert(sizeof(MifareReadBlockParameter) == 0x18,
43 "MifareReadBlockParameter is an invalid size"); 44 "MifareReadBlockParameter is an invalid size");
44 45
45// This is nn::nfc::MifareReadBlockData 46// This is nn::nfc::MifareReadBlockData
46struct MifareReadBlockData { 47struct MifareReadBlockData {
47 DataBlock data; 48 DataBlock data{};
48 u8 sector_number; 49 u8 sector_number{};
49 INSERT_PADDING_BYTES(0x7); 50 INSERT_PADDING_BYTES(0x7);
50}; 51};
51static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); 52static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index 198d0f2b9..e7ca7582e 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -142,9 +142,13 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
142void NfcInterface::StartDetection(HLERequestContext& ctx) { 142void NfcInterface::StartDetection(HLERequestContext& ctx) {
143 IPC::RequestParser rp{ctx}; 143 IPC::RequestParser rp{ctx};
144 const auto device_handle{rp.Pop<u64>()}; 144 const auto device_handle{rp.Pop<u64>()};
145 const auto tag_protocol{rp.PopEnum<NfcProtocol>()}; 145 auto tag_protocol{NfcProtocol::All};
146 LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol); 146
147 if (backend_type == BackendType::Nfc) {
148 tag_protocol = rp.PopEnum<NfcProtocol>();
149 }
147 150
151 LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
148 auto result = GetManager()->StartDetection(device_handle, tag_protocol); 152 auto result = GetManager()->StartDetection(device_handle, tag_protocol);
149 result = TranslateResultToServiceError(result); 153 result = TranslateResultToServiceError(result);
150 154
@@ -170,8 +174,7 @@ void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
170 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); 174 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
171 175
172 TagInfo tag_info{}; 176 TagInfo tag_info{};
173 auto result = 177 auto result = GetManager()->GetTagInfo(device_handle, tag_info);
174 GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
175 result = TranslateResultToServiceError(result); 178 result = TranslateResultToServiceError(result);
176 179
177 if (result.IsSuccess()) { 180 if (result.IsSuccess()) {
@@ -212,8 +215,8 @@ void NfcInterface::ReadMifare(HLERequestContext& ctx) {
212 memcpy(read_commands.data(), buffer.data(), 215 memcpy(read_commands.data(), buffer.data(),
213 number_of_commands * sizeof(MifareReadBlockParameter)); 216 number_of_commands * sizeof(MifareReadBlockParameter));
214 217
215 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", 218 LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle,
216 device_handle, number_of_commands); 219 number_of_commands);
217 220
218 std::vector<MifareReadBlockData> out_data(number_of_commands); 221 std::vector<MifareReadBlockData> out_data(number_of_commands);
219 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); 222 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
@@ -355,7 +358,7 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
355 if (result == ResultApplicationAreaExist) { 358 if (result == ResultApplicationAreaExist) {
356 return NFP::ResultApplicationAreaExist; 359 return NFP::ResultApplicationAreaExist;
357 } 360 }
358 if (result == ResultNotAnAmiibo) { 361 if (result == ResultInvalidTagType) {
359 return NFP::ResultNotAnAmiibo; 362 return NFP::ResultNotAnAmiibo;
360 } 363 }
361 if (result == ResultUnableToAccessBackupFile) { 364 if (result == ResultUnableToAccessBackupFile) {
@@ -381,6 +384,9 @@ Result NfcInterface::TranslateResultToMifare(Result result) const {
381 if (result == ResultTagRemoved) { 384 if (result == ResultTagRemoved) {
382 return Mifare::ResultTagRemoved; 385 return Mifare::ResultTagRemoved;
383 } 386 }
387 if (result == ResultInvalidTagType) {
388 return Mifare::ResultNotAMifare;
389 }
384 LOG_WARNING(Service_NFC, "Result conversion not handled"); 390 LOG_WARNING(Service_NFC, "Result conversion not handled");
385 return result; 391 return result;
386} 392}
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h
index 59a808740..715c0e80c 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -24,7 +24,8 @@ constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
24constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); 24constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
25constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); 25constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
26constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); 26constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
27constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); 27constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178);
28constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); 28constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
29constexpr Result ResultMifareError288(ErrorModule::NFC, 288);
29 30
30} // namespace Service::NFC 31} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h
index c7ebd1fdb..68e724442 100644
--- a/src/core/hle/service/nfc/nfc_types.h
+++ b/src/core/hle/service/nfc/nfc_types.h
@@ -35,32 +35,35 @@ enum class State : u32 {
35 35
36// This is nn::nfc::TagType 36// This is nn::nfc::TagType
37enum class TagType : u32 { 37enum class TagType : u32 {
38 None, 38 None = 0,
39 Type1, // ISO14443A RW 96-2k bytes 106kbit/s 39 Type1 = 1U << 0, // ISO14443A RW. Topaz
40 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s 40 Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN
41 Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s 41 Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa
42 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s 42 Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
43 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s 43 Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
44 Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV
45 Mifare = 1U << 6, // Mifare classic. Skylanders
46 All = 0xFFFFFFFF,
44}; 47};
45 48
46enum class PackedTagType : u8 { 49enum class PackedTagType : u8 {
47 None, 50 None = 0,
48 Type1, // ISO14443A RW 96-2k bytes 106kbit/s 51 Type1 = 1U << 0, // ISO14443A RW. Topaz
49 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s 52 Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN
50 Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s 53 Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa
51 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s 54 Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
52 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s 55 Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
56 Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV
57 Mifare = 1U << 6, // Mifare classic. Skylanders
58 All = 0xFF,
53}; 59};
54 60
55// This is nn::nfc::NfcProtocol 61// This is nn::nfc::NfcProtocol
56// Verify this enum. It might be completely wrong default protocol is 0x48
57enum class NfcProtocol : u32 { 62enum class NfcProtocol : u32 {
58 None, 63 None,
59 TypeA = 1U << 0, // ISO14443A 64 TypeA = 1U << 0, // ISO14443A
60 TypeB = 1U << 1, // ISO14443B 65 TypeB = 1U << 1, // ISO14443B
61 TypeF = 1U << 2, // Sony FeliCa 66 TypeF = 1U << 2, // Sony FeliCa
62 Unknown1 = 1U << 3,
63 Unknown2 = 1U << 5,
64 All = 0xFFFFFFFFU, 67 All = 0xFFFFFFFFU,
65}; 68};
66 69
@@ -69,8 +72,7 @@ enum class TestWaveType : u32 {
69 Unknown, 72 Unknown,
70}; 73};
71 74
72using UniqueSerialNumber = std::array<u8, 7>; 75using UniqueSerialNumber = std::array<u8, 10>;
73using UniqueSerialNumberExtension = std::array<u8, 3>;
74 76
75// This is nn::nfc::DeviceHandle 77// This is nn::nfc::DeviceHandle
76using DeviceHandle = u64; 78using DeviceHandle = u64;
@@ -78,7 +80,6 @@ using DeviceHandle = u64;
78// This is nn::nfc::TagInfo 80// This is nn::nfc::TagInfo
79struct TagInfo { 81struct TagInfo {
80 UniqueSerialNumber uuid; 82 UniqueSerialNumber uuid;
81 UniqueSerialNumberExtension uuid_extension;
82 u8 uuid_length; 83 u8 uuid_length;
83 INSERT_PADDING_BYTES(0x15); 84 INSERT_PADDING_BYTES(0x15);
84 NfcProtocol protocol; 85 NfcProtocol protocol;
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 7d36d5ee6..aed12a7f8 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -85,7 +85,7 @@ enum class CabinetMode : u8 {
85 StartFormatter, 85 StartFormatter,
86}; 86};
87 87
88using LockBytes = std::array<u8, 2>; 88using UuidPart = std::array<u8, 3>;
89using HashData = std::array<u8, 0x20>; 89using HashData = std::array<u8, 0x20>;
90using ApplicationArea = std::array<u8, 0xD8>; 90using ApplicationArea = std::array<u8, 0xD8>;
91using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; 91using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
@@ -93,12 +93,20 @@ using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
93// This is nn::nfp::TagInfo 93// This is nn::nfp::TagInfo
94using TagInfo = NFC::TagInfo; 94using TagInfo = NFC::TagInfo;
95 95
96struct NtagTagUuid {
97 UuidPart part1;
98 UuidPart part2;
99 u8 nintendo_id;
100};
101static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size");
102
96struct TagUuid { 103struct TagUuid {
97 NFC::UniqueSerialNumber uid; 104 UuidPart part1;
105 u8 crc_check1;
106 UuidPart part2;
98 u8 nintendo_id; 107 u8 nintendo_id;
99 LockBytes lock_bytes;
100}; 108};
101static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); 109static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size");
102 110
103struct WriteDate { 111struct WriteDate {
104 u16 year; 112 u16 year;
@@ -231,7 +239,8 @@ struct EncryptedAmiiboFile {
231static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); 239static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
232 240
233struct NTAG215File { 241struct NTAG215File {
234 LockBytes lock_bytes; // Tag UUID 242 u8 uid_crc_check2;
243 u8 internal_number;
235 u16 static_lock; // Set defined pages as read only 244 u16 static_lock; // Set defined pages as read only
236 u32 compability_container; // Defines available memory 245 u32 compability_container; // Defines available memory
237 HashData hmac_data; // Hash 246 HashData hmac_data; // Hash
@@ -250,8 +259,7 @@ struct NTAG215File {
250 u32_be register_info_crc; 259 u32_be register_info_crc;
251 ApplicationArea application_area; // Encrypted Game data 260 ApplicationArea application_area; // Encrypted Game data
252 HashData hmac_tag; // Hash 261 HashData hmac_tag; // Hash
253 NFC::UniqueSerialNumber uid; // Unique serial number 262 TagUuid uid;
254 u8 nintendo_id; // Tag UUID
255 AmiiboModelInfo model_info; 263 AmiiboModelInfo model_info;
256 HashData keygen_salt; // Salt 264 HashData keygen_salt; // Salt
257 u32 dynamic_lock; // Dynamic lock 265 u32 dynamic_lock; // Dynamic lock
@@ -264,7 +272,9 @@ static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be tr
264#pragma pack() 272#pragma pack()
265 273
266struct EncryptedNTAG215File { 274struct EncryptedNTAG215File {
267 TagUuid uuid; // Unique serial number 275 TagUuid uuid;
276 u8 uuid_crc_check2;
277 u8 internal_number;
268 u16 static_lock; // Set defined pages as read only 278 u16 static_lock; // Set defined pages as read only
269 u32 compability_container; // Defines available memory 279 u32 compability_container; // Defines available memory
270 EncryptedAmiiboFile user_memory; // Writable data 280 EncryptedAmiiboFile user_memory; // Writable data
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index ab1f30f9e..a04538d5d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -34,7 +34,7 @@ public:
34 * @returns The result code of the ioctl. 34 * @returns The result code of the ioctl.
35 */ 35 */
36 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 36 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
37 std::vector<u8>& output) = 0; 37 std::span<u8> output) = 0;
38 38
39 /** 39 /**
40 * Handles an ioctl2 request. 40 * Handles an ioctl2 request.
@@ -45,7 +45,7 @@ public:
45 * @returns The result code of the ioctl. 45 * @returns The result code of the ioctl.
46 */ 46 */
47 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 47 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
48 std::span<const u8> inline_input, std::vector<u8>& output) = 0; 48 std::span<const u8> inline_input, std::span<u8> output) = 0;
49 49
50 /** 50 /**
51 * Handles an ioctl3 request. 51 * Handles an ioctl3 request.
@@ -56,7 +56,7 @@ public:
56 * @returns The result code of the ioctl. 56 * @returns The result code of the ioctl.
57 */ 57 */
58 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 58 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 std::vector<u8>& output, std::vector<u8>& inline_output) = 0; 59 std::span<u8> output, std::span<u8> inline_output) = 0;
60 60
61 /** 61 /**
62 * Called once a device is opened 62 * Called once a device is opened
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 5a5b2e305..05a43d8dc 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,19 +18,19 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
18nvdisp_disp0::~nvdisp_disp0() = default; 18nvdisp_disp0::~nvdisp_disp0() = default;
19 19
20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 std::vector<u8>& output) { 21 std::span<u8> output) {
22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
23 return NvResult::NotImplemented; 23 return NvResult::NotImplemented;
24} 24}
25 25
26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 std::span<const u8> inline_input, std::vector<u8>& output) { 27 std::span<const u8> inline_input, std::span<u8> output) {
28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
29 return NvResult::NotImplemented; 29 return NvResult::NotImplemented;
30} 30}
31 31
32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
33 std::vector<u8>& output, std::vector<u8>& inline_output) { 33 std::span<u8> output, std::span<u8> inline_output) {
34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
35 return NvResult::NotImplemented; 35 return NvResult::NotImplemented;
36} 36}
@@ -51,8 +51,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form
51 stride, format, transform, crop_rect}; 51 stride, format, transform, crop_rect};
52 52
53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); 53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences);
54 system.GetPerfStats().EndSystemFrame();
55 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); 54 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
55 system.GetPerfStats().EndSystemFrame();
56 system.GetPerfStats().BeginSystemFrame(); 56 system.GetPerfStats().BeginSystemFrame();
57} 57}
58 58
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index bcd0e3ed5..daee05fe8 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -26,11 +26,11 @@ public:
26 ~nvdisp_disp0() override; 26 ~nvdisp_disp0() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::span<u8> output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::span<const u8> inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::span<u8> output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
33 std::vector<u8>& inline_output) override; 33 std::span<u8> inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
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 681bd0867..07e570a9f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -28,7 +28,7 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Con
28nvhost_as_gpu::~nvhost_as_gpu() = default; 28nvhost_as_gpu::~nvhost_as_gpu() = default;
29 29
30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::vector<u8>& output) { 31 std::span<u8> output) {
32 switch (command.group) { 32 switch (command.group) {
33 case 'A': 33 case 'A':
34 switch (command.cmd) { 34 switch (command.cmd) {
@@ -61,13 +61,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i
61} 61}
62 62
63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
64 std::span<const u8> inline_input, std::vector<u8>& output) { 64 std::span<const u8> inline_input, std::span<u8> output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
67} 67}
68 68
69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
70 std::vector<u8>& output, std::vector<u8>& inline_output) { 70 std::span<u8> output, std::span<u8> inline_output) {
71 switch (command.group) { 71 switch (command.group) {
72 case 'A': 72 case 'A':
73 switch (command.cmd) { 73 switch (command.cmd) {
@@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
87void nvhost_as_gpu::OnOpen(DeviceFD fd) {} 87void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
88void nvhost_as_gpu::OnClose(DeviceFD fd) {} 88void nvhost_as_gpu::OnClose(DeviceFD fd) {}
89 89
90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) { 90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) {
91 IoctlAllocAsEx params{}; 91 IoctlAllocAsEx params{};
92 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
93 93
@@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& ou
141 return NvResult::Success; 141 return NvResult::Success;
142} 142}
143 143
144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) { 144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) {
145 IoctlAllocSpace params{}; 145 IoctlAllocSpace params{};
146 std::memcpy(&params, input.data(), input.size()); 146 std::memcpy(&params, input.data(), input.size());
147 147
@@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
220 mapping_map.erase(offset); 220 mapping_map.erase(offset);
221} 221}
222 222
223NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) { 223NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) {
224 IoctlFreeSpace params{}; 224 IoctlFreeSpace params{};
225 std::memcpy(&params, input.data(), input.size()); 225 std::memcpy(&params, input.data(), input.size());
226 226
@@ -266,15 +266,14 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& ou
266 return NvResult::Success; 266 return NvResult::Success;
267} 267}
268 268
269NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) { 269NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
271 271
272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
273 273
274 std::vector<IoctlRemapEntry> entries(num_entries);
275 std::memcpy(entries.data(), input.data(), input.size());
276
277 std::scoped_lock lock(mutex); 274 std::scoped_lock lock(mutex);
275 entries.resize_destructive(num_entries);
276 std::memcpy(entries.data(), input.data(), input.size());
278 277
279 if (!vm.initialised) { 278 if (!vm.initialised) {
280 return NvResult::BadValue; 279 return NvResult::BadValue;
@@ -320,7 +319,7 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output
320 return NvResult::Success; 319 return NvResult::Success;
321} 320}
322 321
323NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) { 322NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) {
324 IoctlMapBufferEx params{}; 323 IoctlMapBufferEx params{};
325 std::memcpy(&params, input.data(), input.size()); 324 std::memcpy(&params, input.data(), input.size());
326 325
@@ -424,7 +423,7 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>&
424 return NvResult::Success; 423 return NvResult::Success;
425} 424}
426 425
427NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { 426NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {
428 IoctlUnmapBuffer params{}; 427 IoctlUnmapBuffer params{};
429 std::memcpy(&params, input.data(), input.size()); 428 std::memcpy(&params, input.data(), input.size());
430 429
@@ -463,7 +462,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>&
463 return NvResult::Success; 462 return NvResult::Success;
464} 463}
465 464
466NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) { 465NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) {
467 IoctlBindChannel params{}; 466 IoctlBindChannel params{};
468 std::memcpy(&params, input.data(), input.size()); 467 std::memcpy(&params, input.data(), input.size());
469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 468 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
@@ -492,7 +491,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
492 }; 491 };
493} 492}
494 493
495NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) { 494NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) {
496 IoctlGetVaRegions params{}; 495 IoctlGetVaRegions params{};
497 std::memcpy(&params, input.data(), input.size()); 496 std::memcpy(&params, input.data(), input.size());
498 497
@@ -511,8 +510,8 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>&
511 return NvResult::Success; 510 return NvResult::Success;
512} 511}
513 512
514NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output, 513NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output,
515 std::vector<u8>& inline_output) { 514 std::span<u8> inline_output) {
516 IoctlGetVaRegions params{}; 515 IoctlGetVaRegions params{};
517 std::memcpy(&params, input.data(), input.size()); 516 std::memcpy(&params, input.data(), input.size());
518 517
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 1aba8d579..2af3e1260 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -15,6 +15,7 @@
15#include "common/address_space.h" 15#include "common/address_space.h"
16#include "common/common_funcs.h" 16#include "common/common_funcs.h"
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/scratch_buffer.h"
18#include "common/swap.h" 19#include "common/swap.h"
19#include "core/hle/service/nvdrv/core/nvmap.h" 20#include "core/hle/service/nvdrv/core/nvmap.h"
20#include "core/hle/service/nvdrv/devices/nvdevice.h" 21#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -48,11 +49,11 @@ public:
48 ~nvhost_as_gpu() override; 49 ~nvhost_as_gpu() override;
49 50
50 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 51 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
51 std::vector<u8>& output) override; 52 std::span<u8> output) override;
52 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 53 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
53 std::span<const u8> inline_input, std::vector<u8>& output) override; 54 std::span<const u8> inline_input, std::span<u8> output) override;
54 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 55 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
55 std::vector<u8>& inline_output) override; 56 std::span<u8> inline_output) override;
56 57
57 void OnOpen(DeviceFD fd) override; 58 void OnOpen(DeviceFD fd) override;
58 void OnClose(DeviceFD fd) override; 59 void OnClose(DeviceFD fd) override;
@@ -138,18 +139,18 @@ private:
138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, 139 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
139 "IoctlGetVaRegions is incorrect size"); 140 "IoctlGetVaRegions is incorrect size");
140 141
141 NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output); 142 NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output);
142 NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output); 143 NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output);
143 NvResult Remap(std::span<const u8> input, std::vector<u8>& output); 144 NvResult Remap(std::span<const u8> input, std::span<u8> output);
144 NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output); 145 NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output);
145 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); 146 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output);
146 NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output); 147 NvResult FreeSpace(std::span<const u8> input, std::span<u8> output);
147 NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output); 148 NvResult BindChannel(std::span<const u8> input, std::span<u8> output);
148 149
149 void GetVARegionsImpl(IoctlGetVaRegions& params); 150 void GetVARegionsImpl(IoctlGetVaRegions& params);
150 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output); 151 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output);
151 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output, 152 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output,
152 std::vector<u8>& inline_output); 153 std::span<u8> inline_output);
153 154
154 void FreeMappingLocked(u64 offset); 155 void FreeMappingLocked(u64 offset);
155 156
@@ -212,6 +213,7 @@ private:
212 bool initialised{}; 213 bool initialised{};
213 } vm; 214 } vm;
214 std::shared_ptr<Tegra::MemoryManager> gmmu; 215 std::shared_ptr<Tegra::MemoryManager> gmmu;
216 Common::ScratchBuffer<IoctlRemapEntry> entries;
215 217
216 // s32 channel{}; 218 // s32 channel{};
217 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; 219 // 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 e12025560..4d55554b4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -35,7 +35,7 @@ nvhost_ctrl::~nvhost_ctrl() {
35} 35}
36 36
37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
38 std::vector<u8>& output) { 38 std::span<u8> output) {
39 switch (command.group) { 39 switch (command.group) {
40 case 0x0: 40 case 0x0:
41 switch (command.cmd) { 41 switch (command.cmd) {
@@ -64,13 +64,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp
64} 64}
65 65
66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
67 std::span<const u8> inline_input, std::vector<u8>& output) { 67 std::span<const u8> inline_input, std::span<u8> output) {
68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
69 return NvResult::NotImplemented; 69 return NvResult::NotImplemented;
70} 70}
71 71
72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
73 std::vector<u8>& output, std::vector<u8>& inline_outpu) { 73 std::span<u8> output, std::span<u8> inline_outpu) {
74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
75 return NvResult::NotImplemented; 75 return NvResult::NotImplemented;
76} 76}
@@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79 79
80void nvhost_ctrl::OnClose(DeviceFD fd) {} 80void nvhost_ctrl::OnClose(DeviceFD fd) {}
81 81
82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) { 82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) {
83 IocGetConfigParams params{}; 83 IocGetConfigParams params{};
84 std::memcpy(&params, input.data(), sizeof(params)); 84 std::memcpy(&params, input.data(), sizeof(params));
85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
@@ -87,7 +87,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8
87 return NvResult::ConfigVarNotFound; // Returns error on production mode 87 return NvResult::ConfigVarNotFound; // Returns error on production mode
88} 88}
89 89
90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, 90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output,
91 bool is_allocation) { 91 bool is_allocation) {
92 IocCtrlEventWaitParams params{}; 92 IocCtrlEventWaitParams params{};
93 std::memcpy(&params, input.data(), sizeof(params)); 93 std::memcpy(&params, input.data(), sizeof(params));
@@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
231 return NvResult::Success; 231 return NvResult::Success;
232} 232}
233 233
234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) { 234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) {
235 IocCtrlEventRegisterParams params{}; 235 IocCtrlEventRegisterParams params{};
236 std::memcpy(&params, input.data(), sizeof(params)); 236 std::memcpy(&params, input.data(), sizeof(params));
237 const u32 event_id = params.user_event_id; 237 const u32 event_id = params.user_event_id;
@@ -252,7 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vecto
252 return NvResult::Success; 252 return NvResult::Success;
253} 253}
254 254
255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) { 255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) {
256 IocCtrlEventUnregisterParams params{}; 256 IocCtrlEventUnregisterParams params{};
257 std::memcpy(&params, input.data(), sizeof(params)); 257 std::memcpy(&params, input.data(), sizeof(params));
258 const u32 event_id = params.user_event_id & 0x00FF; 258 const u32 event_id = params.user_event_id & 0x00FF;
@@ -262,8 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vec
262 return FreeEvent(event_id); 262 return FreeEvent(event_id);
263} 263}
264 264
265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, 265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) {
266 std::vector<u8>& output) {
267 IocCtrlEventUnregisterBatchParams params{}; 266 IocCtrlEventUnregisterBatchParams params{};
268 std::memcpy(&params, input.data(), sizeof(params)); 267 std::memcpy(&params, input.data(), sizeof(params));
269 u64 event_mask = params.user_events; 268 u64 event_mask = params.user_events;
@@ -281,7 +280,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input,
281 return NvResult::Success; 280 return NvResult::Success;
282} 281}
283 282
284NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) { 283NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) {
285 IocCtrlEventClearParams params{}; 284 IocCtrlEventClearParams params{};
286 std::memcpy(&params, input.data(), sizeof(params)); 285 std::memcpy(&params, input.data(), sizeof(params));
287 286
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index dd2e7888a..2efed4862 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -26,11 +26,11 @@ public:
26 ~nvhost_ctrl() override; 26 ~nvhost_ctrl() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::span<u8> output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::span<const u8> inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::span<u8> output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
33 std::vector<u8>& inline_output) override; 33 std::span<u8> inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
@@ -186,13 +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::vector<u8>& output); 189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output);
190 NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, 190 NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation);
191 bool is_allocation); 191 NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output);
192 NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output); 192 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output);
193 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output); 193 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output);
194 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output); 194 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output);
195 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output);
196 195
197 NvResult FreeEvent(u32 slot); 196 NvResult FreeEvent(u32 slot);
198 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 be3c083db..6081d92e9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -22,7 +22,7 @@ nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
22} 22}
23 23
24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) { 25 std::span<u8> output) {
26 switch (command.group) { 26 switch (command.group) {
27 case 'G': 27 case 'G':
28 switch (command.cmd) { 28 switch (command.cmd) {
@@ -54,13 +54,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>
54} 54}
55 55
56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
57 std::span<const u8> inline_input, std::vector<u8>& output) { 57 std::span<const u8> inline_input, std::span<u8> output) {
58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
59 return NvResult::NotImplemented; 59 return NvResult::NotImplemented;
60} 60}
61 61
62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
63 std::vector<u8>& output, std::vector<u8>& inline_output) { 63 std::span<u8> output, std::span<u8> inline_output) {
64 switch (command.group) { 64 switch (command.group) {
65 case 'G': 65 case 'G':
66 switch (command.cmd) { 66 switch (command.cmd) {
@@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} 82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} 83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
84 84
85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) { 85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) {
86 LOG_DEBUG(Service_NVDRV, "called"); 86 LOG_DEBUG(Service_NVDRV, "called");
87 IoctlCharacteristics params{}; 87 IoctlCharacteristics params{};
88 std::memcpy(&params, input.data(), input.size()); 88 std::memcpy(&params, input.data(), input.size());
@@ -127,8 +127,8 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec
127 return NvResult::Success; 127 return NvResult::Success;
128} 128}
129 129
130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, 130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output,
131 std::vector<u8>& inline_output) { 131 std::span<u8> inline_output) {
132 LOG_DEBUG(Service_NVDRV, "called"); 132 LOG_DEBUG(Service_NVDRV, "called");
133 IoctlCharacteristics params{}; 133 IoctlCharacteristics params{};
134 std::memcpy(&params, input.data(), input.size()); 134 std::memcpy(&params, input.data(), input.size());
@@ -175,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec
175 return NvResult::Success; 175 return NvResult::Success;
176} 176}
177 177
178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) { 178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) {
179 IoctlGpuGetTpcMasksArgs params{}; 179 IoctlGpuGetTpcMasksArgs params{};
180 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
@@ -186,8 +186,8 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>
186 return NvResult::Success; 186 return NvResult::Success;
187} 187}
188 188
189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, 189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output,
190 std::vector<u8>& inline_output) { 190 std::span<u8> inline_output) {
191 IoctlGpuGetTpcMasksArgs params{}; 191 IoctlGpuGetTpcMasksArgs params{};
192 std::memcpy(&params, input.data(), input.size()); 192 std::memcpy(&params, input.data(), input.size());
193 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 193 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
@@ -199,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>
199 return NvResult::Success; 199 return NvResult::Success;
200} 200}
201 201
202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) { 202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) {
203 LOG_DEBUG(Service_NVDRV, "called"); 203 LOG_DEBUG(Service_NVDRV, "called");
204 204
205 IoctlActiveSlotMask params{}; 205 IoctlActiveSlotMask params{};
@@ -212,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vect
212 return NvResult::Success; 212 return NvResult::Success;
213} 213}
214 214
215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) { 215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) {
216 LOG_DEBUG(Service_NVDRV, "called"); 216 LOG_DEBUG(Service_NVDRV, "called");
217 217
218 IoctlZcullGetCtxSize params{}; 218 IoctlZcullGetCtxSize params{};
@@ -224,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector
224 return NvResult::Success; 224 return NvResult::Success;
225} 225}
226 226
227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) { 227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) {
228 LOG_DEBUG(Service_NVDRV, "called"); 228 LOG_DEBUG(Service_NVDRV, "called");
229 229
230 IoctlNvgpuGpuZcullGetInfoArgs params{}; 230 IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -247,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8
247 return NvResult::Success; 247 return NvResult::Success;
248} 248}
249 249
250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) { 250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) {
251 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 251 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
252 252
253 IoctlZbcSetTable params{}; 253 IoctlZbcSetTable params{};
@@ -263,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>
263 return NvResult::Success; 263 return NvResult::Success;
264} 264}
265 265
266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) { 266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) {
267 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 267 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
268 268
269 IoctlZbcQueryTable params{}; 269 IoctlZbcQueryTable params{};
@@ -273,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u
273 return NvResult::Success; 273 return NvResult::Success;
274} 274}
275 275
276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) { 276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) {
277 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 277 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
278 278
279 IoctlFlushL2 params{}; 279 IoctlFlushL2 params{};
@@ -283,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& ou
283 return NvResult::Success; 283 return NvResult::Success;
284} 284}
285 285
286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) { 286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) {
287 LOG_DEBUG(Service_NVDRV, "called"); 287 LOG_DEBUG(Service_NVDRV, "called");
288 288
289 IoctlGetGpuTime params{}; 289 IoctlGetGpuTime params{};
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 b9333d9d3..97995551c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -22,11 +22,11 @@ public:
22 ~nvhost_ctrl_gpu() override; 22 ~nvhost_ctrl_gpu() override;
23 23
24 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 24 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) override; 25 std::span<u8> output) override;
26 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 26 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 std::span<const u8> inline_input, std::vector<u8>& output) override; 27 std::span<const u8> inline_input, std::span<u8> output) override;
28 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 28 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
29 std::vector<u8>& inline_output) override; 29 std::span<u8> inline_output) override;
30 30
31 void OnOpen(DeviceFD fd) override; 31 void OnOpen(DeviceFD fd) override;
32 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
@@ -151,21 +151,21 @@ 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::vector<u8>& output); 154 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output);
155 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, 155 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output,
156 std::vector<u8>& inline_output); 156 std::span<u8> inline_output);
157 157
158 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output); 158 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output);
159 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, 159 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output,
160 std::vector<u8>& inline_output); 160 std::span<u8> inline_output);
161 161
162 NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output); 162 NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output);
163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output); 163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output);
164 NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output); 164 NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output);
165 NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output); 165 NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output);
166 NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output); 166 NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output);
167 NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output); 167 NvResult FlushL2(std::span<const u8> input, std::span<u8> output);
168 NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output); 168 NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output);
169 169
170 EventInterface& events_interface; 170 EventInterface& events_interface;
171 171
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 453a965dc..46a25fcab 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -47,7 +47,7 @@ nvhost_gpu::~nvhost_gpu() {
47} 47}
48 48
49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
50 std::vector<u8>& output) { 50 std::span<u8> output) {
51 switch (command.group) { 51 switch (command.group) {
52 case 0x0: 52 case 0x0:
53 switch (command.cmd) { 53 switch (command.cmd) {
@@ -99,7 +99,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
99}; 99};
100 100
101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
102 std::span<const u8> inline_input, std::vector<u8>& output) { 102 std::span<const u8> inline_input, std::span<u8> output) {
103 switch (command.group) { 103 switch (command.group) {
104 case 'H': 104 case 'H':
105 switch (command.cmd) { 105 switch (command.cmd) {
@@ -113,7 +113,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu
113} 113}
114 114
115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
116 std::vector<u8>& output, std::vector<u8>& inline_output) { 116 std::span<u8> output, std::span<u8> inline_output) {
117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
118 return NvResult::NotImplemented; 118 return NvResult::NotImplemented;
119} 119}
@@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
121void nvhost_gpu::OnOpen(DeviceFD fd) {} 121void nvhost_gpu::OnOpen(DeviceFD fd) {}
122void nvhost_gpu::OnClose(DeviceFD fd) {} 122void nvhost_gpu::OnClose(DeviceFD fd) {}
123 123
124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { 124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {
125 IoctlSetNvmapFD params{}; 125 IoctlSetNvmapFD params{};
126 std::memcpy(&params, input.data(), input.size()); 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);
@@ -130,7 +130,7 @@ NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& outp
130 return NvResult::Success; 130 return NvResult::Success;
131} 131}
132 132
133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) { 133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) {
134 LOG_DEBUG(Service_NVDRV, "called"); 134 LOG_DEBUG(Service_NVDRV, "called");
135 135
136 IoctlClientData params{}; 136 IoctlClientData params{};
@@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& o
139 return NvResult::Success; 139 return NvResult::Success;
140} 140}
141 141
142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) { 142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) {
143 LOG_DEBUG(Service_NVDRV, "called"); 143 LOG_DEBUG(Service_NVDRV, "called");
144 144
145 IoctlClientData params{}; 145 IoctlClientData params{};
@@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& o
149 return NvResult::Success; 149 return NvResult::Success;
150} 150}
151 151
152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) { 152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) {
153 std::memcpy(&zcull_params, input.data(), input.size()); 153 std::memcpy(&zcull_params, input.data(), input.size());
154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
155 zcull_params.mode); 155 zcull_params.mode);
@@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& outpu
158 return NvResult::Success; 158 return NvResult::Success;
159} 159}
160 160
161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) { 161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) {
162 IoctlSetErrorNotifier params{}; 162 IoctlSetErrorNotifier params{};
163 std::memcpy(&params, input.data(), input.size()); 163 std::memcpy(&params, input.data(), input.size());
164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
@@ -168,14 +168,14 @@ NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>
168 return NvResult::Success; 168 return NvResult::Success;
169} 169}
170 170
171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) { 171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) {
172 std::memcpy(&channel_priority, input.data(), input.size()); 172 std::memcpy(&channel_priority, input.data(), input.size());
173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
174 174
175 return NvResult::Success; 175 return NvResult::Success;
176} 176}
177 177
178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) { 178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) {
179 IoctlAllocGpfifoEx2 params{}; 179 IoctlAllocGpfifoEx2 params{};
180 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
181 LOG_WARNING(Service_NVDRV, 181 LOG_WARNING(Service_NVDRV,
@@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>&
197 return NvResult::Success; 197 return NvResult::Success;
198} 198}
199 199
200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) { 200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) {
201 IoctlAllocObjCtx params{}; 201 IoctlAllocObjCtx params{};
202 std::memcpy(&params, input.data(), input.size()); 202 std::memcpy(&params, input.data(), input.size());
203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -208,7 +208,8 @@ NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vecto
208 return NvResult::Success; 208 return NvResult::Success;
209} 209}
210 210
211static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { 211static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList(
212 NvFence fence) {
212 return { 213 return {
213 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, 214 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
214 Tegra::SubmissionMode::Increasing), 215 Tegra::SubmissionMode::Increasing),
@@ -219,35 +220,35 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
219 }; 220 };
220} 221}
221 222
222static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { 223static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList(
223 std::vector<Tegra::CommandHeader> result{ 224 NvFence fence) {
225 boost::container::small_vector<Tegra::CommandHeader, 512> result{
224 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, 226 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
225 Tegra::SubmissionMode::Increasing), 227 Tegra::SubmissionMode::Increasing),
226 {}}; 228 {}};
227 229
228 for (u32 count = 0; count < 2; ++count) { 230 for (u32 count = 0; count < 2; ++count) {
229 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, 231 result.push_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
230 Tegra::SubmissionMode::Increasing)); 232 Tegra::SubmissionMode::Increasing));
231 result.emplace_back( 233 result.push_back(
232 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); 234 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id));
233 } 235 }
234 236
235 return result; 237 return result;
236} 238}
237 239
238static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { 240static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList(
239 std::vector<Tegra::CommandHeader> result{ 241 NvFence fence) {
242 boost::container::small_vector<Tegra::CommandHeader, 512> result{
240 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, 243 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
241 Tegra::SubmissionMode::Increasing), 244 Tegra::SubmissionMode::Increasing),
242 {}}; 245 {}};
243 const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; 246 auto increment_list{BuildIncrementCommandList(fence)};
244 247 result.insert(result.end(), increment_list.begin(), increment_list.end());
245 result.insert(result.end(), increment.begin(), increment.end());
246
247 return result; 248 return result;
248} 249}
249 250
250NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 251NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
251 Tegra::CommandList&& entries) { 252 Tegra::CommandList&& entries) {
252 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 253 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
253 params.num_entries, params.flags.raw); 254 params.num_entries, params.flags.raw);
@@ -293,7 +294,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
293 return NvResult::Success; 294 return NvResult::Success;
294} 295}
295 296
296NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, 297NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,
297 bool kickoff) { 298 bool kickoff) {
298 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 299 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
299 UNIMPLEMENTED(); 300 UNIMPLEMENTED();
@@ -315,7 +316,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>
315} 316}
316 317
317NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 318NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
318 std::vector<u8>& output) { 319 std::span<u8> output) {
319 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 320 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
320 UNIMPLEMENTED(); 321 UNIMPLEMENTED();
321 return NvResult::InvalidSize; 322 return NvResult::InvalidSize;
@@ -327,7 +328,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const
327 return SubmitGPFIFOImpl(params, output, std::move(entries)); 328 return SubmitGPFIFOImpl(params, output, std::move(entries));
328} 329}
329 330
330NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { 331NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) {
331 IoctlGetWaitbase params{}; 332 IoctlGetWaitbase params{};
332 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 333 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
333 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 334 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
@@ -337,7 +338,7 @@ NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& out
337 return NvResult::Success; 338 return NvResult::Success;
338} 339}
339 340
340NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) { 341NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) {
341 IoctlChannelSetTimeout params{}; 342 IoctlChannelSetTimeout params{};
342 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 343 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
343 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 344 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
@@ -345,7 +346,7 @@ NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8
345 return NvResult::Success; 346 return NvResult::Success;
346} 347}
347 348
348NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) { 349NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) {
349 IoctlSetTimeslice params{}; 350 IoctlSetTimeslice params{};
350 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice)); 351 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
351 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 352 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", 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 3ca58202d..529c20526 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -41,11 +41,11 @@ public:
41 ~nvhost_gpu() override; 41 ~nvhost_gpu() override;
42 42
43 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 43 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
44 std::vector<u8>& output) override; 44 std::span<u8> output) override;
45 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 45 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
46 std::span<const u8> inline_input, std::vector<u8>& output) override; 46 std::span<const u8> inline_input, std::span<u8> output) override;
47 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 47 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
48 std::vector<u8>& inline_output) override; 48 std::span<u8> inline_output) override;
49 49
50 void OnOpen(DeviceFD fd) override; 50 void OnOpen(DeviceFD fd) override;
51 void OnClose(DeviceFD fd) override; 51 void OnClose(DeviceFD fd) override;
@@ -186,23 +186,23 @@ 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::vector<u8>& output); 189 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);
190 NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output); 190 NvResult SetClientData(std::span<const u8> input, std::span<u8> output);
191 NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output); 191 NvResult GetClientData(std::span<const u8> input, std::span<u8> output);
192 NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output); 192 NvResult ZCullBind(std::span<const u8> input, std::span<u8> output);
193 NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output); 193 NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output);
194 NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output); 194 NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output);
195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output); 195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output);
196 NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output); 196 NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output);
197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
198 Tegra::CommandList&& entries); 198 Tegra::CommandList&& entries);
199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, 199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,
200 bool kickoff = false); 200 bool kickoff = false);
201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
202 std::vector<u8>& output); 202 std::span<u8> output);
203 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); 203 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
204 NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output); 204 NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output);
205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output); 205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output);
206 206
207 EventInterface& events_interface; 207 EventInterface& events_interface;
208 NvCore::Container& core; 208 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 dc45169ad..a174442a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -16,7 +16,7 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::span<u8> output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
@@ -56,13 +56,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
56} 56}
57 57
58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 std::span<const u8> inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::span<u8> output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::span<u8> output, std::span<u8> inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
68} 68}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0d615bbcb..ad2233c49 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -14,11 +14,11 @@ public:
14 ~nvhost_nvdec() override; 14 ~nvhost_nvdec() override;
15 15
16 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 16 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
17 std::vector<u8>& output) override; 17 std::span<u8> output) override;
18 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 18 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::span<const u8> inline_input, std::vector<u8>& output) override; 19 std::span<const u8> inline_input, std::span<u8> output) override;
20 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 20 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
21 std::vector<u8>& inline_output) override; 21 std::span<u8> inline_output) override;
22 22
23 void OnOpen(DeviceFD fd) override; 23 void OnOpen(DeviceFD fd) override;
24 void OnClose(DeviceFD fd) override; 24 void OnClose(DeviceFD fd) override;
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 1ab51f10b..61649aa4a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -36,7 +36,7 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si
36// Writes the data in src to an offset into the dst vector. The offset is specified in bytes 36// Writes the data in src to an offset into the dst vector. The offset is specified in bytes
37// Returns the number of bytes written into dst. 37// Returns the number of bytes written into dst.
38template <typename T> 38template <typename T>
39std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) { 39std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size_t offset) {
40 if (src.empty()) { 40 if (src.empty()) {
41 return 0; 41 return 0;
42 } 42 }
@@ -72,8 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {
72 return NvResult::Success; 72 return NvResult::Success;
73} 73}
74 74
75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, 75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) {
76 std::vector<u8>& output) {
77 IoctlSubmit params{}; 76 IoctlSubmit params{};
78 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 77 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
79 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 78 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -121,7 +120,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input,
121 return NvResult::Success; 120 return NvResult::Success;
122} 121}
123 122
124NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) { 123NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) {
125 IoctlGetSyncpoint params{}; 124 IoctlGetSyncpoint params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 125 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 126 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -133,7 +132,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vecto
133 return NvResult::Success; 132 return NvResult::Success;
134} 133}
135 134
136NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { 135NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) {
137 IoctlGetWaitbase params{}; 136 IoctlGetWaitbase params{};
138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); 137 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 138 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
@@ -142,7 +141,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector
142 return NvResult::Success; 141 return NvResult::Success;
143} 142}
144 143
145NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) { 144NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) {
146 IoctlMapBuffer params{}; 145 IoctlMapBuffer params{};
147 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 146 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
148 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 147 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -159,7 +158,7 @@ NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u
159 return NvResult::Success; 158 return NvResult::Success;
160} 159}
161 160
162NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { 161NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {
163 IoctlMapBuffer params{}; 162 IoctlMapBuffer params{};
164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 163 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 164 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -173,7 +172,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector
173 return NvResult::Success; 172 return NvResult::Success;
174} 173}
175 174
176NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) { 175NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) {
177 std::memcpy(&submit_timeout, input.data(), input.size()); 176 std::memcpy(&submit_timeout, input.data(), input.size());
178 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 177 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
179 return NvResult::Success; 178 return NvResult::Success;
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 5af26a26f..9bb573bfe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -108,12 +108,12 @@ protected:
108 108
109 /// Ioctl command implementations 109 /// Ioctl command implementations
110 NvResult SetNVMAPfd(std::span<const u8> input); 110 NvResult SetNVMAPfd(std::span<const u8> input);
111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output); 111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output);
112 NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output); 112 NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output);
113 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); 113 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
114 NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output); 114 NvResult MapBuffer(std::span<const u8> input, std::span<u8> output);
115 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); 115 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output);
116 NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output);
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 39f30e7c8..a05c8cdae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,7 +13,7 @@ nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {}
13nvhost_nvjpg::~nvhost_nvjpg() = default; 13nvhost_nvjpg::~nvhost_nvjpg() = default;
14 14
15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) { 16 std::span<u8> output) {
17 switch (command.group) { 17 switch (command.group) {
18 case 'H': 18 case 'H':
19 switch (command.cmd) { 19 switch (command.cmd) {
@@ -32,13 +32,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
32} 32}
33 33
34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
35 std::span<const u8> inline_input, std::vector<u8>& output) { 35 std::span<const u8> inline_input, std::span<u8> output) {
36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
37 return NvResult::NotImplemented; 37 return NvResult::NotImplemented;
38} 38}
39 39
40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
41 std::vector<u8>& output, std::vector<u8>& inline_output) { 41 std::span<u8> output, std::span<u8> inline_output) {
42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
43 return NvResult::NotImplemented; 43 return NvResult::NotImplemented;
44} 44}
@@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
46void nvhost_nvjpg::OnOpen(DeviceFD fd) {} 46void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
47void nvhost_nvjpg::OnClose(DeviceFD fd) {} 47void nvhost_nvjpg::OnClose(DeviceFD fd) {}
48 48
49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { 49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {
50 IoctlSetNvmapFD params{}; 50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), input.size()); 51 std::memcpy(&params, input.data(), input.size());
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 52 LOG_DEBUG(Service_NVDRV, "called, 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 41b57e872..5623e0d47 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,11 +16,11 @@ public:
16 ~nvhost_nvjpg() override; 16 ~nvhost_nvjpg() override;
17 17
18 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 18 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) override; 19 std::span<u8> output) override;
20 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 20 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 std::span<const u8> inline_input, std::vector<u8>& output) override; 21 std::span<const u8> inline_input, std::span<u8> output) override;
22 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 22 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
23 std::vector<u8>& inline_output) override; 23 std::span<u8> inline_output) override;
24 24
25 void OnOpen(DeviceFD fd) override; 25 void OnOpen(DeviceFD fd) override;
26 void OnClose(DeviceFD fd) override; 26 void OnClose(DeviceFD fd) override;
@@ -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::vector<u8>& output); 36 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);
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 b0ea402a7..c0b8684c3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -16,7 +16,7 @@ nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::span<u8> output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
@@ -56,13 +56,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
56} 56}
57 57
58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 std::span<const u8> inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::span<u8> output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::span<u8> output, std::span<u8> inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
68} 68}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b5e350a83..cadbcb0a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,11 +13,11 @@ public:
13 ~nvhost_vic(); 13 ~nvhost_vic();
14 14
15 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 15 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) override; 16 std::span<u8> output) override;
17 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 17 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
18 std::span<const u8> inline_input, std::vector<u8>& output) override; 18 std::span<const u8> inline_input, std::span<u8> output) override;
19 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 19 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
20 std::vector<u8>& inline_output) override; 20 std::span<u8> inline_output) override;
21 21
22 void OnOpen(DeviceFD fd) override; 22 void OnOpen(DeviceFD fd) override;
23 void OnClose(DeviceFD fd) override; 23 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 07417f045..e7f7e273b 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -26,7 +26,7 @@ nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
26nvmap::~nvmap() = default; 26nvmap::~nvmap() = default;
27 27
28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) { 29 std::span<u8> output) {
30 switch (command.group) { 30 switch (command.group) {
31 case 0x1: 31 case 0x1:
32 switch (command.cmd) { 32 switch (command.cmd) {
@@ -55,13 +55,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
55} 55}
56 56
57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
58 std::span<const u8> inline_input, std::vector<u8>& output) { 58 std::span<const u8> inline_input, std::span<u8> output) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
61} 61}
62 62
63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
64 std::vector<u8>& output, std::vector<u8>& inline_output) { 64 std::span<u8> inline_output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
67} 67}
@@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
69void nvmap::OnOpen(DeviceFD fd) {} 69void nvmap::OnOpen(DeviceFD fd) {}
70void nvmap::OnClose(DeviceFD fd) {} 70void nvmap::OnClose(DeviceFD fd) {}
71 71
72NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) { 72NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {
73 IocCreateParams params; 73 IocCreateParams params;
74 std::memcpy(&params, input.data(), sizeof(params)); 74 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); 75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
@@ -89,7 +89,7 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {
89 return NvResult::Success; 89 return NvResult::Success;
90} 90}
91 91
92NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) { 92NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
93 IocAllocParams params; 93 IocAllocParams params;
94 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); 95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
@@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {
137 return result; 137 return result;
138} 138}
139 139
140NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) { 140NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {
141 IocGetIdParams params; 141 IocGetIdParams params;
142 std::memcpy(&params, input.data(), sizeof(params)); 142 std::memcpy(&params, input.data(), sizeof(params));
143 143
@@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {
161 return NvResult::Success; 161 return NvResult::Success;
162} 162}
163 163
164NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) { 164NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {
165 IocFromIdParams params; 165 IocFromIdParams params;
166 std::memcpy(&params, input.data(), sizeof(params)); 166 std::memcpy(&params, input.data(), sizeof(params));
167 167
@@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {
192 return NvResult::Success; 192 return NvResult::Success;
193} 193}
194 194
195NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) { 195NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) {
196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
197 197
198 IocParamParams params; 198 IocParamParams params;
@@ -241,7 +241,7 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {
241 return NvResult::Success; 241 return NvResult::Success;
242} 242}
243 243
244NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) { 244NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
245 IocFreeParams params; 245 IocFreeParams params;
246 std::memcpy(&params, input.data(), sizeof(params)); 246 std::memcpy(&params, input.data(), sizeof(params));
247 247
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 82bd3b118..40c65b430 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -27,11 +27,11 @@ public:
27 nvmap& operator=(const nvmap&) = delete; 27 nvmap& operator=(const nvmap&) = delete;
28 28
29 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 29 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
30 std::vector<u8>& output) override; 30 std::span<u8> output) override;
31 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 31 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
32 std::span<const u8> inline_input, std::vector<u8>& output) override; 32 std::span<const u8> inline_input, std::span<u8> output) override;
33 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 33 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
34 std::vector<u8>& inline_output) override; 34 std::span<u8> inline_output) override;
35 35
36 void OnOpen(DeviceFD fd) override; 36 void OnOpen(DeviceFD fd) override;
37 void OnClose(DeviceFD fd) override; 37 void OnClose(DeviceFD fd) override;
@@ -106,12 +106,12 @@ private:
106 }; 106 };
107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
108 108
109 NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output); 109 NvResult IocCreate(std::span<const u8> input, std::span<u8> output);
110 NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output); 110 NvResult IocAlloc(std::span<const u8> input, std::span<u8> output);
111 NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output); 111 NvResult IocGetId(std::span<const u8> input, std::span<u8> output);
112 NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output); 112 NvResult IocFromId(std::span<const u8> input, std::span<u8> output);
113 NvResult IocParam(std::span<const u8> input, std::vector<u8>& output); 113 NvResult IocParam(std::span<const u8> input, std::span<u8> output);
114 NvResult IocFree(std::span<const u8> input, std::vector<u8>& output); 114 NvResult IocFree(std::span<const u8> input, std::span<u8> output);
115 115
116 NvCore::Container& container; 116 NvCore::Container& container;
117 NvCore::NvMap& file; 117 NvCore::NvMap& file;
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 3d774eec4..9e46ee8dd 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -130,7 +130,7 @@ DeviceFD Module::Open(const std::string& device_name) {
130} 130}
131 131
132NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, 132NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
133 std::vector<u8>& output) { 133 std::span<u8> output) {
134 if (fd < 0) { 134 if (fd < 0) {
135 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 135 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
136 return NvResult::InvalidState; 136 return NvResult::InvalidState;
@@ -147,7 +147,7 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
147} 147}
148 148
149NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 149NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
150 std::span<const u8> inline_input, std::vector<u8>& output) { 150 std::span<const u8> inline_input, std::span<u8> output) {
151 if (fd < 0) { 151 if (fd < 0) {
152 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 152 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
153 return NvResult::InvalidState; 153 return NvResult::InvalidState;
@@ -163,8 +163,8 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
163 return itr->second->Ioctl2(fd, command, input, inline_input, output); 163 return itr->second->Ioctl2(fd, command, input, inline_input, output);
164} 164}
165 165
166NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, 166NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
167 std::vector<u8>& output, std::vector<u8>& inline_output) { 167 std::span<u8> inline_output) {
168 if (fd < 0) { 168 if (fd < 0) {
169 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 169 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
170 return NvResult::InvalidState; 170 return NvResult::InvalidState;
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 668be742b..d8622b3ca 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -80,13 +80,13 @@ public:
80 DeviceFD Open(const std::string& device_name); 80 DeviceFD Open(const std::string& device_name);
81 81
82 /// Sends an ioctl command to the specified file descriptor. 82 /// Sends an ioctl command to the specified file descriptor.
83 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output); 83 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output);
84 84
85 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, 85 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
86 std::span<const u8> inline_input, std::vector<u8>& output); 86 std::span<const u8> inline_input, std::span<u8> output);
87 87
88 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, 88 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
89 std::vector<u8>& inline_output); 89 std::span<u8> inline_output);
90 90
91 /// Closes a device file descriptor and returns operation success. 91 /// Closes a device file descriptor and returns operation success.
92 NvResult Close(DeviceFD fd); 92 NvResult Close(DeviceFD fd);
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index d010a1e03..348207e25 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -63,12 +63,12 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) {
63 } 63 }
64 64
65 // Check device 65 // Check device
66 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 66 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));
67 const auto input_buffer = ctx.ReadBuffer(0); 67 const auto input_buffer = ctx.ReadBuffer(0);
68 68
69 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); 69 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output);
70 if (command.is_out != 0) { 70 if (command.is_out != 0) {
71 ctx.WriteBuffer(output_buffer); 71 ctx.WriteBuffer(tmp_output);
72 } 72 }
73 73
74 IPC::ResponseBuilder rb{ctx, 3}; 74 IPC::ResponseBuilder rb{ctx, 3};
@@ -90,12 +90,12 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) {
90 90
91 const auto input_buffer = ctx.ReadBuffer(0); 91 const auto input_buffer = ctx.ReadBuffer(0);
92 const auto input_inlined_buffer = ctx.ReadBuffer(1); 92 const auto input_inlined_buffer = ctx.ReadBuffer(1);
93 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 93 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));
94 94
95 const auto nv_result = 95 const auto nv_result =
96 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); 96 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, tmp_output);
97 if (command.is_out != 0) { 97 if (command.is_out != 0) {
98 ctx.WriteBuffer(output_buffer); 98 ctx.WriteBuffer(tmp_output);
99 } 99 }
100 100
101 IPC::ResponseBuilder rb{ctx, 3}; 101 IPC::ResponseBuilder rb{ctx, 3};
@@ -116,14 +116,12 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) {
116 } 116 }
117 117
118 const auto input_buffer = ctx.ReadBuffer(0); 118 const auto input_buffer = ctx.ReadBuffer(0);
119 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 119 tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));
120 std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); 120 tmp_output_inline.resize_destructive(ctx.GetWriteBufferSize(1));
121 121 const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output_inline);
122 const auto nv_result =
123 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
124 if (command.is_out != 0) { 122 if (command.is_out != 0) {
125 ctx.WriteBuffer(output_buffer, 0); 123 ctx.WriteBuffer(tmp_output, 0);
126 ctx.WriteBuffer(output_buffer_inline, 1); 124 ctx.WriteBuffer(tmp_output_inline, 1);
127 } 125 }
128 126
129 IPC::ResponseBuilder rb{ctx, 3}; 127 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index 881ea1a6b..4b593ff90 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include "common/scratch_buffer.h"
7#include "core/hle/service/nvdrv/nvdrv.h" 8#include "core/hle/service/nvdrv/nvdrv.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
@@ -33,6 +34,8 @@ private:
33 34
34 u64 pid{}; 35 u64 pid{};
35 bool is_initialized{}; 36 bool is_initialized{};
37 Common::ScratchBuffer<u8> tmp_output;
38 Common::ScratchBuffer<u8> tmp_output_inline;
36}; 39};
37 40
38} // namespace Service::Nvidia 41} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index da2d5890f..5f55cd31e 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -43,14 +43,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
43 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 43 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
44 44
45 while (!stop_token.stop_requested()) { 45 while (!stop_token.stop_requested()) {
46 vsync_signal.wait(false); 46 vsync_signal.Wait();
47 vsync_signal.store(false);
48
49 guard->lock();
50 47
48 const auto lock_guard = Lock();
51 Compose(); 49 Compose();
52
53 guard->unlock();
54 } 50 }
55} 51}
56 52
@@ -69,8 +65,8 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
69 "ScreenComposition", 65 "ScreenComposition",
70 [this](std::uintptr_t, s64 time, 66 [this](std::uintptr_t, s64 time,
71 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 67 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
72 vsync_signal.store(true); 68 { const auto lock_guard = Lock(); }
73 vsync_signal.notify_all(); 69 vsync_signal.Set();
74 return std::chrono::nanoseconds(GetNextTicks()); 70 return std::chrono::nanoseconds(GetNextTicks());
75 }); 71 });
76 72
@@ -96,8 +92,7 @@ Nvnflinger::~Nvnflinger() {
96 if (system.IsMulticore()) { 92 if (system.IsMulticore()) {
97 system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); 93 system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
98 vsync_thread.request_stop(); 94 vsync_thread.request_stop();
99 vsync_signal.store(true); 95 vsync_signal.Set();
100 vsync_signal.notify_all();
101 } else { 96 } else {
102 system.CoreTiming().UnscheduleEvent(single_composition_event, {}); 97 system.CoreTiming().UnscheduleEvent(single_composition_event, {});
103 } 98 }
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index a043cceb2..ef236303a 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -12,6 +12,7 @@
12 12
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/polyfill_thread.h" 14#include "common/polyfill_thread.h"
15#include "common/thread.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16#include "core/hle/service/kernel_helpers.h" 17#include "core/hle/service/kernel_helpers.h"
17 18
@@ -143,7 +144,7 @@ private:
143 144
144 Core::System& system; 145 Core::System& system;
145 146
146 std::atomic<bool> vsync_signal; 147 Common::Event vsync_signal;
147 148
148 std::jthread vsync_thread; 149 std::jthread vsync_thread;
149 150
diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h
index fb56d75d7..23ba315a0 100644
--- a/src/core/hle/service/nvnflinger/parcel.h
+++ b/src/core/hle/service/nvnflinger/parcel.h
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <span> 7#include <span>
8#include <vector> 8#include <vector>
9#include <boost/container/small_vector.hpp>
9 10
10#include "common/alignment.h" 11#include "common/alignment.h"
11#include "common/assert.h" 12#include "common/assert.h"
@@ -167,7 +168,7 @@ public:
167private: 168private:
168 template <typename T> 169 template <typename T>
169 requires(std::is_trivially_copyable_v<T>) 170 requires(std::is_trivially_copyable_v<T>)
170 void WriteImpl(const T& val, std::vector<u8>& buffer) { 171 void WriteImpl(const T& val, boost::container::small_vector<u8, 0x200>& buffer) {
171 const size_t aligned_size = Common::AlignUp(sizeof(T), 4); 172 const size_t aligned_size = Common::AlignUp(sizeof(T), 4);
172 const size_t old_size = buffer.size(); 173 const size_t old_size = buffer.size();
173 buffer.resize(old_size + aligned_size); 174 buffer.resize(old_size + aligned_size);
@@ -176,8 +177,8 @@ private:
176 } 177 }
177 178
178private: 179private:
179 std::vector<u8> m_data_buffer; 180 boost::container::small_vector<u8, 0x200> m_data_buffer;
180 std::vector<u8> m_object_buffer; 181 boost::container::small_vector<u8, 0x200> m_object_buffer;
181}; 182};
182 183
183} // namespace Service::android 184} // namespace Service::android
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 156bc27d8..d1e99b184 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -44,7 +44,7 @@ ServerManager::~ServerManager() {
44 m_event->Signal(); 44 m_event->Signal();
45 45
46 // Wait for processing to stop. 46 // Wait for processing to stop.
47 m_stopped.wait(false); 47 m_stopped.Wait();
48 m_threads.clear(); 48 m_threads.clear();
49 49
50 // Clean up ports. 50 // Clean up ports.
@@ -182,10 +182,7 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
182} 182}
183 183
184Result ServerManager::LoopProcess() { 184Result ServerManager::LoopProcess() {
185 SCOPE_EXIT({ 185 SCOPE_EXIT({ m_stopped.Set(); });
186 m_stopped.store(true);
187 m_stopped.notify_all();
188 });
189 186
190 R_RETURN(this->LoopProcessImpl()); 187 R_RETURN(this->LoopProcessImpl());
191} 188}
diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h
index fdb8af2ff..58b0a0832 100644
--- a/src/core/hle/service/server_manager.h
+++ b/src/core/hle/service/server_manager.h
@@ -3,7 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <atomic>
7#include <functional> 6#include <functional>
8#include <list> 7#include <list>
9#include <map> 8#include <map>
@@ -12,6 +11,7 @@
12#include <vector> 11#include <vector>
13 12
14#include "common/polyfill_thread.h" 13#include "common/polyfill_thread.h"
14#include "common/thread.h"
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16#include "core/hle/service/mutex.h" 16#include "core/hle/service/mutex.h"
17 17
@@ -82,7 +82,7 @@ private:
82 std::list<RequestState> m_deferrals{}; 82 std::list<RequestState> m_deferrals{};
83 83
84 // Host state tracking 84 // Host state tracking
85 std::atomic<bool> m_stopped{}; 85 Common::Event m_stopped{};
86 std::vector<std::jthread> m_threads{}; 86 std::vector<std::jthread> m_threads{};
87 std::stop_source m_stop_source{}; 87 std::stop_source m_stop_source{};
88}; 88};
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index e6293ffb9..9fc01ea90 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <ratio>
7
6#include "common/common_funcs.h" 8#include "common/common_funcs.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/uuid.h" 10#include "common/uuid.h"
@@ -74,18 +76,19 @@ static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>,
74/// https://switchbrew.org/wiki/Glue_services#TimeSpanType 76/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
75struct TimeSpanType { 77struct TimeSpanType {
76 s64 nanoseconds{}; 78 s64 nanoseconds{};
77 static constexpr s64 ns_per_second{1000000000ULL};
78 79
79 s64 ToSeconds() const { 80 s64 ToSeconds() const {
80 return nanoseconds / ns_per_second; 81 return nanoseconds / std::nano::den;
81 } 82 }
82 83
83 static TimeSpanType FromSeconds(s64 seconds) { 84 static TimeSpanType FromSeconds(s64 seconds) {
84 return {seconds * ns_per_second}; 85 return {seconds * std::nano::den};
85 } 86 }
86 87
87 static TimeSpanType FromTicks(u64 ticks, u64 frequency) { 88 template <u64 Frequency>
88 return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency)); 89 static TimeSpanType FromTicks(u64 ticks) {
90 using TicksToNSRatio = std::ratio<std::nano::den, Frequency>;
91 return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)};
89 } 92 }
90}; 93};
91static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); 94static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
index 3dbbb9850..5627b7003 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -10,7 +10,7 @@ namespace Service::Time::Clock {
10 10
11TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { 11TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{ 12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; 13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; 14 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
15 15
16 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { 16 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
index 27600413e..0d9fb3143 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -10,7 +10,7 @@ namespace Service::Time::Clock {
10 10
11SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { 11SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{ 12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; 13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14 14
15 return {ticks_time_span.ToSeconds(), GetClockSourceId()}; 15 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
16} 16}
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 868be60c5..7197ca30f 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -240,8 +240,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestCon
240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; 240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
241 241
242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { 242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
243 const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), 243 const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
244 Core::Hardware::CNTFREQ)}; 244 system.CoreTiming().GetClockTicks())};
245 const s64 base_time_point{context.offset + current_time_point.time_point - 245 const s64 base_time_point{context.offset + current_time_point.time_point -
246 ticks.ToSeconds()}; 246 ticks.ToSeconds()};
247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; 247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index 28667710e..fa0fd0531 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -22,10 +22,6 @@ s64 GetSecondsSinceEpoch() {
22 return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() + 22 return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() +
23 Settings::values.custom_rtc_differential; 23 Settings::values.custom_rtc_differential;
24} 24}
25
26s64 GetExternalRtcValue() {
27 return GetSecondsSinceEpoch() + TimeManager::GetExternalTimeZoneOffset();
28}
29} // Anonymous namespace 25} // Anonymous namespace
30 26
31struct TimeManager::Impl final { 27struct TimeManager::Impl final {
@@ -43,7 +39,7 @@ struct TimeManager::Impl final {
43 std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()}, 39 std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
44 time_zone_content_manager{system} { 40 time_zone_content_manager{system} {
45 41
46 const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())}; 42 const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())};
47 SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {}); 43 SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
48 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); 44 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
49 45
@@ -107,7 +103,7 @@ struct TimeManager::Impl final {
107 103
108 void SetupTimeZoneManager(std::string location_name, 104 void SetupTimeZoneManager(std::string location_name,
109 Clock::SteadyClockTimePoint time_zone_updated_time_point, 105 Clock::SteadyClockTimePoint time_zone_updated_time_point,
110 std::size_t total_location_name_count, u128 time_zone_rule_version, 106 std::vector<std::string> location_names, u128 time_zone_rule_version,
111 FileSys::VirtualFile& vfs_file) { 107 FileSys::VirtualFile& vfs_file) {
112 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( 108 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
113 location_name, vfs_file) != ResultSuccess) { 109 location_name, vfs_file) != ResultSuccess) {
@@ -117,20 +113,13 @@ struct TimeManager::Impl final {
117 113
118 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point); 114 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
119 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount( 115 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
120 total_location_name_count); 116 location_names.size());
117 time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names);
121 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion( 118 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
122 time_zone_rule_version); 119 time_zone_rule_version);
123 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized(); 120 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
124 } 121 }
125 122
126 static s64 GetExternalTimeZoneOffset() {
127 // With "auto" timezone setting, we use the external system's timezone offset
128 if (Settings::GetTimeZoneString() == "auto") {
129 return Common::TimeZone::GetCurrentOffsetSeconds().count();
130 }
131 return 0;
132 }
133
134 void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id, 123 void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id,
135 Clock::TimeSpanType setup_value, 124 Clock::TimeSpanType setup_value,
136 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) { 125 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
@@ -295,19 +284,10 @@ void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
295 284
296void TimeManager::SetupTimeZoneManager(std::string location_name, 285void TimeManager::SetupTimeZoneManager(std::string location_name,
297 Clock::SteadyClockTimePoint time_zone_updated_time_point, 286 Clock::SteadyClockTimePoint time_zone_updated_time_point,
298 std::size_t total_location_name_count, 287 std::vector<std::string> location_names,
299 u128 time_zone_rule_version, 288 u128 time_zone_rule_version,
300 FileSys::VirtualFile& vfs_file) { 289 FileSys::VirtualFile& vfs_file) {
301 impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, 290 impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names,
302 total_location_name_count, time_zone_rule_version, vfs_file); 291 time_zone_rule_version, vfs_file);
303} 292}
304
305/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() {
306 // With "auto" timezone setting, we use the external system's timezone offset
307 if (Settings::GetTimeZoneString() == "auto") {
308 return Common::TimeZone::GetCurrentOffsetSeconds().count();
309 }
310 return 0;
311}
312
313} // namespace Service::Time 293} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
index 4f046f266..84572dbfa 100644
--- a/src/core/hle/service/time/time_manager.h
+++ b/src/core/hle/service/time/time_manager.h
@@ -61,11 +61,9 @@ public:
61 61
62 void SetupTimeZoneManager(std::string location_name, 62 void SetupTimeZoneManager(std::string location_name,
63 Clock::SteadyClockTimePoint time_zone_updated_time_point, 63 Clock::SteadyClockTimePoint time_zone_updated_time_point,
64 std::size_t total_location_name_count, u128 time_zone_rule_version, 64 std::vector<std::string> location_names, u128 time_zone_rule_version,
65 FileSys::VirtualFile& vfs_file); 65 FileSys::VirtualFile& vfs_file);
66 66
67 static s64 GetExternalTimeZoneOffset();
68
69private: 67private:
70 Core::System& system; 68 Core::System& system;
71 69
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index ce1c85bcc..a00676669 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -21,8 +21,9 @@ SharedMemory::~SharedMemory() = default;
21 21
22void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, 22void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
23 Clock::TimeSpanType current_time_point) { 23 Clock::TimeSpanType current_time_point) {
24 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( 24 const Clock::TimeSpanType ticks_time_span{
25 system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; 25 Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
26 system.CoreTiming().GetClockTicks())};
26 const Clock::SteadyClockContext context{ 27 const Clock::SteadyClockContext context{
27 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), 28 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
28 clock_source_id}; 29 clock_source_id};
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index afbfe9715..5d60be67a 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 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 <chrono>
4#include <sstream> 5#include <sstream>
5 6
6#include "common/logging/log.h" 7#include "common/logging/log.h"
@@ -12,7 +13,11 @@
12#include "core/file_sys/registered_cache.h" 13#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/romfs.h" 14#include "core/file_sys/romfs.h"
14#include "core/file_sys/system_archive/system_archive.h" 15#include "core/file_sys/system_archive/system_archive.h"
16#include "core/file_sys/vfs.h"
17#include "core/file_sys/vfs_types.h"
18#include "core/hle/result.h"
15#include "core/hle/service/filesystem/filesystem.h" 19#include "core/hle/service/filesystem/filesystem.h"
20#include "core/hle/service/time/errors.h"
16#include "core/hle/service/time/time_manager.h" 21#include "core/hle/service/time/time_manager.h"
17#include "core/hle/service/time/time_zone_content_manager.h" 22#include "core/hle/service/time/time_zone_content_manager.h"
18 23
@@ -71,19 +76,13 @@ TimeZoneContentManager::TimeZoneContentManager(Core::System& system_)
71 : system{system_}, location_name_cache{BuildLocationNameCache(system)} {} 76 : system{system_}, location_name_cache{BuildLocationNameCache(system)} {}
72 77
73void TimeZoneContentManager::Initialize(TimeManager& time_manager) { 78void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
74 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString(); 79 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto" || timezone_setting == "default") {
77 location_name = Common::TimeZone::GetDefaultTimeZone();
78 } else {
79 location_name = timezone_setting;
80 }
81 80
82 if (FileSys::VirtualFile vfs_file; 81 if (FileSys::VirtualFile vfs_file;
83 GetTimeZoneInfoFile(location_name, vfs_file) == ResultSuccess) { 82 GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) {
84 const auto time_point{ 83 const auto time_point{
85 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; 84 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
86 time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {}, 85 time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {},
87 vfs_file); 86 vfs_file);
88 } else { 87 } else {
89 time_zone_manager.MarkAsInitialized(); 88 time_zone_manager.MarkAsInitialized();
@@ -126,8 +125,15 @@ Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_n
126 125
127 vfs_file = zoneinfo_dir->GetFileRelative(location_name); 126 vfs_file = zoneinfo_dir->GetFileRelative(location_name);
128 if (!vfs_file) { 127 if (!vfs_file) {
129 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", 128 LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.",
130 time_zone_binary_titleid, location_name); 129 time_zone_binary_titleid, location_name);
130 const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()};
131 vfs_file = zoneinfo_dir->GetFile(system_time_zone);
132 }
133
134 if (!vfs_file) {
135 LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
136 time_zone_binary_titleid, location_name);
131 vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone()); 137 vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
132 } 138 }
133 139
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 973f7837a..205371a26 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <climits> 4#include <climits>
5#include <limits>
5 6
6#include "common/assert.h" 7#include "common/assert.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -9,6 +10,7 @@
9#include "core/file_sys/nca_metadata.h" 10#include "core/file_sys/nca_metadata.h"
10#include "core/file_sys/registered_cache.h" 11#include "core/file_sys/registered_cache.h"
11#include "core/hle/service/time/time_zone_manager.h" 12#include "core/hle/service/time/time_zone_manager.h"
13#include "core/hle/service/time/time_zone_types.h"
12 14
13namespace Service::Time::TimeZone { 15namespace Service::Time::TimeZone {
14 16
@@ -128,10 +130,10 @@ static constexpr int GetQZName(const char* name, int offset, char delimiter) {
128} 130}
129 131
130static constexpr int GetTZName(const char* name, int offset) { 132static constexpr int GetTZName(const char* name, int offset) {
131 for (char value{name[offset]}; 133 char c;
132 value != '\0' && !IsDigit(value) && value != ',' && value != '-' && value != '+'; 134
133 offset++) { 135 while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') {
134 value = name[offset]; 136 ++offset;
135 } 137 }
136 return offset; 138 return offset;
137} 139}
@@ -147,6 +149,7 @@ static constexpr bool GetInteger(const char* name, int& offset, int& value, int
147 if (value > max) { 149 if (value > max) {
148 return {}; 150 return {};
149 } 151 }
152 offset++;
150 temp = name[offset]; 153 temp = name[offset];
151 } while (IsDigit(temp)); 154 } while (IsDigit(temp));
152 155
@@ -471,6 +474,13 @@ static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
471 their_std_offset = their_offset; 474 their_std_offset = their_offset;
472 } 475 }
473 } 476 }
477
478 if (rule.time_count > 0) {
479 UNIMPLEMENTED();
480 // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329
481 // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing
482 }
483
474 rule.ttis[0].gmt_offset = -std_offset; 484 rule.ttis[0].gmt_offset = -std_offset;
475 rule.ttis[0].is_dst = false; 485 rule.ttis[0].is_dst = false;
476 rule.ttis[0].abbreviation_list_index = 0; 486 rule.ttis[0].abbreviation_list_index = 0;
@@ -514,6 +524,7 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
514 524
515 constexpr s32 time_zone_max_leaps{50}; 525 constexpr s32 time_zone_max_leaps{50};
516 constexpr s32 time_zone_max_chars{50}; 526 constexpr s32 time_zone_max_chars{50};
527 constexpr s32 time_zone_max_times{1000};
517 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && 528 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
518 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) && 529 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) &&
519 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) && 530 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&
@@ -546,7 +557,7 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
546 for (int index{}; index < time_zone_rule.time_count; ++index) { 557 for (int index{}; index < time_zone_rule.time_count; ++index) {
547 const u8 type{*vfs_file->ReadByte(read_offset)}; 558 const u8 type{*vfs_file->ReadByte(read_offset)};
548 read_offset += sizeof(u8); 559 read_offset += sizeof(u8);
549 if (time_zone_rule.time_count <= type) { 560 if (time_zone_rule.type_count <= type) {
550 return {}; 561 return {};
551 } 562 }
552 if (time_zone_rule.types[index] != 0) { 563 if (time_zone_rule.types[index] != 0) {
@@ -624,16 +635,109 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
624 std::array<char, time_zone_name_max> name{}; 635 std::array<char, time_zone_name_max> name{};
625 std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1)); 636 std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1));
626 637
638 // Fill in computed transition times with temp rule
627 TimeZoneRule temp_rule; 639 TimeZoneRule temp_rule;
628 if (ParsePosixName(name.data(), temp_rule)) { 640 if (ParsePosixName(name.data(), temp_rule)) {
629 UNIMPLEMENTED(); 641 int have_abbreviation = 0;
642 int char_count = time_zone_rule.char_count;
643
644 for (int i = 0; i < temp_rule.type_count; i++) {
645 char* temp_abbreviation =
646 temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index;
647 int j;
648 for (j = 0; j < char_count; j++) {
649 if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) {
650 temp_rule.ttis[i].abbreviation_list_index = j;
651 have_abbreviation++;
652 break;
653 }
654 }
655 if (j >= char_count) {
656 int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation));
657 if (j + temp_abbreviation_length < time_zone_max_chars) {
658 std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation);
659 char_count = j + temp_abbreviation_length + 1;
660 temp_rule.ttis[i].abbreviation_list_index = j;
661 have_abbreviation++;
662 }
663 }
664 }
665
666 if (have_abbreviation == temp_rule.type_count) {
667 time_zone_rule.char_count = char_count;
668
669 // Original comment:
670 /* Ignore any trailing, no-op transitions generated
671 by zic as they don't help here and can run afoul
672 of bugs in zic 2016j or earlier. */
673 // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic
674 while (1 < time_zone_rule.time_count &&
675 (time_zone_rule.types[time_zone_rule.time_count - 1] ==
676 time_zone_rule.types[time_zone_rule.time_count - 2])) {
677 time_zone_rule.time_count--;
678 }
679
680 for (int i = 0;
681 i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times;
682 i++) {
683 const s64 transition_time = temp_rule.ats[i];
684 if (0 < time_zone_rule.time_count &&
685 transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) {
686 continue;
687 }
688
689 time_zone_rule.ats[time_zone_rule.time_count] = transition_time;
690 time_zone_rule.types[time_zone_rule.time_count] =
691 static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]);
692 time_zone_rule.time_count++;
693 }
694 for (int i = 0; i < temp_rule.type_count; i++) {
695 time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i];
696 }
697 }
630 } 698 }
631 } 699 }
700
701 const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool {
702 if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) {
703 return {};
704 }
705
706 const struct TimeTypeInfo* ap = &rule.ttis[a];
707 const struct TimeTypeInfo* bp = &rule.ttis[b];
708
709 return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst &&
710 (std::strcmp(&rule.chars[ap->abbreviation_list_index],
711 &rule.chars[bp->abbreviation_list_index]) == 0));
712 };
713
632 if (time_zone_rule.type_count == 0) { 714 if (time_zone_rule.type_count == 0) {
633 return {}; 715 return {};
634 } 716 }
635 if (time_zone_rule.time_count > 1) { 717 if (time_zone_rule.time_count > 1) {
636 UNIMPLEMENTED(); 718 if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) {
719 s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat;
720 int repeatattype = time_zone_rule.types[0];
721 for (int i = 1; i < time_zone_rule.time_count; ++i) {
722 if (time_zone_rule.ats[i] == repeatat &&
723 typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
724 time_zone_rule.go_back = true;
725 break;
726 }
727 }
728 }
729 if (std::numeric_limits<s64>::min() + seconds_per_repeat <=
730 time_zone_rule.ats[time_zone_rule.time_count - 1]) {
731 s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat;
732 int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1];
733 for (int i = time_zone_rule.time_count; i >= 0; --i) {
734 if (time_zone_rule.ats[i] == repeatat &&
735 typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
736 time_zone_rule.go_ahead = true;
737 break;
738 }
739 }
740 }
637 } 741 }
638 742
639 s32 default_type{}; 743 s32 default_type{};
@@ -745,8 +849,9 @@ static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal&
745static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, 849static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
746 CalendarTimeInternal& calendar_time, 850 CalendarTimeInternal& calendar_time,
747 CalendarAdditionalInfo& calendar_additional_info) { 851 CalendarAdditionalInfo& calendar_additional_info) {
748 if ((rules.go_ahead && time < rules.ats[0]) || 852 ASSERT(rules.go_ahead ? rules.time_count > 0 : true);
749 (rules.go_back && time > rules.ats[rules.time_count - 1])) { 853 if ((rules.go_back && time < rules.ats[0]) ||
854 (rules.go_ahead && time > rules.ats[rules.time_count - 1])) {
750 s64 seconds{}; 855 s64 seconds{};
751 if (time < rules.ats[0]) { 856 if (time < rules.ats[0]) {
752 seconds = rules.ats[0] - time; 857 seconds = rules.ats[0] - time;
@@ -806,9 +911,13 @@ static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
806 911
807 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst; 912 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
808 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]}; 913 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
809 for (int index{}; time_zone[index] != '\0'; ++index) { 914 u32 index;
915 for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' &&
916 index < calendar_additional_info.timezone_name.size() - 1;
917 ++index) {
810 calendar_additional_info.timezone_name[index] = time_zone[index]; 918 calendar_additional_info.timezone_name[index] = time_zone[index];
811 } 919 }
920 calendar_additional_info.timezone_name[index] = '\0';
812 return ResultSuccess; 921 return ResultSuccess;
813} 922}
814 923
@@ -1038,4 +1147,36 @@ Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
1038 return ResultSuccess; 1147 return ResultSuccess;
1039} 1148}
1040 1149
1150Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const {
1151 if (!is_initialized) {
1152 return ERROR_UNINITIALIZED_CLOCK;
1153 }
1154 count = static_cast<u32>(total_location_name_count);
1155
1156 return ResultSuccess;
1157}
1158
1159Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const {
1160 if (!is_initialized) {
1161 return ERROR_UNINITIALIZED_CLOCK;
1162 }
1163 version = time_zone_rule_version;
1164
1165 return ResultSuccess;
1166}
1167
1168Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const {
1169 if (!is_initialized) {
1170 return ERROR_UNINITIALIZED_CLOCK;
1171 }
1172
1173 for (const auto& name : total_location_names) {
1174 LocationName entry{};
1175 std::memcpy(entry.data(), name.c_str(), name.size());
1176 values.push_back(entry);
1177 }
1178
1179 return ResultSuccess;
1180}
1181
1041} // namespace Service::Time::TimeZone 1182} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
index 5ebd4035e..8664f28d1 100644
--- a/src/core/hle/service/time/time_zone_manager.h
+++ b/src/core/hle/service/time/time_zone_manager.h
@@ -21,6 +21,10 @@ public:
21 total_location_name_count = value; 21 total_location_name_count = value;
22 } 22 }
23 23
24 void SetLocationNames(std::vector<std::string> location_names) {
25 total_location_names = location_names;
26 }
27
24 void SetTimeZoneRuleVersion(const u128& value) { 28 void SetTimeZoneRuleVersion(const u128& value) {
25 time_zone_rule_version = value; 29 time_zone_rule_version = value;
26 } 30 }
@@ -33,6 +37,9 @@ public:
33 FileSys::VirtualFile& vfs_file); 37 FileSys::VirtualFile& vfs_file);
34 Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); 38 Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
35 Result GetDeviceLocationName(TimeZone::LocationName& value) const; 39 Result GetDeviceLocationName(TimeZone::LocationName& value) const;
40 Result GetTotalLocationNameCount(s32& count) const;
41 Result GetTimeZoneRuleVersion(u128& version) const;
42 Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const;
36 Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; 43 Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
37 Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; 44 Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
38 Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; 45 Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
@@ -46,6 +53,7 @@ private:
46 std::string device_location_name{"GMT"}; 53 std::string device_location_name{"GMT"};
47 u128 time_zone_rule_version{}; 54 u128 time_zone_rule_version{};
48 std::size_t total_location_name_count{}; 55 std::size_t total_location_name_count{};
56 std::vector<std::string> total_location_names{};
49 Clock::SteadyClockTimePoint time_zone_update_time_point{ 57 Clock::SteadyClockTimePoint time_zone_update_time_point{
50 Clock::SteadyClockTimePoint::GetRandom()}; 58 Clock::SteadyClockTimePoint::GetRandom()};
51}; 59};
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
index cda8d8343..8171c82a5 100644
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -15,10 +15,10 @@ ITimeZoneService::ITimeZoneService(Core::System& system_,
15 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
16 {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, 16 {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
17 {1, nullptr, "SetDeviceLocationName"}, 17 {1, nullptr, "SetDeviceLocationName"},
18 {2, nullptr, "GetTotalLocationNameCount"}, 18 {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
19 {3, nullptr, "LoadLocationNameList"}, 19 {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"},
20 {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, 20 {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
21 {5, nullptr, "GetTimeZoneRuleVersion"}, 21 {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
22 {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"}, 22 {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
23 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, 23 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
24 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, 24 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
@@ -45,6 +45,57 @@ void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) {
45 rb.PushRaw(location_name); 45 rb.PushRaw(location_name);
46} 46}
47 47
48void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) {
49 LOG_DEBUG(Service_Time, "called");
50
51 s32 count{};
52 if (const Result result{
53 time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)};
54 result != ResultSuccess) {
55 IPC::ResponseBuilder rb{ctx, 2};
56 rb.Push(result);
57 return;
58 }
59
60 IPC::ResponseBuilder rb{ctx, 3};
61 rb.Push(ResultSuccess);
62 rb.Push(count);
63}
64
65void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) {
66 LOG_DEBUG(Service_Time, "called");
67
68 std::vector<TimeZone::LocationName> location_names{};
69 if (const Result result{
70 time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)};
71 result != ResultSuccess) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(result);
74 return;
75 }
76
77 ctx.WriteBuffer(location_names);
78 IPC::ResponseBuilder rb{ctx, 3};
79 rb.Push(ResultSuccess);
80 rb.Push(static_cast<s32>(location_names.size()));
81}
82void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) {
83 LOG_DEBUG(Service_Time, "called");
84
85 u128 rule_version{};
86 if (const Result result{
87 time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)};
88 result != ResultSuccess) {
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(result);
91 return;
92 }
93
94 IPC::ResponseBuilder rb{ctx, 6};
95 rb.Push(ResultSuccess);
96 rb.PushRaw(rule_version);
97}
98
48void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { 99void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) {
49 IPC::RequestParser rp{ctx}; 100 IPC::RequestParser rp{ctx};
50 const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()}; 101 const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
@@ -61,20 +112,14 @@ void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) {
61 LOG_DEBUG(Service_Time, "called, location_name={}", location_name); 112 LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
62 113
63 TimeZone::TimeZoneRule time_zone_rule{}; 114 TimeZone::TimeZoneRule time_zone_rule{};
64 if (const Result result{ 115 const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
65 time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
66 result != ResultSuccess) {
67 IPC::ResponseBuilder rb{ctx, 2};
68 rb.Push(result);
69 return;
70 }
71 116
72 std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); 117 std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
73 std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); 118 std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
74 ctx.WriteBuffer(time_zone_rule_outbuffer); 119 ctx.WriteBuffer(time_zone_rule_outbuffer);
75 120
76 IPC::ResponseBuilder rb{ctx, 2}; 121 IPC::ResponseBuilder rb{ctx, 2};
77 rb.Push(ResultSuccess); 122 rb.Push(result);
78} 123}
79 124
80void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { 125void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h
index ea83b5714..952fcb0e2 100644
--- a/src/core/hle/service/time/time_zone_service.h
+++ b/src/core/hle/service/time/time_zone_service.h
@@ -22,6 +22,9 @@ public:
22 22
23private: 23private:
24 void GetDeviceLocationName(HLERequestContext& ctx); 24 void GetDeviceLocationName(HLERequestContext& ctx);
25 void GetTotalLocationNameCount(HLERequestContext& ctx);
26 void LoadLocationNameList(HLERequestContext& ctx);
27 void GetTimeZoneRuleVersion(HLERequestContext& ctx);
25 void LoadTimeZoneRule(HLERequestContext& ctx); 28 void LoadTimeZoneRule(HLERequestContext& ctx);
26 void ToCalendarTime(HLERequestContext& ctx); 29 void ToCalendarTime(HLERequestContext& ctx);
27 void ToCalendarTimeWithMyRule(HLERequestContext& ctx); 30 void ToCalendarTimeWithMyRule(HLERequestContext& ctx);
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index b2b5677c8..52494e0d9 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
195 OnMotionUpdate(port, type, id, value); 195 OnMotionUpdate(port, type, id, value);
196 }}, 196 }},
197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, 197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
198 .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { 198 .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) {
199 OnAmiiboUpdate(port, type, amiibo_data); 199 OnAmiiboUpdate(port, type, tag_info);
200 }}, 200 }},
201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data, 201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
202 Joycon::IrsResolution format) { 202 Joycon::IrsResolution format) {
@@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
291 return Common::Input::NfcState::Success; 291 return Common::Input::NfcState::Success;
292}; 292};
293 293
294Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) {
295 auto handle = GetHandle(identifier);
296 if (handle == nullptr) {
297 return Common::Input::NfcState::Unknown;
298 }
299 return TranslateDriverResult(handle->StartNfcPolling());
300};
301
302Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
303 auto handle = GetHandle(identifier);
304 if (handle == nullptr) {
305 return Common::Input::NfcState::Unknown;
306 }
307 return TranslateDriverResult(handle->StopNfcPolling());
308};
309
310Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
311 std::vector<u8>& out_data) {
312 auto handle = GetHandle(identifier);
313 if (handle == nullptr) {
314 return Common::Input::NfcState::Unknown;
315 }
316 return TranslateDriverResult(handle->ReadAmiiboData(out_data));
317}
318
294Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, 319Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
295 const std::vector<u8>& data) { 320 const std::vector<u8>& data) {
296 auto handle = GetHandle(identifier); 321 auto handle = GetHandle(identifier);
297 if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { 322 if (handle == nullptr) {
298 return Common::Input::NfcState::WriteFailed; 323 return Common::Input::NfcState::Unknown;
299 } 324 }
300 return Common::Input::NfcState::Success; 325 return TranslateDriverResult(handle->WriteNfcData(data));
326};
327
328Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
329 const Common::Input::MifareRequest& request,
330 Common::Input::MifareRequest& data) {
331 auto handle = GetHandle(identifier);
332 if (handle == nullptr) {
333 return Common::Input::NfcState::Unknown;
334 }
335
336 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
337 std::vector<Joycon::MifareReadChunk> read_request{};
338 for (const auto& request_data : request.data) {
339 if (request_data.command == 0) {
340 continue;
341 }
342 Joycon::MifareReadChunk chunk = {
343 .command = command,
344 .sector_key = {},
345 .sector = request_data.sector,
346 };
347 memcpy(chunk.sector_key.data(), request_data.key.data(),
348 sizeof(Joycon::MifareReadChunk::sector_key));
349 read_request.emplace_back(chunk);
350 }
351
352 std::vector<Joycon::MifareReadData> read_data(read_request.size());
353 const auto result = handle->ReadMifareData(read_request, read_data);
354 if (result == Joycon::DriverResult::Success) {
355 for (std::size_t i = 0; i < read_request.size(); i++) {
356 data.data[i] = {
357 .command = static_cast<u8>(command),
358 .sector = read_data[i].sector,
359 .key = {},
360 .data = read_data[i].data,
361 };
362 }
363 }
364 return TranslateDriverResult(result);
365};
366
367Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
368 const Common::Input::MifareRequest& request) {
369 auto handle = GetHandle(identifier);
370 if (handle == nullptr) {
371 return Common::Input::NfcState::Unknown;
372 }
373
374 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
375 std::vector<Joycon::MifareWriteChunk> write_request{};
376 for (const auto& request_data : request.data) {
377 if (request_data.command == 0) {
378 continue;
379 }
380 Joycon::MifareWriteChunk chunk = {
381 .command = command,
382 .sector_key = {},
383 .sector = request_data.sector,
384 .data = {},
385 };
386 memcpy(chunk.sector_key.data(), request_data.key.data(),
387 sizeof(Joycon::MifareReadChunk::sector_key));
388 memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
389 write_request.emplace_back(chunk);
390 }
391
392 return TranslateDriverResult(handle->WriteMifareData(write_request));
301}; 393};
302 394
303Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, 395Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
@@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
403} 495}
404 496
405void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 497void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
406 const std::vector<u8>& amiibo_data) { 498 const Joycon::TagInfo& tag_info) {
407 const auto identifier = GetIdentifier(port, type); 499 const auto identifier = GetIdentifier(port, type);
408 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved 500 const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
409 : Common::Input::NfcState::NewAmiibo; 501 : Common::Input::NfcState::NewAmiibo;
410 SetNfc(identifier, {nfc_state, amiibo_data}); 502
503 const Common::Input::NfcStatus nfc_status{
504 .state = nfc_state,
505 .uuid_length = tag_info.uuid_length,
506 .protocol = tag_info.protocol,
507 .tag_type = tag_info.tag_type,
508 .uuid = tag_info.uuid,
509 };
510
511 SetNfc(identifier, nfc_status);
411} 512}
412 513
413void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 514void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
@@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
726 return "Unknown Switch Controller"; 827 return "Unknown Switch Controller";
727 } 828 }
728} 829}
830
831Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const {
832 switch (result) {
833 case Joycon::DriverResult::Success:
834 return Common::Input::NfcState::Success;
835 case Joycon::DriverResult::Disabled:
836 return Common::Input::NfcState::WrongDeviceState;
837 case Joycon::DriverResult::NotSupported:
838 return Common::Input::NfcState::NotSupported;
839 default:
840 return Common::Input::NfcState::Unknown;
841 }
842}
843
729} // namespace InputCommon 844} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index e3f0ad78f..4c323d7d6 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -15,6 +15,7 @@ using SerialNumber = std::array<u8, 15>;
15struct Battery; 15struct Battery;
16struct Color; 16struct Color;
17struct MotionData; 17struct MotionData;
18struct TagInfo;
18enum class ControllerType : u8; 19enum class ControllerType : u8;
19enum class DriverResult; 20enum class DriverResult;
20enum class IrsResolution; 21enum class IrsResolution;
@@ -39,9 +40,18 @@ public:
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, 40 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override; 41 Common::Input::CameraFormat camera_format) override;
41 42
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 44 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
45 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
46 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
47 std::vector<u8>& out_data) override;
48 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
44 const std::vector<u8>& data) override; 49 const std::vector<u8>& data) override;
50 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
51 const Common::Input::MifareRequest& request,
52 Common::Input::MifareRequest& out_data) override;
53 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
54 const Common::Input::MifareRequest& request) override;
45 55
46 Common::Input::DriverResult SetPollingMode( 56 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; 57 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
@@ -82,7 +92,7 @@ private:
82 const Joycon::MotionData& value); 92 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data); 93 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 94 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
85 const std::vector<u8>& amiibo_data); 95 const Joycon::TagInfo& amiibo_data);
86 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 96 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
87 Joycon::IrsResolution format); 97 Joycon::IrsResolution format);
88 98
@@ -102,6 +112,8 @@ private:
102 /// Returns the name of the device in text format 112 /// Returns the name of the device in text format
103 std::string JoyconName(Joycon::ControllerType type) const; 113 std::string JoyconName(Joycon::ControllerType type) const;
104 114
115 Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const;
116
105 std::jthread scan_thread; 117 std::jthread scan_thread;
106 118
107 // Joycon types are split by type to ease supporting dualjoycon configurations 119 // Joycon types are split by type to ease supporting dualjoycon configurations
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 9a0439bb5..9f26392b1 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -150,6 +150,8 @@ public:
150 if (sdl_controller) { 150 if (sdl_controller) {
151 const auto type = SDL_GameControllerGetType(sdl_controller.get()); 151 const auto type = SDL_GameControllerGetType(sdl_controller.get());
152 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || 152 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ||
153 (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) ||
154 (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) ||
153 (type == SDL_CONTROLLER_TYPE_PS5); 155 (type == SDL_CONTROLLER_TYPE_PS5);
154 } 156 }
155 return false; 157 return false;
@@ -228,9 +230,8 @@ public:
228 return false; 230 return false;
229 } 231 }
230 232
231 Common::Input::BatteryLevel GetBatteryLevel() { 233 Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) {
232 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); 234 switch (battery_level) {
233 switch (level) {
234 case SDL_JOYSTICK_POWER_EMPTY: 235 case SDL_JOYSTICK_POWER_EMPTY:
235 return Common::Input::BatteryLevel::Empty; 236 return Common::Input::BatteryLevel::Empty;
236 case SDL_JOYSTICK_POWER_LOW: 237 case SDL_JOYSTICK_POWER_LOW:
@@ -378,7 +379,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
378 if (joystick_map.find(guid) == joystick_map.end()) { 379 if (joystick_map.find(guid) == joystick_map.end()) {
379 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 380 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
380 PreSetController(joystick->GetPadIdentifier()); 381 PreSetController(joystick->GetPadIdentifier());
381 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
382 joystick->EnableMotion(); 382 joystick->EnableMotion();
383 joystick_map[guid].emplace_back(std::move(joystick)); 383 joystick_map[guid].emplace_back(std::move(joystick));
384 return; 384 return;
@@ -398,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
398 const int port = static_cast<int>(joystick_guid_list.size()); 398 const int port = static_cast<int>(joystick_guid_list.size());
399 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 399 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
400 PreSetController(joystick->GetPadIdentifier()); 400 PreSetController(joystick->GetPadIdentifier());
401 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
402 joystick->EnableMotion(); 401 joystick->EnableMotion();
403 joystick_guid_list.emplace_back(std::move(joystick)); 402 joystick_guid_list.emplace_back(std::move(joystick));
404} 403}
@@ -438,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
438 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { 437 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
439 const PadIdentifier identifier = joystick->GetPadIdentifier(); 438 const PadIdentifier identifier = joystick->GetPadIdentifier();
440 SetButton(identifier, event.jbutton.button, true); 439 SetButton(identifier, event.jbutton.button, true);
441 // Battery doesn't trigger an event so just update every button press
442 SetBattery(identifier, joystick->GetBatteryLevel());
443 } 440 }
444 break; 441 break;
445 } 442 }
@@ -466,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
466 } 463 }
467 break; 464 break;
468 } 465 }
466 case SDL_JOYBATTERYUPDATED: {
467 if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) {
468 const PadIdentifier identifier = joystick->GetPadIdentifier();
469 SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level));
470 }
471 break;
472 }
469 case SDL_JOYDEVICEREMOVED: 473 case SDL_JOYDEVICEREMOVED:
470 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); 474 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
471 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); 475 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
@@ -483,6 +487,10 @@ void SDLDriver::CloseJoysticks() {
483} 487}
484 488
485SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { 489SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
490 // Set our application name. Currently passed to DBus by SDL and visible to the user through
491 // their desktop environment.
492 SDL_SetHint(SDL_HINT_APP_NAME, "yuzu");
493
486 if (!Settings::values.enable_raw_input) { 494 if (!Settings::values.enable_raw_input) {
487 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens 495 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
488 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); 496 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
@@ -501,6 +509,9 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
501 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); 509 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
502 } else { 510 } else {
503 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 511 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
512 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0");
513 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
514 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1");
504 } 515 }
505 516
506 // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled 517 // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
@@ -508,8 +519,11 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
508 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); 519 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
509 } else { 520 } else {
510 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); 521 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
522 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
511 } 523 }
512 524
525 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
526
513 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 527 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
514 // driver on Linux. 528 // driver on Linux.
515 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); 529 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
@@ -789,7 +803,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
789 // This list also excludes Screenshot since there's not really a mapping for that 803 // This list also excludes Screenshot since there's not really a mapping for that
790 ButtonBindings switch_to_sdl_button; 804 ButtonBindings switch_to_sdl_button;
791 805
792 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { 806 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO ||
807 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
808 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) {
793 switch_to_sdl_button = GetNintendoButtonBinding(joystick); 809 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
794 } else { 810 } else {
795 switch_to_sdl_button = GetDefaultButtonBinding(); 811 switch_to_sdl_button = GetDefaultButtonBinding();
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index f8bafe553..180eb53ef 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
29 29
30 switch (polling_mode) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC: 31 case Common::Input::PollingMode::NFC:
32 if (state == State::Initialized) { 32 state = State::Initialized;
33 state = State::WaitingForAmiibo;
34 }
35 return Common::Input::DriverResult::Success; 33 return Common::Input::DriverResult::Success;
36 default: 34 default:
37 if (state == State::AmiiboIsOpen) { 35 if (state == State::TagNearby) {
38 CloseAmiibo(); 36 CloseAmiibo();
39 } 37 }
38 state = State::Disabled;
40 return Common::Input::DriverResult::NotSupported; 39 return Common::Input::DriverResult::NotSupported;
41 } 40 }
42} 41}
@@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
45 [[maybe_unused]] const PadIdentifier& identifier_) const { 44 [[maybe_unused]] const PadIdentifier& identifier_) const {
46 return Common::Input::NfcState::Success; 45 return Common::Input::NfcState::Success;
47} 46}
47Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) {
48 if (state != State::Initialized) {
49 return Common::Input::NfcState::WrongDeviceState;
50 }
51 state = State::WaitingForAmiibo;
52 return Common::Input::NfcState::Success;
53}
54
55Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) {
56 if (state == State::Disabled) {
57 return Common::Input::NfcState::WrongDeviceState;
58 }
59 if (state == State::TagNearby) {
60 CloseAmiibo();
61 }
62 state = State::Initialized;
63 return Common::Input::NfcState::Success;
64}
65
66Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_,
67 std::vector<u8>& out_data) {
68 if (state != State::TagNearby) {
69 return Common::Input::NfcState::WrongDeviceState;
70 }
71
72 if (status.tag_type != 1U << 1) {
73 return Common::Input::NfcState::InvalidTagType;
74 }
75
76 out_data.resize(nfc_data.size());
77 memcpy(out_data.data(), nfc_data.data(), nfc_data.size());
78 return Common::Input::NfcState::Success;
79}
48 80
49Common::Input::NfcState VirtualAmiibo::WriteNfcData( 81Common::Input::NfcState VirtualAmiibo::WriteNfcData(
50 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { 82 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
@@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
66 return Common::Input::NfcState::Success; 98 return Common::Input::NfcState::Success;
67} 99}
68 100
101Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_,
102 const Common::Input::MifareRequest& request,
103 Common::Input::MifareRequest& out_data) {
104 if (state != State::TagNearby) {
105 return Common::Input::NfcState::WrongDeviceState;
106 }
107
108 if (status.tag_type != 1U << 6) {
109 return Common::Input::NfcState::InvalidTagType;
110 }
111
112 for (std::size_t i = 0; i < request.data.size(); i++) {
113 if (request.data[i].command == 0) {
114 continue;
115 }
116 out_data.data[i].command = request.data[i].command;
117 out_data.data[i].sector = request.data[i].sector;
118
119 const std::size_t sector_index =
120 request.data[i].sector * sizeof(Common::Input::MifareData::data);
121
122 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
123 return Common::Input::NfcState::WriteFailed;
124 }
125
126 // Ignore the sector key as we don't support it
127 memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index,
128 sizeof(Common::Input::MifareData::data));
129 }
130
131 return Common::Input::NfcState::Success;
132}
133
134Common::Input::NfcState VirtualAmiibo::WriteMifareData(
135 const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) {
136 if (state != State::TagNearby) {
137 return Common::Input::NfcState::WrongDeviceState;
138 }
139
140 if (status.tag_type != 1U << 6) {
141 return Common::Input::NfcState::InvalidTagType;
142 }
143
144 for (std::size_t i = 0; i < request.data.size(); i++) {
145 if (request.data[i].command == 0) {
146 continue;
147 }
148
149 const std::size_t sector_index =
150 request.data[i].sector * sizeof(Common::Input::MifareData::data);
151
152 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
153 return Common::Input::NfcState::WriteFailed;
154 }
155
156 // Ignore the sector key as we don't support it
157 memcpy(nfc_data.data() + sector_index, request.data[i].data.data(),
158 sizeof(Common::Input::MifareData::data));
159 }
160
161 return Common::Input::NfcState::Success;
162}
163
69VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { 164VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
70 return state; 165 return state;
71} 166}
@@ -82,6 +177,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
82 switch (nfc_file.GetSize()) { 177 switch (nfc_file.GetSize()) {
83 case AmiiboSize: 178 case AmiiboSize:
84 case AmiiboSizeWithoutPassword: 179 case AmiiboSizeWithoutPassword:
180 case AmiiboSizeWithSignature:
85 data.resize(AmiiboSize); 181 data.resize(AmiiboSize);
86 if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) { 182 if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) {
87 return Info::NotAnAmiibo; 183 return Info::NotAnAmiibo;
@@ -109,24 +205,33 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
109 switch (data.size_bytes()) { 205 switch (data.size_bytes()) {
110 case AmiiboSize: 206 case AmiiboSize:
111 case AmiiboSizeWithoutPassword: 207 case AmiiboSizeWithoutPassword:
208 case AmiiboSizeWithSignature:
112 nfc_data.resize(AmiiboSize); 209 nfc_data.resize(AmiiboSize);
210 status.tag_type = 1U << 1;
211 status.uuid_length = 7;
113 break; 212 break;
114 case MifareSize: 213 case MifareSize:
115 nfc_data.resize(MifareSize); 214 nfc_data.resize(MifareSize);
215 status.tag_type = 1U << 6;
216 status.uuid_length = 4;
116 break; 217 break;
117 default: 218 default:
118 return Info::NotAnAmiibo; 219 return Info::NotAnAmiibo;
119 } 220 }
120 221
121 state = State::AmiiboIsOpen; 222 status.uuid = {};
223 status.protocol = 1;
224 state = State::TagNearby;
225 status.state = Common::Input::NfcState::NewAmiibo,
122 memcpy(nfc_data.data(), data.data(), data.size_bytes()); 226 memcpy(nfc_data.data(), data.data(), data.size_bytes());
123 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 227 memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
228 SetNfc(identifier, status);
124 return Info::Success; 229 return Info::Success;
125} 230}
126 231
127VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { 232VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
128 if (state == State::AmiiboIsOpen) { 233 if (state == State::TagNearby) {
129 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 234 SetNfc(identifier, status);
130 return Info::Success; 235 return Info::Success;
131 } 236 }
132 237
@@ -134,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
134} 239}
135 240
136VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { 241VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
137 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo 242 if (state != State::TagNearby) {
138 : State::Initialized; 243 return Info::Success;
139 SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); 244 }
245
246 state = State::WaitingForAmiibo;
247 status.state = Common::Input::NfcState::AmiiboRemoved;
248 SetNfc(identifier, status);
249 status.tag_type = 0;
140 return Info::Success; 250 return Info::Success;
141} 251}
142 252
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 34e97cd91..490f38e05 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -20,9 +20,10 @@ namespace InputCommon {
20class VirtualAmiibo final : public InputEngine { 20class VirtualAmiibo final : public InputEngine {
21public: 21public:
22 enum class State { 22 enum class State {
23 Disabled,
23 Initialized, 24 Initialized,
24 WaitingForAmiibo, 25 WaitingForAmiibo,
25 AmiiboIsOpen, 26 TagNearby,
26 }; 27 };
27 28
28 enum class Info { 29 enum class Info {
@@ -41,9 +42,17 @@ public:
41 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 42 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
42 43
43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 44 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
44 45 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override;
46 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override;
47 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_,
48 std::vector<u8>& out_data) override;
45 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 49 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
46 const std::vector<u8>& data) override; 50 const std::vector<u8>& data) override;
51 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_,
52 const Common::Input::MifareRequest& data,
53 Common::Input::MifareRequest& out_data) override;
54 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_,
55 const Common::Input::MifareRequest& data) override;
47 56
48 State GetCurrentState() const; 57 State GetCurrentState() const;
49 58
@@ -57,11 +66,13 @@ public:
57private: 66private:
58 static constexpr std::size_t AmiiboSize = 0x21C; 67 static constexpr std::size_t AmiiboSize = 0x21C;
59 static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8; 68 static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
69 static constexpr std::size_t AmiiboSizeWithSignature = AmiiboSize + 0x20;
60 static constexpr std::size_t MifareSize = 0x400; 70 static constexpr std::size_t MifareSize = 0x400;
61 71
62 std::string file_path{}; 72 std::string file_path{};
63 State state{State::Initialized}; 73 State state{State::Disabled};
64 std::vector<u8> nfc_data; 74 std::vector<u8> nfc_data;
75 Common::Input::NfcStatus status;
65 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive}; 76 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};
66}; 77};
67} // namespace InputCommon 78} // namespace InputCommon
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 95106f16d..ec984a647 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "common/scope_exit.h"
5#include "common/swap.h" 6#include "common/swap.h"
6#include "common/thread.h" 7#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h" 8#include "input_common/helpers/joycon_driver.h"
@@ -71,6 +72,7 @@ DriverResult JoyconDriver::InitializeDevice() {
71 nfc_enabled = false; 72 nfc_enabled = false;
72 passive_enabled = false; 73 passive_enabled = false;
73 irs_enabled = false; 74 irs_enabled = false;
75 input_only_device = false;
74 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; 76 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
75 gyro_performance = Joycon::GyroPerformance::HZ833; 77 gyro_performance = Joycon::GyroPerformance::HZ833;
76 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; 78 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
@@ -85,16 +87,23 @@ DriverResult JoyconDriver::InitializeDevice() {
85 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); 87 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
86 88
87 // Get fixed joycon info 89 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version); 90 if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) {
89 generic_protocol->SetLowPowerMode(false); 91 // If this command fails the device doesn't accept configuration commands
90 generic_protocol->GetColor(color); 92 input_only_device = true;
91 if (handle_device_type == ControllerType::Pro) {
92 // Some 3rd party controllers aren't pro controllers
93 generic_protocol->GetControllerType(device_type);
94 } else {
95 device_type = handle_device_type;
96 } 93 }
97 generic_protocol->GetSerialNumber(serial_number); 94
95 if (!input_only_device) {
96 generic_protocol->SetLowPowerMode(false);
97 generic_protocol->GetColor(color);
98 if (handle_device_type == ControllerType::Pro) {
99 // Some 3rd party controllers aren't pro controllers
100 generic_protocol->GetControllerType(device_type);
101 } else {
102 device_type = handle_device_type;
103 }
104 generic_protocol->GetSerialNumber(serial_number);
105 }
106
98 supported_features = GetSupportedFeatures(); 107 supported_features = GetSupportedFeatures();
99 108
100 // Get Calibration data 109 // Get Calibration data
@@ -112,7 +121,7 @@ DriverResult JoyconDriver::InitializeDevice() {
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, 121 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration); 122 right_stick_calibration, motion_calibration);
114 123
115 // Start pooling for data 124 // Start polling for data
116 is_connected = true; 125 is_connected = true;
117 if (!input_thread_running) { 126 if (!input_thread_running) {
118 input_thread = 127 input_thread =
@@ -208,7 +217,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); 217 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 } 218 }
210 219
211 if (nfc_protocol->IsEnabled()) { 220 if (nfc_protocol->IsPolling()) {
212 if (amiibo_detected) { 221 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) { 222 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({}); 223 joycon_poller->UpdateAmiibo({});
@@ -218,10 +227,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
218 } 227 }
219 228
220 if (!amiibo_detected) { 229 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C); 230 Joycon::TagInfo tag_info;
222 const auto result = nfc_protocol->ScanAmiibo(data); 231 const auto result = nfc_protocol->GetTagInfo(tag_info);
223 if (result == DriverResult::Success) { 232 if (result == DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data); 233 joycon_poller->UpdateAmiibo(tag_info);
225 amiibo_detected = true; 234 amiibo_detected = true;
226 } 235 }
227 } 236 }
@@ -247,6 +256,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
247} 256}
248 257
249DriverResult JoyconDriver::SetPollingMode() { 258DriverResult JoyconDriver::SetPollingMode() {
259 SCOPE_EXIT({ disable_input_thread = false; });
250 disable_input_thread = true; 260 disable_input_thread = true;
251 261
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); 262 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
@@ -259,6 +269,10 @@ DriverResult JoyconDriver::SetPollingMode() {
259 generic_protocol->EnableImu(false); 269 generic_protocol->EnableImu(false);
260 } 270 }
261 271
272 if (input_only_device) {
273 return DriverResult::NotSupported;
274 }
275
262 if (irs_protocol->IsEnabled()) { 276 if (irs_protocol->IsEnabled()) {
263 irs_protocol->DisableIrs(); 277 irs_protocol->DisableIrs();
264 } 278 }
@@ -276,24 +290,21 @@ DriverResult JoyconDriver::SetPollingMode() {
276 if (irs_enabled && supported_features.irs) { 290 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs(); 291 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) { 292 if (result == DriverResult::Success) {
279 disable_input_thread = false;
280 return result; 293 return result;
281 } 294 }
282 irs_protocol->DisableIrs(); 295 irs_protocol->DisableIrs();
283 LOG_ERROR(Input, "Error enabling IRS"); 296 LOG_ERROR(Input, "Error enabling IRS");
297 return result;
284 } 298 }
285 299
286 if (nfc_enabled && supported_features.nfc) { 300 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc(); 301 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) { 302 if (result == DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result; 303 return result;
294 } 304 }
295 nfc_protocol->DisableNfc(); 305 nfc_protocol->DisableNfc();
296 LOG_ERROR(Input, "Error enabling NFC"); 306 LOG_ERROR(Input, "Error enabling NFC");
307 return result;
297 } 308 }
298 309
299 if (hidbus_enabled && supported_features.hidbus) { 310 if (hidbus_enabled && supported_features.hidbus) {
@@ -303,18 +314,17 @@ DriverResult JoyconDriver::SetPollingMode() {
303 } 314 }
304 if (result == DriverResult::Success) { 315 if (result == DriverResult::Success) {
305 ring_connected = true; 316 ring_connected = true;
306 disable_input_thread = false;
307 return result; 317 return result;
308 } 318 }
309 ring_connected = false; 319 ring_connected = false;
310 ring_protocol->DisableRingCon(); 320 ring_protocol->DisableRingCon();
311 LOG_ERROR(Input, "Error enabling Ringcon"); 321 LOG_ERROR(Input, "Error enabling Ringcon");
322 return result;
312 } 323 }
313 324
314 if (passive_enabled && supported_features.passive) { 325 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode(); 326 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) { 327 if (result == DriverResult::Success) {
317 disable_input_thread = false;
318 return result; 328 return result;
319 } 329 }
320 LOG_ERROR(Input, "Error enabling passive mode"); 330 LOG_ERROR(Input, "Error enabling passive mode");
@@ -328,7 +338,6 @@ DriverResult JoyconDriver::SetPollingMode() {
328 // Switch calls this function after enabling active mode 338 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed(); 339 generic_protocol->TriggersElapsed();
330 340
331 disable_input_thread = false;
332 return result; 341 return result;
333} 342}
334 343
@@ -339,6 +348,10 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
339 .vibration = true, 348 .vibration = true,
340 }; 349 };
341 350
351 if (input_only_device) {
352 return features;
353 }
354
342 if (device_type == ControllerType::Right) { 355 if (device_type == ControllerType::Right) {
343 features.nfc = true; 356 features.nfc = true;
344 features.irs = true; 357 features.irs = true;
@@ -492,9 +505,68 @@ DriverResult JoyconDriver::SetRingConMode() {
492 return result; 505 return result;
493} 506}
494 507
495DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { 508DriverResult JoyconDriver::StartNfcPolling() {
509 std::scoped_lock lock{mutex};
510
511 if (!supported_features.nfc) {
512 return DriverResult::NotSupported;
513 }
514 if (!nfc_protocol->IsEnabled()) {
515 return DriverResult::Disabled;
516 }
517
518 disable_input_thread = true;
519 const auto result = nfc_protocol->StartNFCPollingMode();
520 disable_input_thread = false;
521
522 return result;
523}
524
525DriverResult JoyconDriver::StopNfcPolling() {
526 std::scoped_lock lock{mutex};
527
528 if (!supported_features.nfc) {
529 return DriverResult::NotSupported;
530 }
531 if (!nfc_protocol->IsEnabled()) {
532 return DriverResult::Disabled;
533 }
534
535 disable_input_thread = true;
536 const auto result = nfc_protocol->StopNFCPollingMode();
537 disable_input_thread = false;
538
539 if (amiibo_detected) {
540 amiibo_detected = false;
541 joycon_poller->UpdateAmiibo({});
542 }
543
544 return result;
545}
546
547DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
496 std::scoped_lock lock{mutex}; 548 std::scoped_lock lock{mutex};
549
550 if (!supported_features.nfc) {
551 return DriverResult::NotSupported;
552 }
553 if (!nfc_protocol->IsEnabled()) {
554 return DriverResult::Disabled;
555 }
556 if (!amiibo_detected) {
557 return DriverResult::ErrorWritingData;
558 }
559
560 out_data.resize(0x21C);
497 disable_input_thread = true; 561 disable_input_thread = true;
562 const auto result = nfc_protocol->ReadAmiibo(out_data);
563 disable_input_thread = false;
564
565 return result;
566}
567
568DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
569 std::scoped_lock lock{mutex};
498 570
499 if (!supported_features.nfc) { 571 if (!supported_features.nfc) {
500 return DriverResult::NotSupported; 572 return DriverResult::NotSupported;
@@ -506,9 +578,51 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
506 return DriverResult::ErrorWritingData; 578 return DriverResult::ErrorWritingData;
507 } 579 }
508 580
581 disable_input_thread = true;
509 const auto result = nfc_protocol->WriteAmiibo(data); 582 const auto result = nfc_protocol->WriteAmiibo(data);
583 disable_input_thread = false;
584
585 return result;
586}
587
588DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
589 std::span<MifareReadData> out_data) {
590 std::scoped_lock lock{mutex};
591
592 if (!supported_features.nfc) {
593 return DriverResult::NotSupported;
594 }
595 if (!nfc_protocol->IsEnabled()) {
596 return DriverResult::Disabled;
597 }
598 if (!amiibo_detected) {
599 return DriverResult::ErrorWritingData;
600 }
601
602 disable_input_thread = true;
603 const auto result = nfc_protocol->ReadMifare(data, out_data);
604 disable_input_thread = false;
605
606 return result;
607}
608
609DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
610 std::scoped_lock lock{mutex};
510 611
612 if (!supported_features.nfc) {
613 return DriverResult::NotSupported;
614 }
615 if (!nfc_protocol->IsEnabled()) {
616 return DriverResult::Disabled;
617 }
618 if (!amiibo_detected) {
619 return DriverResult::ErrorWritingData;
620 }
621
622 disable_input_thread = true;
623 const auto result = nfc_protocol->WriteMifare(data);
511 disable_input_thread = false; 624 disable_input_thread = false;
625
512 return result; 626 return result;
513} 627}
514 628
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index e9b2fccbb..45b32d2f8 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -49,7 +49,13 @@ public:
49 DriverResult SetIrMode(); 49 DriverResult SetIrMode();
50 DriverResult SetNfcMode(); 50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode(); 51 DriverResult SetRingConMode();
52 DriverResult StartNfcPolling();
53 DriverResult StopNfcPolling();
54 DriverResult ReadAmiiboData(std::vector<u8>& out_data);
52 DriverResult WriteNfcData(std::span<const u8> data); 55 DriverResult WriteNfcData(std::span<const u8> data);
56 DriverResult ReadMifareData(std::span<const MifareReadChunk> request,
57 std::span<MifareReadData> out_data);
58 DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);
53 59
54 void SetCallbacks(const JoyconCallbacks& callbacks); 60 void SetCallbacks(const JoyconCallbacks& callbacks);
55 61
@@ -114,6 +120,7 @@ private:
114 // Hardware configuration 120 // Hardware configuration
115 u8 leds{}; 121 u8 leds{};
116 ReportMode mode{}; 122 ReportMode mode{};
123 bool input_only_device{};
117 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time 124 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
118 bool hidbus_enabled{}; // External device support 125 bool hidbus_enabled{}; // External device support
119 bool irs_enabled{}; // Infrared camera input 126 bool irs_enabled{}; // Infrared camera input
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 51669261a..88f4cec1c 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -73,7 +73,7 @@ DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, 73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
74 SubCommandResponse& output) { 74 SubCommandResponse& output) {
75 constexpr int timeout_mili = 66; 75 constexpr int timeout_mili = 66;
76 constexpr int MaxTries = 15; 76 constexpr int MaxTries = 3;
77 int tries = 0; 77 int tries = 0;
78 78
79 do { 79 do {
@@ -113,9 +113,7 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
113 return result; 113 return result;
114 } 114 }
115 115
116 result = GetSubCommandResponse(sc, output); 116 return GetSubCommandResponse(sc, output);
117
118 return DriverResult::Success;
119} 117}
120 118
121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { 119DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
@@ -158,7 +156,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
158 156
159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { 157DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5; 158 constexpr std::size_t HeaderSize = 5;
161 constexpr std::size_t MaxTries = 10; 159 constexpr std::size_t MaxTries = 5;
162 std::size_t tries = 0; 160 std::size_t tries = 0;
163 SubCommandResponse response{}; 161 SubCommandResponse response{};
164 std::array<u8, sizeof(ReadSpiPacket)> buffer{}; 162 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 5007b0e18..e0e431156 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x
24using MacAddress = std::array<u8, 6>; 24using MacAddress = std::array<u8, 6>;
25using SerialNumber = std::array<u8, 15>; 25using SerialNumber = std::array<u8, 15>;
26using TagUUID = std::array<u8, 7>; 26using TagUUID = std::array<u8, 7>;
27using MifareUUID = std::array<u8, 4>;
27 28
28enum class ControllerType : u8 { 29enum class ControllerType : u8 {
29 None = 0x00, 30 None = 0x00,
@@ -307,6 +308,19 @@ enum class NFCStatus : u8 {
307 WriteDone = 0x05, 308 WriteDone = 0x05,
308 TagLost = 0x07, 309 TagLost = 0x07,
309 WriteReady = 0x09, 310 WriteReady = 0x09,
311 MifareDone = 0x10,
312};
313
314enum class MifareCmd : u8 {
315 None = 0x00,
316 Read = 0x30,
317 AuthA = 0x60,
318 AuthB = 0x61,
319 Write = 0xA0,
320 Transfer = 0xB0,
321 Decrement = 0xC0,
322 Increment = 0xC1,
323 Store = 0xC2
310}; 324};
311 325
312enum class IrsMode : u8 { 326enum class IrsMode : u8 {
@@ -592,6 +606,14 @@ struct NFCWriteCommandData {
592static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); 606static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
593#pragma pack(pop) 607#pragma pack(pop)
594 608
609struct MifareCommandData {
610 u8 unknown1;
611 u8 unknown2;
612 u8 number_of_short_bytes;
613 MifareUUID uid;
614};
615static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size");
616
595struct NFCPollingCommandData { 617struct NFCPollingCommandData {
596 u8 enable_mifare; 618 u8 enable_mifare;
597 u8 unknown_1; 619 u8 unknown_1;
@@ -629,6 +651,41 @@ struct NFCWritePackage {
629 std::array<NFCDataChunk, 4> data_chunks; 651 std::array<NFCDataChunk, 4> data_chunks;
630}; 652};
631 653
654struct MifareReadChunk {
655 MifareCmd command;
656 std::array<u8, 0x6> sector_key;
657 u8 sector;
658};
659
660struct MifareWriteChunk {
661 MifareCmd command;
662 std::array<u8, 0x6> sector_key;
663 u8 sector;
664 std::array<u8, 0x10> data;
665};
666
667struct MifareReadData {
668 u8 sector;
669 std::array<u8, 0x10> data;
670};
671
672struct MifareReadPackage {
673 MifareCommandData command_data;
674 std::array<MifareReadChunk, 0x10> data_chunks;
675};
676
677struct MifareWritePackage {
678 MifareCommandData command_data;
679 std::array<MifareWriteChunk, 0x10> data_chunks;
680};
681
682struct TagInfo {
683 u8 uuid_length;
684 u8 protocol;
685 u8 tag_type;
686 std::array<u8, 10> uuid;
687};
688
632struct IrsConfigure { 689struct IrsConfigure {
633 MCUCommand command; 690 MCUCommand command;
634 MCUSubCommand sub_command; 691 MCUSubCommand sub_command;
@@ -744,7 +801,7 @@ struct JoyconCallbacks {
744 std::function<void(int, f32)> on_stick_data; 801 std::function<void(int, f32)> on_stick_data;
745 std::function<void(int, const MotionData&)> on_motion_data; 802 std::function<void(int, const MotionData&)> on_motion_data;
746 std::function<void(f32)> on_ring_data; 803 std::function<void(f32)> on_ring_data;
747 std::function<void(const std::vector<u8>&)> on_amiibo_data; 804 std::function<void(const Joycon::TagInfo&)> on_amiibo_data;
748 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data; 805 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
749}; 806};
750 807
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index f7058c4a7..261f46255 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -40,6 +40,16 @@ DriverResult NfcProtocol::EnableNfc() {
40 if (result == DriverResult::Success) { 40 if (result == DriverResult::Success) {
41 result = WaitUntilNfcIs(NFCStatus::Ready); 41 result = WaitUntilNfcIs(NFCStatus::Ready);
42 } 42 }
43 if (result == DriverResult::Success) {
44 MCUCommandResponse output{};
45 result = SendStopPollingRequest(output);
46 }
47 if (result == DriverResult::Success) {
48 result = WaitUntilNfcIs(NFCStatus::Ready);
49 }
50 if (result == DriverResult::Success) {
51 is_enabled = true;
52 }
43 53
44 return result; 54 return result;
45} 55}
@@ -54,37 +64,50 @@ DriverResult NfcProtocol::DisableNfc() {
54 } 64 }
55 65
56 is_enabled = false; 66 is_enabled = false;
67 is_polling = false;
57 68
58 return result; 69 return result;
59} 70}
60 71
61DriverResult NfcProtocol::StartNFCPollingMode() { 72DriverResult NfcProtocol::StartNFCPollingMode() {
62 LOG_DEBUG(Input, "Start NFC pooling Mode"); 73 LOG_DEBUG(Input, "Start NFC polling Mode");
63 ScopedSetBlocking sb(this); 74 ScopedSetBlocking sb(this);
64 DriverResult result{DriverResult::Success}; 75 DriverResult result{DriverResult::Success};
65 76
66 if (result == DriverResult::Success) { 77 if (result == DriverResult::Success) {
67 MCUCommandResponse output{}; 78 MCUCommandResponse output{};
68 result = SendStopPollingRequest(output); 79 result = SendStartPollingRequest(output);
69 } 80 }
70 if (result == DriverResult::Success) { 81 if (result == DriverResult::Success) {
71 result = WaitUntilNfcIs(NFCStatus::Ready); 82 result = WaitUntilNfcIs(NFCStatus::Polling);
72 } 83 }
73 if (result == DriverResult::Success) { 84 if (result == DriverResult::Success) {
85 is_polling = true;
86 }
87
88 return result;
89}
90
91DriverResult NfcProtocol::StopNFCPollingMode() {
92 LOG_DEBUG(Input, "Stop NFC polling Mode");
93 ScopedSetBlocking sb(this);
94 DriverResult result{DriverResult::Success};
95
96 if (result == DriverResult::Success) {
74 MCUCommandResponse output{}; 97 MCUCommandResponse output{};
75 result = SendStartPollingRequest(output); 98 result = SendStopPollingRequest(output);
76 } 99 }
77 if (result == DriverResult::Success) { 100 if (result == DriverResult::Success) {
78 result = WaitUntilNfcIs(NFCStatus::Polling); 101 result = WaitUntilNfcIs(NFCStatus::WriteReady);
79 } 102 }
80 if (result == DriverResult::Success) { 103 if (result == DriverResult::Success) {
81 is_enabled = true; 104 is_polling = false;
82 } 105 }
83 106
84 return result; 107 return result;
85} 108}
86 109
87DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { 110DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {
88 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 111 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
89 return DriverResult::Delayed; 112 return DriverResult::Delayed;
90 } 113 }
@@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
100 } 123 }
101 124
102 if (result == DriverResult::Success) { 125 if (result == DriverResult::Success) {
126 tag_info = {
127 .uuid_length = tag_data.uuid_size,
128 .protocol = 1,
129 .tag_type = tag_data.type,
130 .uuid = {},
131 };
132
133 memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size);
134
135 // Investigate why mifare type is not correct
136 if (tag_info.tag_type == 144) {
137 tag_info.tag_type = 1U << 6;
138 }
139
103 std::string uuid_string; 140 std::string uuid_string;
104 for (auto& content : tag_data.uuid) { 141 for (auto& content : tag_data.uuid) {
105 uuid_string += fmt::format(" {:02x}", content); 142 uuid_string += fmt::format(" {:02x}", content);
106 } 143 }
107 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string); 144 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
145 }
146
147 return result;
148}
149
150DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) {
151 LOG_DEBUG(Input, "Scan for amiibos");
152 ScopedSetBlocking sb(this);
153 DriverResult result{DriverResult::Success};
154 TagFoundData tag_data{};
155
156 if (result == DriverResult::Success) {
157 result = IsTagInRange(tag_data, 7);
158 }
159
160 if (result == DriverResult::Success) {
108 result = GetAmiiboData(data); 161 result = GetAmiiboData(data);
109 } 162 }
110 163
@@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
154 return result; 207 return result;
155} 208}
156 209
210DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request,
211 std::span<MifareReadData> out_data) {
212 LOG_DEBUG(Input, "Read mifare");
213 ScopedSetBlocking sb(this);
214 DriverResult result{DriverResult::Success};
215 TagFoundData tag_data{};
216 MifareUUID tag_uuid{};
217
218 if (result == DriverResult::Success) {
219 result = IsTagInRange(tag_data, 7);
220 }
221 if (result == DriverResult::Success) {
222 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
223 result = GetMifareData(tag_uuid, read_request, out_data);
224 }
225 if (result == DriverResult::Success) {
226 MCUCommandResponse output{};
227 result = SendStopPollingRequest(output);
228 }
229 if (result == DriverResult::Success) {
230 result = WaitUntilNfcIs(NFCStatus::Ready);
231 }
232 if (result == DriverResult::Success) {
233 MCUCommandResponse output{};
234 result = SendStartPollingRequest(output, true);
235 }
236 if (result == DriverResult::Success) {
237 result = WaitUntilNfcIs(NFCStatus::WriteReady);
238 }
239 return result;
240}
241
242DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) {
243 LOG_DEBUG(Input, "Write mifare");
244 ScopedSetBlocking sb(this);
245 DriverResult result{DriverResult::Success};
246 TagFoundData tag_data{};
247 MifareUUID tag_uuid{};
248
249 if (result == DriverResult::Success) {
250 result = IsTagInRange(tag_data, 7);
251 }
252 if (result == DriverResult::Success) {
253 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
254 result = WriteMifareData(tag_uuid, write_request);
255 }
256 if (result == DriverResult::Success) {
257 MCUCommandResponse output{};
258 result = SendStopPollingRequest(output);
259 }
260 if (result == DriverResult::Success) {
261 result = WaitUntilNfcIs(NFCStatus::Ready);
262 }
263 if (result == DriverResult::Success) {
264 MCUCommandResponse output{};
265 result = SendStartPollingRequest(output, true);
266 }
267 if (result == DriverResult::Success) {
268 result = WaitUntilNfcIs(NFCStatus::WriteReady);
269 }
270 return result;
271}
272
157bool NfcProtocol::HasAmiibo() { 273bool NfcProtocol::HasAmiibo() {
158 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 274 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
159 return true; 275 return true;
@@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
341 return result; 457 return result;
342} 458}
343 459
460DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
461 std::span<const MifareReadChunk> read_request,
462 std::span<MifareReadData> out_data) {
463 constexpr std::size_t timeout_limit = 60;
464 const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request);
465 const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data);
466 std::span<const u8> buffer(nfc_buffer_data);
467 DriverResult result = DriverResult::Success;
468 MCUCommandResponse output{};
469 u8 block_id = 1;
470 u8 package_index = 0;
471 std::size_t tries = 0;
472 std::size_t current_position = 0;
473
474 LOG_INFO(Input, "Reading Mifare data");
475
476 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
477 while (current_position < buffer.size() && tries++ < timeout_limit) {
478 const std::size_t next_position =
479 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
480 const std::size_t block_size = next_position - current_position;
481 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
482
483 SendReadDataMifareRequest(output, block_id, is_last_packet,
484 buffer.subspan(current_position, block_size));
485
486 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
487
488 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
489 return DriverResult::ErrorReadingData;
490 }
491
492 // Increase position when data is confirmed by the joycon
493 if (output.mcu_report == MCUReport::NFCState &&
494 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
495 output.mcu_data[3] == block_id) {
496 block_id++;
497 current_position = next_position;
498 }
499 }
500
501 if (result != DriverResult::Success) {
502 return result;
503 }
504
505 // Wait for reply and save the output data
506 while (tries++ < timeout_limit) {
507 result = SendNextPackageRequest(output, package_index);
508 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
509
510 if (result != DriverResult::Success) {
511 return result;
512 }
513
514 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
515 return DriverResult::ErrorReadingData;
516 }
517
518 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
519 constexpr std::size_t DATA_LENGHT = 0x10 + 1;
520 constexpr std::size_t DATA_START = 11;
521 const u8 number_of_elements = output.mcu_data[10];
522 for (std::size_t i = 0; i < number_of_elements; i++) {
523 out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)];
524 memcpy(out_data[i].data.data(),
525 output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT),
526 sizeof(MifareReadData::data));
527 }
528 package_index++;
529 continue;
530 }
531
532 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
533 LOG_INFO(Input, "Finished reading mifare");
534 break;
535 }
536 }
537
538 return result;
539}
540
541DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
542 std::span<const MifareWriteChunk> write_request) {
543 constexpr std::size_t timeout_limit = 60;
544 const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request);
545 const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data);
546 std::span<const u8> buffer(nfc_buffer_data);
547 DriverResult result = DriverResult::Success;
548 MCUCommandResponse output{};
549 u8 block_id = 1;
550 u8 package_index = 0;
551 std::size_t tries = 0;
552 std::size_t current_position = 0;
553
554 LOG_INFO(Input, "Writing Mifare data");
555
556 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
557 while (current_position < buffer.size() && tries++ < timeout_limit) {
558 const std::size_t next_position =
559 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
560 const std::size_t block_size = next_position - current_position;
561 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
562
563 SendReadDataMifareRequest(output, block_id, is_last_packet,
564 buffer.subspan(current_position, block_size));
565
566 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
567
568 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
569 return DriverResult::ErrorReadingData;
570 }
571
572 // Increase position when data is confirmed by the joycon
573 if (output.mcu_report == MCUReport::NFCState &&
574 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
575 output.mcu_data[3] == block_id) {
576 block_id++;
577 current_position = next_position;
578 }
579 }
580
581 if (result != DriverResult::Success) {
582 return result;
583 }
584
585 // Wait for reply and ignore the output data
586 while (tries++ < timeout_limit) {
587 result = SendNextPackageRequest(output, package_index);
588 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
589
590 if (result != DriverResult::Success) {
591 return result;
592 }
593
594 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
595 return DriverResult::ErrorReadingData;
596 }
597
598 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
599 package_index++;
600 continue;
601 }
602
603 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
604 LOG_INFO(Input, "Finished writing mifare");
605 break;
606 }
607 }
608
609 return result;
610}
611
344DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, 612DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
345 bool is_second_attempt) { 613 bool is_second_attempt) {
346 NFCRequestState request{ 614 NFCRequestState request{
@@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
477 output); 745 output);
478} 746}
479 747
748DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
749 bool is_last_packet, std::span<const u8> data) {
750 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
751 NFCRequestState request{
752 .command_argument = NFCCommand::Mifare,
753 .block_id = block_id,
754 .packet_id = {},
755 .packet_flag =
756 is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
757 .data_length = static_cast<u8>(data_size),
758 .raw_data = {},
759 .crc = {},
760 };
761 memcpy(request.raw_data.data(), data.data(), data_size);
762
763 std::array<u8, sizeof(NFCRequestState)> request_data{};
764 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
765 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
766 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
767 output);
768}
769
480std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { 770std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
481 const std::size_t header_size = 771 const std::size_t header_size =
482 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); 772 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
@@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag
498 return serialized_data; 788 return serialized_data;
499} 789}
500 790
791std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const {
792 const std::size_t header_size = sizeof(MifareCommandData);
793 std::vector<u8> serialized_data(header_size);
794 std::size_t start_index = 0;
795
796 memcpy(serialized_data.data(), &package, header_size);
797 start_index += header_size;
798
799 for (const auto& data_chunk : package.data_chunks) {
800 const std::size_t chunk_size = sizeof(MifareReadChunk);
801 if (data_chunk.command == MifareCmd::None) {
802 continue;
803 }
804 serialized_data.resize(start_index + chunk_size);
805 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
806 start_index += chunk_size;
807 }
808
809 return serialized_data;
810}
811
812std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const {
813 const std::size_t header_size = sizeof(MifareCommandData);
814 std::vector<u8> serialized_data(header_size);
815 std::size_t start_index = 0;
816
817 memcpy(serialized_data.data(), &package, header_size);
818 start_index += header_size;
819
820 for (const auto& data_chunk : package.data_chunks) {
821 const std::size_t chunk_size = sizeof(MifareWriteChunk);
822 if (data_chunk.command == MifareCmd::None) {
823 continue;
824 }
825 serialized_data.resize(start_index + chunk_size);
826 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
827 start_index += chunk_size;
828 }
829
830 return serialized_data;
831}
832
501NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, 833NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
502 std::span<const u8> data) const { 834 std::span<const u8> data) const {
503 return { 835 return {
@@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
527 }; 859 };
528} 860}
529 861
862MifareReadPackage NfcProtocol::MakeMifareReadPackage(
863 const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const {
864 MifareReadPackage package{
865 .command_data{
866 .unknown1 = 0xd0,
867 .unknown2 = 0x07,
868 .number_of_short_bytes = static_cast<u8>(
869 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2),
870 .uid = tag_uuid,
871 },
872 .data_chunks = {},
873 };
874
875 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
876 package.data_chunks[i] = read_request[i];
877 }
878
879 return package;
880}
881
882MifareWritePackage NfcProtocol::MakeMifareWritePackage(
883 const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const {
884 MifareWritePackage package{
885 .command_data{
886 .unknown1 = 0xd0,
887 .unknown2 = 0x07,
888 .number_of_short_bytes = static_cast<u8>(
889 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2),
890 .uid = tag_uuid,
891 },
892 .data_chunks = {},
893 };
894
895 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
896 package.data_chunks[i] = read_request[i];
897 }
898
899 return package;
900}
901
530NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { 902NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
531 constexpr u8 NFC_PAGE_SIZE = 4; 903 constexpr u8 NFC_PAGE_SIZE = 4;
532 904
@@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const {
606 return is_enabled; 978 return is_enabled;
607} 979}
608 980
981bool NfcProtocol::IsPolling() const {
982 return is_polling;
983}
984
609} // namespace InputCommon::Joycon 985} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index eb58c427d..0be95e40e 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -25,14 +25,25 @@ public:
25 25
26 DriverResult StartNFCPollingMode(); 26 DriverResult StartNFCPollingMode();
27 27
28 DriverResult ScanAmiibo(std::vector<u8>& data); 28 DriverResult StopNFCPollingMode();
29
30 DriverResult GetTagInfo(Joycon::TagInfo& tag_info);
31
32 DriverResult ReadAmiibo(std::vector<u8>& data);
29 33
30 DriverResult WriteAmiibo(std::span<const u8> data); 34 DriverResult WriteAmiibo(std::span<const u8> data);
31 35
36 DriverResult ReadMifare(std::span<const MifareReadChunk> read_request,
37 std::span<MifareReadData> out_data);
38
39 DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request);
40
32 bool HasAmiibo(); 41 bool HasAmiibo();
33 42
34 bool IsEnabled() const; 43 bool IsEnabled() const;
35 44
45 bool IsPolling() const;
46
36private: 47private:
37 // Number of times the function will be delayed until it outputs valid data 48 // Number of times the function will be delayed until it outputs valid data
38 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; 49 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
@@ -51,6 +62,13 @@ private:
51 62
52 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); 63 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
53 64
65 DriverResult GetMifareData(const MifareUUID& tag_uuid,
66 std::span<const MifareReadChunk> read_request,
67 std::span<MifareReadData> out_data);
68
69 DriverResult WriteMifareData(const MifareUUID& tag_uuid,
70 std::span<const MifareWriteChunk> write_request);
71
54 DriverResult SendStartPollingRequest(MCUCommandResponse& output, 72 DriverResult SendStartPollingRequest(MCUCommandResponse& output,
55 bool is_second_attempt = false); 73 bool is_second_attempt = false);
56 74
@@ -65,17 +83,31 @@ private:
65 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 83 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
66 bool is_last_packet, std::span<const u8> data); 84 bool is_last_packet, std::span<const u8> data);
67 85
86 DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
87 bool is_last_packet, std::span<const u8> data);
88
68 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; 89 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
69 90
91 std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const;
92
93 std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const;
94
70 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; 95 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
71 96
72 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; 97 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
73 98
99 MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid,
100 std::span<const MifareReadChunk> read_request) const;
101
102 MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid,
103 std::span<const MifareWriteChunk> read_request) const;
104
74 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 105 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
75 106
76 TagUUID GetTagUUID(std::span<const u8> data) const; 107 TagUUID GetTagUUID(std::span<const u8> data) const;
77 108
78 bool is_enabled{}; 109 bool is_enabled{};
110 bool is_polling{};
79 std::size_t update_counter{}; 111 std::size_t update_counter{};
80}; 112};
81 113
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
index dca797f7a..1aab9e12a 100644
--- a/src/input_common/helpers/joycon_protocol/poller.cpp
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) {
70 callbacks.on_color_data(color); 70 callbacks.on_color_data(color);
71} 71}
72 72
73void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { 73void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) {
74 callbacks.on_amiibo_data(amiibo_data); 74 callbacks.on_amiibo_data(tag_info);
75} 75}
76 76
77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { 77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h
index 0fa72c6db..3746abe5d 100644
--- a/src/input_common/helpers/joycon_protocol/poller.h
+++ b/src/input_common/helpers/joycon_protocol/poller.h
@@ -36,8 +36,8 @@ public:
36 36
37 void UpdateColor(const Color& color); 37 void UpdateColor(const Color& color);
38 void UpdateRing(s16 value, const RingStatus& ring_status); 38 void UpdateRing(s16 value, const RingStatus& ring_status);
39 void UpdateAmiibo(const std::vector<u8>& amiibo_data); 39 void UpdateAmiibo(const Joycon::TagInfo& tag_info);
40 void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); 40 void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format);
41 41
42private: 42private:
43 void UpdateActiveLeftPadInput(const InputReportActive& input, 43 void UpdateActiveLeftPadInput(const InputReportActive& input,
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 50b5a3dc8..c2d0cbb34 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -143,12 +143,46 @@ public:
143 return Common::Input::NfcState::NotSupported; 143 return Common::Input::NfcState::NotSupported;
144 } 144 }
145 145
146 // Start scanning for nfc tags
147 virtual Common::Input::NfcState StartNfcPolling(
148 [[maybe_unused]] const PadIdentifier& identifier_) {
149 return Common::Input::NfcState::NotSupported;
150 }
151
152 // Start scanning for nfc tags
153 virtual Common::Input::NfcState StopNfcPolling(
154 [[maybe_unused]] const PadIdentifier& identifier_) {
155 return Common::Input::NfcState::NotSupported;
156 }
157
158 // Reads data from amiibo tag
159 virtual Common::Input::NfcState ReadAmiiboData(
160 [[maybe_unused]] const PadIdentifier& identifier_,
161 [[maybe_unused]] std::vector<u8>& out_data) {
162 return Common::Input::NfcState::NotSupported;
163 }
164
146 // Writes data to an nfc tag 165 // Writes data to an nfc tag
147 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, 166 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
148 [[maybe_unused]] const std::vector<u8>& data) { 167 [[maybe_unused]] const std::vector<u8>& data) {
149 return Common::Input::NfcState::NotSupported; 168 return Common::Input::NfcState::NotSupported;
150 } 169 }
151 170
171 // Reads data from mifare tag
172 virtual Common::Input::NfcState ReadMifareData(
173 [[maybe_unused]] const PadIdentifier& identifier_,
174 [[maybe_unused]] const Common::Input::MifareRequest& request,
175 [[maybe_unused]] Common::Input::MifareRequest& out_data) {
176 return Common::Input::NfcState::NotSupported;
177 }
178
179 // Write data to mifare tag
180 virtual Common::Input::NfcState WriteMifareData(
181 [[maybe_unused]] const PadIdentifier& identifier_,
182 [[maybe_unused]] const Common::Input::MifareRequest& request) {
183 return Common::Input::NfcState::NotSupported;
184 }
185
152 // Returns the engine name 186 // Returns the engine name
153 [[nodiscard]] const std::string& GetEngineName() const; 187 [[nodiscard]] const std::string& GetEngineName() const;
154 188
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 380a01542..870e76ab0 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -792,8 +792,7 @@ public:
792 792
793 const Common::Input::CallbackStatus status{ 793 const Common::Input::CallbackStatus status{
794 .type = Common::Input::InputType::Nfc, 794 .type = Common::Input::InputType::Nfc,
795 .nfc_status = nfc_status.state, 795 .nfc_status = nfc_status,
796 .raw_data = nfc_status.data,
797 }; 796 };
798 797
799 TriggerOnChange(status); 798 TriggerOnChange(status);
@@ -836,10 +835,31 @@ public:
836 return input_engine->SupportsNfc(identifier); 835 return input_engine->SupportsNfc(identifier);
837 } 836 }
838 837
838 Common::Input::NfcState StartNfcPolling() {
839 return input_engine->StartNfcPolling(identifier);
840 }
841
842 Common::Input::NfcState StopNfcPolling() {
843 return input_engine->StopNfcPolling(identifier);
844 }
845
846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) {
847 return input_engine->ReadAmiiboData(identifier, out_data);
848 }
849
839 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override { 850 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {
840 return input_engine->WriteNfcData(identifier, data); 851 return input_engine->WriteNfcData(identifier, data);
841 } 852 }
842 853
854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
855 Common::Input::MifareRequest& out_data) {
856 return input_engine->ReadMifareData(identifier, request, out_data);
857 }
858
859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) {
860 return input_engine->WriteMifareData(identifier, request);
861 }
862
843private: 863private:
844 const PadIdentifier identifier; 864 const PadIdentifier identifier;
845 InputEngine* input_engine; 865 InputEngine* input_engine;
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 525b2363c..07e75f9d8 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -216,6 +216,7 @@ add_library(shader_recompiler STATIC
216 frontend/maxwell/translate_program.h 216 frontend/maxwell/translate_program.h
217 host_translate_info.h 217 host_translate_info.h
218 ir_opt/collect_shader_info_pass.cpp 218 ir_opt/collect_shader_info_pass.cpp
219 ir_opt/conditional_barrier_pass.cpp
219 ir_opt/constant_propagation_pass.cpp 220 ir_opt/constant_propagation_pass.cpp
220 ir_opt/dead_code_elimination_pass.cpp 221 ir_opt/dead_code_elimination_pass.cpp
221 ir_opt/dual_vertex_pass.cpp 222 ir_opt/dual_vertex_pass.cpp
@@ -223,6 +224,7 @@ add_library(shader_recompiler STATIC
223 ir_opt/identity_removal_pass.cpp 224 ir_opt/identity_removal_pass.cpp
224 ir_opt/layer_pass.cpp 225 ir_opt/layer_pass.cpp
225 ir_opt/lower_fp16_to_fp32.cpp 226 ir_opt/lower_fp16_to_fp32.cpp
227 ir_opt/lower_fp64_to_fp32.cpp
226 ir_opt/lower_int64_to_int32.cpp 228 ir_opt/lower_int64_to_int32.cpp
227 ir_opt/passes.h 229 ir_opt/passes.h
228 ir_opt/position_pass.cpp 230 ir_opt/position_pass.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index fd4a61a4d..b795c0179 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -461,7 +461,7 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I
461 header += fmt::format("R{},", index); 461 header += fmt::format("R{},", index);
462 } 462 }
463 if (program.local_memory_size > 0) { 463 if (program.local_memory_size > 0) {
464 header += fmt::format("lmem[{}],", program.local_memory_size); 464 header += fmt::format("lmem[{}],", Common::DivCeil(program.local_memory_size, 4U));
465 } 465 }
466 if (program.info.uses_fswzadd) { 466 if (program.info.uses_fswzadd) {
467 header += "FSWZA[4],FSWZB[4],"; 467 header += "FSWZA[4],FSWZB[4],";
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index c3c2281bb..9ff4028c2 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -479,7 +479,7 @@ void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {
479 const u32 remainder{4 - element}; 479 const u32 remainder{4 - element};
480 const TransformFeedbackVarying* xfb_varying{}; 480 const TransformFeedbackVarying* xfb_varying{};
481 const size_t xfb_varying_index{base_index + element}; 481 const size_t xfb_varying_index{base_index + element};
482 if (xfb_varying_index < runtime_info.xfb_varyings.size()) { 482 if (xfb_varying_index < runtime_info.xfb_count) {
483 xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index]; 483 xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index];
484 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr; 484 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;
485 } 485 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 0f86a8004..34592a01f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -387,7 +387,7 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr
387} 387}
388 388
389void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) { 389void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) {
390 if (ctx.runtime_info.xfb_varyings.empty()) { 390 if (ctx.runtime_info.xfb_count == 0) {
391 return; 391 return;
392 } 392 }
393 ctx.AddCapability(spv::Capability::TransformFeedback); 393 ctx.AddCapability(spv::Capability::TransformFeedback);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index fd15f47ea..bec5db173 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -160,7 +160,7 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
160 const u32 remainder{4 - element}; 160 const u32 remainder{4 - element};
161 const TransformFeedbackVarying* xfb_varying{}; 161 const TransformFeedbackVarying* xfb_varying{};
162 const size_t xfb_varying_index{base_attr_index + element}; 162 const size_t xfb_varying_index{base_attr_index + element};
163 if (xfb_varying_index < ctx.runtime_info.xfb_varyings.size()) { 163 if (xfb_varying_index < ctx.runtime_info.xfb_count) {
164 xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index]; 164 xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index];
165 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr; 165 xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;
166 } 166 }
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 17a6d4888..928b35561 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -280,12 +280,18 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
280 RemoveUnreachableBlocks(program); 280 RemoveUnreachableBlocks(program);
281 281
282 // Replace instructions before the SSA rewrite 282 // Replace instructions before the SSA rewrite
283 if (!host_info.support_float64) {
284 Optimization::LowerFp64ToFp32(program);
285 }
283 if (!host_info.support_float16) { 286 if (!host_info.support_float16) {
284 Optimization::LowerFp16ToFp32(program); 287 Optimization::LowerFp16ToFp32(program);
285 } 288 }
286 if (!host_info.support_int64) { 289 if (!host_info.support_int64) {
287 Optimization::LowerInt64ToInt32(program); 290 Optimization::LowerInt64ToInt32(program);
288 } 291 }
292 if (!host_info.support_conditional_barrier) {
293 Optimization::ConditionalBarrierPass(program);
294 }
289 Optimization::SsaRewritePass(program); 295 Optimization::SsaRewritePass(program);
290 296
291 Optimization::ConstantPropagationPass(env, program); 297 Optimization::ConstantPropagationPass(env, program);
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index 2aaa6c5ea..7d2ded907 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -10,6 +10,7 @@ namespace Shader {
10 10
11/// Misc information about the host 11/// Misc information about the host
12struct HostTranslateInfo { 12struct HostTranslateInfo {
13 bool support_float64{}; ///< True when the device supports 64-bit floats
13 bool support_float16{}; ///< True when the device supports 16-bit floats 14 bool support_float16{}; ///< True when the device supports 16-bit floats
14 bool support_int64{}; ///< True when the device supports 64-bit integers 15 bool support_int64{}; ///< True when the device supports 64-bit integers
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 16 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
@@ -17,6 +18,8 @@ struct HostTranslateInfo {
17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS 18 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
18 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry 19 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
19 ///< passthrough shaders 20 ///< passthrough shaders
21 bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
22 ///< control flow
20}; 23};
21 24
22} // namespace Shader 25} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 5a4195217..70292686f 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -424,6 +424,10 @@ void VisitUsages(Info& info, IR::Inst& inst) {
424 info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2; 424 info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2;
425 info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4; 425 info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4;
426 break; 426 break;
427 case IR::Opcode::LoadLocal:
428 case IR::Opcode::WriteLocal:
429 info.uses_local_memory = true;
430 break;
427 default: 431 default:
428 break; 432 break;
429 } 433 }
diff --git a/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp
new file mode 100644
index 000000000..c3ed27f4f
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "shader_recompiler/frontend/ir/program.h"
5#include "shader_recompiler/ir_opt/passes.h"
6
7namespace Shader::Optimization {
8
9void ConditionalBarrierPass(IR::Program& program) {
10 s32 conditional_control_flow_count{0};
11 s32 conditional_return_count{0};
12 for (IR::AbstractSyntaxNode& node : program.syntax_list) {
13 switch (node.type) {
14 case IR::AbstractSyntaxNode::Type::If:
15 case IR::AbstractSyntaxNode::Type::Loop:
16 conditional_control_flow_count++;
17 break;
18 case IR::AbstractSyntaxNode::Type::EndIf:
19 case IR::AbstractSyntaxNode::Type::Repeat:
20 conditional_control_flow_count--;
21 break;
22 case IR::AbstractSyntaxNode::Type::Unreachable:
23 case IR::AbstractSyntaxNode::Type::Return:
24 if (conditional_control_flow_count > 0) {
25 conditional_return_count++;
26 }
27 break;
28 case IR::AbstractSyntaxNode::Type::Block:
29 for (IR::Inst& inst : node.data.block->Instructions()) {
30 if ((conditional_control_flow_count > 0 || conditional_return_count > 0) &&
31 inst.GetOpcode() == IR::Opcode::Barrier) {
32 LOG_WARNING(Shader, "Barrier within conditional control flow");
33 inst.ReplaceOpcode(IR::Opcode::Identity);
34 }
35 }
36 break;
37 default:
38 break;
39 }
40 }
41 ASSERT(conditional_control_flow_count == 0);
42}
43
44} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp b/src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp
new file mode 100644
index 000000000..5db7a38ad
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/lower_fp64_to_fp32.cpp
@@ -0,0 +1,185 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "shader_recompiler/frontend/ir/ir_emitter.h"
5#include "shader_recompiler/frontend/ir/opcodes.h"
6#include "shader_recompiler/frontend/ir/value.h"
7#include "shader_recompiler/ir_opt/passes.h"
8
9namespace Shader::Optimization {
10namespace {
11
12constexpr s32 F64ToF32Exp = +1023 - 127;
13constexpr s32 F32ToF64Exp = +127 - 1023;
14
15IR::F32 PackedF64ToF32(IR::IREmitter& ir, const IR::Value& packed) {
16 const IR::U32 lo{ir.CompositeExtract(packed, 0)};
17 const IR::U32 hi{ir.CompositeExtract(packed, 1)};
18 const IR::U32 sign{ir.BitFieldExtract(hi, ir.Imm32(31), ir.Imm32(1))};
19 const IR::U32 exp{ir.BitFieldExtract(hi, ir.Imm32(20), ir.Imm32(11))};
20 const IR::U32 mantissa_hi{ir.BitFieldExtract(hi, ir.Imm32(0), ir.Imm32(20))};
21 const IR::U32 mantissa_lo{ir.BitFieldExtract(lo, ir.Imm32(29), ir.Imm32(3))};
22 const IR::U32 mantissa{
23 ir.BitwiseOr(ir.ShiftLeftLogical(mantissa_hi, ir.Imm32(3)), mantissa_lo)};
24 const IR::U32 exp_if_subnorm{
25 ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F64ToF32Exp)))};
26 const IR::U32 exp_if_infnan{
27 ir.Select(ir.IEqual(exp, ir.Imm32(0x7ff)), ir.Imm32(0xff), exp_if_subnorm)};
28 const IR::U32 result{
29 ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)),
30 ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(23)), mantissa))};
31 return ir.BitCast<IR::F32>(result);
32}
33
34IR::Value F32ToPackedF64(IR::IREmitter& ir, const IR::Value& raw) {
35 const IR::U32 value{ir.BitCast<IR::U32>(IR::F32(raw))};
36 const IR::U32 sign{ir.BitFieldExtract(value, ir.Imm32(31), ir.Imm32(1))};
37 const IR::U32 exp{ir.BitFieldExtract(value, ir.Imm32(23), ir.Imm32(8))};
38 const IR::U32 mantissa{ir.BitFieldExtract(value, ir.Imm32(0), ir.Imm32(23))};
39 const IR::U32 mantissa_hi{ir.BitFieldExtract(mantissa, ir.Imm32(3), ir.Imm32(20))};
40 const IR::U32 mantissa_lo{ir.BitFieldExtract(mantissa, ir.Imm32(0), ir.Imm32(3))};
41 const IR::U32 exp_if_subnorm{
42 ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F32ToF64Exp)))};
43 const IR::U32 exp_if_infnan{
44 ir.Select(ir.IEqual(exp, ir.Imm32(0xff)), ir.Imm32(0x7ff), exp_if_subnorm)};
45 const IR::U32 lo{ir.ShiftLeftLogical(mantissa_lo, ir.Imm32(29))};
46 const IR::U32 hi{
47 ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)),
48 ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(20)), mantissa_hi))};
49 return ir.CompositeConstruct(lo, hi);
50}
51
52IR::Opcode Replace(IR::Opcode op) {
53 switch (op) {
54 case IR::Opcode::FPAbs64:
55 return IR::Opcode::FPAbs32;
56 case IR::Opcode::FPAdd64:
57 return IR::Opcode::FPAdd32;
58 case IR::Opcode::FPCeil64:
59 return IR::Opcode::FPCeil32;
60 case IR::Opcode::FPFloor64:
61 return IR::Opcode::FPFloor32;
62 case IR::Opcode::FPFma64:
63 return IR::Opcode::FPFma32;
64 case IR::Opcode::FPMul64:
65 return IR::Opcode::FPMul32;
66 case IR::Opcode::FPNeg64:
67 return IR::Opcode::FPNeg32;
68 case IR::Opcode::FPRoundEven64:
69 return IR::Opcode::FPRoundEven32;
70 case IR::Opcode::FPSaturate64:
71 return IR::Opcode::FPSaturate32;
72 case IR::Opcode::FPClamp64:
73 return IR::Opcode::FPClamp32;
74 case IR::Opcode::FPTrunc64:
75 return IR::Opcode::FPTrunc32;
76 case IR::Opcode::CompositeConstructF64x2:
77 return IR::Opcode::CompositeConstructF32x2;
78 case IR::Opcode::CompositeConstructF64x3:
79 return IR::Opcode::CompositeConstructF32x3;
80 case IR::Opcode::CompositeConstructF64x4:
81 return IR::Opcode::CompositeConstructF32x4;
82 case IR::Opcode::CompositeExtractF64x2:
83 return IR::Opcode::CompositeExtractF32x2;
84 case IR::Opcode::CompositeExtractF64x3:
85 return IR::Opcode::CompositeExtractF32x3;
86 case IR::Opcode::CompositeExtractF64x4:
87 return IR::Opcode::CompositeExtractF32x4;
88 case IR::Opcode::CompositeInsertF64x2:
89 return IR::Opcode::CompositeInsertF32x2;
90 case IR::Opcode::CompositeInsertF64x3:
91 return IR::Opcode::CompositeInsertF32x3;
92 case IR::Opcode::CompositeInsertF64x4:
93 return IR::Opcode::CompositeInsertF32x4;
94 case IR::Opcode::FPOrdEqual64:
95 return IR::Opcode::FPOrdEqual32;
96 case IR::Opcode::FPUnordEqual64:
97 return IR::Opcode::FPUnordEqual32;
98 case IR::Opcode::FPOrdNotEqual64:
99 return IR::Opcode::FPOrdNotEqual32;
100 case IR::Opcode::FPUnordNotEqual64:
101 return IR::Opcode::FPUnordNotEqual32;
102 case IR::Opcode::FPOrdLessThan64:
103 return IR::Opcode::FPOrdLessThan32;
104 case IR::Opcode::FPUnordLessThan64:
105 return IR::Opcode::FPUnordLessThan32;
106 case IR::Opcode::FPOrdGreaterThan64:
107 return IR::Opcode::FPOrdGreaterThan32;
108 case IR::Opcode::FPUnordGreaterThan64:
109 return IR::Opcode::FPUnordGreaterThan32;
110 case IR::Opcode::FPOrdLessThanEqual64:
111 return IR::Opcode::FPOrdLessThanEqual32;
112 case IR::Opcode::FPUnordLessThanEqual64:
113 return IR::Opcode::FPUnordLessThanEqual32;
114 case IR::Opcode::FPOrdGreaterThanEqual64:
115 return IR::Opcode::FPOrdGreaterThanEqual32;
116 case IR::Opcode::FPUnordGreaterThanEqual64:
117 return IR::Opcode::FPUnordGreaterThanEqual32;
118 case IR::Opcode::FPIsNan64:
119 return IR::Opcode::FPIsNan32;
120 case IR::Opcode::ConvertS16F64:
121 return IR::Opcode::ConvertS16F32;
122 case IR::Opcode::ConvertS32F64:
123 return IR::Opcode::ConvertS32F32;
124 case IR::Opcode::ConvertS64F64:
125 return IR::Opcode::ConvertS64F32;
126 case IR::Opcode::ConvertU16F64:
127 return IR::Opcode::ConvertU16F32;
128 case IR::Opcode::ConvertU32F64:
129 return IR::Opcode::ConvertU32F32;
130 case IR::Opcode::ConvertU64F64:
131 return IR::Opcode::ConvertU64F32;
132 case IR::Opcode::ConvertF32F64:
133 return IR::Opcode::Identity;
134 case IR::Opcode::ConvertF64F32:
135 return IR::Opcode::Identity;
136 case IR::Opcode::ConvertF64S8:
137 return IR::Opcode::ConvertF32S8;
138 case IR::Opcode::ConvertF64S16:
139 return IR::Opcode::ConvertF32S16;
140 case IR::Opcode::ConvertF64S32:
141 return IR::Opcode::ConvertF32S32;
142 case IR::Opcode::ConvertF64S64:
143 return IR::Opcode::ConvertF32S64;
144 case IR::Opcode::ConvertF64U8:
145 return IR::Opcode::ConvertF32U8;
146 case IR::Opcode::ConvertF64U16:
147 return IR::Opcode::ConvertF32U16;
148 case IR::Opcode::ConvertF64U32:
149 return IR::Opcode::ConvertF32U32;
150 case IR::Opcode::ConvertF64U64:
151 return IR::Opcode::ConvertF32U64;
152 default:
153 return op;
154 }
155}
156
157void Lower(IR::Block& block, IR::Inst& inst) {
158 switch (inst.GetOpcode()) {
159 case IR::Opcode::PackDouble2x32: {
160 IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
161 inst.ReplaceUsesWith(PackedF64ToF32(ir, inst.Arg(0)));
162 break;
163 }
164 case IR::Opcode::UnpackDouble2x32: {
165 IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
166 inst.ReplaceUsesWith(F32ToPackedF64(ir, inst.Arg(0)));
167 break;
168 }
169 default:
170 inst.ReplaceOpcode(Replace(inst.GetOpcode()));
171 break;
172 }
173}
174
175} // Anonymous namespace
176
177void LowerFp64ToFp32(IR::Program& program) {
178 for (IR::Block* const block : program.blocks) {
179 for (IR::Inst& inst : block->Instructions()) {
180 Lower(*block, inst);
181 }
182 }
183}
184
185} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 1f8f2ba95..629d18fa1 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -13,10 +13,12 @@ struct HostTranslateInfo;
13namespace Shader::Optimization { 13namespace Shader::Optimization {
14 14
15void CollectShaderInfoPass(Environment& env, IR::Program& program); 15void CollectShaderInfoPass(Environment& env, IR::Program& program);
16void ConditionalBarrierPass(IR::Program& program);
16void ConstantPropagationPass(Environment& env, IR::Program& program); 17void ConstantPropagationPass(Environment& env, IR::Program& program);
17void DeadCodeEliminationPass(IR::Program& program); 18void DeadCodeEliminationPass(IR::Program& program);
18void GlobalMemoryToStorageBufferPass(IR::Program& program); 19void GlobalMemoryToStorageBufferPass(IR::Program& program);
19void IdentityRemovalPass(IR::Program& program); 20void IdentityRemovalPass(IR::Program& program);
21void LowerFp64ToFp32(IR::Program& program);
20void LowerFp16ToFp32(IR::Program& program); 22void LowerFp16ToFp32(IR::Program& program);
21void LowerInt64ToInt32(IR::Program& program); 23void LowerInt64ToInt32(IR::Program& program);
22void RescalingPass(IR::Program& program); 24void RescalingPass(IR::Program& program);
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index 3b63c249f..619c0b138 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -84,7 +84,8 @@ struct RuntimeInfo {
84 bool glasm_use_storage_buffers{}; 84 bool glasm_use_storage_buffers{};
85 85
86 /// Transform feedback state for each varying 86 /// Transform feedback state for each varying
87 std::vector<TransformFeedbackVarying> xfb_varyings; 87 std::array<TransformFeedbackVarying, 256> xfb_varyings{};
88 u32 xfb_count{0};
88}; 89};
89 90
90} // namespace Shader 91} // namespace Shader
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index d308db942..b4b4afd37 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -172,6 +172,7 @@ struct Info {
172 bool stores_indexed_attributes{}; 172 bool stores_indexed_attributes{};
173 173
174 bool stores_global_memory{}; 174 bool stores_global_memory{};
175 bool uses_local_memory{};
175 176
176 bool uses_fp16{}; 177 bool uses_fp16{};
177 bool uses_fp64{}; 178 bool uses_fp64{};
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 251a4a880..58a45ab67 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -207,7 +207,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
207 if (has_new_downloads) { 207 if (has_new_downloads) {
208 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); 208 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
209 } 209 }
210 tmp_buffer.resize(amount); 210 tmp_buffer.resize_destructive(amount);
211 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); 211 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
212 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); 212 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
213 return true; 213 return true;
@@ -715,13 +715,19 @@ void BufferCache<P>::BindHostIndexBuffer() {
715 715
716template <class P> 716template <class P>
717void BufferCache<P>::BindHostVertexBuffers() { 717void BufferCache<P>::BindHostVertexBuffers() {
718 HostBindings host_bindings; 718 HostBindings<typename P::Buffer> host_bindings;
719 bool any_valid{false}; 719 bool any_valid{false};
720 auto& flags = maxwell3d->dirty.flags; 720 auto& flags = maxwell3d->dirty.flags;
721 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { 721 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
722 const Binding& binding = channel_state->vertex_buffers[index];
723 Buffer& buffer = slot_buffers[binding.buffer_id];
724 TouchBuffer(buffer, binding.buffer_id);
725 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
722 if (!flags[Dirty::VertexBuffer0 + index]) { 726 if (!flags[Dirty::VertexBuffer0 + index]) {
723 continue; 727 continue;
724 } 728 }
729 flags[Dirty::VertexBuffer0 + index] = false;
730
725 host_bindings.min_index = std::min(host_bindings.min_index, index); 731 host_bindings.min_index = std::min(host_bindings.min_index, index);
726 host_bindings.max_index = std::max(host_bindings.max_index, index); 732 host_bindings.max_index = std::max(host_bindings.max_index, index);
727 any_valid = true; 733 any_valid = true;
@@ -735,13 +741,10 @@ void BufferCache<P>::BindHostVertexBuffers() {
735 const Binding& binding = channel_state->vertex_buffers[index]; 741 const Binding& binding = channel_state->vertex_buffers[index];
736 Buffer& buffer = slot_buffers[binding.buffer_id]; 742 Buffer& buffer = slot_buffers[binding.buffer_id];
737 743
738 TouchBuffer(buffer, binding.buffer_id);
739 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
740
741 const u32 stride = maxwell3d->regs.vertex_streams[index].stride; 744 const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
742 const u32 offset = buffer.Offset(binding.cpu_addr); 745 const u32 offset = buffer.Offset(binding.cpu_addr);
743 746
744 host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer)); 747 host_bindings.buffers.push_back(&buffer);
745 host_bindings.offsets.push_back(offset); 748 host_bindings.offsets.push_back(offset);
746 host_bindings.sizes.push_back(binding.size); 749 host_bindings.sizes.push_back(binding.size);
747 host_bindings.strides.push_back(stride); 750 host_bindings.strides.push_back(stride);
@@ -900,7 +903,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
900 if (maxwell3d->regs.transform_feedback_enabled == 0) { 903 if (maxwell3d->regs.transform_feedback_enabled == 0) {
901 return; 904 return;
902 } 905 }
903 HostBindings host_bindings; 906 HostBindings<typename P::Buffer> host_bindings;
904 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { 907 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
905 const Binding& binding = channel_state->transform_feedback_buffers[index]; 908 const Binding& binding = channel_state->transform_feedback_buffers[index];
906 if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 && 909 if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 &&
@@ -913,7 +916,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
913 SynchronizeBuffer(buffer, binding.cpu_addr, size); 916 SynchronizeBuffer(buffer, binding.cpu_addr, size);
914 917
915 const u32 offset = buffer.Offset(binding.cpu_addr); 918 const u32 offset = buffer.Offset(binding.cpu_addr);
916 host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer)); 919 host_bindings.buffers.push_back(&buffer);
917 host_bindings.offsets.push_back(offset); 920 host_bindings.offsets.push_back(offset);
918 host_bindings.sizes.push_back(binding.size); 921 host_bindings.sizes.push_back(binding.size);
919 } 922 }
@@ -1276,7 +1279,7 @@ template <class P>
1276typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr, 1279typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr,
1277 u32 wanted_size) { 1280 u32 wanted_size) {
1278 static constexpr int STREAM_LEAP_THRESHOLD = 16; 1281 static constexpr int STREAM_LEAP_THRESHOLD = 16;
1279 std::vector<BufferId> overlap_ids; 1282 boost::container::small_vector<BufferId, 16> overlap_ids;
1280 VAddr begin = cpu_addr; 1283 VAddr begin = cpu_addr;
1281 VAddr end = cpu_addr + wanted_size; 1284 VAddr end = cpu_addr + wanted_size;
1282 int stream_score = 0; 1285 int stream_score = 0;
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index cf359e241..fe6068cfe 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -105,8 +105,9 @@ static constexpr Binding NULL_BINDING{
105 .buffer_id = NULL_BUFFER_ID, 105 .buffer_id = NULL_BUFFER_ID,
106}; 106};
107 107
108template <typename Buffer>
108struct HostBindings { 109struct HostBindings {
109 boost::container::small_vector<void*, NUM_VERTEX_BUFFERS> buffers; 110 boost::container::small_vector<Buffer*, NUM_VERTEX_BUFFERS> buffers;
110 boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> offsets; 111 boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> offsets;
111 boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> sizes; 112 boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> sizes;
112 boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> strides; 113 boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> strides;
@@ -228,7 +229,7 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
228 using OverlapCounter = boost::icl::split_interval_map<VAddr, int>; 229 using OverlapCounter = boost::icl::split_interval_map<VAddr, int>;
229 230
230 struct OverlapResult { 231 struct OverlapResult {
231 std::vector<BufferId> ids; 232 boost::container::small_vector<BufferId, 16> ids;
232 VAddr begin; 233 VAddr begin;
233 VAddr end; 234 VAddr end;
234 bool has_stream_leap = false; 235 bool has_stream_leap = false;
@@ -581,7 +582,7 @@ private:
581 BufferId inline_buffer_id; 582 BufferId inline_buffer_id;
582 583
583 std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table; 584 std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
584 std::vector<u8> tmp_buffer; 585 Common::ScratchBuffer<u8> tmp_buffer;
585}; 586};
586 587
587} // namespace VideoCommon 588} // namespace VideoCommon
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index 83112dfce..7d660af47 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -63,7 +63,6 @@ struct ChCommand {
63}; 63};
64 64
65using ChCommandHeaderList = std::vector<ChCommandHeader>; 65using ChCommandHeaderList = std::vector<ChCommandHeader>;
66using ChCommandList = std::vector<ChCommand>;
67 66
68struct ThiRegisters { 67struct ThiRegisters {
69 u32_le increment_syncpt{}; 68 u32_le increment_syncpt{};
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 1cdb690ed..8a2784cdc 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8#include <vector> 8#include <vector>
9#include <boost/container/small_vector.hpp>
9#include <queue> 10#include <queue>
10 11
11#include "common/bit_field.h" 12#include "common/bit_field.h"
@@ -102,11 +103,12 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
102struct CommandList final { 103struct CommandList final {
103 CommandList() = default; 104 CommandList() = default;
104 explicit CommandList(std::size_t size) : command_lists(size) {} 105 explicit CommandList(std::size_t size) : command_lists(size) {}
105 explicit CommandList(std::vector<CommandHeader>&& prefetch_command_list_) 106 explicit CommandList(
107 boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
106 : prefetch_command_list{std::move(prefetch_command_list_)} {} 108 : prefetch_command_list{std::move(prefetch_command_list_)} {}
107 109
108 std::vector<CommandListHeader> command_lists; 110 boost::container::small_vector<CommandListHeader, 512> command_lists;
109 std::vector<CommandHeader> prefetch_command_list; 111 boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
110}; 112};
111 113
112/** 114/**
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 0e94c521a..f34090791 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -1,6 +1,7 @@
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#include "common/settings.h"
4#include "video_core/dirty_flags.h" 5#include "video_core/dirty_flags.h"
5#include "video_core/engines/draw_manager.h" 6#include "video_core/engines/draw_manager.h"
6#include "video_core/rasterizer_interface.h" 7#include "video_core/rasterizer_interface.h"
@@ -195,8 +196,12 @@ void DrawManager::DrawTexture() {
195 if (lower_left) { 196 if (lower_left) {
196 draw_texture_state.dst_y0 -= dst_height; 197 draw_texture_state.dst_y0 -= dst_height;
197 } 198 }
198 draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width; 199 draw_texture_state.dst_x1 =
199 draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height; 200 draw_texture_state.dst_x0 +
201 static_cast<f32>(Settings::values.resolution_info.ScaleUp(static_cast<u32>(dst_width)));
202 draw_texture_state.dst_y1 =
203 draw_texture_state.dst_y0 +
204 static_cast<f32>(Settings::values.resolution_info.ScaleUp(static_cast<u32>(dst_height)));
200 draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f; 205 draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
201 draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f; 206 draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
202 draw_texture_state.src_x1 = 207 draw_texture_state.src_x1 =
@@ -207,7 +212,6 @@ void DrawManager::DrawTexture() {
207 draw_texture_state.src_y0; 212 draw_texture_state.src_y0;
208 draw_texture_state.src_sampler = regs.draw_texture.src_sampler; 213 draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
209 draw_texture_state.src_texture = regs.draw_texture.src_texture; 214 draw_texture_state.src_texture = regs.draw_texture.src_texture;
210
211 maxwell3d->rasterizer->DrawTexture(); 215 maxwell3d->rasterizer->DrawTexture();
212} 216}
213 217
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index ebe5536de..bc1eb41e7 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -108,9 +108,11 @@ void MaxwellDMA::Launch() {
108 if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) { 108 if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
109 ASSERT(regs.remap_const.component_size_minus_one == 3); 109 ASSERT(regs.remap_const.component_size_minus_one == 3);
110 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); 110 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
111 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); 111 read_buffer.resize_destructive(regs.line_length_in * sizeof(u32));
112 std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in);
113 std::ranges::fill(span, regs.remap_consta_value);
112 memory_manager.WriteBlockUnsafe(regs.offset_out, 114 memory_manager.WriteBlockUnsafe(regs.offset_out,
113 reinterpret_cast<u8*>(tmp_buffer.data()), 115 reinterpret_cast<u8*>(read_buffer.data()),
114 regs.line_length_in * sizeof(u32)); 116 regs.line_length_in * sizeof(u32));
115 } else { 117 } else {
116 memory_manager.FlushCaching(); 118 memory_manager.FlushCaching();
@@ -126,32 +128,32 @@ void MaxwellDMA::Launch() {
126 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 128 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
127 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); 129 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
128 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); 130 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
129 std::vector<u8> tmp_buffer(16); 131 read_buffer.resize_destructive(16);
130 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 132 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
131 memory_manager.ReadBlockUnsafe( 133 memory_manager.ReadBlockUnsafe(
132 convert_linear_2_blocklinear_addr(regs.offset_in + offset), 134 convert_linear_2_blocklinear_addr(regs.offset_in + offset),
133 tmp_buffer.data(), tmp_buffer.size()); 135 read_buffer.data(), read_buffer.size());
134 memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(), 136 memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(),
135 tmp_buffer.size()); 137 read_buffer.size());
136 } 138 }
137 } else if (is_src_pitch && !is_dst_pitch) { 139 } else if (is_src_pitch && !is_dst_pitch) {
138 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 140 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
139 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); 141 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
140 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); 142 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
141 std::vector<u8> tmp_buffer(16); 143 read_buffer.resize_destructive(16);
142 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 144 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
143 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), 145 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(),
144 tmp_buffer.size()); 146 read_buffer.size());
145 memory_manager.WriteBlockCached( 147 memory_manager.WriteBlockCached(
146 convert_linear_2_blocklinear_addr(regs.offset_out + offset), 148 convert_linear_2_blocklinear_addr(regs.offset_out + offset),
147 tmp_buffer.data(), tmp_buffer.size()); 149 read_buffer.data(), read_buffer.size());
148 } 150 }
149 } else { 151 } else {
150 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { 152 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
151 std::vector<u8> tmp_buffer(regs.line_length_in); 153 read_buffer.resize_destructive(regs.line_length_in);
152 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), 154 memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(),
153 regs.line_length_in); 155 regs.line_length_in);
154 memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(), 156 memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),
155 regs.line_length_in); 157 regs.line_length_in);
156 } 158 }
157 } 159 }
@@ -171,7 +173,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
171 src_operand.address = regs.offset_in; 173 src_operand.address = regs.offset_in;
172 174
173 DMA::BufferOperand dst_operand; 175 DMA::BufferOperand dst_operand;
174 dst_operand.pitch = regs.pitch_out; 176 u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out));
177 dst_operand.pitch = abs_pitch_out;
175 dst_operand.width = regs.line_length_in; 178 dst_operand.width = regs.line_length_in;
176 dst_operand.height = regs.line_count; 179 dst_operand.height = regs.line_count;
177 dst_operand.address = regs.offset_out; 180 dst_operand.address = regs.offset_out;
@@ -218,7 +221,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
218 const size_t src_size = 221 const size_t src_size =
219 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 222 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
220 223
221 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 224 const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count;
222 read_buffer.resize_destructive(src_size); 225 read_buffer.resize_destructive(src_size);
223 write_buffer.resize_destructive(dst_size); 226 write_buffer.resize_destructive(dst_size);
224 227
@@ -227,7 +230,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
227 230
228 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 231 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
229 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 232 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
230 regs.pitch_out); 233 abs_pitch_out);
231 234
232 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); 235 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
233} 236}
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 456f733cf..db385076d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -193,18 +193,13 @@ struct GPU::Impl {
193 } 193 }
194 194
195 [[nodiscard]] u64 GetTicks() const { 195 [[nodiscard]] u64 GetTicks() const {
196 // This values were reversed engineered by fincs from NVN 196 u64 gpu_tick = system.CoreTiming().GetGPUTicks();
197 // The gpu clock is reported in units of 385/625 nanoseconds
198 constexpr u64 gpu_ticks_num = 384;
199 constexpr u64 gpu_ticks_den = 625;
200 197
201 u64 nanoseconds = system.CoreTiming().GetCPUTimeNs().count();
202 if (Settings::values.use_fast_gpu_time.GetValue()) { 198 if (Settings::values.use_fast_gpu_time.GetValue()) {
203 nanoseconds /= 256; 199 gpu_tick /= 256;
204 } 200 }
205 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; 201
206 const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; 202 return gpu_tick;
207 return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den;
208 } 203 }
209 204
210 [[nodiscard]] bool IsAsync() const { 205 [[nodiscard]] bool IsAsync() const {
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index 6ce179167..ce827eb6c 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -4,6 +4,7 @@
4#include <array> 4#include <array>
5#include <bit> 5#include <bit>
6 6
7#include "common/scratch_buffer.h"
7#include "common/settings.h" 8#include "common/settings.h"
8#include "video_core/host1x/codecs/h264.h" 9#include "video_core/host1x/codecs/h264.h"
9#include "video_core/host1x/host1x.h" 10#include "video_core/host1x/host1x.h"
@@ -188,7 +189,8 @@ void H264BitWriter::WriteBit(bool state) {
188} 189}
189 190
190void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { 191void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) {
191 std::vector<u8> scan(count); 192 static Common::ScratchBuffer<u8> scan{};
193 scan.resize_destructive(count);
192 if (count == 16) { 194 if (count == 16) {
193 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); 195 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
194 } else { 196 } else {
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 2442c3c29..e61d9af80 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -33,6 +33,7 @@ set(SHADER_FILES
33 opengl_fidelityfx_fsr.frag 33 opengl_fidelityfx_fsr.frag
34 opengl_fidelityfx_fsr_easu.frag 34 opengl_fidelityfx_fsr_easu.frag
35 opengl_fidelityfx_fsr_rcas.frag 35 opengl_fidelityfx_fsr_rcas.frag
36 opengl_lmem_warmup.comp
36 opengl_present.frag 37 opengl_present.frag
37 opengl_present.vert 38 opengl_present.vert
38 opengl_present_scaleforce.frag 39 opengl_present_scaleforce.frag
diff --git a/src/video_core/host_shaders/opengl_lmem_warmup.comp b/src/video_core/host_shaders/opengl_lmem_warmup.comp
new file mode 100644
index 000000000..518268477
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_lmem_warmup.comp
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// This shader is a workaround for a quirk in NVIDIA OpenGL drivers
5// Shaders using local memory see a great performance benefit if a shader that was dispatched
6// before it had more local memory allocated.
7// This shader allocates the maximum local memory allowed on NVIDIA drivers to ensure that
8// subsequent shaders see the performance boost.
9
10// NOTE: This shader does no actual meaningful work and returns immediately,
11// it is simply a means to have the driver expect a shader using lots of local memory.
12
13#version 450
14
15layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
16
17layout(location = 0) uniform uint uniform_data;
18
19layout(binding = 0, rgba8) uniform writeonly restrict image2DArray dest_image;
20
21#define MAX_LMEM_SIZE 4080 // Size chosen to avoid errors in Nvidia's GLSL compiler
22#define NUM_LMEM_CONSTANTS 1
23#define ARRAY_SIZE MAX_LMEM_SIZE - NUM_LMEM_CONSTANTS
24
25uint lmem_0[ARRAY_SIZE];
26const uvec4 constant_values[NUM_LMEM_CONSTANTS] = uvec4[](uvec4(0));
27
28void main() {
29 const uint global_id = gl_GlobalInvocationID.x;
30 if (global_id <= 128) {
31 // Since the shader is called with a dispatch of 1x1x1
32 // This should always be the case, and this shader will not actually execute
33 return;
34 }
35 for (uint t = 0; t < uniform_data; t++) {
36 const uint offset = (t * uniform_data);
37 lmem_0[offset] = t;
38 }
39 const uint offset = (gl_GlobalInvocationID.y * uniform_data + gl_GlobalInvocationID.x);
40 const uint value = lmem_0[offset];
41 const uint const_value = constant_values[offset / 4][offset % 4];
42 const uvec4 color = uvec4(value + const_value);
43
44 // A "side-effect" is needed so the variables don't get optimized out,
45 // but this should never execute so there should be no clobbering of previously bound state.
46 imageStore(dest_image, ivec3(gl_GlobalInvocationID), color);
47}
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 7b2cde7a7..45141e488 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -111,7 +111,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp
111 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr); 111 [[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
112 SetEntry<false>(current_gpu_addr, entry_type); 112 SetEntry<false>(current_gpu_addr, entry_type);
113 if (current_entry_type != entry_type) { 113 if (current_entry_type != entry_type) {
114 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); 114 rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, page_size);
115 } 115 }
116 if constexpr (entry_type == EntryType::Mapped) { 116 if constexpr (entry_type == EntryType::Mapped) {
117 const VAddr current_cpu_addr = cpu_addr + offset; 117 const VAddr current_cpu_addr = cpu_addr + offset;
@@ -134,7 +134,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
134 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); 134 [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
135 SetEntry<true>(current_gpu_addr, entry_type); 135 SetEntry<true>(current_gpu_addr, entry_type);
136 if (current_entry_type != entry_type) { 136 if (current_entry_type != entry_type) {
137 rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); 137 rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, big_page_size);
138 } 138 }
139 if constexpr (entry_type == EntryType::Mapped) { 139 if constexpr (entry_type == EntryType::Mapped) {
140 const VAddr current_cpu_addr = cpu_addr + offset; 140 const VAddr current_cpu_addr = cpu_addr + offset;
@@ -587,7 +587,7 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
587 587
588void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, 588void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
589 VideoCommon::CacheType which) { 589 VideoCommon::CacheType which) {
590 std::vector<u8> tmp_buffer(size); 590 tmp_buffer.resize_destructive(size);
591 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which); 591 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);
592 592
593 // The output block must be flushed in case it has data modified from the GPU. 593 // The output block must be flushed in case it has data modified from the GPU.
@@ -670,9 +670,9 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons
670 return result; 670 return result;
671} 671}
672 672
673std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( 673boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32>
674 GPUVAddr gpu_addr, std::size_t size) const { 674MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const {
675 std::vector<std::pair<GPUVAddr, std::size_t>> result{}; 675 boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> result{};
676 GetSubmappedRangeImpl<true>(gpu_addr, size, result); 676 GetSubmappedRangeImpl<true>(gpu_addr, size, result);
677 return result; 677 return result;
678} 678}
@@ -680,8 +680,9 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
680template <bool is_gpu_address> 680template <bool is_gpu_address>
681void MemoryManager::GetSubmappedRangeImpl( 681void MemoryManager::GetSubmappedRangeImpl(
682 GPUVAddr gpu_addr, std::size_t size, 682 GPUVAddr gpu_addr, std::size_t size,
683 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& 683 boost::container::small_vector<
684 result) const { 684 std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>& result)
685 const {
685 std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>> 686 std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>
686 last_segment{}; 687 last_segment{};
687 std::optional<VAddr> old_page_addr{}; 688 std::optional<VAddr> old_page_addr{};
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 794535122..4202c26ff 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -8,10 +8,12 @@
8#include <mutex> 8#include <mutex>
9#include <optional> 9#include <optional>
10#include <vector> 10#include <vector>
11#include <boost/container/small_vector.hpp>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/multi_level_page_table.h" 14#include "common/multi_level_page_table.h"
14#include "common/range_map.h" 15#include "common/range_map.h"
16#include "common/scratch_buffer.h"
15#include "common/virtual_buffer.h" 17#include "common/virtual_buffer.h"
16#include "video_core/cache_types.h" 18#include "video_core/cache_types.h"
17#include "video_core/pte_kind.h" 19#include "video_core/pte_kind.h"
@@ -107,8 +109,8 @@ public:
107 * if the region is continuous, a single pair will be returned. If it's unmapped, an empty 109 * if the region is continuous, a single pair will be returned. If it's unmapped, an empty
108 * vector will be returned; 110 * vector will be returned;
109 */ 111 */
110 std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr, 112 boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> GetSubmappedRange(
111 std::size_t size) const; 113 GPUVAddr gpu_addr, std::size_t size) const;
112 114
113 GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, 115 GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size,
114 PTEKind kind = PTEKind::INVALID, bool is_big_pages = true); 116 PTEKind kind = PTEKind::INVALID, bool is_big_pages = true);
@@ -165,7 +167,8 @@ private:
165 template <bool is_gpu_address> 167 template <bool is_gpu_address>
166 void GetSubmappedRangeImpl( 168 void GetSubmappedRangeImpl(
167 GPUVAddr gpu_addr, std::size_t size, 169 GPUVAddr gpu_addr, std::size_t size,
168 std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& 170 boost::container::small_vector<
171 std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>&
169 result) const; 172 result) const;
170 173
171 Core::System& system; 174 Core::System& system;
@@ -215,8 +218,8 @@ private:
215 Common::VirtualBuffer<u32> big_page_table_cpu; 218 Common::VirtualBuffer<u32> big_page_table_cpu;
216 219
217 std::vector<u64> big_page_continuous; 220 std::vector<u64> big_page_continuous;
218 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 221 boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash{};
219 std::vector<std::pair<VAddr, std::size_t>> page_stash2{}; 222 boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash2{};
220 223
221 mutable std::mutex guard; 224 mutable std::mutex guard;
222 225
@@ -226,6 +229,8 @@ private:
226 std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator; 229 std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;
227 230
228 static std::atomic<size_t> unique_identifier_generator; 231 static std::atomic<size_t> unique_identifier_generator;
232
233 Common::ScratchBuffer<u8> tmp_buffer;
229}; 234};
230 235
231} // namespace Tegra 236} // namespace Tegra
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 0cc546a3a..38d553d3c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -232,12 +232,12 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
232 } 232 }
233} 233}
234 234
235void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { 235void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
236 for (u32 index = 0; index < bindings.buffers.size(); index++) { 236 for (u32 index = 0; index < bindings.buffers.size(); ++index) {
237 BindVertexBuffer( 237 BindVertexBuffer(bindings.min_index + index, *bindings.buffers[index],
238 bindings.min_index + index, *reinterpret_cast<Buffer*>(bindings.buffers[index]), 238 static_cast<u32>(bindings.offsets[index]),
239 static_cast<u32>(bindings.offsets[index]), static_cast<u32>(bindings.sizes[index]), 239 static_cast<u32>(bindings.sizes[index]),
240 static_cast<u32>(bindings.strides[index])); 240 static_cast<u32>(bindings.strides[index]));
241 } 241 }
242} 242}
243 243
@@ -329,10 +329,9 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, Buffer& buffer,
329 static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size)); 329 static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size));
330} 330}
331 331
332void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) { 332void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
333 for (u32 index = 0; index < bindings.buffers.size(); index++) { 333 for (u32 index = 0; index < bindings.buffers.size(); ++index) {
334 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index, 334 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index, bindings.buffers[index]->Handle(),
335 reinterpret_cast<Buffer*>(bindings.buffers[index])->Handle(),
336 static_cast<GLintptr>(bindings.offsets[index]), 335 static_cast<GLintptr>(bindings.offsets[index]),
337 static_cast<GLsizeiptr>(bindings.sizes[index])); 336 static_cast<GLsizeiptr>(bindings.sizes[index]));
338 } 337 }
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index e4e000284..41b746f3b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -87,7 +87,8 @@ public:
87 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); 87 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
88 88
89 void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); 89 void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
90 void BindVertexBuffers(VideoCommon::HostBindings& bindings); 90
91 void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
91 92
92 void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); 93 void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
93 94
@@ -100,7 +101,8 @@ public:
100 bool is_written); 101 bool is_written);
101 102
102 void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size); 103 void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size);
103 void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); 104
105 void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
104 106
105 void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, 107 void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
106 VideoCore::Surface::PixelFormat format); 108 VideoCore::Surface::PixelFormat format);
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 1a0cea9b7..f9ca55c36 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -63,6 +63,7 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
63 writes_global_memory = !use_storage_buffers && 63 writes_global_memory = !use_storage_buffers &&
64 std::ranges::any_of(info.storage_buffers_descriptors, 64 std::ranges::any_of(info.storage_buffers_descriptors,
65 [](const auto& desc) { return desc.is_written; }); 65 [](const auto& desc) { return desc.is_written; });
66 uses_local_memory = info.uses_local_memory;
66 if (force_context_flush) { 67 if (force_context_flush) {
67 std::scoped_lock lock{built_mutex}; 68 std::scoped_lock lock{built_mutex};
68 built_fence.Create(); 69 built_fence.Create();
@@ -87,7 +88,8 @@ void ComputePipeline::Configure() {
87 texture_cache.SynchronizeComputeDescriptors(); 88 texture_cache.SynchronizeComputeDescriptors();
88 89
89 boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; 90 boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
90 std::array<GLuint, MAX_TEXTURES> samplers; 91 boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
92 std::array<GLuint, MAX_TEXTURES> gl_samplers;
91 std::array<GLuint, MAX_TEXTURES> textures; 93 std::array<GLuint, MAX_TEXTURES> textures;
92 std::array<GLuint, MAX_IMAGES> images; 94 std::array<GLuint, MAX_IMAGES> images;
93 GLsizei sampler_binding{}; 95 GLsizei sampler_binding{};
@@ -131,7 +133,6 @@ void ComputePipeline::Configure() {
131 for (u32 index = 0; index < desc.count; ++index) { 133 for (u32 index = 0; index < desc.count; ++index) {
132 const auto handle{read_handle(desc, index)}; 134 const auto handle{read_handle(desc, index)};
133 views.push_back({handle.first}); 135 views.push_back({handle.first});
134 samplers[sampler_binding++] = 0;
135 } 136 }
136 } 137 }
137 for (const auto& desc : info.image_buffer_descriptors) { 138 for (const auto& desc : info.image_buffer_descriptors) {
@@ -142,8 +143,8 @@ void ComputePipeline::Configure() {
142 const auto handle{read_handle(desc, index)}; 143 const auto handle{read_handle(desc, index)};
143 views.push_back({handle.first}); 144 views.push_back({handle.first});
144 145
145 Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); 146 VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
146 samplers[sampler_binding++] = sampler->Handle(); 147 samplers.push_back(sampler);
147 } 148 }
148 } 149 }
149 for (const auto& desc : info.image_descriptors) { 150 for (const auto& desc : info.image_descriptors) {
@@ -186,10 +187,17 @@ void ComputePipeline::Configure() {
186 187
187 const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers + 188 const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
188 num_image_buffers}; 189 num_image_buffers};
190 const VideoCommon::SamplerId* samplers_it{samplers.data()};
189 texture_binding += num_texture_buffers; 191 texture_binding += num_texture_buffers;
190 image_binding += num_image_buffers; 192 image_binding += num_image_buffers;
191 193
192 u32 texture_scaling_mask{}; 194 u32 texture_scaling_mask{};
195
196 for (const auto& desc : info.texture_buffer_descriptors) {
197 for (u32 index = 0; index < desc.count; ++index) {
198 gl_samplers[sampler_binding++] = 0;
199 }
200 }
193 for (const auto& desc : info.texture_descriptors) { 201 for (const auto& desc : info.texture_descriptors) {
194 for (u32 index = 0; index < desc.count; ++index) { 202 for (u32 index = 0; index < desc.count; ++index) {
195 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; 203 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -198,6 +206,12 @@ void ComputePipeline::Configure() {
198 texture_scaling_mask |= 1u << texture_binding; 206 texture_scaling_mask |= 1u << texture_binding;
199 } 207 }
200 ++texture_binding; 208 ++texture_binding;
209
210 const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
211 const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
212 !image_view.SupportsAnisotropy()};
213 gl_samplers[sampler_binding++] =
214 use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
201 } 215 }
202 } 216 }
203 u32 image_scaling_mask{}; 217 u32 image_scaling_mask{};
@@ -228,7 +242,7 @@ void ComputePipeline::Configure() {
228 if (texture_binding != 0) { 242 if (texture_binding != 0) {
229 ASSERT(texture_binding == sampler_binding); 243 ASSERT(texture_binding == sampler_binding);
230 glBindTextures(0, texture_binding, textures.data()); 244 glBindTextures(0, texture_binding, textures.data());
231 glBindSamplers(0, sampler_binding, samplers.data()); 245 glBindSamplers(0, sampler_binding, gl_samplers.data());
232 } 246 }
233 if (image_binding != 0) { 247 if (image_binding != 0) {
234 glBindImageTextures(0, image_binding, images.data()); 248 glBindImageTextures(0, image_binding, images.data());
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 9bcc72b59..c26b4fa5e 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -59,6 +59,10 @@ public:
59 return writes_global_memory; 59 return writes_global_memory;
60 } 60 }
61 61
62 [[nodiscard]] bool UsesLocalMemory() const noexcept {
63 return uses_local_memory;
64 }
65
62 void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, 66 void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_,
63 Tegra::MemoryManager* gpu_memory_) { 67 Tegra::MemoryManager* gpu_memory_) {
64 kepler_compute = kepler_compute_; 68 kepler_compute = kepler_compute_;
@@ -84,6 +88,7 @@ private:
84 88
85 bool use_storage_buffers{}; 89 bool use_storage_buffers{};
86 bool writes_global_memory{}; 90 bool writes_global_memory{};
91 bool uses_local_memory{};
87 92
88 std::mutex built_mutex; 93 std::mutex built_mutex;
89 std::condition_variable built_condvar; 94 std::condition_variable built_condvar;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 400c21981..33e63c17d 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -194,6 +194,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
194 has_bool_ref_bug = true; 194 has_bool_ref_bug = true;
195 } 195 }
196 } 196 }
197 has_lmem_perf_bug = is_nvidia;
197 198
198 strict_context_required = emu_window.StrictContextRequired(); 199 strict_context_required = emu_window.StrictContextRequired();
199 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. 200 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
@@ -201,6 +202,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
201 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && 202 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() &&
202 !(is_amd || (is_intel && !is_linux)) && !strict_context_required; 203 !(is_amd || (is_intel && !is_linux)) && !strict_context_required;
203 use_driver_cache = is_nvidia; 204 use_driver_cache = is_nvidia;
205 supports_conditional_barriers = !is_intel;
204 206
205 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 207 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
206 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); 208 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index cc0b95f1a..a5a6bbbba 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -188,6 +188,14 @@ public:
188 return strict_context_required; 188 return strict_context_required;
189 } 189 }
190 190
191 bool SupportsConditionalBarriers() const {
192 return supports_conditional_barriers;
193 }
194
195 bool HasLmemPerfBug() const {
196 return has_lmem_perf_bug;
197 }
198
191private: 199private:
192 static bool TestVariableAoffi(); 200 static bool TestVariableAoffi();
193 static bool TestPreciseBug(); 201 static bool TestPreciseBug();
@@ -233,6 +241,8 @@ private:
233 bool has_bool_ref_bug{}; 241 bool has_bool_ref_bug{};
234 bool can_report_memory{}; 242 bool can_report_memory{};
235 bool strict_context_required{}; 243 bool strict_context_required{};
244 bool supports_conditional_barriers{};
245 bool has_lmem_perf_bug{};
236 246
237 std::string vendor_name; 247 std::string vendor_name;
238}; 248};
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 89000d6e0..23a48c6fe 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -215,6 +215,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
215 215
216 writes_global_memory |= std::ranges::any_of( 216 writes_global_memory |= std::ranges::any_of(
217 info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; }); 217 info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; });
218 uses_local_memory |= info.uses_local_memory;
218 } 219 }
219 ASSERT(num_textures <= MAX_TEXTURES); 220 ASSERT(num_textures <= MAX_TEXTURES);
220 ASSERT(num_images <= MAX_IMAGES); 221 ASSERT(num_images <= MAX_IMAGES);
@@ -275,9 +276,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
275template <typename Spec> 276template <typename Spec>
276void GraphicsPipeline::ConfigureImpl(bool is_indexed) { 277void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
277 std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; 278 std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
278 std::array<GLuint, MAX_TEXTURES> samplers; 279 std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
279 size_t views_index{}; 280 size_t views_index{};
280 GLsizei sampler_binding{}; 281 size_t samplers_index{};
281 282
282 texture_cache.SynchronizeGraphicsDescriptors(); 283 texture_cache.SynchronizeGraphicsDescriptors();
283 284
@@ -337,7 +338,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
337 for (u32 index = 0; index < desc.count; ++index) { 338 for (u32 index = 0; index < desc.count; ++index) {
338 const auto handle{read_handle(desc, index)}; 339 const auto handle{read_handle(desc, index)};
339 views[views_index++] = {handle.first}; 340 views[views_index++] = {handle.first};
340 samplers[sampler_binding++] = 0;
341 } 341 }
342 } 342 }
343 } 343 }
@@ -351,8 +351,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
351 const auto handle{read_handle(desc, index)}; 351 const auto handle{read_handle(desc, index)};
352 views[views_index++] = {handle.first}; 352 views[views_index++] = {handle.first};
353 353
354 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; 354 VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
355 samplers[sampler_binding++] = sampler->Handle(); 355 samplers[samplers_index++] = sampler;
356 } 356 }
357 } 357 }
358 if constexpr (Spec::has_images) { 358 if constexpr (Spec::has_images) {
@@ -445,10 +445,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
445 program_manager.BindSourcePrograms(source_programs); 445 program_manager.BindSourcePrograms(source_programs);
446 } 446 }
447 const VideoCommon::ImageViewInOut* views_it{views.data()}; 447 const VideoCommon::ImageViewInOut* views_it{views.data()};
448 const VideoCommon::SamplerId* samplers_it{samplers.data()};
448 GLsizei texture_binding = 0; 449 GLsizei texture_binding = 0;
449 GLsizei image_binding = 0; 450 GLsizei image_binding = 0;
451 GLsizei sampler_binding{};
450 std::array<GLuint, MAX_TEXTURES> textures; 452 std::array<GLuint, MAX_TEXTURES> textures;
451 std::array<GLuint, MAX_IMAGES> images; 453 std::array<GLuint, MAX_IMAGES> images;
454 std::array<GLuint, MAX_TEXTURES> gl_samplers;
452 const auto prepare_stage{[&](size_t stage) { 455 const auto prepare_stage{[&](size_t stage) {
453 buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]); 456 buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
454 buffer_cache.BindHostStageBuffers(stage); 457 buffer_cache.BindHostStageBuffers(stage);
@@ -465,6 +468,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
465 u32 stage_image_binding{}; 468 u32 stage_image_binding{};
466 469
467 const auto& info{stage_infos[stage]}; 470 const auto& info{stage_infos[stage]};
471 if constexpr (Spec::has_texture_buffers) {
472 for (const auto& desc : info.texture_buffer_descriptors) {
473 for (u32 index = 0; index < desc.count; ++index) {
474 gl_samplers[sampler_binding++] = 0;
475 }
476 }
477 }
468 for (const auto& desc : info.texture_descriptors) { 478 for (const auto& desc : info.texture_descriptors) {
469 for (u32 index = 0; index < desc.count; ++index) { 479 for (u32 index = 0; index < desc.count; ++index) {
470 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; 480 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -474,6 +484,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
474 } 484 }
475 ++texture_binding; 485 ++texture_binding;
476 ++stage_texture_binding; 486 ++stage_texture_binding;
487
488 const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
489 const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
490 !image_view.SupportsAnisotropy()};
491 gl_samplers[sampler_binding++] =
492 use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
477 } 493 }
478 } 494 }
479 for (const auto& desc : info.image_descriptors) { 495 for (const auto& desc : info.image_descriptors) {
@@ -534,7 +550,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
534 if (texture_binding != 0) { 550 if (texture_binding != 0) {
535 ASSERT(texture_binding == sampler_binding); 551 ASSERT(texture_binding == sampler_binding);
536 glBindTextures(0, texture_binding, textures.data()); 552 glBindTextures(0, texture_binding, textures.data());
537 glBindSamplers(0, sampler_binding, samplers.data()); 553 glBindSamplers(0, sampler_binding, gl_samplers.data());
538 } 554 }
539 if (image_binding != 0) { 555 if (image_binding != 0) {
540 glBindImageTextures(0, image_binding, images.data()); 556 glBindImageTextures(0, image_binding, images.data());
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 7bab3be0a..7b3d7eae8 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -98,6 +98,10 @@ public:
98 return writes_global_memory; 98 return writes_global_memory;
99 } 99 }
100 100
101 [[nodiscard]] bool UsesLocalMemory() const noexcept {
102 return uses_local_memory;
103 }
104
101 [[nodiscard]] bool IsBuilt() noexcept; 105 [[nodiscard]] bool IsBuilt() noexcept;
102 106
103 template <typename Spec> 107 template <typename Spec>
@@ -146,6 +150,7 @@ private:
146 150
147 bool use_storage_buffers{}; 151 bool use_storage_buffers{};
148 bool writes_global_memory{}; 152 bool writes_global_memory{};
153 bool uses_local_memory{};
149 154
150 static constexpr std::size_t XFB_ENTRY_STRIDE = 3; 155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
151 GLsizei num_xfb_attribs{}; 156 GLsizei num_xfb_attribs{};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index fc711c44a..edf527f2d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -222,6 +222,9 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
222 gpu.TickWork(); 222 gpu.TickWork();
223 223
224 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 224 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
225 if (pipeline->UsesLocalMemory()) {
226 program_manager.LocalMemoryWarmup();
227 }
225 pipeline->SetEngine(maxwell3d, gpu_memory); 228 pipeline->SetEngine(maxwell3d, gpu_memory);
226 pipeline->Configure(is_indexed); 229 pipeline->Configure(is_indexed);
227 230
@@ -371,6 +374,9 @@ void RasterizerOpenGL::DispatchCompute() {
371 if (!pipeline) { 374 if (!pipeline) {
372 return; 375 return;
373 } 376 }
377 if (pipeline->UsesLocalMemory()) {
378 program_manager.LocalMemoryWarmup();
379 }
374 pipeline->SetEngine(kepler_compute, gpu_memory); 380 pipeline->SetEngine(kepler_compute, gpu_memory);
375 pipeline->Configure(); 381 pipeline->Configure();
376 const auto& qmd{kepler_compute->launch_description}; 382 const auto& qmd{kepler_compute->launch_description};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 6ecda2984..0329ed820 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -85,7 +85,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
85 case Shader::Stage::VertexB: 85 case Shader::Stage::VertexB:
86 case Shader::Stage::Geometry: 86 case Shader::Stage::Geometry:
87 if (!use_assembly_shaders && key.xfb_enabled != 0) { 87 if (!use_assembly_shaders && key.xfb_enabled != 0) {
88 info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state); 88 auto [varyings, count] = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state);
89 info.xfb_varyings = varyings;
90 info.xfb_count = count;
89 } 91 }
90 break; 92 break;
91 case Shader::Stage::TessellationEval: 93 case Shader::Stage::TessellationEval:
@@ -232,12 +234,14 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
232 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), 234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
233 }, 235 },
234 host_info{ 236 host_info{
237 .support_float64 = true,
235 .support_float16 = false, 238 .support_float16 = false,
236 .support_int64 = device.HasShaderInt64(), 239 .support_int64 = device.HasShaderInt64(),
237 .needs_demote_reorder = device.IsAmd(), 240 .needs_demote_reorder = device.IsAmd(),
238 .support_snorm_render_buffer = false, 241 .support_snorm_render_buffer = false,
239 .support_viewport_index_layer = device.HasVertexViewportLayer(), 242 .support_viewport_index_layer = device.HasVertexViewportLayer(),
240 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), 243 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
244 .support_conditional_barrier = device.SupportsConditionalBarriers(),
241 } { 245 } {
242 if (use_asynchronous_shaders) { 246 if (use_asynchronous_shaders) {
243 workers = CreateWorkers(); 247 workers = CreateWorkers();
diff --git a/src/video_core/renderer_opengl/gl_shader_context.h b/src/video_core/renderer_opengl/gl_shader_context.h
index 207a75d42..d12cd06fa 100644
--- a/src/video_core/renderer_opengl/gl_shader_context.h
+++ b/src/video_core/renderer_opengl/gl_shader_context.h
@@ -16,9 +16,9 @@ struct ShaderPools {
16 inst.ReleaseContents(); 16 inst.ReleaseContents();
17 } 17 }
18 18
19 Shader::ObjectPool<Shader::IR::Inst> inst; 19 Shader::ObjectPool<Shader::IR::Inst> inst{8192};
20 Shader::ObjectPool<Shader::IR::Block> block; 20 Shader::ObjectPool<Shader::IR::Block> block{32};
21 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; 21 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
22}; 22};
23 23
24struct Context { 24struct Context {
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 98841ae65..03d4b9d06 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -3,7 +3,9 @@
3 3
4#include <glad/glad.h> 4#include <glad/glad.h>
5 5
6#include "video_core/host_shaders/opengl_lmem_warmup_comp.h"
6#include "video_core/renderer_opengl/gl_shader_manager.h" 7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
7 9
8namespace OpenGL { 10namespace OpenGL {
9 11
@@ -17,6 +19,10 @@ ProgramManager::ProgramManager(const Device& device) {
17 if (device.UseAssemblyShaders()) { 19 if (device.UseAssemblyShaders()) {
18 glEnable(GL_COMPUTE_PROGRAM_NV); 20 glEnable(GL_COMPUTE_PROGRAM_NV);
19 } 21 }
22 if (device.HasLmemPerfBug()) {
23 lmem_warmup_program =
24 CreateProgram(HostShaders::OPENGL_LMEM_WARMUP_COMP, GL_COMPUTE_SHADER);
25 }
20} 26}
21 27
22void ProgramManager::BindComputeProgram(GLuint program) { 28void ProgramManager::BindComputeProgram(GLuint program) {
@@ -98,6 +104,13 @@ void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NU
98 104
99void ProgramManager::RestoreGuestCompute() {} 105void ProgramManager::RestoreGuestCompute() {}
100 106
107void ProgramManager::LocalMemoryWarmup() {
108 if (lmem_warmup_program.handle != 0) {
109 BindComputeProgram(lmem_warmup_program.handle);
110 glDispatchCompute(1, 1, 1);
111 }
112}
113
101void ProgramManager::BindPipeline() { 114void ProgramManager::BindPipeline() {
102 if (!is_pipeline_bound) { 115 if (!is_pipeline_bound) {
103 is_pipeline_bound = true; 116 is_pipeline_bound = true;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 07ffab77f..852d8c88e 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -30,6 +30,8 @@ public:
30 30
31 void RestoreGuestCompute(); 31 void RestoreGuestCompute();
32 32
33 void LocalMemoryWarmup();
34
33private: 35private:
34 void BindPipeline(); 36 void BindPipeline();
35 37
@@ -44,6 +46,7 @@ private:
44 u32 current_stage_mask = 0; 46 u32 current_stage_mask = 0;
45 std::array<GLuint, NUM_STAGES> current_programs{}; 47 std::array<GLuint, NUM_STAGES> current_programs{};
46 GLuint current_assembly_compute_program = 0; 48 GLuint current_assembly_compute_program = 0;
49 OGLProgram lmem_warmup_program;
47}; 50};
48 51
49} // namespace OpenGL 52} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 1c5dbcdd8..3b446be07 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1268,36 +1268,48 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) {
1268 1268
1269 UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); 1269 UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1);
1270 1270
1271 sampler.Create(); 1271 const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
1272 const GLuint handle = sampler.handle; 1272
1273 glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u)); 1273 const auto create_sampler = [&](const f32 anisotropy) {
1274 glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v)); 1274 OGLSampler new_sampler;
1275 glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p)); 1275 new_sampler.Create();
1276 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode); 1276 const GLuint handle = new_sampler.handle;
1277 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func); 1277 glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
1278 glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag); 1278 glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
1279 glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min); 1279 glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
1280 glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias()); 1280 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
1281 glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod()); 1281 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
1282 glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod()); 1282 glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
1283 glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data()); 1283 glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
1284 1284 glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
1285 if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) { 1285 glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
1286 const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); 1286 glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
1287 glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy); 1287 glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
1288 } else { 1288
1289 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required"); 1289 if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
1290 } 1290 glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
1291 if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) { 1291 } else {
1292 glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter); 1292 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
1293 } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) { 1293 }
1294 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required"); 1294 if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
1295 } 1295 glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
1296 if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) { 1296 } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
1297 glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless); 1297 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
1298 } else if (seamless == GL_FALSE) { 1298 }
1299 // We default to false because it's more common 1299 if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
1300 LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required"); 1300 glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
1301 } else if (seamless == GL_FALSE) {
1302 // We default to false because it's more common
1303 LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
1304 }
1305 return new_sampler;
1306 };
1307
1308 sampler = create_sampler(max_anisotropy);
1309
1310 const f32 max_anisotropy_default = static_cast<f32>(1U << config.max_anisotropy);
1311 if (max_anisotropy > max_anisotropy_default) {
1312 sampler_default_anisotropy = create_sampler(max_anisotropy_default);
1301 } 1313 }
1302} 1314}
1303 1315
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 1148b73d7..3676eaaa9 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -309,12 +309,21 @@ class Sampler {
309public: 309public:
310 explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); 310 explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
311 311
312 GLuint Handle() const noexcept { 312 [[nodiscard]] GLuint Handle() const noexcept {
313 return sampler.handle; 313 return sampler.handle;
314 } 314 }
315 315
316 [[nodiscard]] GLuint HandleWithDefaultAnisotropy() const noexcept {
317 return sampler_default_anisotropy.handle;
318 }
319
320 [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
321 return static_cast<bool>(sampler_default_anisotropy.handle);
322 }
323
316private: 324private:
317 OGLSampler sampler; 325 OGLSampler sampler;
326 OGLSampler sampler_default_anisotropy;
318}; 327};
319 328
320class Framebuffer { 329class Framebuffer {
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 983e1c2e1..71c783709 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -178,7 +178,7 @@ public:
178inline void PushImageDescriptors(TextureCache& texture_cache, 178inline void PushImageDescriptors(TextureCache& texture_cache,
179 GuestDescriptorQueue& guest_descriptor_queue, 179 GuestDescriptorQueue& guest_descriptor_queue,
180 const Shader::Info& info, RescalingPushConstant& rescaling, 180 const Shader::Info& info, RescalingPushConstant& rescaling,
181 const VkSampler*& samplers, 181 const VideoCommon::SamplerId*& samplers,
182 const VideoCommon::ImageViewInOut*& views) { 182 const VideoCommon::ImageViewInOut*& views) {
183 const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); 183 const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
184 const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); 184 const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
@@ -187,10 +187,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
187 for (const auto& desc : info.texture_descriptors) { 187 for (const auto& desc : info.texture_descriptors) {
188 for (u32 index = 0; index < desc.count; ++index) { 188 for (u32 index = 0; index < desc.count; ++index) {
189 const VideoCommon::ImageViewId image_view_id{(views++)->id}; 189 const VideoCommon::ImageViewId image_view_id{(views++)->id};
190 const VkSampler sampler{*(samplers++)}; 190 const VideoCommon::SamplerId sampler_id{*(samplers++)};
191 ImageView& image_view{texture_cache.GetImageView(image_view_id)}; 191 ImageView& image_view{texture_cache.GetImageView(image_view_id)};
192 const VkImageView vk_image_view{image_view.Handle(desc.type)}; 192 const VkImageView vk_image_view{image_view.Handle(desc.type)};
193 guest_descriptor_queue.AddSampledImage(vk_image_view, sampler); 193 const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
194 const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
195 !image_view.SupportsAnisotropy()};
196 const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
197 : sampler.Handle()};
198 guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
194 rescaling.PushTexture(texture_cache.IsRescaling(image_view)); 199 rescaling.PushTexture(texture_cache.IsRescaling(image_view));
195 } 200 }
196 } 201 }
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index d72d99899..f47301ad5 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -361,7 +361,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
361 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, 361 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
362 }; 362 };
363 // Measuring a popular game, this number never exceeds the specified size once data is warmed up 363 // Measuring a popular game, this number never exceeds the specified size once data is warmed up
364 boost::container::small_vector<VkBufferCopy, 3> vk_copies(copies.size()); 364 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
365 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); 365 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
366 scheduler.RequestOutsideRenderPassOperationContext(); 366 scheduler.RequestOutsideRenderPassOperationContext();
367 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { 367 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) {
@@ -501,11 +501,10 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
501 } 501 }
502} 502}
503 503
504void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { 504void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
505 boost::container::small_vector<VkBuffer, 32> buffer_handles; 505 boost::container::small_vector<VkBuffer, 32> buffer_handles;
506 for (u32 index = 0; index < bindings.buffers.size(); index++) { 506 for (u32 index = 0; index < bindings.buffers.size(); ++index) {
507 auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]); 507 auto handle = bindings.buffers[index]->Handle();
508 auto handle = buffer.Handle();
509 if (handle == VK_NULL_HANDLE) { 508 if (handle == VK_NULL_HANDLE) {
510 bindings.offsets[index] = 0; 509 bindings.offsets[index] = 0;
511 bindings.sizes[index] = VK_WHOLE_SIZE; 510 bindings.sizes[index] = VK_WHOLE_SIZE;
@@ -517,20 +516,17 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings)
517 buffer_handles.push_back(handle); 516 buffer_handles.push_back(handle);
518 } 517 }
519 if (device.IsExtExtendedDynamicStateSupported()) { 518 if (device.IsExtExtendedDynamicStateSupported()) {
520 scheduler.Record([bindings = bindings, 519 scheduler.Record([bindings = std::move(bindings),
521 buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { 520 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
522 cmdbuf.BindVertexBuffers2EXT( 521 cmdbuf.BindVertexBuffers2EXT(
523 bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), 522 bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(),
524 reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()), 523 bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data());
525 reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()),
526 reinterpret_cast<const VkDeviceSize*>(bindings.strides.data()));
527 }); 524 });
528 } else { 525 } else {
529 scheduler.Record([bindings = bindings, 526 scheduler.Record([bindings = std::move(bindings),
530 buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { 527 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
531 cmdbuf.BindVertexBuffers( 528 cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index,
532 bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), 529 buffer_handles.data(), bindings.offsets.data());
533 reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()));
534 }); 530 });
535 } 531 }
536} 532}
@@ -556,23 +552,21 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
556 }); 552 });
557} 553}
558 554
559void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) { 555void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
560 if (!device.IsExtTransformFeedbackSupported()) { 556 if (!device.IsExtTransformFeedbackSupported()) {
561 // Already logged in the rasterizer 557 // Already logged in the rasterizer
562 return; 558 return;
563 } 559 }
564 boost::container::small_vector<VkBuffer, 4> buffer_handles; 560 boost::container::small_vector<VkBuffer, 4> buffer_handles;
565 for (u32 index = 0; index < bindings.buffers.size(); index++) { 561 for (u32 index = 0; index < bindings.buffers.size(); ++index) {
566 auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]); 562 buffer_handles.push_back(bindings.buffers[index]->Handle());
567 buffer_handles.push_back(buffer.Handle()); 563 }
568 } 564 scheduler.Record([bindings = std::move(bindings),
569 scheduler.Record( 565 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
570 [bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { 566 cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()),
571 cmdbuf.BindTransformFeedbackBuffersEXT( 567 buffer_handles.data(), bindings.offsets.data(),
572 0, static_cast<u32>(buffer_handles.size()), buffer_handles.data(), 568 bindings.sizes.data());
573 reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()), 569 });
574 reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()));
575 });
576} 570}
577 571
578void BufferCacheRuntime::ReserveNullBuffer() { 572void BufferCacheRuntime::ReserveNullBuffer() {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 92d3e9f32..cdeef8846 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -97,10 +97,12 @@ public:
97 void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); 97 void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
98 98
99 void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); 99 void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
100 void BindVertexBuffers(VideoCommon::HostBindings& bindings); 100
101 void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
101 102
102 void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); 103 void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
103 void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); 104
105 void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
104 106
105 std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage, 107 std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
106 [[maybe_unused]] u32 binding_index, u32 size) { 108 [[maybe_unused]] u32 binding_index, u32 size) {
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 733e70d9d..73e585c2b 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
115 115
116 static constexpr size_t max_elements = 64; 116 static constexpr size_t max_elements = 64;
117 boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; 117 boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
118 boost::container::static_vector<VkSampler, max_elements> samplers; 118 boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers;
119 119
120 const auto& qmd{kepler_compute.launch_description}; 120 const auto& qmd{kepler_compute.launch_description};
121 const auto& cbufs{qmd.const_buffer_config}; 121 const auto& cbufs{qmd.const_buffer_config};
@@ -160,8 +160,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
160 const auto handle{read_handle(desc, index)}; 160 const auto handle{read_handle(desc, index)};
161 views.push_back({handle.first}); 161 views.push_back({handle.first});
162 162
163 Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); 163 VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
164 samplers.push_back(sampler->Handle()); 164 samplers.push_back(sampler);
165 } 165 }
166 } 166 }
167 for (const auto& desc : info.image_descriptors) { 167 for (const auto& desc : info.image_descriptors) {
@@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
192 buffer_cache.BindHostComputeBuffers(); 192 buffer_cache.BindHostComputeBuffers();
193 193
194 RescalingPushConstant rescaling; 194 RescalingPushConstant rescaling;
195 const VkSampler* samplers_it{samplers.data()}; 195 const VideoCommon::SamplerId* samplers_it{samplers.data()};
196 const VideoCommon::ImageViewInOut* views_it{views.data()}; 196 const VideoCommon::ImageViewInOut* views_it{views.data()};
197 PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, 197 PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
198 views_it); 198 views_it);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 506b78f08..c1595642e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
298template <typename Spec> 298template <typename Spec>
299void GraphicsPipeline::ConfigureImpl(bool is_indexed) { 299void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
300 std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; 300 std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
301 std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers; 301 std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers;
302 size_t sampler_index{}; 302 size_t sampler_index{};
303 size_t view_index{}; 303 size_t view_index{};
304 304
@@ -367,8 +367,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
367 const auto handle{read_handle(desc, index)}; 367 const auto handle{read_handle(desc, index)};
368 views[view_index++] = {handle.first}; 368 views[view_index++] = {handle.first};
369 369
370 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; 370 VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
371 samplers[sampler_index++] = sampler->Handle(); 371 samplers[sampler_index++] = sampler;
372 } 372 }
373 } 373 }
374 if constexpr (Spec::has_images) { 374 if constexpr (Spec::has_images) {
@@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
453 453
454 RescalingPushConstant rescaling; 454 RescalingPushConstant rescaling;
455 RenderAreaPushConstant render_area; 455 RenderAreaPushConstant render_area;
456 const VkSampler* samplers_it{samplers.data()}; 456 const VideoCommon::SamplerId* samplers_it{samplers.data()};
457 const VideoCommon::ImageViewInOut* views_it{views.data()}; 457 const VideoCommon::ImageViewInOut* views_it{views.data()};
458 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 458 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
459 buffer_cache.BindHostStageBuffers(stage); 459 buffer_cache.BindHostStageBuffers(stage);
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index b128c4f6e..6b288b994 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -3,6 +3,7 @@
3 3
4#include <thread> 4#include <thread>
5 5
6#include "common/polyfill_ranges.h"
6#include "common/settings.h" 7#include "common/settings.h"
7#include "video_core/renderer_vulkan/vk_master_semaphore.h" 8#include "video_core/renderer_vulkan/vk_master_semaphore.h"
8#include "video_core/vulkan_common/vulkan_device.h" 9#include "video_core/vulkan_common/vulkan_device.h"
@@ -74,15 +75,9 @@ void MasterSemaphore::Refresh() {
74 75
75void MasterSemaphore::Wait(u64 tick) { 76void MasterSemaphore::Wait(u64 tick) {
76 if (!semaphore) { 77 if (!semaphore) {
77 // If we don't support timeline semaphores, use an atomic wait 78 // If we don't support timeline semaphores, wait for the value normally
78 while (true) { 79 std::unique_lock lk{free_mutex};
79 u64 current_value = gpu_tick.load(std::memory_order_relaxed); 80 free_cv.wait(lk, [&] { return gpu_tick.load(std::memory_order_relaxed) >= tick; });
80 if (current_value >= tick) {
81 return;
82 }
83 gpu_tick.wait(current_value);
84 }
85
86 return; 81 return;
87 } 82 }
88 83
@@ -197,11 +192,13 @@ void MasterSemaphore::WaitThread(std::stop_token token) {
197 192
198 fence.Wait(); 193 fence.Wait();
199 fence.Reset(); 194 fence.Reset();
200 gpu_tick.store(host_tick);
201 gpu_tick.notify_all();
202 195
203 std::scoped_lock lock{free_mutex}; 196 {
204 free_queue.push_front(std::move(fence)); 197 std::scoped_lock lock{free_mutex};
198 free_queue.push_front(std::move(fence));
199 gpu_tick.store(host_tick);
200 }
201 free_cv.notify_one();
205 } 202 }
206} 203}
207 204
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 1e7c90215..3f599d7bd 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -72,6 +72,7 @@ private:
72 std::atomic<u64> current_tick{1}; ///< Current logical tick. 72 std::atomic<u64> current_tick{1}; ///< Current logical tick.
73 std::mutex wait_mutex; 73 std::mutex wait_mutex;
74 std::mutex free_mutex; 74 std::mutex free_mutex;
75 std::condition_variable free_cv;
75 std::condition_variable_any wait_cv; 76 std::condition_variable_any wait_cv;
76 std::queue<Waitable> wait_queue; ///< Queue for the fences to be waited on by the wait thread. 77 std::queue<Waitable> wait_queue; ///< Queue for the fences to be waited on by the wait thread.
77 std::deque<vk::Fence> free_queue; ///< Holds available fences for submission. 78 std::deque<vk::Fence> free_queue; ///< Holds available fences for submission.
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 9482e91b0..9f316113c 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -167,7 +167,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
167 info.fixed_state_point_size = point_size; 167 info.fixed_state_point_size = point_size;
168 } 168 }
169 if (key.state.xfb_enabled) { 169 if (key.state.xfb_enabled) {
170 info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); 170 auto [varyings, count] =
171 VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
172 info.xfb_varyings = varyings;
173 info.xfb_count = count;
171 } 174 }
172 info.convert_depth_mode = gl_ndc; 175 info.convert_depth_mode = gl_ndc;
173 } 176 }
@@ -214,7 +217,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
214 info.fixed_state_point_size = point_size; 217 info.fixed_state_point_size = point_size;
215 } 218 }
216 if (key.state.xfb_enabled != 0) { 219 if (key.state.xfb_enabled != 0) {
217 info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); 220 auto [varyings, count] =
221 VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
222 info.xfb_varyings = varyings;
223 info.xfb_count = count;
218 } 224 }
219 info.convert_depth_mode = gl_ndc; 225 info.convert_depth_mode = gl_ndc;
220 break; 226 break;
@@ -350,6 +356,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
350 .has_broken_spirv_subgroup_mask_vector_extract_dynamic = 356 .has_broken_spirv_subgroup_mask_vector_extract_dynamic =
351 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY}; 357 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY};
352 host_info = Shader::HostTranslateInfo{ 358 host_info = Shader::HostTranslateInfo{
359 .support_float64 = device.IsFloat64Supported(),
353 .support_float16 = device.IsFloat16Supported(), 360 .support_float16 = device.IsFloat16Supported(),
354 .support_int64 = device.IsShaderInt64Supported(), 361 .support_int64 = device.IsShaderInt64Supported(),
355 .needs_demote_reorder = 362 .needs_demote_reorder =
@@ -357,6 +364,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
357 .support_snorm_render_buffer = true, 364 .support_snorm_render_buffer = true,
358 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 365 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
359 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 366 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
367 .support_conditional_barrier = device.SupportsConditionalBarriers(),
360 }; 368 };
361 369
362 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { 370 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
@@ -703,10 +711,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
703std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 711std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
704 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 712 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
705 PipelineStatistics* statistics, bool build_in_parallel) try { 713 PipelineStatistics* statistics, bool build_in_parallel) try {
706 // TODO: Remove this when Intel fixes their shader compiler. 714 if (device.HasBrokenCompute()) {
707 // https://github.com/IGCIT/Intel-GPU-Community-Issue-Tracker-IGCIT/issues/159
708 if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS &&
709 !Settings::values.enable_compute_pipelines.GetValue()) {
710 LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash()); 715 LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", key.Hash());
711 return nullptr; 716 return nullptr;
712 } 717 }
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 15aa7e224..e323ea0fd 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -92,9 +92,9 @@ struct ShaderPools {
92 inst.ReleaseContents(); 92 inst.ReleaseContents();
93 } 93 }
94 94
95 Shader::ObjectPool<Shader::IR::Inst> inst; 95 Shader::ObjectPool<Shader::IR::Inst> inst{8192};
96 Shader::ObjectPool<Shader::IR::Block> block; 96 Shader::ObjectPool<Shader::IR::Block> block{32};
97 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; 97 Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
98}; 98};
99 99
100class PipelineCache : public VideoCommon::ShaderCache { 100class PipelineCache : public VideoCommon::ShaderCache {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8711e2a87..f3cef09dd 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -330,9 +330,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
330 }; 330 };
331} 331}
332 332
333[[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( 333[[maybe_unused]] [[nodiscard]] boost::container::small_vector<VkBufferCopy, 16>
334 std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { 334TransformBufferCopies(std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) {
335 std::vector<VkBufferCopy> result(copies.size()); 335 boost::container::small_vector<VkBufferCopy, 16> result(copies.size());
336 std::ranges::transform( 336 std::ranges::transform(
337 copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) { 337 copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) {
338 return VkBufferCopy{ 338 return VkBufferCopy{
@@ -344,7 +344,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
344 return result; 344 return result;
345} 345}
346 346
347[[nodiscard]] std::vector<VkBufferImageCopy> TransformBufferImageCopies( 347[[nodiscard]] boost::container::small_vector<VkBufferImageCopy, 16> TransformBufferImageCopies(
348 std::span<const BufferImageCopy> copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) { 348 std::span<const BufferImageCopy> copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) {
349 struct Maker { 349 struct Maker {
350 VkBufferImageCopy operator()(const BufferImageCopy& copy) const { 350 VkBufferImageCopy operator()(const BufferImageCopy& copy) const {
@@ -377,14 +377,14 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
377 VkImageAspectFlags aspect_mask; 377 VkImageAspectFlags aspect_mask;
378 }; 378 };
379 if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { 379 if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
380 std::vector<VkBufferImageCopy> result(copies.size() * 2); 380 boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size() * 2);
381 std::ranges::transform(copies, result.begin(), 381 std::ranges::transform(copies, result.begin(),
382 Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT}); 382 Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT});
383 std::ranges::transform(copies, result.begin() + copies.size(), 383 std::ranges::transform(copies, result.begin() + copies.size(),
384 Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT}); 384 Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT});
385 return result; 385 return result;
386 } else { 386 } else {
387 std::vector<VkBufferImageCopy> result(copies.size()); 387 boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size());
388 std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask}); 388 std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask});
389 return result; 389 return result;
390 } 390 }
@@ -867,8 +867,8 @@ void TextureCacheRuntime::BarrierFeedbackLoop() {
867 867
868void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, 868void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
869 std::span<const VideoCommon::ImageCopy> copies) { 869 std::span<const VideoCommon::ImageCopy> copies) {
870 std::vector<VkBufferImageCopy> vk_in_copies(copies.size()); 870 boost::container::small_vector<VkBufferImageCopy, 16> vk_in_copies(copies.size());
871 std::vector<VkBufferImageCopy> vk_out_copies(copies.size()); 871 boost::container::small_vector<VkBufferImageCopy, 16> vk_out_copies(copies.size());
872 const VkImageAspectFlags src_aspect_mask = src.AspectMask(); 872 const VkImageAspectFlags src_aspect_mask = src.AspectMask();
873 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); 873 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask();
874 874
@@ -1157,7 +1157,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
1157 1157
1158void TextureCacheRuntime::CopyImage(Image& dst, Image& src, 1158void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
1159 std::span<const VideoCommon::ImageCopy> copies) { 1159 std::span<const VideoCommon::ImageCopy> copies) {
1160 std::vector<VkImageCopy> vk_copies(copies.size()); 1160 boost::container::small_vector<VkImageCopy, 16> vk_copies(copies.size());
1161 const VkImageAspectFlags aspect_mask = dst.AspectMask(); 1161 const VkImageAspectFlags aspect_mask = dst.AspectMask();
1162 ASSERT(aspect_mask == src.AspectMask()); 1162 ASSERT(aspect_mask == src.AspectMask());
1163 1163
@@ -1332,7 +1332,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
1332 ScaleDown(true); 1332 ScaleDown(true);
1333 } 1333 }
1334 scheduler->RequestOutsideRenderPassOperationContext(); 1334 scheduler->RequestOutsideRenderPassOperationContext();
1335 std::vector vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); 1335 auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
1336 const VkBuffer src_buffer = buffer; 1336 const VkBuffer src_buffer = buffer;
1337 const VkImage vk_image = *original_image; 1337 const VkImage vk_image = *original_image;
1338 const VkImageAspectFlags vk_aspect_mask = aspect_mask; 1338 const VkImageAspectFlags vk_aspect_mask = aspect_mask;
@@ -1367,8 +1367,9 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS
1367 if (is_rescaled) { 1367 if (is_rescaled) {
1368 ScaleDown(); 1368 ScaleDown();
1369 } 1369 }
1370 boost::container::small_vector<VkBuffer, 1> buffers_vector{}; 1370 boost::container::small_vector<VkBuffer, 8> buffers_vector{};
1371 boost::container::small_vector<std::vector<VkBufferImageCopy>, 1> vk_copies; 1371 boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8>
1372 vk_copies;
1372 for (size_t index = 0; index < buffers_span.size(); index++) { 1373 for (size_t index = 0; index < buffers_span.size(); index++) {
1373 buffers_vector.emplace_back(buffers_span[index]); 1374 buffers_vector.emplace_back(buffers_span[index]);
1374 vk_copies.emplace_back( 1375 vk_copies.emplace_back(
@@ -1802,27 +1803,36 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
1802 // Some games have samplers with garbage. Sanitize them here. 1803 // Some games have samplers with garbage. Sanitize them here.
1803 const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); 1804 const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
1804 1805
1805 sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{ 1806 const auto create_sampler = [&](const f32 anisotropy) {
1806 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 1807 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
1807 .pNext = pnext, 1808 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1808 .flags = 0, 1809 .pNext = pnext,
1809 .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), 1810 .flags = 0,
1810 .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), 1811 .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
1811 .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), 1812 .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
1812 .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), 1813 .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
1813 .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), 1814 .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
1814 .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), 1815 .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
1815 .mipLodBias = tsc.LodBias(), 1816 .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
1816 .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE), 1817 .mipLodBias = tsc.LodBias(),
1817 .maxAnisotropy = max_anisotropy, 1818 .anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
1818 .compareEnable = tsc.depth_compare_enabled, 1819 .maxAnisotropy = anisotropy,
1819 .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), 1820 .compareEnable = tsc.depth_compare_enabled,
1820 .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), 1821 .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
1821 .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), 1822 .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
1822 .borderColor = 1823 .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
1823 arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), 1824 .borderColor =
1824 .unnormalizedCoordinates = VK_FALSE, 1825 arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
1825 }); 1826 .unnormalizedCoordinates = VK_FALSE,
1827 });
1828 };
1829
1830 sampler = create_sampler(max_anisotropy);
1831
1832 const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
1833 if (max_anisotropy > max_anisotropy_default) {
1834 sampler_default_anisotropy = create_sampler(max_anisotropy_default);
1835 }
1826} 1836}
1827 1837
1828Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, 1838Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
@@ -1849,7 +1859,7 @@ Framebuffer::~Framebuffer() = default;
1849void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, 1859void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1850 std::span<ImageView*, NUM_RT> color_buffers, 1860 std::span<ImageView*, NUM_RT> color_buffers,
1851 ImageView* depth_buffer, bool is_rescaled) { 1861 ImageView* depth_buffer, bool is_rescaled) {
1852 std::vector<VkImageView> attachments; 1862 boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;
1853 RenderPassKey renderpass_key{}; 1863 RenderPassKey renderpass_key{};
1854 s32 num_layers = 1; 1864 s32 num_layers = 1;
1855 1865
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0f7a5ffd4..f14525dcb 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -279,8 +279,17 @@ public:
279 return *sampler; 279 return *sampler;
280 } 280 }
281 281
282 [[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
283 return *sampler_default_anisotropy;
284 }
285
286 [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
287 return static_cast<bool>(sampler_default_anisotropy);
288 }
289
282private: 290private:
283 vk::Sampler sampler; 291 vk::Sampler sampler;
292 vk::Sampler sampler_default_anisotropy;
284}; 293};
285 294
286class Framebuffer { 295class Framebuffer {
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index c5213875b..4db948b6d 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -151,11 +151,9 @@ void ShaderCache::RemovePendingShaders() {
151 marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()), 151 marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
152 marked_for_removal.end()); 152 marked_for_removal.end());
153 153
154 std::vector<ShaderInfo*> removed_shaders; 154 boost::container::small_vector<ShaderInfo*, 16> removed_shaders;
155 removed_shaders.reserve(marked_for_removal.size());
156 155
157 std::scoped_lock lock{lookup_mutex}; 156 std::scoped_lock lock{lookup_mutex};
158
159 for (Entry* const entry : marked_for_removal) { 157 for (Entry* const entry : marked_for_removal) {
160 removed_shaders.push_back(entry->data); 158 removed_shaders.push_back(entry->data);
161 159
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 1b8a17ee8..55d49d017 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <optional> 7#include <optional>
8#include <vector> 8#include <vector>
9#include <boost/container/small_vector.hpp>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -108,8 +109,8 @@ struct ImageBase {
108 std::vector<ImageViewInfo> image_view_infos; 109 std::vector<ImageViewInfo> image_view_infos;
109 std::vector<ImageViewId> image_view_ids; 110 std::vector<ImageViewId> image_view_ids;
110 111
111 std::vector<u32> slice_offsets; 112 boost::container::small_vector<u32, 16> slice_offsets;
112 std::vector<SubresourceBase> slice_subresources; 113 boost::container::small_vector<SubresourceBase, 16> slice_subresources;
113 114
114 std::vector<AliasedImage> aliased_images; 115 std::vector<AliasedImage> aliased_images;
115 std::vector<ImageId> overlapping_images; 116 std::vector<ImageId> overlapping_images;
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index e8ddde691..b72788c6d 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -22,6 +22,9 @@ using Tegra::Texture::TICEntry;
22using VideoCore::Surface::PixelFormat; 22using VideoCore::Surface::PixelFormat;
23using VideoCore::Surface::SurfaceType; 23using VideoCore::Surface::SurfaceType;
24 24
25constexpr u32 RescaleHeightThreshold = 288;
26constexpr u32 DownscaleHeightThreshold = 512;
27
25ImageInfo::ImageInfo(const TICEntry& config) noexcept { 28ImageInfo::ImageInfo(const TICEntry& config) noexcept {
26 forced_flushed = config.IsPitchLinear() && !Settings::values.use_reactive_flushing.GetValue(); 29 forced_flushed = config.IsPitchLinear() && !Settings::values.use_reactive_flushing.GetValue();
27 dma_downloaded = forced_flushed; 30 dma_downloaded = forced_flushed;
@@ -113,8 +116,9 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
113 layer_stride = CalculateLayerStride(*this); 116 layer_stride = CalculateLayerStride(*this);
114 maybe_unaligned_layer_stride = CalculateLayerSize(*this); 117 maybe_unaligned_layer_stride = CalculateLayerSize(*this);
115 rescaleable &= (block.depth == 0) && resources.levels == 1; 118 rescaleable &= (block.depth == 0) && resources.levels == 1;
116 rescaleable &= size.height > 256 || GetFormatType(format) != SurfaceType::ColorTexture; 119 rescaleable &= size.height > RescaleHeightThreshold ||
117 downscaleable = size.height > 512; 120 GetFormatType(format) != SurfaceType::ColorTexture;
121 downscaleable = size.height > DownscaleHeightThreshold;
118 } 122 }
119} 123}
120 124
@@ -152,8 +156,8 @@ ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
152 size.depth = ct.depth; 156 size.depth = ct.depth;
153 } else { 157 } else {
154 rescaleable = block.depth == 0; 158 rescaleable = block.depth == 0;
155 rescaleable &= size.height > 256; 159 rescaleable &= size.height > RescaleHeightThreshold;
156 downscaleable = size.height > 512; 160 downscaleable = size.height > DownscaleHeightThreshold;
157 type = ImageType::e2D; 161 type = ImageType::e2D;
158 resources.layers = ct.depth; 162 resources.layers = ct.depth;
159 } 163 }
@@ -232,8 +236,8 @@ ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept {
232 .height = config.height, 236 .height = config.height,
233 .depth = 1, 237 .depth = 1,
234 }; 238 };
235 rescaleable = block.depth == 0 && size.height > 256; 239 rescaleable = block.depth == 0 && size.height > RescaleHeightThreshold;
236 downscaleable = size.height > 512; 240 downscaleable = size.height > DownscaleHeightThreshold;
237 } 241 }
238} 242}
239 243
@@ -275,8 +279,8 @@ ImageInfo::ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept {
275 resources.layers = 1; 279 resources.layers = 1;
276 layer_stride = CalculateLayerStride(*this); 280 layer_stride = CalculateLayerStride(*this);
277 maybe_unaligned_layer_stride = CalculateLayerSize(*this); 281 maybe_unaligned_layer_stride = CalculateLayerSize(*this);
278 rescaleable = block.depth == 0 && size.height > 256; 282 rescaleable = block.depth == 0 && size.height > RescaleHeightThreshold;
279 downscaleable = size.height > 512; 283 downscaleable = size.height > DownscaleHeightThreshold;
280} 284}
281 285
282} // namespace VideoCommon 286} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index d134b6738..0c5f4450d 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -45,4 +45,56 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
45 45
46ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} 46ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
47 47
48bool ImageViewBase::SupportsAnisotropy() const noexcept {
49 const bool has_mips = range.extent.levels > 1;
50 const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray;
51 if (!has_mips || !is_2d) {
52 return false;
53 }
54
55 switch (format) {
56 case PixelFormat::R8_UNORM:
57 case PixelFormat::R8_SNORM:
58 case PixelFormat::R8_SINT:
59 case PixelFormat::R8_UINT:
60 case PixelFormat::BC4_UNORM:
61 case PixelFormat::BC4_SNORM:
62 case PixelFormat::BC5_UNORM:
63 case PixelFormat::BC5_SNORM:
64 case PixelFormat::R32G32_FLOAT:
65 case PixelFormat::R32G32_SINT:
66 case PixelFormat::R32_FLOAT:
67 case PixelFormat::R16_FLOAT:
68 case PixelFormat::R16_UNORM:
69 case PixelFormat::R16_SNORM:
70 case PixelFormat::R16_UINT:
71 case PixelFormat::R16_SINT:
72 case PixelFormat::R16G16_UNORM:
73 case PixelFormat::R16G16_FLOAT:
74 case PixelFormat::R16G16_UINT:
75 case PixelFormat::R16G16_SINT:
76 case PixelFormat::R16G16_SNORM:
77 case PixelFormat::R8G8_UNORM:
78 case PixelFormat::R8G8_SNORM:
79 case PixelFormat::R8G8_SINT:
80 case PixelFormat::R8G8_UINT:
81 case PixelFormat::R32G32_UINT:
82 case PixelFormat::R32_UINT:
83 case PixelFormat::R32_SINT:
84 case PixelFormat::G4R4_UNORM:
85 // Depth formats
86 case PixelFormat::D32_FLOAT:
87 case PixelFormat::D16_UNORM:
88 // Stencil formats
89 case PixelFormat::S8_UINT:
90 // DepthStencil formats
91 case PixelFormat::D24_UNORM_S8_UINT:
92 case PixelFormat::S8_UINT_D24_UNORM:
93 case PixelFormat::D32_FLOAT_S8_UINT:
94 return false;
95 default:
96 return true;
97 }
98}
99
48} // namespace VideoCommon 100} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h
index a25ae1d4a..87549ffff 100644
--- a/src/video_core/texture_cache/image_view_base.h
+++ b/src/video_core/texture_cache/image_view_base.h
@@ -33,6 +33,8 @@ struct ImageViewBase {
33 return type == ImageViewType::Buffer; 33 return type == ImageViewType::Buffer;
34 } 34 }
35 35
36 [[nodiscard]] bool SupportsAnisotropy() const noexcept;
37
36 ImageId image_id{}; 38 ImageId image_id{};
37 GPUVAddr gpu_addr = 0; 39 GPUVAddr gpu_addr = 0;
38 PixelFormat format{}; 40 PixelFormat format{};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index c7f7448e9..d3f03a995 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -186,6 +186,10 @@ void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
186 186
187template <class P> 187template <class P>
188void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) { 188void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
189 if (!Settings::values.barrier_feedback_loops.GetValue()) {
190 return;
191 }
192
189 const bool requires_barrier = [&] { 193 const bool requires_barrier = [&] {
190 for (const auto& view : views) { 194 for (const auto& view : views) {
191 if (!view.id) { 195 if (!view.id) {
@@ -222,30 +226,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
222 226
223template <class P> 227template <class P>
224typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { 228typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
229 return &slot_samplers[GetGraphicsSamplerId(index)];
230}
231
232template <class P>
233typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
234 return &slot_samplers[GetComputeSamplerId(index)];
235}
236
237template <class P>
238SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) {
225 if (index > channel_state->graphics_sampler_table.Limit()) { 239 if (index > channel_state->graphics_sampler_table.Limit()) {
226 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); 240 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
227 return &slot_samplers[NULL_SAMPLER_ID]; 241 return NULL_SAMPLER_ID;
228 } 242 }
229 const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); 243 const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
230 SamplerId& id = channel_state->graphics_sampler_ids[index]; 244 SamplerId& id = channel_state->graphics_sampler_ids[index];
231 if (is_new) { 245 if (is_new) {
232 id = FindSampler(descriptor); 246 id = FindSampler(descriptor);
233 } 247 }
234 return &slot_samplers[id]; 248 return id;
235} 249}
236 250
237template <class P> 251template <class P>
238typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { 252SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) {
239 if (index > channel_state->compute_sampler_table.Limit()) { 253 if (index > channel_state->compute_sampler_table.Limit()) {
240 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); 254 LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
241 return &slot_samplers[NULL_SAMPLER_ID]; 255 return NULL_SAMPLER_ID;
242 } 256 }
243 const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); 257 const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
244 SamplerId& id = channel_state->compute_sampler_ids[index]; 258 SamplerId& id = channel_state->compute_sampler_ids[index];
245 if (is_new) { 259 if (is_new) {
246 id = FindSampler(descriptor); 260 id = FindSampler(descriptor);
247 } 261 }
248 return &slot_samplers[id]; 262 return id;
263}
264
265template <class P>
266const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept {
267 return slot_samplers[id];
268}
269
270template <class P>
271typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
272 return slot_samplers[id];
249} 273}
250 274
251template <class P> 275template <class P>
@@ -280,7 +304,7 @@ void TextureCache<P>::SynchronizeComputeDescriptors() {
280} 304}
281 305
282template <class P> 306template <class P>
283bool TextureCache<P>::RescaleRenderTargets(bool is_clear) { 307bool TextureCache<P>::RescaleRenderTargets() {
284 auto& flags = maxwell3d->dirty.flags; 308 auto& flags = maxwell3d->dirty.flags;
285 u32 scale_rating = 0; 309 u32 scale_rating = 0;
286 bool rescaled = false; 310 bool rescaled = false;
@@ -318,13 +342,13 @@ bool TextureCache<P>::RescaleRenderTargets(bool is_clear) {
318 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; 342 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
319 if (flags[Dirty::ColorBuffer0 + index] || force) { 343 if (flags[Dirty::ColorBuffer0 + index] || force) {
320 flags[Dirty::ColorBuffer0 + index] = false; 344 flags[Dirty::ColorBuffer0 + index] = false;
321 BindRenderTarget(&color_buffer_id, FindColorBuffer(index, is_clear)); 345 BindRenderTarget(&color_buffer_id, FindColorBuffer(index));
322 } 346 }
323 check_rescale(color_buffer_id, tmp_color_images[index]); 347 check_rescale(color_buffer_id, tmp_color_images[index]);
324 } 348 }
325 if (flags[Dirty::ZetaBuffer] || force) { 349 if (flags[Dirty::ZetaBuffer] || force) {
326 flags[Dirty::ZetaBuffer] = false; 350 flags[Dirty::ZetaBuffer] = false;
327 BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear)); 351 BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer());
328 } 352 }
329 check_rescale(render_targets.depth_buffer_id, tmp_depth_image); 353 check_rescale(render_targets.depth_buffer_id, tmp_depth_image);
330 354
@@ -389,7 +413,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
389 return; 413 return;
390 } 414 }
391 415
392 const bool rescaled = RescaleRenderTargets(is_clear); 416 const bool rescaled = RescaleRenderTargets();
393 if (is_rescaling != rescaled) { 417 if (is_rescaling != rescaled) {
394 flags[Dirty::RescaleViewports] = true; 418 flags[Dirty::RescaleViewports] = true;
395 flags[Dirty::RescaleScissors] = true; 419 flags[Dirty::RescaleScissors] = true;
@@ -502,7 +526,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
502 526
503template <class P> 527template <class P>
504void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { 528void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
505 std::vector<ImageId> images; 529 boost::container::small_vector<ImageId, 16> images;
506 ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { 530 ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
507 if (!image.IsSafeDownload()) { 531 if (!image.IsSafeDownload()) {
508 return; 532 return;
@@ -555,7 +579,7 @@ std::optional<VideoCore::RasterizerDownloadArea> TextureCache<P>::GetFlushArea(V
555 579
556template <class P> 580template <class P>
557void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { 581void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
558 std::vector<ImageId> deleted_images; 582 boost::container::small_vector<ImageId, 16> deleted_images;
559 ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); 583 ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); });
560 for (const ImageId id : deleted_images) { 584 for (const ImageId id : deleted_images) {
561 Image& image = slot_images[id]; 585 Image& image = slot_images[id];
@@ -569,7 +593,7 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
569 593
570template <class P> 594template <class P>
571void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { 595void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) {
572 std::vector<ImageId> deleted_images; 596 boost::container::small_vector<ImageId, 16> deleted_images;
573 ForEachImageInRegionGPU(as_id, gpu_addr, size, 597 ForEachImageInRegionGPU(as_id, gpu_addr, size,
574 [&](ImageId id, Image&) { deleted_images.push_back(id); }); 598 [&](ImageId id, Image&) { deleted_images.push_back(id); });
575 for (const ImageId id : deleted_images) { 599 for (const ImageId id : deleted_images) {
@@ -1077,7 +1101,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
1077 const bool native_bgr = runtime.HasNativeBgr(); 1101 const bool native_bgr = runtime.HasNativeBgr();
1078 const bool flexible_formats = True(options & RelaxedOptions::Format); 1102 const bool flexible_formats = True(options & RelaxedOptions::Format);
1079 ImageId image_id{}; 1103 ImageId image_id{};
1080 boost::container::small_vector<ImageId, 1> image_ids; 1104 boost::container::small_vector<ImageId, 8> image_ids;
1081 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 1105 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
1082 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 1106 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
1083 return false; 1107 return false;
@@ -1598,7 +1622,7 @@ ImageId TextureCache<P>::FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr)
1598 } 1622 }
1599 } 1623 }
1600 ImageId image_id{}; 1624 ImageId image_id{};
1601 boost::container::small_vector<ImageId, 1> image_ids; 1625 boost::container::small_vector<ImageId, 8> image_ids;
1602 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 1626 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
1603 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 1627 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
1604 return false; 1628 return false;
@@ -1658,7 +1682,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
1658} 1682}
1659 1683
1660template <class P> 1684template <class P>
1661ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) { 1685ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
1662 const auto& regs = maxwell3d->regs; 1686 const auto& regs = maxwell3d->regs;
1663 if (index >= regs.rt_control.count) { 1687 if (index >= regs.rt_control.count) {
1664 return ImageViewId{}; 1688 return ImageViewId{};
@@ -1672,11 +1696,11 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index, bool is_clear) {
1672 return ImageViewId{}; 1696 return ImageViewId{};
1673 } 1697 }
1674 const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode); 1698 const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode);
1675 return FindRenderTargetView(info, gpu_addr, is_clear); 1699 return FindRenderTargetView(info, gpu_addr);
1676} 1700}
1677 1701
1678template <class P> 1702template <class P>
1679ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) { 1703ImageViewId TextureCache<P>::FindDepthBuffer() {
1680 const auto& regs = maxwell3d->regs; 1704 const auto& regs = maxwell3d->regs;
1681 if (!regs.zeta_enable) { 1705 if (!regs.zeta_enable) {
1682 return ImageViewId{}; 1706 return ImageViewId{};
@@ -1686,18 +1710,16 @@ ImageViewId TextureCache<P>::FindDepthBuffer(bool is_clear) {
1686 return ImageViewId{}; 1710 return ImageViewId{};
1687 } 1711 }
1688 const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode); 1712 const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode);
1689 return FindRenderTargetView(info, gpu_addr, is_clear); 1713 return FindRenderTargetView(info, gpu_addr);
1690} 1714}
1691 1715
1692template <class P> 1716template <class P>
1693ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, 1717ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr) {
1694 bool is_clear) {
1695 const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{};
1696 ImageId image_id{}; 1718 ImageId image_id{};
1697 bool delete_state = has_deleted_images; 1719 bool delete_state = has_deleted_images;
1698 do { 1720 do {
1699 has_deleted_images = false; 1721 has_deleted_images = false;
1700 image_id = FindOrInsertImage(info, gpu_addr, options); 1722 image_id = FindOrInsertImage(info, gpu_addr);
1701 delete_state |= has_deleted_images; 1723 delete_state |= has_deleted_images;
1702 } while (has_deleted_images); 1724 } while (has_deleted_images);
1703 has_deleted_images = delete_state; 1725 has_deleted_images = delete_state;
@@ -1920,7 +1942,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1920 image.map_view_id = map_id; 1942 image.map_view_id = map_id;
1921 return; 1943 return;
1922 } 1944 }
1923 std::vector<ImageViewId> sparse_maps{}; 1945 boost::container::small_vector<ImageViewId, 16> sparse_maps;
1924 ForEachSparseSegment( 1946 ForEachSparseSegment(
1925 image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { 1947 image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
1926 auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id); 1948 auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id);
@@ -2195,7 +2217,7 @@ void TextureCache<P>::MarkModification(ImageBase& image) noexcept {
2195 2217
2196template <class P> 2218template <class P>
2197void TextureCache<P>::SynchronizeAliases(ImageId image_id) { 2219void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
2198 boost::container::small_vector<const AliasedImage*, 1> aliased_images; 2220 boost::container::small_vector<const AliasedImage*, 8> aliased_images;
2199 Image& image = slot_images[image_id]; 2221 Image& image = slot_images[image_id];
2200 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); 2222 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled);
2201 bool any_modified = True(image.flags & ImageFlagBits::GpuModified); 2223 bool any_modified = True(image.flags & ImageFlagBits::GpuModified);
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 3bfa92154..e9ec91265 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -56,7 +56,7 @@ struct ImageViewInOut {
56struct AsyncDecodeContext { 56struct AsyncDecodeContext {
57 ImageId image_id; 57 ImageId image_id;
58 Common::ScratchBuffer<u8> decoded_data; 58 Common::ScratchBuffer<u8> decoded_data;
59 std::vector<BufferImageCopy> copies; 59 boost::container::small_vector<BufferImageCopy, 16> copies;
60 std::mutex mutex; 60 std::mutex mutex;
61 std::atomic_bool complete; 61 std::atomic_bool complete;
62}; 62};
@@ -159,6 +159,18 @@ public:
159 /// Get the sampler from the compute descriptor table in the specified index 159 /// Get the sampler from the compute descriptor table in the specified index
160 Sampler* GetComputeSampler(u32 index); 160 Sampler* GetComputeSampler(u32 index);
161 161
162 /// Get the sampler id from the graphics descriptor table in the specified index
163 SamplerId GetGraphicsSamplerId(u32 index);
164
165 /// Get the sampler id from the compute descriptor table in the specified index
166 SamplerId GetComputeSamplerId(u32 index);
167
168 /// Return a constant reference to the given sampler id
169 [[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept;
170
171 /// Return a reference to the given sampler id
172 [[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept;
173
162 /// Refresh the state for graphics image view and sampler descriptors 174 /// Refresh the state for graphics image view and sampler descriptors
163 void SynchronizeGraphicsDescriptors(); 175 void SynchronizeGraphicsDescriptors();
164 176
@@ -166,9 +178,8 @@ public:
166 void SynchronizeComputeDescriptors(); 178 void SynchronizeComputeDescriptors();
167 179
168 /// Updates the Render Targets if they can be rescaled 180 /// Updates the Render Targets if they can be rescaled
169 /// @param is_clear True when the render targets are being used for clears
170 /// @retval True if the Render Targets have been rescaled. 181 /// @retval True if the Render Targets have been rescaled.
171 bool RescaleRenderTargets(bool is_clear); 182 bool RescaleRenderTargets();
172 183
173 /// Update bound render targets and upload memory if necessary 184 /// Update bound render targets and upload memory if necessary
174 /// @param is_clear True when the render targets are being used for clears 185 /// @param is_clear True when the render targets are being used for clears
@@ -324,14 +335,13 @@ private:
324 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); 335 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
325 336
326 /// Find or create an image view for the given color buffer index 337 /// Find or create an image view for the given color buffer index
327 [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear); 338 [[nodiscard]] ImageViewId FindColorBuffer(size_t index);
328 339
329 /// Find or create an image view for the depth buffer 340 /// Find or create an image view for the depth buffer
330 [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear); 341 [[nodiscard]] ImageViewId FindDepthBuffer();
331 342
332 /// Find or create a view for a render target with the given image parameters 343 /// Find or create a view for a render target with the given image parameters
333 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, 344 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr);
334 bool is_clear);
335 345
336 /// Iterates over all the images in a region calling func 346 /// Iterates over all the images in a region calling func
337 template <typename Func> 347 template <typename Func>
@@ -419,7 +429,7 @@ private:
419 429
420 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table; 430 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
421 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table; 431 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table;
422 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; 432 std::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
423 433
424 VAddr virtual_invalid_space{}; 434 VAddr virtual_invalid_space{};
425 435
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 95a5b47d8..f781cb7a0 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -329,13 +329,13 @@ template <u32 GOB_EXTENT>
329 329
330[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D( 330[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D(
331 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) { 331 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
332 const std::vector<u32> slice_offsets = CalculateSliceOffsets(new_info); 332 const auto slice_offsets = CalculateSliceOffsets(new_info);
333 const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr); 333 const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr);
334 const auto it = std::ranges::find(slice_offsets, diff); 334 const auto it = std::ranges::find(slice_offsets, diff);
335 if (it == slice_offsets.end()) { 335 if (it == slice_offsets.end()) {
336 return std::nullopt; 336 return std::nullopt;
337 } 337 }
338 const std::vector subresources = CalculateSliceSubresources(new_info); 338 const auto subresources = CalculateSliceSubresources(new_info);
339 const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)]; 339 const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)];
340 const ImageInfo& info = overlap.info; 340 const ImageInfo& info = overlap.info;
341 if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) { 341 if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) {
@@ -655,9 +655,9 @@ LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept {
655 return sizes; 655 return sizes;
656} 656}
657 657
658std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { 658boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info) {
659 ASSERT(info.type == ImageType::e3D); 659 ASSERT(info.type == ImageType::e3D);
660 std::vector<u32> offsets; 660 boost::container::small_vector<u32, 16> offsets;
661 offsets.reserve(NumSlices(info)); 661 offsets.reserve(NumSlices(info));
662 662
663 const LevelInfo level_info = MakeLevelInfo(info); 663 const LevelInfo level_info = MakeLevelInfo(info);
@@ -679,9 +679,10 @@ std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) {
679 return offsets; 679 return offsets;
680} 680}
681 681
682std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info) { 682boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources(
683 const ImageInfo& info) {
683 ASSERT(info.type == ImageType::e3D); 684 ASSERT(info.type == ImageType::e3D);
684 std::vector<SubresourceBase> subresources; 685 boost::container::small_vector<SubresourceBase, 16> subresources;
685 subresources.reserve(NumSlices(info)); 686 subresources.reserve(NumSlices(info));
686 for (s32 level = 0; level < info.resources.levels; ++level) { 687 for (s32 level = 0; level < info.resources.levels; ++level) {
687 const s32 depth = AdjustMipSize(info.size.depth, level); 688 const s32 depth = AdjustMipSize(info.size.depth, level);
@@ -723,8 +724,10 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept {
723 } 724 }
724} 725}
725 726
726std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, 727boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(const ImageInfo& dst,
727 SubresourceBase base, u32 up_scale, u32 down_shift) { 728 const ImageInfo& src,
729 SubresourceBase base,
730 u32 up_scale, u32 down_shift) {
728 ASSERT(dst.resources.levels >= src.resources.levels); 731 ASSERT(dst.resources.levels >= src.resources.levels);
729 732
730 const bool is_dst_3d = dst.type == ImageType::e3D; 733 const bool is_dst_3d = dst.type == ImageType::e3D;
@@ -733,7 +736,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
733 ASSERT(src.resources.levels == 1); 736 ASSERT(src.resources.levels == 1);
734 } 737 }
735 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D}; 738 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D};
736 std::vector<ImageCopy> copies; 739 boost::container::small_vector<ImageCopy, 16> copies;
737 copies.reserve(src.resources.levels); 740 copies.reserve(src.resources.levels);
738 for (s32 level = 0; level < src.resources.levels; ++level) { 741 for (s32 level = 0; level < src.resources.levels; ++level) {
739 ImageCopy& copy = copies.emplace_back(); 742 ImageCopy& copy = copies.emplace_back();
@@ -770,9 +773,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
770 return copies; 773 return copies;
771} 774}
772 775
773std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, u32 up_scale, 776boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(const ImageInfo& src,
774 u32 down_shift) { 777 u32 up_scale,
775 std::vector<ImageCopy> copies; 778 u32 down_shift) {
779 boost::container::small_vector<ImageCopy, 16> copies;
776 copies.reserve(src.resources.levels); 780 copies.reserve(src.resources.levels);
777 const bool is_3d = src.type == ImageType::e3D; 781 const bool is_3d = src.type == ImageType::e3D;
778 for (s32 level = 0; level < src.resources.levels; ++level) { 782 for (s32 level = 0; level < src.resources.levels; ++level) {
@@ -824,9 +828,11 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config
824 return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value(); 828 return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value();
825} 829}
826 830
827std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 831boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
828 const ImageInfo& info, std::span<const u8> input, 832 GPUVAddr gpu_addr,
829 std::span<u8> output) { 833 const ImageInfo& info,
834 std::span<const u8> input,
835 std::span<u8> output) {
830 const size_t guest_size_bytes = input.size_bytes(); 836 const size_t guest_size_bytes = input.size_bytes();
831 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 837 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
832 const Extent3D size = info.size; 838 const Extent3D size = info.size;
@@ -861,7 +867,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP
861 info.tile_width_spacing); 867 info.tile_width_spacing);
862 size_t guest_offset = 0; 868 size_t guest_offset = 0;
863 u32 host_offset = 0; 869 u32 host_offset = 0;
864 std::vector<BufferImageCopy> copies(num_levels); 870 boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
865 871
866 for (s32 level = 0; level < num_levels; ++level) { 872 for (s32 level = 0; level < num_levels; ++level) {
867 const Extent3D level_size = AdjustMipSize(size, level); 873 const Extent3D level_size = AdjustMipSize(size, level);
@@ -978,7 +984,7 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
978 } 984 }
979} 985}
980 986
981std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) { 987boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const ImageInfo& info) {
982 const Extent3D size = info.size; 988 const Extent3D size = info.size;
983 const u32 bytes_per_block = BytesPerBlock(info.format); 989 const u32 bytes_per_block = BytesPerBlock(info.format);
984 if (info.type == ImageType::Linear) { 990 if (info.type == ImageType::Linear) {
@@ -1006,7 +1012,7 @@ std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) {
1006 1012
1007 u32 host_offset = 0; 1013 u32 host_offset = 0;
1008 1014
1009 std::vector<BufferImageCopy> copies(num_levels); 1015 boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);
1010 for (s32 level = 0; level < num_levels; ++level) { 1016 for (s32 level = 0; level < num_levels; ++level) {
1011 const Extent3D level_size = AdjustMipSize(size, level); 1017 const Extent3D level_size = AdjustMipSize(size, level);
1012 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); 1018 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size);
@@ -1042,10 +1048,10 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) {
1042 return AdjustMipBlockSize(num_tiles, level_info.block, level); 1048 return AdjustMipBlockSize(num_tiles, level_info.block, level);
1043} 1049}
1044 1050
1045std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { 1051boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) {
1046 const Extent2D tile_size = DefaultBlockSize(info.format); 1052 const Extent2D tile_size = DefaultBlockSize(info.format);
1047 if (info.type == ImageType::Linear) { 1053 if (info.type == ImageType::Linear) {
1048 return std::vector{SwizzleParameters{ 1054 return {SwizzleParameters{
1049 .num_tiles = AdjustTileSize(info.size, tile_size), 1055 .num_tiles = AdjustTileSize(info.size, tile_size),
1050 .block = {}, 1056 .block = {},
1051 .buffer_offset = 0, 1057 .buffer_offset = 0,
@@ -1057,7 +1063,7 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) {
1057 const s32 num_levels = info.resources.levels; 1063 const s32 num_levels = info.resources.levels;
1058 1064
1059 u32 guest_offset = 0; 1065 u32 guest_offset = 0;
1060 std::vector<SwizzleParameters> params(num_levels); 1066 boost::container::small_vector<SwizzleParameters, 16> params(num_levels);
1061 for (s32 level = 0; level < num_levels; ++level) { 1067 for (s32 level = 0; level < num_levels; ++level) {
1062 const Extent3D level_size = AdjustMipSize(size, level); 1068 const Extent3D level_size = AdjustMipSize(size, level);
1063 const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); 1069 const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 84aa6880d..ab45a43c4 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -5,6 +5,7 @@
5 5
6#include <optional> 6#include <optional>
7#include <span> 7#include <span>
8#include <boost/container/small_vector.hpp>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/scratch_buffer.h" 11#include "common/scratch_buffer.h"
@@ -40,9 +41,10 @@ struct OverlapResult {
40 41
41[[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept; 42[[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept;
42 43
43[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); 44[[nodiscard]] boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info);
44 45
45[[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info); 46[[nodiscard]] boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources(
47 const ImageInfo& info);
46 48
47[[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level); 49[[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level);
48 50
@@ -51,21 +53,18 @@ struct OverlapResult {
51 53
52[[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept; 54[[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept;
53 55
54[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, 56[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(
55 const ImageInfo& src, 57 const ImageInfo& dst, const ImageInfo& src, SubresourceBase base, u32 up_scale = 1,
56 SubresourceBase base, u32 up_scale = 1, 58 u32 down_shift = 0);
57 u32 down_shift = 0);
58 59
59[[nodiscard]] std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, 60[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(
60 u32 up_scale = 1, 61 const ImageInfo& src, u32 up_scale = 1, u32 down_shift = 0);
61 u32 down_shift = 0);
62 62
63[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); 63[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
64 64
65[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, 65[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(
66 GPUVAddr gpu_addr, const ImageInfo& info, 66 Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
67 std::span<const u8> input, 67 std::span<const u8> input, std::span<u8> output);
68 std::span<u8> output);
69 68
70[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 69[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
71 const ImageBase& image, std::span<u8> output); 70 const ImageBase& image, std::span<u8> output);
@@ -73,13 +72,15 @@ struct OverlapResult {
73void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, 72void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
74 std::span<BufferImageCopy> copies); 73 std::span<BufferImageCopy> copies);
75 74
76[[nodiscard]] std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info); 75[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(
76 const ImageInfo& info);
77 77
78[[nodiscard]] Extent3D MipSize(Extent3D size, u32 level); 78[[nodiscard]] Extent3D MipSize(Extent3D size, u32 level);
79 79
80[[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level); 80[[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level);
81 81
82[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); 82[[nodiscard]] boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(
83 const ImageInfo& info);
83 84
84void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 85void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
85 std::span<const BufferImageCopy> copies, std::span<const u8> memory, 86 std::span<const BufferImageCopy> copies, std::span<const u8> memory,
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index 4a80a59f9..d8b88d9bc 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -62,7 +62,12 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept {
62} 62}
63 63
64float TSCEntry::MaxAnisotropy() const noexcept { 64float TSCEntry::MaxAnisotropy() const noexcept {
65 if (max_anisotropy == 0 && mipmap_filter != TextureMipmapFilter::Linear) { 65 const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
66 const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
67 const bool is_bilinear_filter = min_filter == TextureFilter::Linear &&
68 reduction_filter == SamplerReduction::WeightedAverage;
69 if (max_anisotropy == 0 && (!is_suitable_mipmap_filter || !has_regular_lods ||
70 !is_bilinear_filter || depth_compare_enabled)) {
66 return 1.0f; 71 return 1.0f;
67 } 72 }
68 const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); 73 const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();
diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp
index 155599316..1f353d2df 100644
--- a/src/video_core/transform_feedback.cpp
+++ b/src/video_core/transform_feedback.cpp
@@ -13,7 +13,7 @@
13 13
14namespace VideoCommon { 14namespace VideoCommon {
15 15
16std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( 16std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings(
17 const TransformFeedbackState& state) { 17 const TransformFeedbackState& state) {
18 static constexpr std::array VECTORS{ 18 static constexpr std::array VECTORS{
19 28U, // gl_Position 19 28U, // gl_Position
@@ -62,7 +62,8 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
62 216U, // gl_TexCoord[6] 62 216U, // gl_TexCoord[6]
63 220U, // gl_TexCoord[7] 63 220U, // gl_TexCoord[7]
64 }; 64 };
65 std::vector<Shader::TransformFeedbackVarying> xfb(256); 65 std::array<Shader::TransformFeedbackVarying, 256> xfb{};
66 u32 count{0};
66 for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) { 67 for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) {
67 const auto& locations = state.varyings[buffer]; 68 const auto& locations = state.varyings[buffer];
68 const auto& layout = state.layouts[buffer]; 69 const auto& layout = state.layouts[buffer];
@@ -103,11 +104,12 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
103 } 104 }
104 } 105 }
105 xfb[attribute] = varying; 106 xfb[attribute] = varying;
107 count = std::max(count, attribute);
106 highest = std::max(highest, (base_offset + varying.components) * 4); 108 highest = std::max(highest, (base_offset + varying.components) * 4);
107 } 109 }
108 UNIMPLEMENTED_IF(highest != layout.stride); 110 UNIMPLEMENTED_IF(highest != layout.stride);
109 } 111 }
110 return xfb; 112 return {xfb, count + 1};
111} 113}
112 114
113} // namespace VideoCommon 115} // namespace VideoCommon
diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h
index d13eb16c3..401b1352a 100644
--- a/src/video_core/transform_feedback.h
+++ b/src/video_core/transform_feedback.h
@@ -24,7 +24,7 @@ struct TransformFeedbackState {
24 varyings; 24 varyings;
25}; 25};
26 26
27std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( 27std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings(
28 const TransformFeedbackState& state); 28 const TransformFeedbackState& state);
29 29
30} // namespace VideoCommon 30} // namespace VideoCommon
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 0158b6b0d..b11abe311 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -316,6 +316,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
316std::vector<const char*> ExtensionListForVulkan( 316std::vector<const char*> ExtensionListForVulkan(
317 const std::set<std::string, std::less<>>& extensions) { 317 const std::set<std::string, std::less<>>& extensions) {
318 std::vector<const char*> output; 318 std::vector<const char*> output;
319 output.reserve(extensions.size());
319 for (const auto& extension : extensions) { 320 for (const auto& extension : extensions) {
320 output.push_back(extension.c_str()); 321 output.push_back(extension.c_str());
321 } 322 }
@@ -344,6 +345,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
344 const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; 345 const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
345 const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP; 346 const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP;
346 const bool is_s8gen2 = device_id == 0x43050a01; 347 const bool is_s8gen2 = device_id == 0x43050a01;
348 const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
347 349
348 if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) { 350 if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) {
349 LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway"); 351 LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
@@ -386,10 +388,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
386 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, 388 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
387 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal); 389 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
388 390
391 supports_conditional_barriers = !(is_intel_anv || is_intel_windows);
392
389 CollectPhysicalMemoryInfo(); 393 CollectPhysicalMemoryInfo();
390 CollectToolingInfo(); 394 CollectToolingInfo();
391 395
392#ifdef ANDROID
393 if (is_qualcomm || is_turnip) { 396 if (is_qualcomm || is_turnip) {
394 LOG_WARNING(Render_Vulkan, 397 LOG_WARNING(Render_Vulkan,
395 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); 398 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
@@ -409,7 +412,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
409 extensions.push_descriptor = false; 412 extensions.push_descriptor = false;
410 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); 413 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
411 414
412#ifdef ARCHITECTURE_arm64 415#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
413 // Patch the driver to enable BCn textures. 416 // Patch the driver to enable BCn textures.
414 const auto major = (properties.properties.driverVersion >> 24) << 2; 417 const auto major = (properties.properties.driverVersion >> 24) << 2;
415 const auto minor = (properties.properties.driverVersion >> 12) & 0xFFFU; 418 const auto minor = (properties.properties.driverVersion >> 12) & 0xFFFU;
@@ -429,18 +432,23 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
429 } else { 432 } else {
430 LOG_WARNING(Render_Vulkan, "Adreno driver can't be patched to enable BCn textures"); 433 LOG_WARNING(Render_Vulkan, "Adreno driver can't be patched to enable BCn textures");
431 } 434 }
432#endif // ARCHITECTURE_arm64 435#endif
433 } 436 }
434 437
435 const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
436 if (is_arm) { 438 if (is_arm) {
437 must_emulate_scaled_formats = true; 439 must_emulate_scaled_formats = true;
438 440
439 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); 441 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
440 extensions.extended_dynamic_state = false; 442 extensions.extended_dynamic_state = false;
441 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 443 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
444
445 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
446 features.extended_dynamic_state2.extendedDynamicState2 = false;
447 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
448 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
449 extensions.extended_dynamic_state2 = false;
450 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
442 } 451 }
443#endif // ANDROID
444 452
445 if (is_nvidia) { 453 if (is_nvidia) {
446 const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff; 454 const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;
@@ -555,6 +563,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
555 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); 563 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
556 cant_blit_msaa = true; 564 cant_blit_msaa = true;
557 } 565 }
566 has_broken_compute =
567 CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) &&
568 !Settings::values.enable_compute_pipelines.GetValue();
558 if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { 569 if (is_intel_anv || (is_qualcomm && !is_s8gen2)) {
559 LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); 570 LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
560 must_emulate_bgr565 = true; 571 must_emulate_bgr565 = true;
@@ -776,9 +787,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
776 787
777 FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); 788 FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
778 FOR_EACH_VK_EXTENSION(EXTENSION); 789 FOR_EACH_VK_EXTENSION(EXTENSION);
779#ifdef _WIN32
780 FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
781#endif
782 790
783#undef FEATURE_EXTENSION 791#undef FEATURE_EXTENSION
784#undef EXTENSION 792#undef EXTENSION
@@ -797,11 +805,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
797 805
798 FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION); 806 FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION);
799 FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION); 807 FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION);
800#ifdef _WIN32
801 FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(CHECK_EXTENSION);
802#else
803 FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(CHECK_EXTENSION);
804#endif
805 808
806 if (requires_swapchain) { 809 if (requires_swapchain) {
807 CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 810 CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index d62a103a1..0b634a876 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/logging/log.h"
13#include "common/settings.h" 14#include "common/settings.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 15#include "video_core/vulkan_common/vulkan_wrapper.h"
15 16
@@ -68,7 +69,6 @@
68 EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ 69 EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
69 EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ 70 EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
70 EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ 71 EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
71 EXTENSION(KHR, EXTERNAL_MEMORY_FD, external_memory_fd) \
72 EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ 72 EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
73 EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \ 73 EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \
74 EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \ 74 EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \
@@ -80,9 +80,6 @@
80 EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ 80 EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
81 EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) 81 EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle)
82 82
83#define FOR_EACH_VK_EXTENSION_WIN32(EXTENSION) \
84 EXTENSION(KHR, EXTERNAL_MEMORY_WIN32, external_memory_win32)
85
86// Define extensions which must be supported. 83// Define extensions which must be supported.
87#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ 84#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
88 EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \ 85 EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
@@ -90,12 +87,6 @@
90 EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \ 87 EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \
91 EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) 88 EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)
92 89
93#define FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(EXTENSION_NAME) \
94 EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)
95
96#define FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(EXTENSION_NAME) \
97 EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)
98
99// Define extensions where the absence of the extension may result in a degraded experience. 90// Define extensions where the absence of the extension may result in a degraded experience.
100#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \ 91#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \
101 EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \ 92 EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \
@@ -300,6 +291,11 @@ public:
300 return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; 291 return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
301 } 292 }
302 293
294 /// Returns true if the device suppors float64 natively.
295 bool IsFloat64Supported() const {
296 return features.features.shaderFloat64;
297 }
298
303 /// Returns true if the device supports float16 natively. 299 /// Returns true if the device supports float16 natively.
304 bool IsFloat16Supported() const { 300 bool IsFloat16Supported() const {
305 return features.shader_float16_int8.shaderFloat16; 301 return features.shader_float16_int8.shaderFloat16;
@@ -523,6 +519,11 @@ public:
523 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue(); 519 return has_renderdoc || has_nsight_graphics || Settings::values.renderer_debug.GetValue();
524 } 520 }
525 521
522 /// @returns True if compute pipelines can cause crashing.
523 bool HasBrokenCompute() const {
524 return has_broken_compute;
525 }
526
526 /// Returns true when the device does not properly support cube compatibility. 527 /// Returns true when the device does not properly support cube compatibility.
527 bool HasBrokenCubeImageCompability() const { 528 bool HasBrokenCubeImageCompability() const {
528 return has_broken_cube_compatibility; 529 return has_broken_cube_compatibility;
@@ -580,6 +581,26 @@ public:
580 return properties.properties.limits.maxVertexInputBindings; 581 return properties.properties.limits.maxVertexInputBindings;
581 } 582 }
582 583
584 bool SupportsConditionalBarriers() const {
585 return supports_conditional_barriers;
586 }
587
588 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
589 u32 driver_version) {
590 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
591 const u32 major = VK_API_VERSION_MAJOR(driver_version);
592 const u32 minor = VK_API_VERSION_MINOR(driver_version);
593 const u32 patch = VK_API_VERSION_PATCH(driver_version);
594 if (major == 0 && minor == 405 && patch < 286) {
595 LOG_WARNING(
596 Render_Vulkan,
597 "Intel proprietary drivers 0.405.0 until 0.405.286 have broken compute");
598 return true;
599 }
600 }
601 return false;
602 }
603
583private: 604private:
584 /// Checks if the physical device is suitable and configures the object state 605 /// Checks if the physical device is suitable and configures the object state
585 /// with all necessary info about its properties. 606 /// with all necessary info about its properties.
@@ -627,7 +648,6 @@ private:
627 FOR_EACH_VK_FEATURE_1_3(FEATURE); 648 FOR_EACH_VK_FEATURE_1_3(FEATURE);
628 FOR_EACH_VK_FEATURE_EXT(FEATURE); 649 FOR_EACH_VK_FEATURE_EXT(FEATURE);
629 FOR_EACH_VK_EXTENSION(EXTENSION); 650 FOR_EACH_VK_EXTENSION(EXTENSION);
630 FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
631 651
632#undef EXTENSION 652#undef EXTENSION
633#undef FEATURE 653#undef FEATURE
@@ -674,6 +694,7 @@ private:
674 bool is_integrated{}; ///< Is GPU an iGPU. 694 bool is_integrated{}; ///< Is GPU an iGPU.
675 bool is_virtual{}; ///< Is GPU a virtual GPU. 695 bool is_virtual{}; ///< Is GPU a virtual GPU.
676 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. 696 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
697 bool has_broken_compute{}; ///< Compute shaders can cause crashes
677 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit 698 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
678 bool has_renderdoc{}; ///< Has RenderDoc attached 699 bool has_renderdoc{}; ///< Has RenderDoc attached
679 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 700 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
@@ -683,6 +704,7 @@ private:
683 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. 704 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
684 bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3. 705 bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
685 bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3. 706 bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
707 bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
686 u64 device_access_memory{}; ///< Total size of device local memory in bytes. 708 u64 device_access_memory{}; ///< Total size of device local memory in bytes.
687 u32 sets_per_pool{}; ///< Sets per Description Pool 709 u32 sets_per_pool{}; ///< Sets per Description Pool
688 710
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 8676bfd8a..fe98e3605 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -213,6 +213,8 @@ add_executable(yuzu
213 util/url_request_interceptor.h 213 util/url_request_interceptor.h
214 util/util.cpp 214 util/util.cpp
215 util/util.h 215 util/util.h
216 vk_device_info.cpp
217 vk_device_info.h
216 compatdb.cpp 218 compatdb.cpp
217 compatdb.h 219 compatdb.h
218 yuzu.qrc 220 yuzu.qrc
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index cc6b6a25a..bdd1497b5 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -105,14 +105,12 @@ void EmuThread::run() {
105 std::unique_lock lk{m_should_run_mutex}; 105 std::unique_lock lk{m_should_run_mutex};
106 if (m_should_run) { 106 if (m_should_run) {
107 m_system.Run(); 107 m_system.Run();
108 m_is_running.store(true); 108 m_stopped.Reset();
109 m_is_running.notify_all();
110 109
111 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); 110 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
112 } else { 111 } else {
113 m_system.Pause(); 112 m_system.Pause();
114 m_is_running.store(false); 113 m_stopped.Set();
115 m_is_running.notify_all();
116 114
117 EmulationPaused(lk); 115 EmulationPaused(lk);
118 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); 116 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index b7b9d4141..87b23df12 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -3,7 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <atomic>
7#include <condition_variable> 6#include <condition_variable>
8#include <cstddef> 7#include <cstddef>
9#include <memory> 8#include <memory>
@@ -88,7 +87,7 @@ public:
88 87
89 // Wait until paused, if pausing. 88 // Wait until paused, if pausing.
90 if (!should_run) { 89 if (!should_run) {
91 m_is_running.wait(true); 90 m_stopped.Wait();
92 } 91 }
93 } 92 }
94 93
@@ -97,7 +96,7 @@ public:
97 * @return True if the emulation thread is running, otherwise false 96 * @return True if the emulation thread is running, otherwise false
98 */ 97 */
99 bool IsRunning() const { 98 bool IsRunning() const {
100 return m_is_running.load() || m_should_run; 99 return m_should_run;
101 } 100 }
102 101
103 /** 102 /**
@@ -118,7 +117,7 @@ private:
118 std::stop_source m_stop_source; 117 std::stop_source m_stop_source;
119 std::mutex m_should_run_mutex; 118 std::mutex m_should_run_mutex;
120 std::condition_variable_any m_should_run_cv; 119 std::condition_variable_any m_should_run_cv;
121 std::atomic<bool> m_is_running{false}; 120 Common::Event m_stopped;
122 bool m_should_run{true}; 121 bool m_should_run{true};
123 122
124signals: 123signals:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index b58a1e9d1..87ab88cfa 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -774,6 +774,7 @@ void Config::ReadRendererValues() {
774 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 774 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
775 ReadGlobalSetting(Settings::values.enable_compute_pipelines); 775 ReadGlobalSetting(Settings::values.enable_compute_pipelines);
776 ReadGlobalSetting(Settings::values.use_video_framerate); 776 ReadGlobalSetting(Settings::values.use_video_framerate);
777 ReadGlobalSetting(Settings::values.barrier_feedback_loops);
777 ReadGlobalSetting(Settings::values.bg_red); 778 ReadGlobalSetting(Settings::values.bg_red);
778 ReadGlobalSetting(Settings::values.bg_green); 779 ReadGlobalSetting(Settings::values.bg_green);
779 ReadGlobalSetting(Settings::values.bg_blue); 780 ReadGlobalSetting(Settings::values.bg_blue);
@@ -1444,6 +1445,7 @@ void Config::SaveRendererValues() {
1444 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 1445 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1445 WriteGlobalSetting(Settings::values.enable_compute_pipelines); 1446 WriteGlobalSetting(Settings::values.enable_compute_pipelines);
1446 WriteGlobalSetting(Settings::values.use_video_framerate); 1447 WriteGlobalSetting(Settings::values.use_video_framerate);
1448 WriteGlobalSetting(Settings::values.barrier_feedback_loops);
1447 WriteGlobalSetting(Settings::values.bg_red); 1449 WriteGlobalSetting(Settings::values.bg_red);
1448 WriteGlobalSetting(Settings::values.bg_green); 1450 WriteGlobalSetting(Settings::values.bg_green);
1449 WriteGlobalSetting(Settings::values.bg_blue); 1451 WriteGlobalSetting(Settings::values.bg_blue);
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 8e76a819a..bdf83ebfe 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -6,6 +6,7 @@
6#include "common/settings.h" 6#include "common/settings.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "ui_configure.h" 8#include "ui_configure.h"
9#include "vk_device_info.h"
9#include "yuzu/configuration/config.h" 10#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_audio.h" 11#include "yuzu/configuration/configure_audio.h"
11#include "yuzu/configuration/configure_cpu.h" 12#include "yuzu/configuration/configure_cpu.h"
@@ -28,6 +29,7 @@
28 29
29ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, 30ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
30 InputCommon::InputSubsystem* input_subsystem, 31 InputCommon::InputSubsystem* input_subsystem,
32 std::vector<VkDeviceInfo::Record>& vk_device_records,
31 Core::System& system_, bool enable_web_config) 33 Core::System& system_, bool enable_web_config)
32 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, 34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
33 registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, 35 registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_,
@@ -38,7 +40,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
38 general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, 40 general_tab{std::make_unique<ConfigureGeneral>(system_, this)},
39 graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, 41 graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)},
40 graphics_tab{std::make_unique<ConfigureGraphics>( 42 graphics_tab{std::make_unique<ConfigureGraphics>(
41 system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this)}, 43 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
44 this)},
42 hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, 45 hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
43 input_tab{std::make_unique<ConfigureInput>(system_, this)}, 46 input_tab{std::make_unique<ConfigureInput>(system_, this)},
44 network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, 47 network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index a086a07c4..2a08b7fee 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -4,7 +4,9 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <vector>
7#include <QDialog> 8#include <QDialog>
9#include "yuzu/vk_device_info.h"
8 10
9namespace Core { 11namespace Core {
10class System; 12class System;
@@ -40,8 +42,9 @@ class ConfigureDialog : public QDialog {
40 42
41public: 43public:
42 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, 44 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
43 InputCommon::InputSubsystem* input_subsystem, Core::System& system_, 45 InputCommon::InputSubsystem* input_subsystem,
44 bool enable_web_config = true); 46 std::vector<VkDeviceInfo::Record>& vk_device_records,
47 Core::System& system_, bool enable_web_config = true);
45 ~ConfigureDialog() override; 48 ~ConfigureDialog() override;
46 49
47 void ApplyConfiguration(); 50 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 431585216..a4965524a 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -1,10 +1,6 @@
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 this early to include Vulkan headers how we want to
5#include "video_core/vulkan_common/vulkan_device.h"
6#include "video_core/vulkan_common/vulkan_wrapper.h"
7
8#include <algorithm> 4#include <algorithm>
9#include <functional> 5#include <functional>
10#include <iosfwd> 6#include <iosfwd>
@@ -34,13 +30,11 @@
34#include "common/settings.h" 30#include "common/settings.h"
35#include "core/core.h" 31#include "core/core.h"
36#include "ui_configure_graphics.h" 32#include "ui_configure_graphics.h"
37#include "video_core/vulkan_common/vulkan_instance.h"
38#include "video_core/vulkan_common/vulkan_library.h"
39#include "video_core/vulkan_common/vulkan_surface.h"
40#include "yuzu/configuration/configuration_shared.h" 33#include "yuzu/configuration/configuration_shared.h"
41#include "yuzu/configuration/configure_graphics.h" 34#include "yuzu/configuration/configure_graphics.h"
42#include "yuzu/qt_common.h" 35#include "yuzu/qt_common.h"
43#include "yuzu/uisettings.h" 36#include "yuzu/uisettings.h"
37#include "yuzu/vk_device_info.h"
44 38
45static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, 39static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
46 VK_PRESENT_MODE_FIFO_KHR}; 40 VK_PRESENT_MODE_FIFO_KHR};
@@ -77,9 +71,10 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode)
77} 71}
78 72
79ConfigureGraphics::ConfigureGraphics(const Core::System& system_, 73ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
74 std::vector<VkDeviceInfo::Record>& records_,
80 const std::function<void()>& expose_compute_option_, 75 const std::function<void()>& expose_compute_option_,
81 QWidget* parent) 76 QWidget* parent)
82 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, 77 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, records{records_},
83 expose_compute_option{expose_compute_option_}, system{system_} { 78 expose_compute_option{expose_compute_option_}, system{system_} {
84 vulkan_device = Settings::values.vulkan_device.GetValue(); 79 vulkan_device = Settings::values.vulkan_device.GetValue();
85 RetrieveVulkanDevices(); 80 RetrieveVulkanDevices();
@@ -504,47 +499,19 @@ void ConfigureGraphics::UpdateAPILayout() {
504 } 499 }
505} 500}
506 501
507void ConfigureGraphics::RetrieveVulkanDevices() try { 502void ConfigureGraphics::RetrieveVulkanDevices() {
508 if (UISettings::values.has_broken_vulkan) {
509 return;
510 }
511
512 using namespace Vulkan;
513
514 auto* window = this->window()->windowHandle();
515 auto wsi = QtCommon::GetWindowSystemInfo(window);
516
517 vk::InstanceDispatch dld;
518 const auto library = OpenLibrary();
519 const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
520 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
521 vk::SurfaceKHR surface = CreateSurface(instance, wsi);
522
523 vulkan_devices.clear(); 503 vulkan_devices.clear();
524 vulkan_devices.reserve(physical_devices.size()); 504 vulkan_devices.reserve(records.size());
525 device_present_modes.clear(); 505 device_present_modes.clear();
526 device_present_modes.reserve(physical_devices.size()); 506 device_present_modes.reserve(records.size());
527 for (const VkPhysicalDevice device : physical_devices) { 507 for (const auto& record : records) {
528 const auto physical_device = vk::PhysicalDevice(device, dld); 508 vulkan_devices.push_back(QString::fromStdString(record.name));
529 const std::string name = physical_device.GetProperties().deviceName; 509 device_present_modes.push_back(record.vsync_support);
530 const std::vector<VkPresentModeKHR> present_modes = 510
531 physical_device.GetSurfacePresentModesKHR(*surface); 511 if (record.has_broken_compute) {
532 vulkan_devices.push_back(QString::fromStdString(name));
533 device_present_modes.push_back(present_modes);
534
535 VkPhysicalDeviceDriverProperties driver_properties{};
536 driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
537 driver_properties.pNext = nullptr;
538 VkPhysicalDeviceProperties2 properties{};
539 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
540 properties.pNext = &driver_properties;
541 dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);
542 if (driver_properties.driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
543 expose_compute_option(); 512 expose_compute_option();
544 } 513 }
545 } 514 }
546} catch (const Vulkan::vk::Exception& exception) {
547 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
548} 515}
549 516
550Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 517Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 364b1cac2..be9310b74 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -12,6 +12,7 @@
12#include <qobjectdefs.h> 12#include <qobjectdefs.h>
13#include <vulkan/vulkan_core.h> 13#include <vulkan/vulkan_core.h>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "vk_device_info.h"
15 16
16class QEvent; 17class QEvent;
17class QObject; 18class QObject;
@@ -39,6 +40,7 @@ class ConfigureGraphics : public QWidget {
39 40
40public: 41public:
41 explicit ConfigureGraphics(const Core::System& system_, 42 explicit ConfigureGraphics(const Core::System& system_,
43 std::vector<VkDeviceInfo::Record>& records,
42 const std::function<void()>& expose_compute_option_, 44 const std::function<void()>& expose_compute_option_,
43 QWidget* parent = nullptr); 45 QWidget* parent = nullptr);
44 ~ConfigureGraphics() override; 46 ~ConfigureGraphics() override;
@@ -77,6 +79,7 @@ private:
77 ConfigurationShared::CheckState use_disk_shader_cache; 79 ConfigurationShared::CheckState use_disk_shader_cache;
78 ConfigurationShared::CheckState use_asynchronous_gpu_emulation; 80 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
79 81
82 std::vector<VkDeviceInfo::Record>& records;
80 std::vector<QString> vulkan_devices; 83 std::vector<QString> vulkan_devices;
81 std::vector<std::vector<VkPresentModeKHR>> device_present_modes; 84 std::vector<std::vector<VkPresentModeKHR>> device_present_modes;
82 std::vector<VkPresentModeKHR> 85 std::vector<VkPresentModeKHR>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 0463ac8b9..c0a044767 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -43,6 +43,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
43 ui->enable_compute_pipelines_checkbox->setChecked( 43 ui->enable_compute_pipelines_checkbox->setChecked(
44 Settings::values.enable_compute_pipelines.GetValue()); 44 Settings::values.enable_compute_pipelines.GetValue());
45 ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); 45 ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue());
46 ui->barrier_feedback_loops_checkbox->setChecked(
47 Settings::values.barrier_feedback_loops.GetValue());
46 48
47 if (Settings::IsConfiguringGlobal()) { 49 if (Settings::IsConfiguringGlobal()) {
48 ui->gpu_accuracy->setCurrentIndex( 50 ui->gpu_accuracy->setCurrentIndex(
@@ -94,6 +96,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
94 enable_compute_pipelines); 96 enable_compute_pipelines);
95 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate, 97 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate,
96 ui->use_video_framerate_checkbox, use_video_framerate); 98 ui->use_video_framerate_checkbox, use_video_framerate);
99 ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops,
100 ui->barrier_feedback_loops_checkbox,
101 barrier_feedback_loops);
97} 102}
98 103
99void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 104void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -130,6 +135,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
130 Settings::values.enable_compute_pipelines.UsingGlobal()); 135 Settings::values.enable_compute_pipelines.UsingGlobal());
131 ui->use_video_framerate_checkbox->setEnabled( 136 ui->use_video_framerate_checkbox->setEnabled(
132 Settings::values.use_video_framerate.UsingGlobal()); 137 Settings::values.use_video_framerate.UsingGlobal());
138 ui->barrier_feedback_loops_checkbox->setEnabled(
139 Settings::values.barrier_feedback_loops.UsingGlobal());
133 140
134 return; 141 return;
135 } 142 }
@@ -157,6 +164,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
157 ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox, 164 ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox,
158 Settings::values.use_video_framerate, 165 Settings::values.use_video_framerate,
159 use_video_framerate); 166 use_video_framerate);
167 ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox,
168 Settings::values.barrier_feedback_loops,
169 barrier_feedback_loops);
160 ConfigurationShared::SetColoredComboBox( 170 ConfigurationShared::SetColoredComboBox(
161 ui->gpu_accuracy, ui->label_gpu_accuracy, 171 ui->gpu_accuracy, ui->label_gpu_accuracy,
162 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 172 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index a4dc8ceb0..369a7c83e 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -48,6 +48,7 @@ private:
48 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; 48 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
49 ConfigurationShared::CheckState enable_compute_pipelines; 49 ConfigurationShared::CheckState enable_compute_pipelines;
50 ConfigurationShared::CheckState use_video_framerate; 50 ConfigurationShared::CheckState use_video_framerate;
51 ConfigurationShared::CheckState barrier_feedback_loops;
51 52
52 const Core::System& system; 53 const Core::System& system;
53}; 54};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index e7f0ef6be..d527a6f38 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -202,6 +202,16 @@ Compute pipelines are always enabled on all other drivers.</string>
202 </widget> 202 </widget>
203 </item> 203 </item>
204 <item> 204 <item>
205 <widget class="QCheckBox" name="barrier_feedback_loops_checkbox">
206 <property name="toolTip">
207 <string>Improves rendering of transparency effects in specific games.</string>
208 </property>
209 <property name="text">
210 <string>Barrier feedback loops</string>
211 </property>
212 </widget>
213 </item>
214 <item>
205 <widget class="QWidget" name="af_layout" native="true"> 215 <widget class="QWidget" name="af_layout" native="true">
206 <layout class="QHBoxLayout" name="horizontalLayout_1"> 216 <layout class="QHBoxLayout" name="horizontalLayout_1">
207 <property name="leftMargin"> 217 <property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 7ac162586..eb96e6068 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <string> 7#include <string>
8#include <utility> 8#include <utility>
9#include <vector>
9 10
10#include <fmt/format.h> 11#include <fmt/format.h>
11 12
@@ -34,8 +35,10 @@
34#include "yuzu/configuration/configure_system.h" 35#include "yuzu/configuration/configure_system.h"
35#include "yuzu/uisettings.h" 36#include "yuzu/uisettings.h"
36#include "yuzu/util/util.h" 37#include "yuzu/util/util.h"
38#include "yuzu/vk_device_info.h"
37 39
38ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, 40ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
41 std::vector<VkDeviceInfo::Record>& vk_device_records,
39 Core::System& system_) 42 Core::System& system_)
40 : QDialog(parent), 43 : QDialog(parent),
41 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} { 44 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} {
@@ -50,7 +53,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
50 general_tab = std::make_unique<ConfigureGeneral>(system_, this); 53 general_tab = std::make_unique<ConfigureGeneral>(system_, this);
51 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); 54 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this);
52 graphics_tab = std::make_unique<ConfigureGraphics>( 55 graphics_tab = std::make_unique<ConfigureGraphics>(
53 system_, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); 56 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this);
54 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); 57 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
55 system_tab = std::make_unique<ConfigureSystem>(system_, this); 58 system_tab = std::make_unique<ConfigureSystem>(system_, this);
56 59
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index 85752f1fa..7ec1ded06 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -5,11 +5,13 @@
5 5
6#include <memory> 6#include <memory>
7#include <string> 7#include <string>
8#include <vector>
8 9
9#include <QDialog> 10#include <QDialog>
10#include <QList> 11#include <QList>
11 12
12#include "core/file_sys/vfs_types.h" 13#include "core/file_sys/vfs_types.h"
14#include "vk_device_info.h"
13#include "yuzu/configuration/config.h" 15#include "yuzu/configuration/config.h"
14 16
15namespace Core { 17namespace Core {
@@ -45,6 +47,7 @@ class ConfigurePerGame : public QDialog {
45public: 47public:
46 // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263 48 // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
47 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, 49 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
50 std::vector<VkDeviceInfo::Record>& vk_device_records,
48 Core::System& system_); 51 Core::System& system_);
49 ~ConfigurePerGame() override; 52 ~ConfigurePerGame() override;
50 53
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
index 514dff372..38ecccc3d 100644
--- a/src/yuzu/configuration/configure_ringcon.ui
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -23,7 +23,7 @@
23 </size> 23 </size>
24 </property> 24 </property>
25 <property name="text"> 25 <property name="text">
26 <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string> 26 <string>To use Ring-Con, configure player 1 as right Joy-Con (both physical and emulated), and player 2 as left Joy-Con (left physical and dual emulated) before starting the game.</string>
27 </property> 27 </property>
28 <property name="wordWrap"> 28 <property name="wordWrap">
29 <bool>true</bool> 29 <bool>true</bool>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 286ccc5cd..f1ae312c6 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -144,8 +144,7 @@ void ConfigureSystem::ApplyConfiguration() {
144 if (ui->custom_rtc_checkbox->isChecked()) { 144 if (ui->custom_rtc_checkbox->isChecked()) {
145 Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch(); 145 Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch();
146 if (system.IsPoweredOn()) { 146 if (system.IsPoweredOn()) {
147 const s64 posix_time{*Settings::values.custom_rtc + 147 const s64 posix_time{*Settings::values.custom_rtc};
148 Service::Time::TimeManager::GetExternalTimeZoneOffset()};
149 system.GetTimeManager().UpdateLocalSystemClockTime(posix_time); 148 system.GetTimeManager().UpdateLocalSystemClockTime(posix_time);
150 } 149 }
151 } else { 150 } else {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9d06b21b6..2133f7343 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -147,6 +147,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
147#include "yuzu/startup_checks.h" 147#include "yuzu/startup_checks.h"
148#include "yuzu/uisettings.h" 148#include "yuzu/uisettings.h"
149#include "yuzu/util/clickable_label.h" 149#include "yuzu/util/clickable_label.h"
150#include "yuzu/vk_device_info.h"
150 151
151#ifdef YUZU_DBGHELP 152#ifdef YUZU_DBGHELP
152#include "yuzu/mini_dump.h" 153#include "yuzu/mini_dump.h"
@@ -440,10 +441,20 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
440 441
441 renderer_status_button->setDisabled(true); 442 renderer_status_button->setDisabled(true);
442 renderer_status_button->setChecked(false); 443 renderer_status_button->setChecked(false);
444 } else {
445 VkDeviceInfo::PopulateRecords(vk_device_records, this->window()->windowHandle());
443 } 446 }
444 447
445#if defined(HAVE_SDL2) && !defined(_WIN32) 448#if defined(HAVE_SDL2) && !defined(_WIN32)
446 SDL_InitSubSystem(SDL_INIT_VIDEO); 449 SDL_InitSubSystem(SDL_INIT_VIDEO);
450
451 // Set a screensaver inhibition reason string. Currently passed to DBus by SDL and visible to
452 // the user through their desktop environment.
453 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
454 //: computer from sleeping
455 QByteArray wakelock_reason = tr("Running a game").toLatin1();
456 SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data());
457
447 // SDL disables the screen saver by default, and setting the hint 458 // SDL disables the screen saver by default, and setting the hint
448 // SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver 459 // SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver
449 // for now. 460 // for now.
@@ -1620,45 +1631,6 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
1620} 1631}
1621 1632
1622#ifdef __unix__ 1633#ifdef __unix__
1623static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
1624 if (!QDBusConnection::sessionBus().isConnected()) {
1625 return {};
1626 }
1627 // reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit
1628 QDBusInterface xdp(QString::fromLatin1("org.freedesktop.portal.Desktop"),
1629 QString::fromLatin1("/org/freedesktop/portal/desktop"),
1630 QString::fromLatin1("org.freedesktop.portal.Inhibit"));
1631 if (!xdp.isValid()) {
1632 LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint");
1633 return {};
1634 }
1635 QVariantMap options = {};
1636 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
1637 //: computer from sleeping
1638 options.insert(QString::fromLatin1("reason"),
1639 QCoreApplication::translate("GMainWindow", "yuzu is running a game"));
1640 // 0x4: Suspend lock; 0x8: Idle lock
1641 QDBusReply<QDBusObjectPath> reply =
1642 xdp.call(QString::fromLatin1("Inhibit"),
1643 QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options);
1644
1645 if (reply.isValid()) {
1646 return reply.value();
1647 }
1648 LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}",
1649 reply.error().message().toStdString());
1650 return {};
1651}
1652
1653static void ReleaseWakeLockLinux(QDBusObjectPath lock) {
1654 if (!QDBusConnection::sessionBus().isConnected()) {
1655 return;
1656 }
1657 QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(),
1658 QString::fromLatin1("org.freedesktop.portal.Request"));
1659 unlocker.call(QString::fromLatin1("Close"));
1660}
1661
1662std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0}; 1634std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0};
1663 1635
1664void GMainWindow::SetupSigInterrupts() { 1636void GMainWindow::SetupSigInterrupts() {
@@ -1711,12 +1683,6 @@ void GMainWindow::PreventOSSleep() {
1711 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 1683 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
1712#elif defined(HAVE_SDL2) 1684#elif defined(HAVE_SDL2)
1713 SDL_DisableScreenSaver(); 1685 SDL_DisableScreenSaver();
1714#ifdef __unix__
1715 auto reply = HoldWakeLockLinux(winId());
1716 if (reply) {
1717 wake_lock = std::move(reply.value());
1718 }
1719#endif
1720#endif 1686#endif
1721} 1687}
1722 1688
@@ -1725,11 +1691,6 @@ void GMainWindow::AllowOSSleep() {
1725 SetThreadExecutionState(ES_CONTINUOUS); 1691 SetThreadExecutionState(ES_CONTINUOUS);
1726#elif defined(HAVE_SDL2) 1692#elif defined(HAVE_SDL2)
1727 SDL_EnableScreenSaver(); 1693 SDL_EnableScreenSaver();
1728#ifdef __unix__
1729 if (!wake_lock.path().isEmpty()) {
1730 ReleaseWakeLockLinux(wake_lock);
1731 }
1732#endif
1733#endif 1694#endif
1734} 1695}
1735 1696
@@ -3067,7 +3028,7 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename) {
3067 return false; 3028 return false;
3068 } 3029 }
3069 3030
3070 std::array<u8, 0x1000> buffer{}; 3031 std::vector<u8> buffer(1_MiB);
3071 3032
3072 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { 3033 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
3073 if (install_progress->wasCanceled()) { 3034 if (install_progress->wasCanceled()) {
@@ -3494,7 +3455,8 @@ void GMainWindow::OnConfigure() {
3494 const auto old_language_index = Settings::values.language_index.GetValue(); 3455 const auto old_language_index = Settings::values.language_index.GetValue();
3495 3456
3496 Settings::SetConfiguringGlobal(true); 3457 Settings::SetConfiguringGlobal(true);
3497 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system, 3458 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(),
3459 vk_device_records, *system,
3498 !multiplayer_state->IsHostingPublicRoom()); 3460 !multiplayer_state->IsHostingPublicRoom());
3499 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, 3461 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
3500 &GMainWindow::OnLanguageChanged); 3462 &GMainWindow::OnLanguageChanged);
@@ -3765,7 +3727,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
3765 const auto v_file = Core::GetGameFileFromPath(vfs, file_name); 3727 const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
3766 3728
3767 Settings::SetConfiguringGlobal(false); 3729 Settings::SetConfiguringGlobal(false);
3768 ConfigurePerGame dialog(this, title_id, file_name, *system); 3730 ConfigurePerGame dialog(this, title_id, file_name, vk_device_records, *system);
3769 dialog.LoadFromFile(v_file); 3731 dialog.LoadFromFile(v_file);
3770 const auto result = dialog.exec(); 3732 const auto result = dialog.exec();
3771 3733
@@ -3836,7 +3798,7 @@ void GMainWindow::OnLoadAmiibo() {
3836 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3798 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3837 3799
3838 // Remove amiibo if one is connected 3800 // Remove amiibo if one is connected
3839 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3801 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3840 virtual_amiibo->CloseAmiibo(); 3802 virtual_amiibo->CloseAmiibo();
3841 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3803 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3842 return; 3804 return;
@@ -3864,7 +3826,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
3864 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3826 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3865 const QString title = tr("Error loading Amiibo data"); 3827 const QString title = tr("Error loading Amiibo data");
3866 // Remove amiibo if one is connected 3828 // Remove amiibo if one is connected
3867 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3829 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3868 virtual_amiibo->CloseAmiibo(); 3830 virtual_amiibo->CloseAmiibo();
3869 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3831 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3870 return; 3832 return;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6bb70972f..2cfb96257 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -118,6 +118,10 @@ enum class ReinitializeKeyBehavior {
118 Warning, 118 Warning,
119}; 119};
120 120
121namespace VkDeviceInfo {
122class Record;
123}
124
121class GMainWindow : public QMainWindow { 125class GMainWindow : public QMainWindow {
122 Q_OBJECT 126 Q_OBJECT
123 127
@@ -418,6 +422,8 @@ private:
418 422
419 GameListPlaceholder* game_list_placeholder; 423 GameListPlaceholder* game_list_placeholder;
420 424
425 std::vector<VkDeviceInfo::Record> vk_device_records;
426
421 // Status bar elements 427 // Status bar elements
422 QLabel* message_label = nullptr; 428 QLabel* message_label = nullptr;
423 QLabel* shader_building_label = nullptr; 429 QLabel* shader_building_label = nullptr;
@@ -498,8 +504,6 @@ private:
498#ifdef __unix__ 504#ifdef __unix__
499 QSocketNotifier* sig_interrupt_notifier; 505 QSocketNotifier* sig_interrupt_notifier;
500 static std::array<int, 3> sig_interrupt_fds; 506 static std::array<int, 3> sig_interrupt_fds;
501
502 QDBusObjectPath wake_lock{};
503#endif 507#endif
504 508
505protected: 509protected:
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
new file mode 100644
index 000000000..7c26a3dc7
--- /dev/null
+++ b/src/yuzu/vk_device_info.cpp
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <utility>
5#include <vector>
6#include "common/dynamic_library.h"
7#include "common/logging/log.h"
8#include "video_core/vulkan_common/vulkan_device.h"
9#include "video_core/vulkan_common/vulkan_instance.h"
10#include "video_core/vulkan_common/vulkan_library.h"
11#include "video_core/vulkan_common/vulkan_surface.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13#include "vulkan/vulkan_core.h"
14#include "yuzu/qt_common.h"
15#include "yuzu/vk_device_info.h"
16
17class QWindow;
18
19namespace VkDeviceInfo {
20Record::Record(std::string_view name_, const std::vector<VkPresentModeKHR>& vsync_modes_,
21 bool has_broken_compute_)
22 : name{name_}, vsync_support{vsync_modes_}, has_broken_compute{has_broken_compute_} {}
23
24Record::~Record() = default;
25
26void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
27 using namespace Vulkan;
28
29 auto wsi = QtCommon::GetWindowSystemInfo(window);
30
31 vk::InstanceDispatch dld;
32 const auto library = OpenLibrary();
33 const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
34 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
35 vk::SurfaceKHR surface = CreateSurface(instance, wsi);
36
37 records.clear();
38 records.reserve(physical_devices.size());
39 for (const VkPhysicalDevice device : physical_devices) {
40 const auto physical_device = vk::PhysicalDevice(device, dld);
41 const std::string name = physical_device.GetProperties().deviceName;
42 const std::vector<VkPresentModeKHR> present_modes =
43 physical_device.GetSurfacePresentModesKHR(*surface);
44
45 VkPhysicalDeviceDriverProperties driver_properties{};
46 driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
47 driver_properties.pNext = nullptr;
48 VkPhysicalDeviceProperties2 properties{};
49 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
50 properties.pNext = &driver_properties;
51 dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);
52
53 bool has_broken_compute{Vulkan::Device::CheckBrokenCompute(
54 driver_properties.driverID, properties.properties.driverVersion)};
55
56 records.push_back(VkDeviceInfo::Record(name, present_modes, has_broken_compute));
57 }
58} catch (const Vulkan::vk::Exception& exception) {
59 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
60}
61} // namespace VkDeviceInfo
diff --git a/src/yuzu/vk_device_info.h b/src/yuzu/vk_device_info.h
new file mode 100644
index 000000000..bda8262f4
--- /dev/null
+++ b/src/yuzu/vk_device_info.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <iterator>
8#include <memory>
9#include <string>
10#include <string_view>
11#include <vector>
12#include "common/common_types.h"
13#include "vulkan/vulkan_core.h"
14
15class QWindow;
16
17namespace Settings {
18enum class VSyncMode : u32;
19}
20// #include "common/settings.h"
21
22namespace VkDeviceInfo {
23// Short class to record Vulkan driver information for configuration purposes
24class Record {
25public:
26 explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
27 bool has_broken_compute);
28 ~Record();
29
30 const std::string name;
31 const std::vector<VkPresentModeKHR> vsync_support;
32 const bool has_broken_compute;
33};
34
35void PopulateRecords(std::vector<Record>& records, QWindow* window);
36} // namespace VkDeviceInfo
diff --git a/vcpkg.json b/vcpkg.json
index 26f545c6c..7d9e631a1 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -1,7 +1,7 @@
1{ 1{
2 "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", 2 "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
3 "name": "yuzu", 3 "name": "yuzu",
4 "builtin-baseline": "656fcc6ab2b05c6d999b7eaca717027ac3738f71", 4 "builtin-baseline": "cbf56573a987527b39272e88cbdd11389b78c6e4",
5 "version": "1.0", 5 "version": "1.0",
6 "dependencies": [ 6 "dependencies": [
7 "boost-algorithm", 7 "boost-algorithm",