summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt47
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt140
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt54
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt51
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt40
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt214
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ContentTypeSelectionDialogFragment.kt68
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt51
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt148
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt456
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LaunchGameDialogFragment.kt61
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt83
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt175
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt43
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt75
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt341
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/AddonUtil.kt8
-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/FileUtil.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt110
-rw-r--r--src/android/app/src/main/jni/android_config.cpp19
-rw-r--r--src/android/app/src/main/jni/android_config.h2
-rw-r--r--src/android/app/src/main/jni/android_settings.h3
-rw-r--r--src/android/app/src/main/jni/game_metadata.cpp39
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp95
-rw-r--r--src/android/app/src/main/jni/id_cache.h15
-rw-r--r--src/android/app/src/main/jni/native.cpp117
-rw-r--r--src/android/app/src/main/jni/native.h3
-rw-r--r--src/android/app/src/main/jni/native_config.cpp217
-rw-r--r--src/android/app/src/main/res/drawable/ic_save.xml9
-rw-r--r--src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml82
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml99
-rw-r--r--src/android/app/src/main/res/layout/card_installable.xml3
-rw-r--r--src/android/app/src/main/res/layout/card_installable_icon.xml89
-rw-r--r--src/android/app/src/main/res/layout/card_simple_outlined.xml (renamed from src/android/app/src/main/res/layout/card_applet_option.xml)20
-rw-r--r--src/android/app/src/main/res/layout/fragment_addons.xml47
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_info.xml125
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_properties.xml86
-rw-r--r--src/android/app/src/main/res/layout/list_item_addon.xml57
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting.xml10
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml75
-rw-r--r--src/android/app/src/main/res/menu/menu_in_game.xml5
-rw-r--r--src/android/app/src/main/res/navigation/emulation_navigation.xml4
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml48
-rw-r--r--src/android/app/src/main/res/values/dimens.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml47
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp3
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp3
-rw-r--r--src/audio_core/sink/sink.h12
-rw-r--r--src/audio_core/sink/sink_stream.cpp41
-rw-r--r--src/common/common_funcs.h6
-rw-r--r--src/common/fs/path_util.cpp44
-rw-r--r--src/common/fs/path_util.h8
-rw-r--r--src/common/host_memory.cpp38
-rw-r--r--src/common/host_memory.h2
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h10
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_setting.h5
-rw-r--r--src/core/CMakeLists.txt26
-rw-r--r--src/core/arm/nce/arm_nce.cpp107
-rw-r--r--src/core/arm/nce/arm_nce.h13
-rw-r--r--src/core/arm/nce/arm_nce.s60
-rw-r--r--src/core/arm/nce/arm_nce_asm_definitions.h3
-rw-r--r--src/core/arm/nce/interpreter_visitor.cpp825
-rw-r--r--src/core/arm/nce/interpreter_visitor.h103
-rw-r--r--src/core/arm/nce/visitor_base.h2777
-rw-r--r--src/core/file_sys/vfs.cpp8
-rw-r--r--src/core/file_sys/vfs_real.cpp8
-rw-r--r--src/core/hid/input_interpreter.cpp3
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp5
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp14
-rw-r--r--src/core/hle/kernel/kernel.cpp15
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.h7
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp21
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h19
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h3
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp23
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h48
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp36
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h90
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp23
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h33
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp19
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h16
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp28
-rw-r--r--src/core/hle/service/hid/controllers/npad.h314
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/palma.h3
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_format.h240
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.cpp53
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.h44
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp19
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h18
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp24
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h32
-rw-r--r--src/core/hle/service/hid/controllers/types/debug_pad_types.h31
-rw-r--r--src/core/hle/service/hid/controllers/types/gesture_types.h77
-rw-r--r--src/core/hle/service/hid/controllers/types/keyboard_types.h20
-rw-r--r--src/core/hle/service/hid/controllers/types/mouse_types.h8
-rw-r--r--src/core/hle/service/hid/controllers/types/npad_types.h254
-rw-r--r--src/core/hle/service/hid/controllers/types/touch_types.h90
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h112
-rw-r--r--src/core/hle/service/hid/hid_server.cpp23
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp3
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp65
-rw-r--r--src/core/hle/service/hid/resource_manager.h8
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp3
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp42
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h11
-rw-r--r--src/core/hle/service/set/appln_settings.cpp12
-rw-r--r--src/core/hle/service/set/appln_settings.h36
-rw-r--r--src/core/hle/service/set/device_settings.cpp12
-rw-r--r--src/core/hle/service/set/device_settings.h54
-rw-r--r--src/core/hle/service/set/private_settings.cpp12
-rw-r--r--src/core/hle/service/set/private_settings.h72
-rw-r--r--src/core/hle/service/set/set.h24
-rw-r--r--src/core/hle/service/set/set_sys.cpp666
-rw-r--r--src/core/hle/service/set/set_sys.h373
-rw-r--r--src/core/hle/service/set/system_settings.cpp51
-rw-r--r--src/core/hle/service/set/system_settings.h699
-rw-r--r--src/core/hle/service/ssl/ssl.cpp6
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp25
-rw-r--r--src/core/hle/service/vi/display/vi_display.h15
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp4
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h13
-rw-r--r--src/core/hle/service/vi/vi.cpp3
-rw-r--r--src/core/memory/cheat_engine.cpp2
-rw-r--r--src/frontend_common/config.cpp1
-rw-r--r--src/tests/video_core/memory_tracker.cpp6
-rw-r--r--src/video_core/buffer_cache/word_manager.h2
-rw-r--r--src/video_core/rasterizer_accelerated.cpp99
-rw-r--r--src/video_core/rasterizer_accelerated.h29
-rw-r--r--src/video_core/rasterizer_interface.h2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.h2
-rw-r--r--src/video_core/shader_cache.cpp4
-rw-r--r--src/video_core/texture_cache/texture_cache.h10
-rw-r--r--src/yuzu/configuration/configure_ui.cpp4
-rw-r--r--src/yuzu/configuration/qt_config.cpp5
-rw-r--r--src/yuzu/main.cpp6
-rw-r--r--src/yuzu/uisettings.h3
207 files changed, 10354 insertions, 2402 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 5721327e7..f763c657e 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -174,7 +174,8 @@ android {
174 "-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work 174 "-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
175 "-DYUZU_USE_BUNDLED_VCPKG=ON", 175 "-DYUZU_USE_BUNDLED_VCPKG=ON",
176 "-DYUZU_USE_BUNDLED_FFMPEG=ON", 176 "-DYUZU_USE_BUNDLED_FFMPEG=ON",
177 "-DYUZU_ENABLE_LTO=ON" 177 "-DYUZU_ENABLE_LTO=ON",
178 "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
178 ) 179 )
179 180
180 abiFilters("arm64-v8a", "x86_64") 181 abiFilters("arm64-v8a", "x86_64")
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 e0f01127c..010c44951 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -230,8 +230,6 @@ object NativeLibrary {
230 */ 230 */
231 external fun onTouchReleased(finger_id: Int) 231 external fun onTouchReleased(finger_id: Int)
232 232
233 external fun initGameIni(gameID: String?)
234
235 external fun setAppDirectory(directory: String) 233 external fun setAppDirectory(directory: String)
236 234
237 /** 235 /**
@@ -241,6 +239,8 @@ object NativeLibrary {
241 */ 239 */
242 external fun installFileToNand(filename: String, extension: String): Int 240 external fun installFileToNand(filename: String, extension: String): Int
243 241
242 external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean
243
244 external fun initializeGpuDriver( 244 external fun initializeGpuDriver(
245 hookLibDir: String?, 245 hookLibDir: String?,
246 customDriverDir: String?, 246 customDriverDir: String?,
@@ -252,18 +252,11 @@ object NativeLibrary {
252 252
253 external fun initializeSystem(reload: Boolean) 253 external fun initializeSystem(reload: Boolean)
254 254
255 external fun defaultCPUCore(): Int
256
257 /** 255 /**
258 * Begins emulation. 256 * Begins emulation.
259 */ 257 */
260 external fun run(path: String?) 258 external fun run(path: String?)
261 259
262 /**
263 * Begins emulation from the specified savestate.
264 */
265 external fun run(path: String?, savestatePath: String?, deleteSavestate: Boolean)
266
267 // Surface Handling 260 // Surface Handling
268 external fun surfaceChanged(surf: Surface?) 261 external fun surfaceChanged(surf: Surface?)
269 262
@@ -304,10 +297,9 @@ object NativeLibrary {
304 */ 297 */
305 external fun getCpuBackend(): String 298 external fun getCpuBackend(): String
306 299
307 /** 300 external fun applySettings()
308 * Notifies the core emulation that the orientation has changed. 301
309 */ 302 external fun logSettings()
310 external fun notifyOrientationChange(layout_option: Int, rotation: Int)
311 303
312 enum class CoreError { 304 enum class CoreError {
313 ErrorSystemFiles, 305 ErrorSystemFiles,
@@ -539,6 +531,35 @@ object NativeLibrary {
539 external fun isFirmwareAvailable(): Boolean 531 external fun isFirmwareAvailable(): Boolean
540 532
541 /** 533 /**
534 * Checks the PatchManager for any addons that are available
535 *
536 * @param path Path to game file. Can be a [Uri].
537 * @param programId String representation of a game's program ID
538 * @return Array of pairs where the first value is the name of an addon and the second is the version
539 */
540 external fun getAddonsForFile(path: String, programId: String): Array<Pair<String, String>>?
541
542 /**
543 * Gets the save location for a specific game
544 *
545 * @param programId String representation of a game's program ID
546 * @return Save data path that may not exist yet
547 */
548 external fun getSavePath(programId: String): String
549
550 /**
551 * Adds a file to the manual filesystem provider in our EmulationSession instance
552 * @param path Path to the file we're adding. Can be a string representation of a [Uri] or
553 * a normal path
554 */
555 external fun addFileToFilesystemProvider(path: String)
556
557 /**
558 * Clears all files added to the manual filesystem provider in our EmulationSession instance
559 */
560 external fun clearFilesystemProvider()
561
562 /**
542 * Button type for use in onTouchEvent 563 * Button type for use in onTouchEvent
543 */ 564 */
544 object ButtonType { 565 object ButtonType {
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 f41d7bdbf..9b08f008d 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
@@ -172,7 +172,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
172 172
173 override fun onUserLeaveHint() { 173 override fun onUserLeaveHint() {
174 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 174 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
175 if (BooleanSetting.PICTURE_IN_PICTURE.boolean && !isInPictureInPictureMode) { 175 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
176 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() 176 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
177 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() 177 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
178 enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) 178 enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
@@ -284,7 +284,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
284 284
285 private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder(): 285 private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder():
286 PictureInPictureParams.Builder { 286 PictureInPictureParams.Builder {
287 val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) { 287 val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.getInt()) {
288 0 -> Rational(16, 9) 288 0 -> Rational(16, 9)
289 1 -> Rational(4, 3) 289 1 -> Rational(4, 3)
290 2 -> Rational(21, 9) 290 2 -> Rational(21, 9)
@@ -331,7 +331,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
331 pictureInPictureActions.add(pauseRemoteAction) 331 pictureInPictureActions.add(pauseRemoteAction)
332 } 332 }
333 333
334 if (BooleanSetting.AUDIO_MUTED.boolean) { 334 if (BooleanSetting.AUDIO_MUTED.getBoolean()) {
335 val unmuteIcon = Icon.createWithResource( 335 val unmuteIcon = Icon.createWithResource(
336 this@EmulationActivity, 336 this@EmulationActivity,
337 R.drawable.ic_pip_unmute 337 R.drawable.ic_pip_unmute
@@ -376,7 +376,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
376 val isEmulationActive = emulationViewModel.emulationStarted.value && 376 val isEmulationActive = emulationViewModel.emulationStarted.value &&
377 !emulationViewModel.isEmulationStopping.value 377 !emulationViewModel.isEmulationStopping.value
378 pictureInPictureParamsBuilder.setAutoEnterEnabled( 378 pictureInPictureParamsBuilder.setAutoEnterEnabled(
379 BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive 379 BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive
380 ) 380 )
381 } 381 }
382 setPictureInPictureParams(pictureInPictureParamsBuilder.build()) 382 setPictureInPictureParams(pictureInPictureParamsBuilder.build())
@@ -390,9 +390,13 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
390 if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation() 390 if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
391 } 391 }
392 if (intent.action == actionUnmute) { 392 if (intent.action == actionUnmute) {
393 if (BooleanSetting.AUDIO_MUTED.boolean) BooleanSetting.AUDIO_MUTED.setBoolean(false) 393 if (BooleanSetting.AUDIO_MUTED.getBoolean()) {
394 BooleanSetting.AUDIO_MUTED.setBoolean(false)
395 }
394 } else if (intent.action == actionMute) { 396 } else if (intent.action == actionMute) {
395 if (!BooleanSetting.AUDIO_MUTED.boolean) BooleanSetting.AUDIO_MUTED.setBoolean(true) 397 if (!BooleanSetting.AUDIO_MUTED.getBoolean()) {
398 BooleanSetting.AUDIO_MUTED.setBoolean(true)
399 }
396 } 400 }
397 buildPictureInPictureParams() 401 buildPictureInPictureParams()
398 } 402 }
@@ -423,7 +427,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
423 } catch (ignored: Exception) { 427 } catch (ignored: Exception) {
424 } 428 }
425 // Always resume audio, since there is no UI button 429 // Always resume audio, since there is no UI button
426 if (BooleanSetting.AUDIO_MUTED.boolean) BooleanSetting.AUDIO_MUTED.setBoolean(false) 430 if (BooleanSetting.AUDIO_MUTED.getBoolean()) {
431 BooleanSetting.AUDIO_MUTED.setBoolean(false)
432 }
427 } 433 }
428 } 434 }
429 435
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
new file mode 100644
index 000000000..15c7ca3c9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
@@ -0,0 +1,52 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.adapters
5
6import android.view.LayoutInflater
7import android.view.ViewGroup
8import androidx.recyclerview.widget.AsyncDifferConfig
9import androidx.recyclerview.widget.DiffUtil
10import androidx.recyclerview.widget.ListAdapter
11import androidx.recyclerview.widget.RecyclerView
12import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
13import org.yuzu.yuzu_emu.model.Addon
14
15class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>(
16 AsyncDifferConfig.Builder(DiffCallback()).build()
17) {
18 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
19 ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
20 .also { return AddonViewHolder(it) }
21 }
22
23 override fun getItemCount(): Int = currentList.size
24
25 override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
26 holder.bind(currentList[position])
27
28 inner class AddonViewHolder(val binding: ListItemAddonBinding) :
29 RecyclerView.ViewHolder(binding.root) {
30 fun bind(addon: Addon) {
31 binding.root.setOnClickListener {
32 binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
33 }
34 binding.title.text = addon.title
35 binding.version.text = addon.version
36 binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
37 addon.enabled = checked
38 }
39 binding.addonSwitch.isChecked = addon.enabled
40 }
41 }
42
43 private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
44 override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
45 return oldItem == newItem
46 }
47
48 override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
49 return oldItem == newItem
50 }
51 }
52}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
index a21a705c1..4a05c5be9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
@@ -15,7 +15,7 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
15import org.yuzu.yuzu_emu.NativeLibrary 15import org.yuzu.yuzu_emu.NativeLibrary
16import org.yuzu.yuzu_emu.R 16import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.YuzuApplication 17import org.yuzu.yuzu_emu.YuzuApplication
18import org.yuzu.yuzu_emu.databinding.CardAppletOptionBinding 18import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
19import org.yuzu.yuzu_emu.model.Applet 19import org.yuzu.yuzu_emu.model.Applet
20import org.yuzu.yuzu_emu.model.AppletInfo 20import org.yuzu.yuzu_emu.model.AppletInfo
21import org.yuzu.yuzu_emu.model.Game 21import org.yuzu.yuzu_emu.model.Game
@@ -28,7 +28,7 @@ class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
28 parent: ViewGroup, 28 parent: ViewGroup,
29 viewType: Int 29 viewType: Int
30 ): AppletAdapter.AppletViewHolder { 30 ): AppletAdapter.AppletViewHolder {
31 CardAppletOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) 31 CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
32 .apply { root.setOnClickListener(this@AppletAdapter) } 32 .apply { root.setOnClickListener(this@AppletAdapter) }
33 .also { return AppletViewHolder(it) } 33 .also { return AppletViewHolder(it) }
34 } 34 }
@@ -65,7 +65,7 @@ class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
65 view.findNavController().navigate(action) 65 view.findNavController().navigate(action)
66 } 66 }
67 67
68 inner class AppletViewHolder(val binding: CardAppletOptionBinding) : 68 inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) :
69 RecyclerView.ViewHolder(binding.root) { 69 RecyclerView.ViewHolder(binding.root) {
70 lateinit var applet: Applet 70 lateinit var applet: Applet
71 71
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
index 0e818cab9..d290a656c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
@@ -42,7 +42,7 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
42 if (driverViewModel.selectedDriver > position) { 42 if (driverViewModel.selectedDriver > position) {
43 driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1) 43 driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
44 } 44 }
45 if (GpuDriverHelper.customDriverData == driverData.second) { 45 if (GpuDriverHelper.customDriverSettingData == driverData.second) {
46 driverViewModel.setSelectedDriverIndex(0) 46 driverViewModel.setSelectedDriverIndex(0)
47 } 47 }
48 driverViewModel.driversToDelete.add(driverData.first) 48 driverViewModel.driversToDelete.add(driverData.first)
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 2ef638559..a578f0de8 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
@@ -44,19 +44,20 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
44 44
45class GameAdapter(private val activity: AppCompatActivity) : 45class GameAdapter(private val activity: AppCompatActivity) :
46 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), 46 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
47 View.OnClickListener { 47 View.OnClickListener,
48 View.OnLongClickListener {
48 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { 49 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
49 // Create a new view. 50 // Create a new view.
50 val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) 51 val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
51 binding.cardGame.setOnClickListener(this) 52 binding.cardGame.setOnClickListener(this)
53 binding.cardGame.setOnLongClickListener(this)
52 54
53 // Use that view to create a ViewHolder. 55 // Use that view to create a ViewHolder.
54 return GameViewHolder(binding) 56 return GameViewHolder(binding)
55 } 57 }
56 58
57 override fun onBindViewHolder(holder: GameViewHolder, position: Int) { 59 override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
58 holder.bind(currentList[position]) 60 holder.bind(currentList[position])
59 }
60 61
61 override fun getItemCount(): Int = currentList.size 62 override fun getItemCount(): Int = currentList.size
62 63
@@ -125,8 +126,15 @@ class GameAdapter(private val activity: AppCompatActivity) :
125 } 126 }
126 } 127 }
127 128
128 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) 129 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
130 view.findNavController().navigate(action)
131 }
132
133 override fun onLongClick(view: View): Boolean {
134 val holder = view.tag as GameViewHolder
135 val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
129 view.findNavController().navigate(action) 136 view.findNavController().navigate(action)
137 return true
130 } 138 }
131 139
132 inner class GameViewHolder(val binding: CardGameBinding) : 140 inner class GameViewHolder(val binding: CardGameBinding) :
@@ -157,7 +165,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
157 165
158 private class DiffCallback : DiffUtil.ItemCallback<Game>() { 166 private class DiffCallback : DiffUtil.ItemCallback<Game>() {
159 override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { 167 override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
160 return oldItem.programId == newItem.programId 168 return oldItem == newItem
161 } 169 }
162 170
163 override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { 171 override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
new file mode 100644
index 000000000..95841d786
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -0,0 +1,140 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.adapters
5
6import android.text.TextUtils
7import android.view.LayoutInflater
8import android.view.View
9import android.view.ViewGroup
10import androidx.core.content.res.ResourcesCompat
11import androidx.lifecycle.Lifecycle
12import androidx.lifecycle.LifecycleOwner
13import androidx.lifecycle.lifecycleScope
14import androidx.lifecycle.repeatOnLifecycle
15import androidx.recyclerview.widget.RecyclerView
16import kotlinx.coroutines.launch
17import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
18import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
19import org.yuzu.yuzu_emu.model.GameProperty
20import org.yuzu.yuzu_emu.model.InstallableProperty
21import org.yuzu.yuzu_emu.model.SubmenuProperty
22
23class GamePropertiesAdapter(
24 private val viewLifecycle: LifecycleOwner,
25 private var properties: List<GameProperty>
26) :
27 RecyclerView.Adapter<GamePropertiesAdapter.GamePropertyViewHolder>() {
28 override fun onCreateViewHolder(
29 parent: ViewGroup,
30 viewType: Int
31 ): GamePropertyViewHolder {
32 val inflater = LayoutInflater.from(parent.context)
33 return when (viewType) {
34 PropertyType.Submenu.ordinal -> {
35 SubmenuPropertyViewHolder(
36 CardSimpleOutlinedBinding.inflate(
37 inflater,
38 parent,
39 false
40 )
41 )
42 }
43
44 else -> InstallablePropertyViewHolder(
45 CardInstallableIconBinding.inflate(
46 inflater,
47 parent,
48 false
49 )
50 )
51 }
52 }
53
54 override fun getItemCount(): Int = properties.size
55
56 override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) =
57 holder.bind(properties[position])
58
59 override fun getItemViewType(position: Int): Int {
60 return when (properties[position]) {
61 is SubmenuProperty -> PropertyType.Submenu.ordinal
62 else -> PropertyType.Installable.ordinal
63 }
64 }
65
66 sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
67 abstract fun bind(property: GameProperty)
68 }
69
70 inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) :
71 GamePropertyViewHolder(binding.root) {
72 override fun bind(property: GameProperty) {
73 val submenuProperty = property as SubmenuProperty
74
75 binding.root.setOnClickListener {
76 submenuProperty.action.invoke()
77 }
78
79 binding.title.setText(submenuProperty.titleId)
80 binding.description.setText(submenuProperty.descriptionId)
81 binding.icon.setImageDrawable(
82 ResourcesCompat.getDrawable(
83 binding.icon.context.resources,
84 submenuProperty.iconId,
85 binding.icon.context.theme
86 )
87 )
88
89 binding.details.postDelayed({
90 binding.details.isSelected = true
91 binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE
92 }, 3000)
93
94 if (submenuProperty.details != null) {
95 binding.details.visibility = View.VISIBLE
96 binding.details.text = submenuProperty.details.invoke()
97 } else if (submenuProperty.detailsFlow != null) {
98 binding.details.visibility = View.VISIBLE
99 viewLifecycle.lifecycleScope.launch {
100 viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
101 submenuProperty.detailsFlow.collect { binding.details.text = it }
102 }
103 }
104 } else {
105 binding.details.visibility = View.GONE
106 }
107 }
108 }
109
110 inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
111 GamePropertyViewHolder(binding.root) {
112 override fun bind(property: GameProperty) {
113 val installableProperty = property as InstallableProperty
114
115 binding.title.setText(installableProperty.titleId)
116 binding.description.setText(installableProperty.descriptionId)
117 binding.icon.setImageDrawable(
118 ResourcesCompat.getDrawable(
119 binding.icon.context.resources,
120 installableProperty.iconId,
121 binding.icon.context.theme
122 )
123 )
124
125 if (installableProperty.install != null) {
126 binding.buttonInstall.visibility = View.VISIBLE
127 binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() }
128 }
129 if (installableProperty.export != null) {
130 binding.buttonExport.visibility = View.VISIBLE
131 binding.buttonExport.setOnClickListener { installableProperty.export.invoke() }
132 }
133 }
134 }
135
136 enum class PropertyType {
137 Submenu,
138 Installable
139 }
140}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
index aeda8d222..0ba465356 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractBooleanSetting : AbstractSetting { 6interface AbstractBooleanSetting : AbstractSetting {
7 val boolean: Boolean 7 fun getBoolean(needsGlobal: Boolean = false): Boolean
8
9 fun setBoolean(value: Boolean) 8 fun setBoolean(value: Boolean)
10} 9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
index 606519ad8..cf6300535 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractByteSetting : AbstractSetting { 6interface AbstractByteSetting : AbstractSetting {
7 val byte: Byte 7 fun getByte(needsGlobal: Boolean = false): Byte
8
9 fun setByte(value: Byte) 8 fun setByte(value: Byte)
10} 9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
index 974925eed..c6c0bcf34 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractFloatSetting : AbstractSetting { 6interface AbstractFloatSetting : AbstractSetting {
7 val float: Float 7 fun getFloat(needsGlobal: Boolean = false): Float
8
9 fun setFloat(value: Float) 8 fun setFloat(value: Float)
10} 9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
index 89b285b10..826402c34 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractIntSetting : AbstractSetting { 6interface AbstractIntSetting : AbstractSetting {
7 val int: Int 7 fun getInt(needsGlobal: Boolean = false): Int
8
9 fun setInt(value: Int) 8 fun setInt(value: Int)
10} 9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
index 4873942db..2b62cc06b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractLongSetting : AbstractSetting { 6interface AbstractLongSetting : AbstractSetting {
7 val long: Long 7 fun getLong(needsGlobal: Boolean = false): Long
8
9 fun setLong(value: Long) 8 fun setLong(value: Long)
10} 9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
index 8b6d29fe5..3b78c7cf0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
@@ -7,12 +7,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8interface AbstractSetting { 8interface AbstractSetting {
9 val key: String 9 val key: String
10 val category: Settings.Category
11 val defaultValue: Any 10 val defaultValue: Any
12 val androidDefault: Any?
13 get() = null
14 val valueAsString: String
15 get() = ""
16 11
17 val isRuntimeModifiable: Boolean 12 val isRuntimeModifiable: Boolean
18 get() = NativeConfig.getIsRuntimeModifiable(key) 13 get() = NativeConfig.getIsRuntimeModifiable(key)
@@ -20,5 +15,17 @@ interface AbstractSetting {
20 val pairedSettingKey: String 15 val pairedSettingKey: String
21 get() = NativeConfig.getPairedSettingKey(key) 16 get() = NativeConfig.getPairedSettingKey(key)
22 17
18 val isSwitchable: Boolean
19 get() = NativeConfig.getIsSwitchable(key)
20
21 var global: Boolean
22 get() = NativeConfig.usingGlobal(key)
23 set(value) = NativeConfig.setGlobal(key, value)
24
25 val isSaveable: Boolean
26 get() = NativeConfig.getIsSaveable(key)
27
28 fun getValueAsString(needsGlobal: Boolean = false): String
29
23 fun reset() 30 fun reset()
24} 31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
index 91407ccbb..8bfa81e4a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractShortSetting : AbstractSetting { 6interface AbstractShortSetting : AbstractSetting {
7 val short: Short 7 fun getShort(needsGlobal: Boolean = false): Short
8
9 fun setShort(value: Short) 8 fun setShort(value: Short)
10} 9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
index c8935cc48..6ff8fd3f9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractStringSetting : AbstractSetting { 6interface AbstractStringSetting : AbstractSetting {
7 val string: String 7 fun getString(needsGlobal: Boolean = false): String
8
9 fun setString(value: String) 8 fun setString(value: String)
10} 9}
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 8476ce867..16f06cd0a 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
@@ -5,36 +5,34 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class BooleanSetting( 8enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
9 override val key: String, 9 AUDIO_MUTED("audio_muted"),
10 override val category: Settings.Category, 10 CPU_DEBUG_MODE("cpu_debug_mode"),
11 override val androidDefault: Boolean? = null 11 FASTMEM("cpuopt_fastmem"),
12) : AbstractBooleanSetting { 12 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives"),
13 AUDIO_MUTED("audio_muted", Settings.Category.Audio), 13 RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
14 CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu), 14 USE_DOCKED_MODE("use_docked_mode"),
15 FASTMEM("cpuopt_fastmem", Settings.Category.Cpu), 15 RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
16 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu), 16 RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
17 RENDERER_USE_SPEED_LIMIT("use_speed_limit", Settings.Category.Core), 17 RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
18 USE_DOCKED_MODE("use_docked_mode", Settings.Category.System, false), 18 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
19 RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache", Settings.Category.Renderer), 19 RENDERER_DEBUG("debug"),
20 RENDERER_FORCE_MAX_CLOCK("force_max_clock", Settings.Category.Renderer), 20 PICTURE_IN_PICTURE("picture_in_picture"),
21 RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders", Settings.Category.Renderer), 21 USE_CUSTOM_RTC("custom_rtc_enabled");
22 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing", Settings.Category.Renderer, false), 22
23 RENDERER_DEBUG("debug", Settings.Category.Renderer), 23 override fun getBoolean(needsGlobal: Boolean): Boolean =
24 PICTURE_IN_PICTURE("picture_in_picture", Settings.Category.Android), 24 NativeConfig.getBoolean(key, needsGlobal)
25 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.Category.System); 25
26 26 override fun setBoolean(value: Boolean) {
27 override val boolean: Boolean 27 if (NativeConfig.isPerGameConfigLoaded()) {
28 get() = NativeConfig.getBoolean(key, false) 28 global = false
29 29 }
30 override fun setBoolean(value: Boolean) = NativeConfig.setBoolean(key, value) 30 NativeConfig.setBoolean(key, value)
31
32 override val defaultValue: Boolean by lazy {
33 androidDefault ?: NativeConfig.getBoolean(key, true)
34 } 31 }
35 32
36 override val valueAsString: String 33 override val defaultValue: Boolean by lazy { NativeConfig.getDefaultToString(key).toBoolean() }
37 get() = if (boolean) "1" else "0" 34
35 override fun getValueAsString(needsGlobal: Boolean): String = getBoolean(needsGlobal).toString()
38 36
39 override fun reset() = NativeConfig.setBoolean(key, defaultValue) 37 override fun reset() = NativeConfig.setBoolean(key, defaultValue)
40} 38}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
index 6ec0a765e..7b7fac211 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
@@ -5,21 +5,21 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class ByteSetting( 8enum class ByteSetting(override val key: String) : AbstractByteSetting {
9 override val key: String, 9 AUDIO_VOLUME("volume");
10 override val category: Settings.Category
11) : AbstractByteSetting {
12 AUDIO_VOLUME("volume", Settings.Category.Audio);
13 10
14 override val byte: Byte 11 override fun getByte(needsGlobal: Boolean): Byte = NativeConfig.getByte(key, needsGlobal)
15 get() = NativeConfig.getByte(key, false)
16 12
17 override fun setByte(value: Byte) = NativeConfig.setByte(key, value) 13 override fun setByte(value: Byte) {
14 if (NativeConfig.isPerGameConfigLoaded()) {
15 global = false
16 }
17 NativeConfig.setByte(key, value)
18 }
18 19
19 override val defaultValue: Byte by lazy { NativeConfig.getByte(key, true) } 20 override val defaultValue: Byte by lazy { NativeConfig.getDefaultToString(key).toByte() }
20 21
21 override val valueAsString: String 22 override fun getValueAsString(needsGlobal: Boolean): String = getByte(needsGlobal).toString()
22 get() = byte.toString()
23 23
24 override fun reset() = NativeConfig.setByte(key, defaultValue) 24 override fun reset() = NativeConfig.setByte(key, defaultValue)
25} 25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
index 0181d06f2..4644824d8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
@@ -5,22 +5,22 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class FloatSetting( 8enum class FloatSetting(override val key: String) : AbstractFloatSetting {
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractFloatSetting {
12 // No float settings currently exist 9 // No float settings currently exist
13 EMPTY_SETTING("", Settings.Category.UiGeneral); 10 EMPTY_SETTING("");
14 11
15 override val float: Float 12 override fun getFloat(needsGlobal: Boolean): Float = NativeConfig.getFloat(key, false)
16 get() = NativeConfig.getFloat(key, false)
17 13
18 override fun setFloat(value: Float) = NativeConfig.setFloat(key, value) 14 override fun setFloat(value: Float) {
15 if (NativeConfig.isPerGameConfigLoaded()) {
16 global = false
17 }
18 NativeConfig.setFloat(key, value)
19 }
19 20
20 override val defaultValue: Float by lazy { NativeConfig.getFloat(key, true) } 21 override val defaultValue: Float by lazy { NativeConfig.getDefaultToString(key).toFloat() }
21 22
22 override val valueAsString: String 23 override fun getValueAsString(needsGlobal: Boolean): String = getFloat(needsGlobal).toString()
23 get() = float.toString()
24 24
25 override fun reset() = NativeConfig.setFloat(key, defaultValue) 25 override fun reset() = NativeConfig.setFloat(key, defaultValue)
26} 26}
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 ef10b209f..21e4e1afd 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
@@ -5,36 +5,33 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class IntSetting( 8enum class IntSetting(override val key: String) : AbstractIntSetting {
9 override val key: String, 9 CPU_BACKEND("cpu_backend"),
10 override val category: Settings.Category, 10 CPU_ACCURACY("cpu_accuracy"),
11 override val androidDefault: Int? = null 11 REGION_INDEX("region_index"),
12) : AbstractIntSetting { 12 LANGUAGE_INDEX("language_index"),
13 CPU_BACKEND("cpu_backend", Settings.Category.Cpu), 13 RENDERER_BACKEND("backend"),
14 CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), 14 RENDERER_ACCURACY("gpu_accuracy"),
15 REGION_INDEX("region_index", Settings.Category.System), 15 RENDERER_RESOLUTION("resolution_setup"),
16 LANGUAGE_INDEX("language_index", Settings.Category.System), 16 RENDERER_VSYNC("use_vsync"),
17 RENDERER_BACKEND("backend", Settings.Category.Renderer), 17 RENDERER_SCALING_FILTER("scaling_filter"),
18 RENDERER_ACCURACY("gpu_accuracy", Settings.Category.Renderer, 0), 18 RENDERER_ANTI_ALIASING("anti_aliasing"),
19 RENDERER_RESOLUTION("resolution_setup", Settings.Category.Renderer), 19 RENDERER_SCREEN_LAYOUT("screen_layout"),
20 RENDERER_VSYNC("use_vsync", Settings.Category.Renderer), 20 RENDERER_ASPECT_RATIO("aspect_ratio"),
21 RENDERER_SCALING_FILTER("scaling_filter", Settings.Category.Renderer), 21 AUDIO_OUTPUT_ENGINE("output_engine");
22 RENDERER_ANTI_ALIASING("anti_aliasing", Settings.Category.Renderer), 22
23 RENDERER_SCREEN_LAYOUT("screen_layout", Settings.Category.Android), 23 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
24 RENDERER_ASPECT_RATIO("aspect_ratio", Settings.Category.Renderer), 24
25 AUDIO_OUTPUT_ENGINE("output_engine", Settings.Category.Audio); 25 override fun setInt(value: Int) {
26 26 if (NativeConfig.isPerGameConfigLoaded()) {
27 override val int: Int 27 global = false
28 get() = NativeConfig.getInt(key, false) 28 }
29 29 NativeConfig.setInt(key, value)
30 override fun setInt(value: Int) = NativeConfig.setInt(key, value)
31
32 override val defaultValue: Int by lazy {
33 androidDefault ?: NativeConfig.getInt(key, true)
34 } 30 }
35 31
36 override val valueAsString: String 32 override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
37 get() = int.toString() 33
34 override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
38 35
39 override fun reset() = NativeConfig.setInt(key, defaultValue) 36 override fun reset() = NativeConfig.setInt(key, defaultValue)
40} 37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
index c526fc4cf..e3efd516c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
@@ -5,21 +5,21 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class LongSetting( 8enum class LongSetting(override val key: String) : AbstractLongSetting {
9 override val key: String, 9 CUSTOM_RTC("custom_rtc");
10 override val category: Settings.Category
11) : AbstractLongSetting {
12 CUSTOM_RTC("custom_rtc", Settings.Category.System);
13 10
14 override val long: Long 11 override fun getLong(needsGlobal: Boolean): Long = NativeConfig.getLong(key, needsGlobal)
15 get() = NativeConfig.getLong(key, false)
16 12
17 override fun setLong(value: Long) = NativeConfig.setLong(key, value) 13 override fun setLong(value: Long) {
14 if (NativeConfig.isPerGameConfigLoaded()) {
15 global = false
16 }
17 NativeConfig.setLong(key, value)
18 }
18 19
19 override val defaultValue: Long by lazy { NativeConfig.getLong(key, true) } 20 override val defaultValue: Long by lazy { NativeConfig.getDefaultToString(key).toLong() }
20 21
21 override val valueAsString: String 22 override fun getValueAsString(needsGlobal: Boolean): String = getLong(needsGlobal).toString()
22 get() = long.toString()
23 23
24 override fun reset() = NativeConfig.setLong(key, defaultValue) 24 override fun reset() = NativeConfig.setLong(key, defaultValue)
25} 25}
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 e3cd66185..9551fc05e 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
@@ -6,62 +6,11 @@ package org.yuzu.yuzu_emu.features.settings.model
6import org.yuzu.yuzu_emu.R 6import org.yuzu.yuzu_emu.R
7 7
8object Settings { 8object Settings {
9 enum class Category {
10 Android,
11 Audio,
12 Core,
13 Cpu,
14 CpuDebug,
15 CpuUnsafe,
16 Renderer,
17 RendererAdvanced,
18 RendererDebug,
19 System,
20 SystemAudio,
21 DataStorage,
22 Debugging,
23 DebuggingGraphics,
24 Miscellaneous,
25 Network,
26 WebService,
27 AddOns,
28 Controls,
29 Ui,
30 UiGeneral,
31 UiLayout,
32 UiGameList,
33 Screenshots,
34 Shortcuts,
35 Multiplayer,
36 Services,
37 Paths,
38 MaxEnum
39 }
40
41 val settingsList = listOf<AbstractSetting>(
42 *BooleanSetting.values(),
43 *ByteSetting.values(),
44 *ShortSetting.values(),
45 *IntSetting.values(),
46 *FloatSetting.values(),
47 *LongSetting.values(),
48 *StringSetting.values()
49 )
50
51 const val SECTION_GENERAL = "General"
52 const val SECTION_SYSTEM = "System"
53 const val SECTION_RENDERER = "Renderer"
54 const val SECTION_AUDIO = "Audio"
55 const val SECTION_CPU = "Cpu"
56 const val SECTION_THEME = "Theme"
57 const val SECTION_DEBUG = "Debug"
58
59 enum class MenuTag(val titleId: Int) { 9 enum class MenuTag(val titleId: Int) {
60 SECTION_ROOT(R.string.advanced_settings), 10 SECTION_ROOT(R.string.advanced_settings),
61 SECTION_SYSTEM(R.string.preferences_system), 11 SECTION_SYSTEM(R.string.preferences_system),
62 SECTION_RENDERER(R.string.preferences_graphics), 12 SECTION_RENDERER(R.string.preferences_graphics),
63 SECTION_AUDIO(R.string.preferences_audio), 13 SECTION_AUDIO(R.string.preferences_audio),
64 SECTION_CPU(R.string.cpu),
65 SECTION_THEME(R.string.preferences_theme), 14 SECTION_THEME(R.string.preferences_theme),
66 SECTION_DEBUG(R.string.preferences_debug); 15 SECTION_DEBUG(R.string.preferences_debug);
67 } 16 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
index c9a0c664c..16eb4ffdd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
@@ -5,21 +5,21 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class ShortSetting( 8enum class ShortSetting(override val key: String) : AbstractShortSetting {
9 override val key: String, 9 RENDERER_SPEED_LIMIT("speed_limit");
10 override val category: Settings.Category
11) : AbstractShortSetting {
12 RENDERER_SPEED_LIMIT("speed_limit", Settings.Category.Core);
13 10
14 override val short: Short 11 override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
15 get() = NativeConfig.getShort(key, false)
16 12
17 override fun setShort(value: Short) = NativeConfig.setShort(key, value) 13 override fun setShort(value: Short) {
14 if (NativeConfig.isPerGameConfigLoaded()) {
15 global = false
16 }
17 NativeConfig.setShort(key, value)
18 }
18 19
19 override val defaultValue: Short by lazy { NativeConfig.getShort(key, true) } 20 override val defaultValue: Short by lazy { NativeConfig.getDefaultToString(key).toShort() }
20 21
21 override val valueAsString: String 22 override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
22 get() = short.toString()
23 23
24 override fun reset() = NativeConfig.setShort(key, defaultValue) 24 override fun reset() = NativeConfig.setShort(key, defaultValue)
25} 25}
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 9bb3e66d4..a0d8cfede 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
@@ -5,22 +5,21 @@ package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig 6import org.yuzu.yuzu_emu.utils.NativeConfig
7 7
8enum class StringSetting( 8enum class StringSetting(override val key: String) : AbstractStringSetting {
9 override val key: String, 9 DRIVER_PATH("driver_path");
10 override val category: Settings.Category
11) : AbstractStringSetting {
12 // No string settings currently exist
13 EMPTY_SETTING("", Settings.Category.UiGeneral);
14 10
15 override val string: String 11 override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
16 get() = NativeConfig.getString(key, false)
17 12
18 override fun setString(value: String) = NativeConfig.setString(key, value) 13 override fun setString(value: String) {
14 if (NativeConfig.isPerGameConfigLoaded()) {
15 global = false
16 }
17 NativeConfig.setString(key, value)
18 }
19 19
20 override val defaultValue: String by lazy { NativeConfig.getString(key, true) } 20 override val defaultValue: String by lazy { NativeConfig.getDefaultToString(key) }
21 21
22 override val valueAsString: String 22 override fun getValueAsString(needsGlobal: Boolean): String = getString(needsGlobal)
23 get() = string
24 23
25 override fun reset() = NativeConfig.setString(key, defaultValue) 24 override fun reset() = NativeConfig.setString(key, defaultValue)
26} 25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index 8bc164197..1d81f5f2b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
@@ -12,7 +12,6 @@ class DateTimeSetting(
12) : SettingsItem(longSetting, titleId, descriptionId) { 12) : SettingsItem(longSetting, titleId, descriptionId) {
13 override val type = TYPE_DATETIME_SETTING 13 override val type = TYPE_DATETIME_SETTING
14 14
15 var value: Long 15 fun getValue(needsGlobal: Boolean = false): Long = longSetting.getLong(needsGlobal)
16 get() = longSetting.long 16 fun setValue(value: Long) = (setting as AbstractLongSetting).setLong(value)
17 set(value) = (setting as AbstractLongSetting).setLong(value)
18} 17}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index e198b18a0..2e97aee2c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -11,8 +11,8 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
11import org.yuzu.yuzu_emu.features.settings.model.ByteSetting 11import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
12import org.yuzu.yuzu_emu.features.settings.model.IntSetting 12import org.yuzu.yuzu_emu.features.settings.model.IntSetting
13import org.yuzu.yuzu_emu.features.settings.model.LongSetting 13import org.yuzu.yuzu_emu.features.settings.model.LongSetting
14import org.yuzu.yuzu_emu.features.settings.model.Settings
15import org.yuzu.yuzu_emu.features.settings.model.ShortSetting 14import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
15import org.yuzu.yuzu_emu.utils.NativeConfig
16 16
17/** 17/**
18 * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. 18 * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
@@ -30,10 +30,26 @@ abstract class SettingsItem(
30 30
31 val isEditable: Boolean 31 val isEditable: Boolean
32 get() { 32 get() {
33 // Can't edit settings that aren't saveable in per-game config even if they are switchable
34 if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
35 return false
36 }
37
33 if (!NativeLibrary.isRunning()) return true 38 if (!NativeLibrary.isRunning()) return true
39
40 // Prevent editing settings that were modified in per-game config while editing global
41 // config
42 if (!NativeConfig.isPerGameConfigLoaded() && !setting.global) {
43 return false
44 }
45
34 return setting.isRuntimeModifiable 46 return setting.isRuntimeModifiable
35 } 47 }
36 48
49 val needsRuntimeGlobal: Boolean
50 get() = NativeLibrary.isRunning() && !setting.global &&
51 !NativeConfig.isPerGameConfigLoaded()
52
37 companion object { 53 companion object {
38 const val TYPE_HEADER = 0 54 const val TYPE_HEADER = 0
39 const val TYPE_SWITCH = 1 55 const val TYPE_SWITCH = 1
@@ -48,8 +64,9 @@ abstract class SettingsItem(
48 64
49 val emptySetting = object : AbstractSetting { 65 val emptySetting = object : AbstractSetting {
50 override val key: String = "" 66 override val key: String = ""
51 override val category: Settings.Category = Settings.Category.Ui
52 override val defaultValue: Any = false 67 override val defaultValue: Any = false
68 override val isSaveable = true
69 override fun getValueAsString(needsGlobal: Boolean): String = ""
53 override fun reset() {} 70 override fun reset() {}
54 } 71 }
55 72
@@ -270,9 +287,9 @@ abstract class SettingsItem(
270 ) 287 )
271 288
272 val fastmem = object : AbstractBooleanSetting { 289 val fastmem = object : AbstractBooleanSetting {
273 override val boolean: Boolean 290 override fun getBoolean(needsGlobal: Boolean): Boolean =
274 get() = 291 BooleanSetting.FASTMEM.getBoolean() &&
275 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean 292 BooleanSetting.FASTMEM_EXCLUSIVES.getBoolean()
276 293
277 override fun setBoolean(value: Boolean) { 294 override fun setBoolean(value: Boolean) {
278 BooleanSetting.FASTMEM.setBoolean(value) 295 BooleanSetting.FASTMEM.setBoolean(value)
@@ -280,9 +297,24 @@ abstract class SettingsItem(
280 } 297 }
281 298
282 override val key: String = FASTMEM_COMBINED 299 override val key: String = FASTMEM_COMBINED
283 override val category = Settings.Category.Cpu
284 override val isRuntimeModifiable: Boolean = false 300 override val isRuntimeModifiable: Boolean = false
285 override val defaultValue: Boolean = true 301 override val defaultValue: Boolean = true
302 override val isSwitchable: Boolean = true
303 override var global: Boolean
304 get() {
305 return BooleanSetting.FASTMEM.global &&
306 BooleanSetting.FASTMEM_EXCLUSIVES.global
307 }
308 set(value) {
309 BooleanSetting.FASTMEM.global = value
310 BooleanSetting.FASTMEM_EXCLUSIVES.global = value
311 }
312
313 override val isSaveable = true
314
315 override fun getValueAsString(needsGlobal: Boolean): String =
316 getBoolean().toString()
317
286 override fun reset() = setBoolean(defaultValue) 318 override fun reset() = setBoolean(defaultValue)
287 } 319 }
288 put(SwitchSetting(fastmem, R.string.fastmem, 0)) 320 put(SwitchSetting(fastmem, R.string.fastmem, 0))
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 705527a73..97a5a9e59 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
@@ -15,16 +15,11 @@ class SingleChoiceSetting(
15) : SettingsItem(setting, titleId, descriptionId) { 15) : SettingsItem(setting, titleId, descriptionId) {
16 override val type = TYPE_SINGLE_CHOICE 16 override val type = TYPE_SINGLE_CHOICE
17 17
18 var selectedValue: Int 18 fun getSelectedValue(needsGlobal: Boolean = false) =
19 get() { 19 when (setting) {
20 return when (setting) { 20 is AbstractIntSetting -> setting.getInt(needsGlobal)
21 is AbstractIntSetting -> setting.int 21 else -> -1
22 else -> -1
23 }
24 }
25 set(value) {
26 when (setting) {
27 is AbstractIntSetting -> setting.setInt(value)
28 }
29 } 22 }
23
24 fun setSelectedValue(value: Int) = (setting as AbstractIntSetting).setInt(value)
30} 25}
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 c3b5df02c..b9b709bf7 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
@@ -20,22 +20,20 @@ class SliderSetting(
20) : SettingsItem(setting, titleId, descriptionId) { 20) : SettingsItem(setting, titleId, descriptionId) {
21 override val type = TYPE_SLIDER 21 override val type = TYPE_SLIDER
22 22
23 var selectedValue: Int 23 fun getSelectedValue(needsGlobal: Boolean = false) =
24 get() { 24 when (setting) {
25 return when (setting) { 25 is AbstractByteSetting -> setting.getByte(needsGlobal).toInt()
26 is AbstractByteSetting -> setting.byte.toInt() 26 is AbstractShortSetting -> setting.getShort(needsGlobal).toInt()
27 is AbstractShortSetting -> setting.short.toInt() 27 is AbstractIntSetting -> setting.getInt(needsGlobal)
28 is AbstractIntSetting -> setting.int 28 is AbstractFloatSetting -> setting.getFloat(needsGlobal).roundToInt()
29 is AbstractFloatSetting -> setting.float.roundToInt() 29 else -> -1
30 else -> -1
31 }
32 } 30 }
33 set(value) { 31
34 when (setting) { 32 fun setSelectedValue(value: Int) =
35 is AbstractByteSetting -> setting.setByte(value.toByte()) 33 when (setting) {
36 is AbstractShortSetting -> setting.setShort(value.toShort()) 34 is AbstractByteSetting -> setting.setByte(value.toByte())
37 is AbstractIntSetting -> setting.setInt(value) 35 is AbstractShortSetting -> setting.setShort(value.toShort())
38 is AbstractFloatSetting -> setting.setFloat(value.toFloat()) 36 is AbstractFloatSetting -> setting.setFloat(value.toFloat())
39 } 37 else -> (setting as AbstractIntSetting).setInt(value)
40 } 38 }
41} 39}
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 871dab4f3..ba7920f50 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
@@ -17,14 +17,13 @@ class StringSingleChoiceSetting(
17 fun getValueAt(index: Int): String = 17 fun getValueAt(index: Int): String =
18 if (index >= 0 && index < values.size) values[index] else "" 18 if (index >= 0 && index < values.size) values[index] else ""
19 19
20 var selectedValue: String 20 fun getSelectedValue(needsGlobal: Boolean = false) = stringSetting.getString(needsGlobal)
21 get() = stringSetting.string 21 fun setSelectedValue(value: String) = stringSetting.setString(value)
22 set(value) = stringSetting.setString(value)
23 22
24 val selectValueIndex: Int 23 val selectValueIndex: Int
25 get() { 24 get() {
26 for (i in values.indices) { 25 for (i in values.indices) {
27 if (values[i] == selectedValue) { 26 if (values[i] == getSelectedValue()) {
28 return i 27 return i
29 } 28 }
30 } 29 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 416967e64..44d47dd69 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
@@ -14,18 +14,18 @@ class SwitchSetting(
14) : SettingsItem(setting, titleId, descriptionId) { 14) : SettingsItem(setting, titleId, descriptionId) {
15 override val type = TYPE_SWITCH 15 override val type = TYPE_SWITCH
16 16
17 var checked: Boolean 17 fun getIsChecked(needsGlobal: Boolean = false): Boolean {
18 get() { 18 return when (setting) {
19 return when (setting) { 19 is AbstractIntSetting -> setting.getInt(needsGlobal) == 1
20 is AbstractIntSetting -> setting.int == 1 20 is AbstractBooleanSetting -> setting.getBoolean(needsGlobal)
21 is AbstractBooleanSetting -> setting.boolean 21 else -> false
22 else -> false
23 }
24 } 22 }
25 set(value) { 23 }
26 when (setting) { 24
27 is AbstractIntSetting -> setting.setInt(if (value) 1 else 0) 25 fun setChecked(value: Boolean) {
28 is AbstractBooleanSetting -> setting.setBoolean(value) 26 when (setting) {
29 } 27 is AbstractIntSetting -> setting.setInt(if (value) 1 else 0)
28 is AbstractBooleanSetting -> setting.setBoolean(value)
30 } 29 }
30 }
31} 31}
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 64bfc6dd0..6f072241a 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
@@ -19,10 +19,9 @@ import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.fragment.NavHostFragment 19import androidx.navigation.fragment.NavHostFragment
20import androidx.navigation.navArgs 20import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.CoroutineScope
23import kotlinx.coroutines.Dispatchers
24import kotlinx.coroutines.flow.collectLatest 22import kotlinx.coroutines.flow.collectLatest
25import kotlinx.coroutines.launch 23import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.NativeLibrary
26import java.io.IOException 25import java.io.IOException
27import org.yuzu.yuzu_emu.R 26import org.yuzu.yuzu_emu.R
28import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 27import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@@ -46,6 +45,9 @@ class SettingsActivity : AppCompatActivity() {
46 binding = ActivitySettingsBinding.inflate(layoutInflater) 45 binding = ActivitySettingsBinding.inflate(layoutInflater)
47 setContentView(binding.root) 46 setContentView(binding.root)
48 47
48 if (!NativeConfig.isPerGameConfigLoaded() && args.game != null) {
49 SettingsFile.loadCustomConfig(args.game!!)
50 }
49 settingsViewModel.game = args.game 51 settingsViewModel.game = args.game
50 52
51 val navHostFragment = 53 val navHostFragment =
@@ -126,7 +128,6 @@ class SettingsActivity : AppCompatActivity() {
126 128
127 override fun onStart() { 129 override fun onStart() {
128 super.onStart() 130 super.onStart()
129 // TODO: Load custom settings contextually
130 if (!DirectoryInitialization.areDirectoriesReady) { 131 if (!DirectoryInitialization.areDirectoriesReady) {
131 DirectoryInitialization.start() 132 DirectoryInitialization.start()
132 } 133 }
@@ -134,24 +135,35 @@ class SettingsActivity : AppCompatActivity() {
134 135
135 override fun onStop() { 136 override fun onStop() {
136 super.onStop() 137 super.onStop()
137 CoroutineScope(Dispatchers.IO).launch { 138 Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
138 NativeConfig.saveSettings() 139 if (isFinishing) {
140 NativeLibrary.applySettings()
141 if (args.game == null) {
142 NativeConfig.saveGlobalConfig()
143 } else if (NativeConfig.isPerGameConfigLoaded()) {
144 NativeLibrary.logSettings()
145 NativeConfig.savePerGameConfig()
146 NativeConfig.unloadPerGameConfig()
147 }
139 } 148 }
140 } 149 }
141 150
142 override fun onDestroy() {
143 settingsViewModel.clear()
144 super.onDestroy()
145 }
146
147 fun onSettingsReset() { 151 fun onSettingsReset() {
148 // Delete settings file because the user may have changed values that do not exist in the UI 152 // Delete settings file because the user may have changed values that do not exist in the UI
149 NativeConfig.unloadConfig() 153 if (args.game == null) {
150 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 154 NativeConfig.unloadGlobalConfig()
151 if (!settingsFile.delete()) { 155 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
152 throw IOException("Failed to delete $settingsFile") 156 if (!settingsFile.delete()) {
157 throw IOException("Failed to delete $settingsFile")
158 }
159 NativeConfig.initializeGlobalConfig()
160 } else {
161 NativeConfig.unloadPerGameConfig()
162 val settingsFile = SettingsFile.getCustomSettingsFile(args.game!!)
163 if (!settingsFile.delete()) {
164 throw IOException("Failed to delete $settingsFile")
165 }
153 } 166 }
154 NativeConfig.initializeConfig()
155 167
156 Toast.makeText( 168 Toast.makeText(
157 applicationContext, 169 applicationContext,
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 af2c1e582..be9b3031b 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
@@ -102,8 +102,9 @@ class SettingsAdapter(
102 return currentList[position].type 102 return currentList[position].type
103 } 103 }
104 104
105 fun onBooleanClick(item: SwitchSetting, checked: Boolean) { 105 fun onBooleanClick(item: SwitchSetting, checked: Boolean, position: Int) {
106 item.checked = checked 106 item.setChecked(checked)
107 notifyItemChanged(position)
107 settingsViewModel.setShouldReloadSettingsList(true) 108 settingsViewModel.setShouldReloadSettingsList(true)
108 } 109 }
109 110
@@ -126,7 +127,7 @@ class SettingsAdapter(
126 } 127 }
127 128
128 fun onDateTimeClick(item: DateTimeSetting, position: Int) { 129 fun onDateTimeClick(item: DateTimeSetting, position: Int) {
129 val storedTime = item.value * 1000 130 val storedTime = item.getValue() * 1000
130 131
131 // Helper to extract hour and minute from epoch time 132 // Helper to extract hour and minute from epoch time
132 val calendar: Calendar = Calendar.getInstance() 133 val calendar: Calendar = Calendar.getInstance()
@@ -159,9 +160,9 @@ class SettingsAdapter(
159 var epochTime: Long = datePicker.selection!! / 1000 160 var epochTime: Long = datePicker.selection!! / 1000
160 epochTime += timePicker.hour.toLong() * 60 * 60 161 epochTime += timePicker.hour.toLong() * 60 * 60
161 epochTime += timePicker.minute.toLong() * 60 162 epochTime += timePicker.minute.toLong() * 60
162 if (item.value != epochTime) { 163 if (item.getValue() != epochTime) {
163 notifyItemChanged(position) 164 notifyItemChanged(position)
164 item.value = epochTime 165 item.setValue(epochTime)
165 } 166 }
166 } 167 }
167 datePicker.show( 168 datePicker.show(
@@ -195,6 +196,12 @@ class SettingsAdapter(
195 return true 196 return true
196 } 197 }
197 198
199 fun onClearClick(item: SettingsItem, position: Int) {
200 item.setting.global = true
201 notifyItemChanged(position)
202 settingsViewModel.setShouldReloadSettingsList(true)
203 }
204
198 private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() { 205 private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
199 override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean { 206 override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
200 return oldItem.setting.key == newItem.setting.key 207 return oldItem.setting.key == newItem.setting.key
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 769baf744..d7ab0b5d9 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
@@ -66,7 +66,13 @@ class SettingsFragment : Fragment() {
66 args.menuTag 66 args.menuTag
67 ) 67 )
68 68
69 binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) 69 binding.toolbarSettingsLayout.title = if (args.menuTag == Settings.MenuTag.SECTION_ROOT &&
70 args.game != null
71 ) {
72 args.game!!.title
73 } else {
74 getString(args.menuTag.titleId)
75 }
70 binding.listSettings.apply { 76 binding.listSettings.apply {
71 adapter = settingsAdapter 77 adapter = settingsAdapter
72 layoutManager = LinearLayoutManager(requireContext()) 78 layoutManager = LinearLayoutManager(requireContext())
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 7425728c6..a7e965589 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,6 +7,7 @@ import android.content.SharedPreferences
7import android.os.Build 7import android.os.Build
8import android.widget.Toast 8import android.widget.Toast
9import androidx.preference.PreferenceManager 9import androidx.preference.PreferenceManager
10import org.yuzu.yuzu_emu.NativeLibrary
10import org.yuzu.yuzu_emu.R 11import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 12import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 13import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@@ -31,12 +32,27 @@ class SettingsFragmentPresenter(
31 private val preferences: SharedPreferences 32 private val preferences: SharedPreferences
32 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 33 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
33 34
34 // Extension for populating settings list based on paired settings 35 // Extension for altering settings list based on each setting's properties
35 fun ArrayList<SettingsItem>.add(key: String) { 36 fun ArrayList<SettingsItem>.add(key: String) {
36 val item = SettingsItem.settingsItems[key]!! 37 val item = SettingsItem.settingsItems[key]!!
38 if (settingsViewModel.game != null && !item.setting.isSwitchable) {
39 return
40 }
41
42 if (!NativeConfig.isPerGameConfigLoaded() && !NativeLibrary.isRunning()) {
43 item.setting.global = true
44 }
45
37 val pairedSettingKey = item.setting.pairedSettingKey 46 val pairedSettingKey = item.setting.pairedSettingKey
38 if (pairedSettingKey.isNotEmpty()) { 47 if (pairedSettingKey.isNotEmpty()) {
39 val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false) 48 val pairedSettingValue = NativeConfig.getBoolean(
49 pairedSettingKey,
50 if (NativeLibrary.isRunning() && !NativeConfig.isPerGameConfigLoaded()) {
51 !NativeConfig.usingGlobal(pairedSettingKey)
52 } else {
53 NativeConfig.usingGlobal(pairedSettingKey)
54 }
55 )
40 if (!pairedSettingValue) return 56 if (!pairedSettingValue) return
41 } 57 }
42 add(item) 58 add(item)
@@ -153,8 +169,8 @@ class SettingsFragmentPresenter(
153 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 169 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
154 sl.apply { 170 sl.apply {
155 val theme: AbstractIntSetting = object : AbstractIntSetting { 171 val theme: AbstractIntSetting = object : AbstractIntSetting {
156 override val int: Int 172 override fun getInt(needsGlobal: Boolean): Int =
157 get() = preferences.getInt(Settings.PREF_THEME, 0) 173 preferences.getInt(Settings.PREF_THEME, 0)
158 174
159 override fun setInt(value: Int) { 175 override fun setInt(value: Int) {
160 preferences.edit() 176 preferences.edit()
@@ -164,8 +180,8 @@ class SettingsFragmentPresenter(
164 } 180 }
165 181
166 override val key: String = Settings.PREF_THEME 182 override val key: String = Settings.PREF_THEME
167 override val category = Settings.Category.UiGeneral
168 override val isRuntimeModifiable: Boolean = false 183 override val isRuntimeModifiable: Boolean = false
184 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
169 override val defaultValue: Int = 0 185 override val defaultValue: Int = 0
170 override fun reset() { 186 override fun reset() {
171 preferences.edit() 187 preferences.edit()
@@ -197,8 +213,8 @@ class SettingsFragmentPresenter(
197 } 213 }
198 214
199 val themeMode: AbstractIntSetting = object : AbstractIntSetting { 215 val themeMode: AbstractIntSetting = object : AbstractIntSetting {
200 override val int: Int 216 override fun getInt(needsGlobal: Boolean): Int =
201 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) 217 preferences.getInt(Settings.PREF_THEME_MODE, -1)
202 218
203 override fun setInt(value: Int) { 219 override fun setInt(value: Int) {
204 preferences.edit() 220 preferences.edit()
@@ -208,8 +224,8 @@ class SettingsFragmentPresenter(
208 } 224 }
209 225
210 override val key: String = Settings.PREF_THEME_MODE 226 override val key: String = Settings.PREF_THEME_MODE
211 override val category = Settings.Category.UiGeneral
212 override val isRuntimeModifiable: Boolean = false 227 override val isRuntimeModifiable: Boolean = false
228 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
213 override val defaultValue: Int = -1 229 override val defaultValue: Int = -1
214 override fun reset() { 230 override fun reset() {
215 preferences.edit() 231 preferences.edit()
@@ -230,8 +246,8 @@ class SettingsFragmentPresenter(
230 ) 246 )
231 247
232 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { 248 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
233 override val boolean: Boolean 249 override fun getBoolean(needsGlobal: Boolean): Boolean =
234 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 250 preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
235 251
236 override fun setBoolean(value: Boolean) { 252 override fun setBoolean(value: Boolean) {
237 preferences.edit() 253 preferences.edit()
@@ -241,8 +257,10 @@ class SettingsFragmentPresenter(
241 } 257 }
242 258
243 override val key: String = Settings.PREF_BLACK_BACKGROUNDS 259 override val key: String = Settings.PREF_BLACK_BACKGROUNDS
244 override val category = Settings.Category.UiGeneral
245 override val isRuntimeModifiable: Boolean = false 260 override val isRuntimeModifiable: Boolean = false
261 override fun getValueAsString(needsGlobal: Boolean): String =
262 getBoolean().toString()
263
246 override val defaultValue: Boolean = false 264 override val defaultValue: Boolean = false
247 override fun reset() { 265 override fun reset() {
248 preferences.edit() 266 preferences.edit()
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 525f013f8..5ad0899dd 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
@@ -13,6 +13,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
13import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting 13import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
14import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 14import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
15import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 15import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
16import org.yuzu.yuzu_emu.utils.NativeConfig
16 17
17class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 18class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
18 SettingViewHolder(binding.root, adapter) { 19 SettingViewHolder(binding.root, adapter) {
@@ -29,12 +30,23 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
29 } 30 }
30 31
31 binding.textSettingValue.visibility = View.VISIBLE 32 binding.textSettingValue.visibility = View.VISIBLE
32 val epochTime = setting.value 33 val epochTime = setting.getValue()
33 val instant = Instant.ofEpochMilli(epochTime * 1000) 34 val instant = Instant.ofEpochMilli(epochTime * 1000)
34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) 35 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) 36 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
36 binding.textSettingValue.text = dateFormatter.format(zonedTime) 37 binding.textSettingValue.text = dateFormatter.format(zonedTime)
37 38
39 binding.buttonClear.visibility = if (setting.setting.global ||
40 !NativeConfig.isPerGameConfigLoaded()
41 ) {
42 View.GONE
43 } else {
44 View.VISIBLE
45 }
46 binding.buttonClear.setOnClickListener {
47 adapter.onClearClick(setting, bindingAdapterPosition)
48 }
49
38 setStyle(setting.isEditable, binding) 50 setStyle(setting.isEditable, binding)
39 } 51 }
40 52
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 036195624..507184238 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -38,6 +38,7 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
38 binding.textSettingDescription.visibility = View.GONE 38 binding.textSettingDescription.visibility = View.GONE
39 } 39 }
40 binding.textSettingValue.visibility = View.GONE 40 binding.textSettingValue.visibility = View.GONE
41 binding.buttonClear.visibility = View.GONE
41 42
42 setStyle(setting.isEditable, binding) 43 setStyle(setting.isEditable, binding)
43 } 44 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
index 0fd1d2eaa..d26887df8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
@@ -41,6 +41,7 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
41 binding.textSettingName.alpha = opacity 41 binding.textSettingName.alpha = opacity
42 binding.textSettingDescription.alpha = opacity 42 binding.textSettingDescription.alpha = opacity
43 binding.textSettingValue.alpha = opacity 43 binding.textSettingValue.alpha = opacity
44 binding.buttonClear.isEnabled = isEditable
44 } 45 }
45 46
46 fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) { 47 fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) {
@@ -48,5 +49,6 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
48 val opacity = if (isEditable) 1.0f else 0.5f 49 val opacity = if (isEditable) 1.0f else 0.5f
49 binding.textSettingName.alpha = opacity 50 binding.textSettingName.alpha = opacity
50 binding.textSettingDescription.alpha = opacity 51 binding.textSettingDescription.alpha = opacity
52 binding.buttonClear.isEnabled = isEditable
51 } 53 }
52} 54}
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 80d1b22c1..02dab3785 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
@@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting 9import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
10import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.NativeConfig
12 13
13class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 14class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
14 SettingViewHolder(binding.root, adapter) { 15 SettingViewHolder(binding.root, adapter) {
@@ -29,20 +30,31 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
29 val resMgr = binding.textSettingValue.context.resources 30 val resMgr = binding.textSettingValue.context.resources
30 val values = resMgr.getIntArray(item.valuesId) 31 val values = resMgr.getIntArray(item.valuesId)
31 for (i in values.indices) { 32 for (i in values.indices) {
32 if (values[i] == item.selectedValue) { 33 if (values[i] == item.getSelectedValue()) {
33 binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i] 34 binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
34 break 35 break
35 } 36 }
36 } 37 }
37 } else if (item is StringSingleChoiceSetting) { 38 } else if (item is StringSingleChoiceSetting) {
38 for (i in item.values.indices) { 39 for (i in item.values.indices) {
39 if (item.values[i] == item.selectedValue) { 40 if (item.values[i] == item.getSelectedValue()) {
40 binding.textSettingValue.text = item.choices[i] 41 binding.textSettingValue.text = item.choices[i]
41 break 42 break
42 } 43 }
43 } 44 }
44 } 45 }
45 46
47 binding.buttonClear.visibility = if (setting.setting.global ||
48 !NativeConfig.isPerGameConfigLoaded()
49 ) {
50 View.GONE
51 } else {
52 View.VISIBLE
53 }
54 binding.buttonClear.setOnClickListener {
55 adapter.onClearClick(setting, bindingAdapterPosition)
56 }
57
46 setStyle(setting.isEditable, binding) 58 setStyle(setting.isEditable, binding)
47 } 59 }
48 60
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index b83c90100..596c18012 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
9import 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.SliderSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.NativeConfig
12 13
13class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 14class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
14 SettingViewHolder(binding.root, adapter) { 15 SettingViewHolder(binding.root, adapter) {
@@ -26,10 +27,21 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
26 binding.textSettingValue.visibility = View.VISIBLE 27 binding.textSettingValue.visibility = View.VISIBLE
27 binding.textSettingValue.text = String.format( 28 binding.textSettingValue.text = String.format(
28 binding.textSettingValue.context.getString(R.string.value_with_units), 29 binding.textSettingValue.context.getString(R.string.value_with_units),
29 setting.selectedValue, 30 setting.getSelectedValue(),
30 setting.units 31 setting.units
31 ) 32 )
32 33
34 binding.buttonClear.visibility = if (setting.setting.global ||
35 !NativeConfig.isPerGameConfigLoaded()
36 ) {
37 View.GONE
38 } else {
39 View.VISIBLE
40 }
41 binding.buttonClear.setOnClickListener {
42 adapter.onClearClick(setting, bindingAdapterPosition)
43 }
44
33 setStyle(setting.isEditable, binding) 45 setStyle(setting.isEditable, binding)
34 } 46 }
35 47
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index 8100c65dd..20d35a17d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -37,6 +37,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
37 binding.textSettingDescription.visibility = View.GONE 37 binding.textSettingDescription.visibility = View.GONE
38 } 38 }
39 binding.textSettingValue.visibility = View.GONE 39 binding.textSettingValue.visibility = View.GONE
40 binding.buttonClear.visibility = View.GONE
40 } 41 }
41 42
42 override fun onClick(clicked: View) { 43 override fun onClick(clicked: View) {
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 57fdeaa20..d26bf9374 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
@@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
9import 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 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
12import org.yuzu.yuzu_emu.utils.NativeConfig
12 13
13class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : 14class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
14 SettingViewHolder(binding.root, adapter) { 15 SettingViewHolder(binding.root, adapter) {
@@ -27,9 +28,20 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
27 } 28 }
28 29
29 binding.switchWidget.setOnCheckedChangeListener(null) 30 binding.switchWidget.setOnCheckedChangeListener(null)
30 binding.switchWidget.isChecked = setting.checked 31 binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
31 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> 32 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
32 adapter.onBooleanClick(item, binding.switchWidget.isChecked) 33 adapter.onBooleanClick(item, binding.switchWidget.isChecked, bindingAdapterPosition)
34 }
35
36 binding.buttonClear.visibility = if (setting.setting.global ||
37 !NativeConfig.isPerGameConfigLoaded()
38 ) {
39 View.GONE
40 } else {
41 View.VISIBLE
42 }
43 binding.buttonClear.setOnClickListener {
44 adapter.onClearClick(setting, bindingAdapterPosition)
33 } 45 }
34 46
35 setStyle(setting.isEditable, binding) 47 setStyle(setting.isEditable, binding)
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 3ae5b4653..5d523be67 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,15 +3,27 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import android.net.Uri
7import org.yuzu.yuzu_emu.model.Game
6import java.io.* 8import java.io.*
7import org.yuzu.yuzu_emu.utils.DirectoryInitialization 9import org.yuzu.yuzu_emu.utils.DirectoryInitialization
10import org.yuzu.yuzu_emu.utils.FileUtil
11import org.yuzu.yuzu_emu.utils.NativeConfig
8 12
9/** 13/**
10 * Contains static methods for interacting with .ini files in which settings are stored. 14 * Contains static methods for interacting with .ini files in which settings are stored.
11 */ 15 */
12object SettingsFile { 16object SettingsFile {
13 const val FILE_NAME_CONFIG = "config" 17 const val FILE_NAME_CONFIG = "config.ini"
14 18
15 fun getSettingsFile(fileName: String): File = 19 fun getSettingsFile(fileName: String): File =
16 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") 20 File(DirectoryInitialization.userDirectory + "/config/" + fileName)
21
22 fun getCustomSettingsFile(game: Game): File =
23 File(DirectoryInitialization.userDirectory + "/config/custom/" + game.settingsName + ".ini")
24
25 fun loadCustomConfig(game: Game) {
26 val fileName = FileUtil.getFilename(Uri.parse(game.path))
27 NativeConfig.initializePerGameConfig(game.programId, fileName)
28 }
17} 29}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
new file mode 100644
index 000000000..0dce8ad8d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -0,0 +1,214 @@
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.annotation.SuppressLint
7import android.content.Intent
8import android.os.Bundle
9import android.view.LayoutInflater
10import android.view.View
11import android.view.ViewGroup
12import androidx.activity.result.contract.ActivityResultContracts
13import androidx.core.view.ViewCompat
14import androidx.core.view.WindowInsetsCompat
15import androidx.core.view.updatePadding
16import androidx.documentfile.provider.DocumentFile
17import androidx.fragment.app.Fragment
18import androidx.fragment.app.activityViewModels
19import androidx.lifecycle.Lifecycle
20import androidx.lifecycle.lifecycleScope
21import androidx.lifecycle.repeatOnLifecycle
22import androidx.navigation.findNavController
23import androidx.navigation.fragment.navArgs
24import androidx.recyclerview.widget.LinearLayoutManager
25import com.google.android.material.transition.MaterialSharedAxis
26import kotlinx.coroutines.launch
27import org.yuzu.yuzu_emu.R
28import org.yuzu.yuzu_emu.adapters.AddonAdapter
29import org.yuzu.yuzu_emu.databinding.FragmentAddonsBinding
30import org.yuzu.yuzu_emu.model.AddonViewModel
31import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.AddonUtil
33import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
34import java.io.File
35
36class AddonsFragment : Fragment() {
37 private var _binding: FragmentAddonsBinding? = null
38 private val binding get() = _binding!!
39
40 private val homeViewModel: HomeViewModel by activityViewModels()
41 private val addonViewModel: AddonViewModel by activityViewModels()
42
43 private val args by navArgs<AddonsFragmentArgs>()
44
45 override fun onCreate(savedInstanceState: Bundle?) {
46 super.onCreate(savedInstanceState)
47 addonViewModel.onOpenAddons(args.game)
48 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
49 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
50 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
51 }
52
53 override fun onCreateView(
54 inflater: LayoutInflater,
55 container: ViewGroup?,
56 savedInstanceState: Bundle?
57 ): View {
58 _binding = FragmentAddonsBinding.inflate(inflater)
59 return binding.root
60 }
61
62 // This is using the correct scope, lint is just acting up
63 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
64 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
65 super.onViewCreated(view, savedInstanceState)
66 homeViewModel.setNavigationVisibility(visible = false, animated = false)
67 homeViewModel.setStatusBarShadeVisibility(false)
68
69 binding.toolbarAddons.setNavigationOnClickListener {
70 binding.root.findNavController().popBackStack()
71 }
72
73 binding.toolbarAddons.title = getString(R.string.addons_game, args.game.title)
74
75 binding.listAddons.apply {
76 layoutManager = LinearLayoutManager(requireContext())
77 adapter = AddonAdapter()
78 }
79
80 viewLifecycleOwner.lifecycleScope.apply {
81 launch {
82 repeatOnLifecycle(Lifecycle.State.STARTED) {
83 addonViewModel.addonList.collect {
84 (binding.listAddons.adapter as AddonAdapter).submitList(it)
85 }
86 }
87 }
88 launch {
89 repeatOnLifecycle(Lifecycle.State.STARTED) {
90 addonViewModel.showModInstallPicker.collect {
91 if (it) {
92 installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
93 addonViewModel.showModInstallPicker(false)
94 }
95 }
96 }
97 }
98 launch {
99 repeatOnLifecycle(Lifecycle.State.STARTED) {
100 addonViewModel.showModNoticeDialog.collect {
101 if (it) {
102 MessageDialogFragment.newInstance(
103 requireActivity(),
104 titleId = R.string.addon_notice,
105 descriptionId = R.string.addon_notice_description,
106 positiveAction = { addonViewModel.showModInstallPicker(true) }
107 ).show(parentFragmentManager, MessageDialogFragment.TAG)
108 addonViewModel.showModNoticeDialog(false)
109 }
110 }
111 }
112 }
113 }
114
115 binding.buttonInstall.setOnClickListener {
116 ContentTypeSelectionDialogFragment().show(
117 parentFragmentManager,
118 ContentTypeSelectionDialogFragment.TAG
119 )
120 }
121
122 setInsets()
123 }
124
125 override fun onResume() {
126 super.onResume()
127 addonViewModel.refreshAddons()
128 }
129
130 override fun onDestroy() {
131 super.onDestroy()
132 addonViewModel.onCloseAddons()
133 }
134
135 val installAddon =
136 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
137 if (result == null) {
138 return@registerForActivityResult
139 }
140
141 val externalAddonDirectory = DocumentFile.fromTreeUri(requireContext(), result)
142 if (externalAddonDirectory == null) {
143 MessageDialogFragment.newInstance(
144 requireActivity(),
145 titleId = R.string.invalid_directory,
146 descriptionId = R.string.invalid_directory_description
147 ).show(parentFragmentManager, MessageDialogFragment.TAG)
148 return@registerForActivityResult
149 }
150
151 val isValid = externalAddonDirectory.listFiles()
152 .any { AddonUtil.validAddonDirectories.contains(it.name) }
153 val errorMessage = MessageDialogFragment.newInstance(
154 requireActivity(),
155 titleId = R.string.invalid_directory,
156 descriptionId = R.string.invalid_directory_description
157 )
158 if (isValid) {
159 IndeterminateProgressDialogFragment.newInstance(
160 requireActivity(),
161 R.string.installing_game_content,
162 false
163 ) {
164 val parentDirectoryName = externalAddonDirectory.name
165 val internalAddonDirectory =
166 File(args.game.addonDir + parentDirectoryName)
167 try {
168 externalAddonDirectory.copyFilesTo(internalAddonDirectory)
169 } catch (_: Exception) {
170 return@newInstance errorMessage
171 }
172 addonViewModel.refreshAddons()
173 return@newInstance getString(R.string.addon_installed_successfully)
174 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
175 } else {
176 errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
177 }
178 }
179
180 private fun setInsets() =
181 ViewCompat.setOnApplyWindowInsetsListener(
182 binding.root
183 ) { _: View, windowInsets: WindowInsetsCompat ->
184 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
185 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
186
187 val leftInsets = barInsets.left + cutoutInsets.left
188 val rightInsets = barInsets.right + cutoutInsets.right
189
190 val mlpToolbar = binding.toolbarAddons.layoutParams as ViewGroup.MarginLayoutParams
191 mlpToolbar.leftMargin = leftInsets
192 mlpToolbar.rightMargin = rightInsets
193 binding.toolbarAddons.layoutParams = mlpToolbar
194
195 val mlpAddonsList = binding.listAddons.layoutParams as ViewGroup.MarginLayoutParams
196 mlpAddonsList.leftMargin = leftInsets
197 mlpAddonsList.rightMargin = rightInsets
198 binding.listAddons.layoutParams = mlpAddonsList
199 binding.listAddons.updatePadding(
200 bottom = barInsets.bottom +
201 resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
202 )
203
204 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
205 val mlpFab =
206 binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
207 mlpFab.leftMargin = leftInsets + fabSpacing
208 mlpFab.rightMargin = rightInsets + fabSpacing
209 mlpFab.bottomMargin = barInsets.bottom + fabSpacing
210 binding.buttonInstall.layoutParams = mlpFab
211
212 windowInsets
213 }
214}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ContentTypeSelectionDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ContentTypeSelectionDialogFragment.kt
new file mode 100644
index 000000000..c1d8b9ea5
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ContentTypeSelectionDialogFragment.kt
@@ -0,0 +1,68 @@
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.DialogInterface
8import android.os.Bundle
9import androidx.fragment.app.DialogFragment
10import androidx.fragment.app.activityViewModels
11import androidx.preference.PreferenceManager
12import com.google.android.material.dialog.MaterialAlertDialogBuilder
13import org.yuzu.yuzu_emu.R
14import org.yuzu.yuzu_emu.YuzuApplication
15import org.yuzu.yuzu_emu.model.AddonViewModel
16import org.yuzu.yuzu_emu.ui.main.MainActivity
17
18class ContentTypeSelectionDialogFragment : DialogFragment() {
19 private val addonViewModel: AddonViewModel by activityViewModels()
20
21 private val preferences get() =
22 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
23
24 private var selectedItem = 0
25
26 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
27 val launchOptions =
28 arrayOf(getString(R.string.updates_and_dlc), getString(R.string.mods_and_cheats))
29
30 if (savedInstanceState != null) {
31 selectedItem = savedInstanceState.getInt(SELECTED_ITEM)
32 }
33
34 val mainActivity = requireActivity() as MainActivity
35 return MaterialAlertDialogBuilder(requireContext())
36 .setTitle(R.string.select_content_type)
37 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
38 when (selectedItem) {
39 0 -> mainActivity.installGameUpdate.launch(arrayOf("*/*"))
40 else -> {
41 if (!preferences.getBoolean(MOD_NOTICE_SEEN, false)) {
42 preferences.edit().putBoolean(MOD_NOTICE_SEEN, true).apply()
43 addonViewModel.showModNoticeDialog(true)
44 return@setPositiveButton
45 }
46 addonViewModel.showModInstallPicker(true)
47 }
48 }
49 }
50 .setSingleChoiceItems(launchOptions, 0) { _: DialogInterface, i: Int ->
51 selectedItem = i
52 }
53 .setNegativeButton(android.R.string.cancel, null)
54 .show()
55 }
56
57 override fun onSaveInstanceState(outState: Bundle) {
58 super.onSaveInstanceState(outState)
59 outState.putInt(SELECTED_ITEM, selectedItem)
60 }
61
62 companion object {
63 const val TAG = "ContentTypeSelectionDialogFragment"
64
65 private const val SELECTED_ITEM = "SelectedItem"
66 private const val MOD_NOTICE_SEEN = "ModNoticeSeen"
67 }
68}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index df21d74b2..cc71254dc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.lifecycleScope 16import androidx.lifecycle.lifecycleScope
17import androidx.navigation.findNavController 17import androidx.navigation.findNavController
18import androidx.navigation.fragment.navArgs
18import androidx.recyclerview.widget.GridLayoutManager 19import androidx.recyclerview.widget.GridLayoutManager
19import com.google.android.material.transition.MaterialSharedAxis 20import com.google.android.material.transition.MaterialSharedAxis
20import kotlinx.coroutines.flow.collectLatest 21import kotlinx.coroutines.flow.collectLatest
@@ -36,6 +37,8 @@ class DriverManagerFragment : Fragment() {
36 private val homeViewModel: HomeViewModel by activityViewModels() 37 private val homeViewModel: HomeViewModel by activityViewModels()
37 private val driverViewModel: DriverViewModel by activityViewModels() 38 private val driverViewModel: DriverViewModel by activityViewModels()
38 39
40 private val args by navArgs<DriverManagerFragmentArgs>()
41
39 override fun onCreate(savedInstanceState: Bundle?) { 42 override fun onCreate(savedInstanceState: Bundle?) {
40 super.onCreate(savedInstanceState) 43 super.onCreate(savedInstanceState)
41 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) 44 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
@@ -57,7 +60,9 @@ class DriverManagerFragment : Fragment() {
57 homeViewModel.setNavigationVisibility(visible = false, animated = true) 60 homeViewModel.setNavigationVisibility(visible = false, animated = true)
58 homeViewModel.setStatusBarShadeVisibility(visible = false) 61 homeViewModel.setStatusBarShadeVisibility(visible = false)
59 62
60 if (!driverViewModel.isInteractionAllowed) { 63 driverViewModel.onOpenDriverManager(args.game)
64
65 if (!driverViewModel.isInteractionAllowed.value) {
61 DriversLoadingDialogFragment().show( 66 DriversLoadingDialogFragment().show(
62 childFragmentManager, 67 childFragmentManager,
63 DriversLoadingDialogFragment.TAG 68 DriversLoadingDialogFragment.TAG
@@ -102,10 +107,9 @@ class DriverManagerFragment : Fragment() {
102 setInsets() 107 setInsets()
103 } 108 }
104 109
105 // Start installing requested driver 110 override fun onDestroy() {
106 override fun onStop() { 111 super.onDestroy()
107 super.onStop() 112 driverViewModel.onCloseDriverManager(args.game)
108 driverViewModel.onCloseDriverManager()
109 } 113 }
110 114
111 private fun setInsets() = 115 private fun setInsets() =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
index f8c34346a..6a47b29f0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
@@ -47,25 +47,9 @@ class DriversLoadingDialogFragment : DialogFragment() {
47 viewLifecycleOwner.lifecycleScope.apply { 47 viewLifecycleOwner.lifecycleScope.apply {
48 launch { 48 launch {
49 repeatOnLifecycle(Lifecycle.State.RESUMED) { 49 repeatOnLifecycle(Lifecycle.State.RESUMED) {
50 driverViewModel.areDriversLoading.collect { checkForDismiss() } 50 driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
51 } 51 }
52 } 52 }
53 launch {
54 repeatOnLifecycle(Lifecycle.State.RESUMED) {
55 driverViewModel.isDriverReady.collect { checkForDismiss() }
56 }
57 }
58 launch {
59 repeatOnLifecycle(Lifecycle.State.RESUMED) {
60 driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
61 }
62 }
63 }
64 }
65
66 private fun checkForDismiss() {
67 if (driverViewModel.isInteractionAllowed) {
68 dismiss()
69 } 53 }
70 } 54 }
71 55
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 734c1d5ca..d7b38f62d 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
@@ -52,6 +52,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
52import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding 52import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
53import org.yuzu.yuzu_emu.features.settings.model.IntSetting 53import org.yuzu.yuzu_emu.features.settings.model.IntSetting
54import org.yuzu.yuzu_emu.features.settings.model.Settings 54import org.yuzu.yuzu_emu.features.settings.model.Settings
55import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
55import org.yuzu.yuzu_emu.model.DriverViewModel 56import org.yuzu.yuzu_emu.model.DriverViewModel
56import org.yuzu.yuzu_emu.model.Game 57import org.yuzu.yuzu_emu.model.Game
57import org.yuzu.yuzu_emu.model.EmulationViewModel 58import org.yuzu.yuzu_emu.model.EmulationViewModel
@@ -127,6 +128,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
127 return 128 return
128 } 129 }
129 130
131 // Always load custom settings when launching a game from an intent
132 if (args.custom || intentGame != null) {
133 SettingsFile.loadCustomConfig(game)
134 NativeConfig.unloadPerGameConfig()
135 } else {
136 NativeConfig.reloadGlobalConfig()
137 }
138
139 // Install the selected driver asynchronously as the game starts
140 driverViewModel.onLaunchGame()
141
130 // So this fragment doesn't restart on configuration changes; i.e. rotation. 142 // So this fragment doesn't restart on configuration changes; i.e. rotation.
131 retainInstance = true 143 retainInstance = true
132 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 144 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
@@ -217,6 +229,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
217 true 229 true
218 } 230 }
219 231
232 R.id.menu_settings_per_game -> {
233 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
234 args.game,
235 Settings.MenuTag.SECTION_ROOT
236 )
237 binding.root.findNavController().navigate(action)
238 true
239 }
240
220 R.id.menu_overlay_controls -> { 241 R.id.menu_overlay_controls -> {
221 showOverlayOptions() 242 showOverlayOptions()
222 true 243 true
@@ -332,15 +353,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
332 } 353 }
333 launch { 354 launch {
334 repeatOnLifecycle(Lifecycle.State.RESUMED) { 355 repeatOnLifecycle(Lifecycle.State.RESUMED) {
335 driverViewModel.isDriverReady.collect { 356 driverViewModel.isInteractionAllowed.collect {
336 if (it && !emulationState.isRunning) { 357 if (it) {
337 if (!DirectoryInitialization.areDirectoriesReady) { 358 onEmulationStart()
338 DirectoryInitialization.start()
339 }
340
341 updateScreenLayout()
342
343 emulationState.run(emulationActivity!!.isActivityRecreated)
344 } 359 }
345 } 360 }
346 } 361 }
@@ -348,6 +363,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
348 } 363 }
349 } 364 }
350 365
366 private fun onEmulationStart() {
367 if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
368 if (!DirectoryInitialization.areDirectoriesReady) {
369 DirectoryInitialization.start()
370 }
371
372 updateScreenLayout()
373
374 emulationState.run(emulationActivity!!.isActivityRecreated)
375 }
376 }
377
351 override fun onConfigurationChanged(newConfig: Configuration) { 378 override fun onConfigurationChanged(newConfig: Configuration) {
352 super.onConfigurationChanged(newConfig) 379 super.onConfigurationChanged(newConfig)
353 if (_binding == null) { 380 if (_binding == null) {
@@ -435,7 +462,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
435 @SuppressLint("SourceLockedOrientationActivity") 462 @SuppressLint("SourceLockedOrientationActivity")
436 private fun updateOrientation() { 463 private fun updateOrientation() {
437 emulationActivity?.let { 464 emulationActivity?.let {
438 it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) { 465 it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.getInt()) {
439 Settings.LayoutOption_MobileLandscape -> 466 Settings.LayoutOption_MobileLandscape ->
440 ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 467 ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
441 Settings.LayoutOption_MobilePortrait -> 468 Settings.LayoutOption_MobilePortrait ->
@@ -617,7 +644,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
617 @SuppressLint("SourceLockedOrientationActivity") 644 @SuppressLint("SourceLockedOrientationActivity")
618 private fun startConfiguringControls() { 645 private fun startConfiguringControls() {
619 // Lock the current orientation to prevent editing inconsistencies 646 // Lock the current orientation to prevent editing inconsistencies
620 if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) { 647 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) {
621 emulationActivity?.let { 648 emulationActivity?.let {
622 it.requestedOrientation = 649 it.requestedOrientation =
623 if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { 650 if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -635,7 +662,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
635 binding.doneControlConfig.visibility = View.GONE 662 binding.doneControlConfig.visibility = View.GONE
636 binding.surfaceInputOverlay.setIsInEditMode(false) 663 binding.surfaceInputOverlay.setIsInEditMode(false)
637 // Unlock the orientation if it was locked for editing 664 // Unlock the orientation if it was locked for editing
638 if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) { 665 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) {
639 emulationActivity?.let { 666 emulationActivity?.let {
640 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 667 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
641 } 668 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt
index b6c2e4635..1ea1e036e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt
@@ -13,6 +13,7 @@ import org.yuzu.yuzu_emu.R
13import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding 13import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding
14import org.yuzu.yuzu_emu.model.GameDir 14import org.yuzu.yuzu_emu.model.GameDir
15import org.yuzu.yuzu_emu.model.GamesViewModel 15import org.yuzu.yuzu_emu.model.GamesViewModel
16import org.yuzu.yuzu_emu.utils.NativeConfig
16import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable 17import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
17 18
18class GameFolderPropertiesDialogFragment : DialogFragment() { 19class GameFolderPropertiesDialogFragment : DialogFragment() {
@@ -49,6 +50,11 @@ class GameFolderPropertiesDialogFragment : DialogFragment() {
49 .show() 50 .show()
50 } 51 }
51 52
53 override fun onStop() {
54 super.onStop()
55 NativeConfig.saveGlobalConfig()
56 }
57
52 override fun onSaveInstanceState(outState: Bundle) { 58 override fun onSaveInstanceState(outState: Bundle) {
53 super.onSaveInstanceState(outState) 59 super.onSaveInstanceState(outState)
54 outState.putBoolean(DEEP_SCAN, deepScan) 60 outState.putBoolean(DEEP_SCAN, deepScan)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
new file mode 100644
index 000000000..fa2a4c9f9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
@@ -0,0 +1,148 @@
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.content.ClipData
7import android.content.ClipboardManager
8import android.content.Context
9import android.net.Uri
10import android.os.Build
11import android.os.Bundle
12import android.view.LayoutInflater
13import android.view.View
14import android.view.ViewGroup
15import android.widget.Toast
16import androidx.core.view.ViewCompat
17import androidx.core.view.WindowInsetsCompat
18import androidx.core.view.updatePadding
19import androidx.fragment.app.Fragment
20import androidx.fragment.app.activityViewModels
21import androidx.navigation.findNavController
22import androidx.navigation.fragment.navArgs
23import com.google.android.material.transition.MaterialSharedAxis
24import org.yuzu.yuzu_emu.R
25import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
26import org.yuzu.yuzu_emu.model.HomeViewModel
27import org.yuzu.yuzu_emu.utils.GameMetadata
28
29class GameInfoFragment : Fragment() {
30 private var _binding: FragmentGameInfoBinding? = null
31 private val binding get() = _binding!!
32
33 private val homeViewModel: HomeViewModel by activityViewModels()
34
35 private val args by navArgs<GameInfoFragmentArgs>()
36
37 override fun onCreate(savedInstanceState: Bundle?) {
38 super.onCreate(savedInstanceState)
39 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
40 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
41 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
42
43 // Check for an up-to-date version string
44 args.game.version = GameMetadata.getVersion(args.game.path, true)
45 }
46
47 override fun onCreateView(
48 inflater: LayoutInflater,
49 container: ViewGroup?,
50 savedInstanceState: Bundle?
51 ): View {
52 _binding = FragmentGameInfoBinding.inflate(inflater)
53 return binding.root
54 }
55
56 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
57 super.onViewCreated(view, savedInstanceState)
58 homeViewModel.setNavigationVisibility(visible = false, animated = false)
59 homeViewModel.setStatusBarShadeVisibility(false)
60
61 binding.apply {
62 toolbarInfo.title = args.game.title
63 toolbarInfo.setNavigationOnClickListener {
64 view.findNavController().popBackStack()
65 }
66
67 val pathString = Uri.parse(args.game.path).path ?: ""
68 path.setHint(R.string.path)
69 pathField.setText(pathString)
70 pathField.setOnClickListener { copyToClipboard(getString(R.string.path), pathString) }
71
72 programId.setHint(R.string.program_id)
73 programIdField.setText(args.game.programIdHex)
74 programIdField.setOnClickListener {
75 copyToClipboard(getString(R.string.program_id), args.game.programIdHex)
76 }
77
78 if (args.game.developer.isNotEmpty()) {
79 developer.setHint(R.string.developer)
80 developerField.setText(args.game.developer)
81 developerField.setOnClickListener {
82 copyToClipboard(getString(R.string.developer), args.game.developer)
83 }
84 } else {
85 developer.visibility = View.GONE
86 }
87
88 version.setHint(R.string.version)
89 versionField.setText(args.game.version)
90 versionField.setOnClickListener {
91 copyToClipboard(getString(R.string.version), args.game.version)
92 }
93
94 buttonCopy.setOnClickListener {
95 val details = """
96 ${args.game.title}
97 ${getString(R.string.path)} - $pathString
98 ${getString(R.string.program_id)} - ${args.game.programIdHex}
99 ${getString(R.string.developer)} - ${args.game.developer}
100 ${getString(R.string.version)} - ${args.game.version}
101 """.trimIndent()
102 copyToClipboard(args.game.title, details)
103 }
104 }
105
106 setInsets()
107 }
108
109 private fun copyToClipboard(label: String, body: String) {
110 val clipBoard =
111 requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
112 val clip = ClipData.newPlainText(label, body)
113 clipBoard.setPrimaryClip(clip)
114
115 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
116 Toast.makeText(
117 requireContext(),
118 R.string.copied_to_clipboard,
119 Toast.LENGTH_SHORT
120 ).show()
121 }
122 }
123
124 private fun setInsets() =
125 ViewCompat.setOnApplyWindowInsetsListener(
126 binding.root
127 ) { _: View, windowInsets: WindowInsetsCompat ->
128 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
129 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
130
131 val leftInsets = barInsets.left + cutoutInsets.left
132 val rightInsets = barInsets.right + cutoutInsets.right
133
134 val mlpToolbar = binding.toolbarInfo.layoutParams as ViewGroup.MarginLayoutParams
135 mlpToolbar.leftMargin = leftInsets
136 mlpToolbar.rightMargin = rightInsets
137 binding.toolbarInfo.layoutParams = mlpToolbar
138
139 val mlpScrollAbout = binding.scrollInfo.layoutParams as ViewGroup.MarginLayoutParams
140 mlpScrollAbout.leftMargin = leftInsets
141 mlpScrollAbout.rightMargin = rightInsets
142 binding.scrollInfo.layoutParams = mlpScrollAbout
143
144 binding.contentInfo.updatePadding(bottom = barInsets.bottom)
145
146 windowInsets
147 }
148}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
new file mode 100644
index 000000000..b1d3c0040
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -0,0 +1,456 @@
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.annotation.SuppressLint
7import android.os.Bundle
8import android.text.TextUtils
9import android.view.LayoutInflater
10import android.view.View
11import android.view.ViewGroup
12import android.widget.Toast
13import androidx.activity.result.contract.ActivityResultContracts
14import androidx.core.view.ViewCompat
15import androidx.core.view.WindowInsetsCompat
16import androidx.core.view.updatePadding
17import androidx.fragment.app.Fragment
18import androidx.fragment.app.activityViewModels
19import androidx.lifecycle.Lifecycle
20import androidx.lifecycle.lifecycleScope
21import androidx.lifecycle.repeatOnLifecycle
22import androidx.navigation.findNavController
23import androidx.navigation.fragment.navArgs
24import androidx.recyclerview.widget.GridLayoutManager
25import com.google.android.material.transition.MaterialSharedAxis
26import kotlinx.coroutines.Dispatchers
27import kotlinx.coroutines.launch
28import kotlinx.coroutines.withContext
29import org.yuzu.yuzu_emu.HomeNavigationDirections
30import org.yuzu.yuzu_emu.R
31import org.yuzu.yuzu_emu.YuzuApplication
32import org.yuzu.yuzu_emu.adapters.GamePropertiesAdapter
33import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
34import org.yuzu.yuzu_emu.features.settings.model.Settings
35import org.yuzu.yuzu_emu.model.DriverViewModel
36import org.yuzu.yuzu_emu.model.GameProperty
37import org.yuzu.yuzu_emu.model.GamesViewModel
38import org.yuzu.yuzu_emu.model.HomeViewModel
39import org.yuzu.yuzu_emu.model.InstallableProperty
40import org.yuzu.yuzu_emu.model.SubmenuProperty
41import org.yuzu.yuzu_emu.model.TaskState
42import org.yuzu.yuzu_emu.utils.DirectoryInitialization
43import org.yuzu.yuzu_emu.utils.FileUtil
44import org.yuzu.yuzu_emu.utils.GameIconUtils
45import org.yuzu.yuzu_emu.utils.GpuDriverHelper
46import org.yuzu.yuzu_emu.utils.MemoryUtil
47import java.io.BufferedInputStream
48import java.io.BufferedOutputStream
49import java.io.File
50
51class GamePropertiesFragment : Fragment() {
52 private var _binding: FragmentGamePropertiesBinding? = null
53 private val binding get() = _binding!!
54
55 private val homeViewModel: HomeViewModel by activityViewModels()
56 private val gamesViewModel: GamesViewModel by activityViewModels()
57 private val driverViewModel: DriverViewModel by activityViewModels()
58
59 private val args by navArgs<GamePropertiesFragmentArgs>()
60
61 override fun onCreate(savedInstanceState: Bundle?) {
62 super.onCreate(savedInstanceState)
63 enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, true)
64 returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
65 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
66 }
67
68 override fun onCreateView(
69 inflater: LayoutInflater,
70 container: ViewGroup?,
71 savedInstanceState: Bundle?
72 ): View {
73 _binding = FragmentGamePropertiesBinding.inflate(layoutInflater)
74 return binding.root
75 }
76
77 // This is using the correct scope, lint is just acting up
78 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
79 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
80 super.onViewCreated(view, savedInstanceState)
81 homeViewModel.setNavigationVisibility(visible = false, animated = true)
82 homeViewModel.setStatusBarShadeVisibility(true)
83
84 binding.buttonBack.setOnClickListener {
85 view.findNavController().popBackStack()
86 }
87
88 GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
89 binding.title.text = args.game.title
90 binding.title.postDelayed(
91 {
92 binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE
93 binding.title.isSelected = true
94 },
95 3000
96 )
97
98 binding.buttonStart.setOnClickListener {
99 LaunchGameDialogFragment.newInstance(args.game)
100 .show(childFragmentManager, LaunchGameDialogFragment.TAG)
101 }
102
103 reloadList()
104
105 viewLifecycleOwner.lifecycleScope.apply {
106 launch {
107 repeatOnLifecycle(Lifecycle.State.STARTED) {
108 homeViewModel.openImportSaves.collect {
109 if (it) {
110 importSaves.launch(arrayOf("application/zip"))
111 homeViewModel.setOpenImportSaves(false)
112 }
113 }
114 }
115 }
116 launch {
117 repeatOnLifecycle(Lifecycle.State.STARTED) {
118 homeViewModel.reloadPropertiesList.collect {
119 if (it) {
120 reloadList()
121 homeViewModel.reloadPropertiesList(false)
122 }
123 }
124 }
125 }
126 }
127
128 setInsets()
129 }
130
131 override fun onDestroy() {
132 super.onDestroy()
133 gamesViewModel.reloadGames(true)
134 }
135
136 private fun reloadList() {
137 _binding ?: return
138
139 driverViewModel.updateDriverNameForGame(args.game)
140 val properties = mutableListOf<GameProperty>().apply {
141 add(
142 SubmenuProperty(
143 R.string.info,
144 R.string.info_description,
145 R.drawable.ic_info_outline
146 ) {
147 val action = GamePropertiesFragmentDirections
148 .actionPerGamePropertiesFragmentToGameInfoFragment(args.game)
149 binding.root.findNavController().navigate(action)
150 }
151 )
152 add(
153 SubmenuProperty(
154 R.string.preferences_settings,
155 R.string.per_game_settings_description,
156 R.drawable.ic_settings
157 ) {
158 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
159 args.game,
160 Settings.MenuTag.SECTION_ROOT
161 )
162 binding.root.findNavController().navigate(action)
163 }
164 )
165
166 if (GpuDriverHelper.supportsCustomDriverLoading()) {
167 add(
168 SubmenuProperty(
169 R.string.gpu_driver_manager,
170 R.string.install_gpu_driver_description,
171 R.drawable.ic_build,
172 detailsFlow = driverViewModel.selectedDriverTitle
173 ) {
174 val action = GamePropertiesFragmentDirections
175 .actionPerGamePropertiesFragmentToDriverManagerFragment(args.game)
176 binding.root.findNavController().navigate(action)
177 }
178 )
179 }
180
181 if (!args.game.isHomebrew) {
182 add(
183 SubmenuProperty(
184 R.string.add_ons,
185 R.string.add_ons_description,
186 R.drawable.ic_edit
187 ) {
188 val action = GamePropertiesFragmentDirections
189 .actionPerGamePropertiesFragmentToAddonsFragment(args.game)
190 binding.root.findNavController().navigate(action)
191 }
192 )
193 add(
194 InstallableProperty(
195 R.string.save_data,
196 R.string.save_data_description,
197 R.drawable.ic_save,
198 {
199 MessageDialogFragment.newInstance(
200 requireActivity(),
201 titleId = R.string.import_save_warning,
202 descriptionId = R.string.import_save_warning_description,
203 positiveAction = { homeViewModel.setOpenImportSaves(true) }
204 ).show(parentFragmentManager, MessageDialogFragment.TAG)
205 },
206 if (File(args.game.saveDir).exists()) {
207 { exportSaves.launch(args.game.saveZipName) }
208 } else {
209 null
210 }
211 )
212 )
213
214 val saveDirFile = File(args.game.saveDir)
215 if (saveDirFile.exists()) {
216 add(
217 SubmenuProperty(
218 R.string.delete_save_data,
219 R.string.delete_save_data_description,
220 R.drawable.ic_delete,
221 action = {
222 MessageDialogFragment.newInstance(
223 requireActivity(),
224 titleId = R.string.delete_save_data,
225 descriptionId = R.string.delete_save_data_warning_description,
226 positiveAction = {
227 File(args.game.saveDir).deleteRecursively()
228 Toast.makeText(
229 YuzuApplication.appContext,
230 R.string.save_data_deleted_successfully,
231 Toast.LENGTH_SHORT
232 ).show()
233 homeViewModel.reloadPropertiesList(true)
234 }
235 ).show(parentFragmentManager, MessageDialogFragment.TAG)
236 }
237 )
238 )
239 }
240
241 val shaderCacheDir = File(
242 DirectoryInitialization.userDirectory +
243 "/shader/" + args.game.settingsName.lowercase()
244 )
245 if (shaderCacheDir.exists()) {
246 add(
247 SubmenuProperty(
248 R.string.clear_shader_cache,
249 R.string.clear_shader_cache_description,
250 R.drawable.ic_delete,
251 {
252 if (shaderCacheDir.exists()) {
253 val bytes = shaderCacheDir.walkTopDown().filter { it.isFile }
254 .map { it.length() }.sum()
255 MemoryUtil.bytesToSizeUnit(bytes.toFloat())
256 } else {
257 MemoryUtil.bytesToSizeUnit(0f)
258 }
259 }
260 ) {
261 MessageDialogFragment.newInstance(
262 requireActivity(),
263 titleId = R.string.clear_shader_cache,
264 descriptionId = R.string.clear_shader_cache_warning_description,
265 positiveAction = {
266 shaderCacheDir.deleteRecursively()
267 Toast.makeText(
268 YuzuApplication.appContext,
269 R.string.cleared_shaders_successfully,
270 Toast.LENGTH_SHORT
271 ).show()
272 homeViewModel.reloadPropertiesList(true)
273 }
274 ).show(parentFragmentManager, MessageDialogFragment.TAG)
275 }
276 )
277 }
278 }
279 }
280 binding.listProperties.apply {
281 layoutManager =
282 GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
283 adapter = GamePropertiesAdapter(viewLifecycleOwner, properties)
284 }
285 }
286
287 override fun onResume() {
288 super.onResume()
289 driverViewModel.updateDriverNameForGame(args.game)
290 }
291
292 private fun setInsets() =
293 ViewCompat.setOnApplyWindowInsetsListener(
294 binding.root
295 ) { _: View, windowInsets: WindowInsetsCompat ->
296 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
297 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
298
299 val leftInsets = barInsets.left + cutoutInsets.left
300 val rightInsets = barInsets.right + cutoutInsets.right
301
302 val smallLayout = resources.getBoolean(R.bool.small_layout)
303 if (smallLayout) {
304 val mlpListAll =
305 binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
306 mlpListAll.leftMargin = leftInsets
307 mlpListAll.rightMargin = rightInsets
308 binding.listAll.layoutParams = mlpListAll
309 } else {
310 if (ViewCompat.getLayoutDirection(binding.root) ==
311 ViewCompat.LAYOUT_DIRECTION_LTR
312 ) {
313 val mlpListAll =
314 binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
315 mlpListAll.rightMargin = rightInsets
316 binding.listAll.layoutParams = mlpListAll
317
318 val mlpIconLayout =
319 binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
320 mlpIconLayout.topMargin = barInsets.top
321 mlpIconLayout.leftMargin = leftInsets
322 binding.iconLayout!!.layoutParams = mlpIconLayout
323 } else {
324 val mlpListAll =
325 binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
326 mlpListAll.leftMargin = leftInsets
327 binding.listAll.layoutParams = mlpListAll
328
329 val mlpIconLayout =
330 binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
331 mlpIconLayout.topMargin = barInsets.top
332 mlpIconLayout.rightMargin = rightInsets
333 binding.iconLayout!!.layoutParams = mlpIconLayout
334 }
335 }
336
337 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
338 val mlpFab =
339 binding.buttonStart.layoutParams as ViewGroup.MarginLayoutParams
340 mlpFab.leftMargin = leftInsets + fabSpacing
341 mlpFab.rightMargin = rightInsets + fabSpacing
342 mlpFab.bottomMargin = barInsets.bottom + fabSpacing
343 binding.buttonStart.layoutParams = mlpFab
344
345 binding.layoutAll.updatePadding(
346 top = barInsets.top,
347 bottom = barInsets.bottom +
348 resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
349 )
350
351 windowInsets
352 }
353
354 private val importSaves =
355 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
356 if (result == null) {
357 return@registerForActivityResult
358 }
359
360 val inputZip = requireContext().contentResolver.openInputStream(result)
361 val savesFolder = File(args.game.saveDir)
362 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
363 cacheSaveDir.mkdir()
364
365 if (inputZip == null) {
366 Toast.makeText(
367 YuzuApplication.appContext,
368 getString(R.string.fatal_error),
369 Toast.LENGTH_LONG
370 ).show()
371 return@registerForActivityResult
372 }
373
374 IndeterminateProgressDialogFragment.newInstance(
375 requireActivity(),
376 R.string.save_files_importing,
377 false
378 ) {
379 try {
380 FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
381 val files = cacheSaveDir.listFiles()
382 var savesFolderFile: File? = null
383 if (files != null) {
384 val savesFolderName = args.game.programIdHex
385 for (file in files) {
386 if (file.isDirectory && file.name == savesFolderName) {
387 savesFolderFile = file
388 break
389 }
390 }
391 }
392
393 if (savesFolderFile != null) {
394 savesFolder.deleteRecursively()
395 savesFolder.mkdir()
396 savesFolderFile.copyRecursively(savesFolder)
397 savesFolderFile.deleteRecursively()
398 }
399
400 withContext(Dispatchers.Main) {
401 if (savesFolderFile == null) {
402 MessageDialogFragment.newInstance(
403 requireActivity(),
404 titleId = R.string.save_file_invalid_zip_structure,
405 descriptionId = R.string.save_file_invalid_zip_structure_description
406 ).show(parentFragmentManager, MessageDialogFragment.TAG)
407 return@withContext
408 }
409 Toast.makeText(
410 YuzuApplication.appContext,
411 getString(R.string.save_file_imported_success),
412 Toast.LENGTH_LONG
413 ).show()
414 homeViewModel.reloadPropertiesList(true)
415 }
416
417 cacheSaveDir.deleteRecursively()
418 } catch (e: Exception) {
419 Toast.makeText(
420 YuzuApplication.appContext,
421 getString(R.string.fatal_error),
422 Toast.LENGTH_LONG
423 ).show()
424 }
425 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
426 }
427
428 /**
429 * Exports the save file located in the given folder path by creating a zip file and opening a
430 * file picker to save.
431 */
432 private val exportSaves = registerForActivityResult(
433 ActivityResultContracts.CreateDocument("application/zip")
434 ) { result ->
435 if (result == null) {
436 return@registerForActivityResult
437 }
438
439 IndeterminateProgressDialogFragment.newInstance(
440 requireActivity(),
441 R.string.save_files_exporting,
442 false
443 ) {
444 val saveLocation = args.game.saveDir
445 val zipResult = FileUtil.zipFromInternalStorage(
446 File(saveLocation),
447 saveLocation.replaceAfterLast("/", ""),
448 BufferedOutputStream(requireContext().contentResolver.openOutputStream(result))
449 )
450 return@newInstance when (zipResult) {
451 TaskState.Completed -> getString(R.string.export_success)
452 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
453 }
454 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
455 }
456}
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 3addc2e63..6ddd758e6 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,6 +68,9 @@ class HomeSettingsFragment : Fragment() {
68 } 68 }
69 69
70 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 70 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
71 super.onViewCreated(view, savedInstanceState)
72 homeViewModel.setNavigationVisibility(visible = true, animated = true)
73 homeViewModel.setStatusBarShadeVisibility(visible = true)
71 mainActivity = requireActivity() as MainActivity 74 mainActivity = requireActivity() as MainActivity
72 75
73 val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply { 76 val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
@@ -91,13 +94,14 @@ class HomeSettingsFragment : Fragment() {
91 R.string.install_gpu_driver_description, 94 R.string.install_gpu_driver_description,
92 R.drawable.ic_build, 95 R.drawable.ic_build,
93 { 96 {
94 binding.root.findNavController() 97 val action = HomeSettingsFragmentDirections
95 .navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment) 98 .actionHomeSettingsFragmentToDriverManagerFragment(null)
99 binding.root.findNavController().navigate(action)
96 }, 100 },
97 { GpuDriverHelper.supportsCustomDriverLoading() }, 101 { GpuDriverHelper.supportsCustomDriverLoading() },
98 R.string.custom_driver_not_supported, 102 R.string.custom_driver_not_supported,
99 R.string.custom_driver_not_supported_description, 103 R.string.custom_driver_not_supported_description,
100 driverViewModel.selectedDriverMetadata 104 driverViewModel.selectedDriverTitle
101 ) 105 )
102 ) 106 )
103 add( 107 add(
@@ -212,8 +216,11 @@ class HomeSettingsFragment : Fragment() {
212 override fun onStart() { 216 override fun onStart() {
213 super.onStart() 217 super.onStart()
214 exitTransition = null 218 exitTransition = null
215 homeViewModel.setNavigationVisibility(visible = true, animated = true) 219 }
216 homeViewModel.setStatusBarShadeVisibility(visible = true) 220
221 override fun onResume() {
222 super.onResume()
223 driverViewModel.updateDriverNameForGame(null)
217 } 224 }
218 225
219 override fun onDestroyView() { 226 override fun onDestroyView() {
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 7e467814d..8847e5531 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
@@ -122,7 +122,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
122 activity: FragmentActivity, 122 activity: FragmentActivity,
123 titleId: Int, 123 titleId: Int,
124 cancellable: Boolean = false, 124 cancellable: Boolean = false,
125 task: () -> Any 125 task: suspend () -> Any
126 ): IndeterminateProgressDialogFragment { 126 ): IndeterminateProgressDialogFragment {
127 val dialog = IndeterminateProgressDialogFragment() 127 val dialog = IndeterminateProgressDialogFragment()
128 val args = Bundle() 128 val args = Bundle()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 6940fc757..569727b90 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -21,8 +21,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
21import org.yuzu.yuzu_emu.model.HomeViewModel 21import org.yuzu.yuzu_emu.model.HomeViewModel
22import org.yuzu.yuzu_emu.model.Installable 22import org.yuzu.yuzu_emu.model.Installable
23import org.yuzu.yuzu_emu.ui.main.MainActivity 23import org.yuzu.yuzu_emu.ui.main.MainActivity
24import java.time.LocalDateTime
25import java.time.format.DateTimeFormatter
26 24
27class InstallableFragment : Fragment() { 25class InstallableFragment : Fragment() {
28 private var _binding: FragmentInstallablesBinding? = null 26 private var _binding: FragmentInstallablesBinding? = null
@@ -75,28 +73,6 @@ class InstallableFragment : Fragment() {
75 R.string.install_firmware_description, 73 R.string.install_firmware_description,
76 install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) } 74 install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
77 ), 75 ),
78 if (mainActivity.savesFolderRoot != "") {
79 Installable(
80 R.string.manage_save_data,
81 R.string.import_export_saves_description,
82 install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
83 export = {
84 mainActivity.exportSaves.launch(
85 "yuzu saves - ${
86 LocalDateTime.now().format(
87 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
88 )
89 }.zip"
90 )
91 }
92 )
93 } else {
94 Installable(
95 R.string.manage_save_data,
96 R.string.import_export_saves_description,
97 install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }
98 )
99 },
100 Installable( 76 Installable(
101 R.string.install_prod_keys, 77 R.string.install_prod_keys,
102 R.string.install_prod_keys_description, 78 R.string.install_prod_keys_description,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LaunchGameDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LaunchGameDialogFragment.kt
new file mode 100644
index 000000000..e1ac46c48
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LaunchGameDialogFragment.kt
@@ -0,0 +1,61 @@
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.DialogInterface
8import android.os.Bundle
9import androidx.fragment.app.DialogFragment
10import androidx.navigation.fragment.findNavController
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.HomeNavigationDirections
13import org.yuzu.yuzu_emu.R
14import org.yuzu.yuzu_emu.model.Game
15import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
16
17class LaunchGameDialogFragment : DialogFragment() {
18 private var selectedItem = 1
19
20 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
21 val game = requireArguments().parcelable<Game>(GAME)
22 val launchOptions = arrayOf(getString(R.string.global), getString(R.string.custom))
23
24 if (savedInstanceState != null) {
25 selectedItem = savedInstanceState.getInt(SELECTED_ITEM)
26 }
27
28 return MaterialAlertDialogBuilder(requireContext())
29 .setTitle(R.string.launch_options)
30 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
31 val action = HomeNavigationDirections
32 .actionGlobalEmulationActivity(game, selectedItem != 0)
33 requireParentFragment().findNavController().navigate(action)
34 }
35 .setSingleChoiceItems(launchOptions, 1) { _: DialogInterface, i: Int ->
36 selectedItem = i
37 }
38 .setNegativeButton(android.R.string.cancel, null)
39 .show()
40 }
41
42 override fun onSaveInstanceState(outState: Bundle) {
43 super.onSaveInstanceState(outState)
44 outState.putInt(SELECTED_ITEM, selectedItem)
45 }
46
47 companion object {
48 const val TAG = "LaunchGameDialogFragment"
49
50 const val GAME = "Game"
51 const val SELECTED_ITEM = "SelectedItem"
52
53 fun newInstance(game: Game): LaunchGameDialogFragment {
54 val args = Bundle()
55 args.putParcelable(GAME, game)
56 val fragment = LaunchGameDialogFragment()
57 fragment.arguments = args
58 return fragment
59 }
60 }
61}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
index a6183d19e..32062b6fe 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
@@ -27,30 +27,31 @@ class MessageDialogFragment : DialogFragment() {
27 val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! 27 val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
28 val helpLinkId = requireArguments().getInt(HELP_LINK) 28 val helpLinkId = requireArguments().getInt(HELP_LINK)
29 29
30 val dialog = MaterialAlertDialogBuilder(requireContext()) 30 val builder = MaterialAlertDialogBuilder(requireContext())
31 .setPositiveButton(R.string.close, null)
32 31
33 if (titleId != 0) dialog.setTitle(titleId) 32 if (messageDialogViewModel.positiveAction == null) {
34 if (titleString.isNotEmpty()) dialog.setTitle(titleString) 33 builder.setPositiveButton(R.string.close, null)
34 } else {
35 builder.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
36 messageDialogViewModel.positiveAction?.invoke()
37 }.setNegativeButton(android.R.string.cancel, null)
38 }
39
40 if (titleId != 0) builder.setTitle(titleId)
41 if (titleString.isNotEmpty()) builder.setTitle(titleString)
35 42
36 if (descriptionId != 0) { 43 if (descriptionId != 0) {
37 dialog.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY)) 44 builder.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
38 } 45 }
39 if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString) 46 if (descriptionString.isNotEmpty()) builder.setMessage(descriptionString)
40 47
41 if (helpLinkId != 0) { 48 if (helpLinkId != 0) {
42 dialog.setNeutralButton(R.string.learn_more) { _, _ -> 49 builder.setNeutralButton(R.string.learn_more) { _, _ ->
43 openLink(getString(helpLinkId)) 50 openLink(getString(helpLinkId))
44 } 51 }
45 } 52 }
46 53
47 return dialog.show() 54 return builder.show()
48 }
49
50 override fun onDismiss(dialog: DialogInterface) {
51 super.onDismiss(dialog)
52 messageDialogViewModel.dismissAction.invoke()
53 messageDialogViewModel.clear()
54 } 55 }
55 56
56 private fun openLink(link: String) { 57 private fun openLink(link: String) {
@@ -74,7 +75,7 @@ class MessageDialogFragment : DialogFragment() {
74 descriptionId: Int = 0, 75 descriptionId: Int = 0,
75 descriptionString: String = "", 76 descriptionString: String = "",
76 helpLinkId: Int = 0, 77 helpLinkId: Int = 0,
77 dismissAction: () -> Unit = {} 78 positiveAction: (() -> Unit)? = null
78 ): MessageDialogFragment { 79 ): MessageDialogFragment {
79 val dialog = MessageDialogFragment() 80 val dialog = MessageDialogFragment()
80 val bundle = Bundle() 81 val bundle = Bundle()
@@ -85,8 +86,10 @@ class MessageDialogFragment : DialogFragment() {
85 putString(DESCRIPTION_STRING, descriptionString) 86 putString(DESCRIPTION_STRING, descriptionString)
86 putInt(HELP_LINK, helpLinkId) 87 putInt(HELP_LINK, helpLinkId)
87 } 88 }
88 ViewModelProvider(activity)[MessageDialogViewModel::class.java].dismissAction = 89 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
89 dismissAction 90 clear()
91 this.positiveAction = positiveAction
92 }
90 dialog.arguments = bundle 93 dialog.arguments = bundle
91 return dialog 94 return dialog
92 } 95 }
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 2dbca76a5..64b295fbd 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
@@ -24,6 +24,7 @@ import androidx.lifecycle.repeatOnLifecycle
24import androidx.preference.PreferenceManager 24import androidx.preference.PreferenceManager
25import info.debatty.java.stringsimilarity.Jaccard 25import info.debatty.java.stringsimilarity.Jaccard
26import info.debatty.java.stringsimilarity.JaroWinkler 26import info.debatty.java.stringsimilarity.JaroWinkler
27import kotlinx.coroutines.flow.collectLatest
27import kotlinx.coroutines.launch 28import kotlinx.coroutines.launch
28import java.util.Locale 29import java.util.Locale
29import org.yuzu.yuzu_emu.R 30import org.yuzu.yuzu_emu.R
@@ -60,7 +61,9 @@ class SearchFragment : Fragment() {
60 // This is using the correct scope, lint is just acting up 61 // This is using the correct scope, lint is just acting up
61 @SuppressLint("UnsafeRepeatOnLifecycleDetector") 62 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
62 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 63 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
63 homeViewModel.setNavigationVisibility(visible = true, animated = false) 64 super.onViewCreated(view, savedInstanceState)
65 homeViewModel.setNavigationVisibility(visible = true, animated = true)
66 homeViewModel.setStatusBarShadeVisibility(true)
64 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 67 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
65 68
66 if (savedInstanceState != null) { 69 if (savedInstanceState != null) {
@@ -99,7 +102,7 @@ class SearchFragment : Fragment() {
99 } 102 }
100 launch { 103 launch {
101 repeatOnLifecycle(Lifecycle.State.CREATED) { 104 repeatOnLifecycle(Lifecycle.State.CREATED) {
102 gamesViewModel.games.collect { filterAndSearch() } 105 gamesViewModel.games.collectLatest { filterAndSearch() }
103 } 106 }
104 } 107 }
105 launch { 108 launch {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
index b88d2c038..60e029f34 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
@@ -70,7 +70,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
70 sliderBinding = DialogSliderBinding.inflate(layoutInflater) 70 sliderBinding = DialogSliderBinding.inflate(layoutInflater)
71 val item = settingsViewModel.clickedItem as SliderSetting 71 val item = settingsViewModel.clickedItem as SliderSetting
72 72
73 settingsViewModel.setSliderTextValue(item.selectedValue.toFloat(), item.units) 73 settingsViewModel.setSliderTextValue(item.getSelectedValue().toFloat(), item.units)
74 sliderBinding.slider.apply { 74 sliderBinding.slider.apply {
75 valueFrom = item.min.toFloat() 75 valueFrom = item.min.toFloat()
76 valueTo = item.max.toFloat() 76 valueTo = item.max.toFloat()
@@ -136,18 +136,18 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
136 is SingleChoiceSetting -> { 136 is SingleChoiceSetting -> {
137 val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting 137 val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting
138 val value = getValueForSingleChoiceSelection(scSetting, which) 138 val value = getValueForSingleChoiceSelection(scSetting, which)
139 scSetting.selectedValue = value 139 scSetting.setSelectedValue(value)
140 } 140 }
141 141
142 is StringSingleChoiceSetting -> { 142 is StringSingleChoiceSetting -> {
143 val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting 143 val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting
144 val value = scSetting.getValueAt(which) 144 val value = scSetting.getValueAt(which)
145 scSetting.selectedValue = value 145 scSetting.setSelectedValue(value)
146 } 146 }
147 147
148 is SliderSetting -> { 148 is SliderSetting -> {
149 val sliderSetting = settingsViewModel.clickedItem as SliderSetting 149 val sliderSetting = settingsViewModel.clickedItem as SliderSetting
150 sliderSetting.selectedValue = settingsViewModel.sliderProgress.value 150 sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
151 } 151 }
152 } 152 }
153 closeDialog() 153 closeDialog()
@@ -171,7 +171,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
171 } 171 }
172 172
173 private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { 173 private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
174 val value = item.selectedValue 174 val value = item.getSelectedValue()
175 val valuesId = item.valuesId 175 val valuesId = item.valuesId
176 if (valuesId > 0) { 176 if (valuesId > 0) {
177 val valuesArray = requireContext().resources.getIntArray(valuesId) 177 val valuesArray = requireContext().resources.getIntArray(valuesId)
@@ -211,7 +211,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
211 throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!") 211 throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!")
212 212
213 SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress( 213 SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress(
214 (clickedItem as SliderSetting).selectedValue.toFloat() 214 (clickedItem as SliderSetting).getSelectedValue().toFloat()
215 ) 215 )
216 } 216 }
217 settingsViewModel.clickedItem = clickedItem 217 settingsViewModel.clickedItem = clickedItem
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 eb5edaa10..064342cdd 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
@@ -304,6 +304,11 @@ class SetupFragment : Fragment() {
304 setInsets() 304 setInsets()
305 } 305 }
306 306
307 override fun onStop() {
308 super.onStop()
309 NativeConfig.saveGlobalConfig()
310 }
311
307 override fun onSaveInstanceState(outState: Bundle) { 312 override fun onSaveInstanceState(outState: Bundle) {
308 super.onSaveInstanceState(outState) 313 super.onSaveInstanceState(outState)
309 if (_binding != null) { 314 if (_binding != null) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt
new file mode 100644
index 000000000..ed79a8b02
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6data class Addon(
7 var enabled: Boolean,
8 val title: String,
9 val version: String
10)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt
new file mode 100644
index 000000000..075252f5b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import androidx.lifecycle.ViewModel
7import androidx.lifecycle.viewModelScope
8import kotlinx.coroutines.Dispatchers
9import kotlinx.coroutines.flow.MutableStateFlow
10import kotlinx.coroutines.flow.asStateFlow
11import kotlinx.coroutines.launch
12import kotlinx.coroutines.withContext
13import org.yuzu.yuzu_emu.NativeLibrary
14import org.yuzu.yuzu_emu.utils.NativeConfig
15import java.util.concurrent.atomic.AtomicBoolean
16
17class AddonViewModel : ViewModel() {
18 private val _addonList = MutableStateFlow(mutableListOf<Addon>())
19 val addonList get() = _addonList.asStateFlow()
20
21 private val _showModInstallPicker = MutableStateFlow(false)
22 val showModInstallPicker get() = _showModInstallPicker.asStateFlow()
23
24 private val _showModNoticeDialog = MutableStateFlow(false)
25 val showModNoticeDialog get() = _showModNoticeDialog.asStateFlow()
26
27 var game: Game? = null
28
29 private val isRefreshing = AtomicBoolean(false)
30
31 fun onOpenAddons(game: Game) {
32 this.game = game
33 refreshAddons()
34 }
35
36 fun refreshAddons() {
37 if (isRefreshing.get() || game == null) {
38 return
39 }
40 isRefreshing.set(true)
41 viewModelScope.launch {
42 withContext(Dispatchers.IO) {
43 val addonList = mutableListOf<Addon>()
44 val disabledAddons = NativeConfig.getDisabledAddons(game!!.programId)
45 NativeLibrary.getAddonsForFile(game!!.path, game!!.programId)?.forEach {
46 val name = it.first.replace("[D] ", "")
47 addonList.add(Addon(!disabledAddons.contains(name), name, it.second))
48 }
49 addonList.sortBy { it.title }
50 _addonList.value = addonList
51 isRefreshing.set(false)
52 }
53 }
54 }
55
56 fun onCloseAddons() {
57 if (_addonList.value.isEmpty()) {
58 return
59 }
60
61 NativeConfig.setDisabledAddons(
62 game!!.programId,
63 _addonList.value.mapNotNull {
64 if (it.enabled) {
65 null
66 } else {
67 it.title
68 }
69 }.toTypedArray()
70 )
71 NativeConfig.saveGlobalConfig()
72 _addonList.value.clear()
73 game = null
74 }
75
76 fun showModInstallPicker(install: Boolean) {
77 _showModInstallPicker.value = install
78 }
79
80 fun showModNoticeDialog(show: Boolean) {
81 _showModNoticeDialog.value = show
82 }
83}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
index 62945ad65..76accf8f3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
@@ -7,81 +7,83 @@ import androidx.lifecycle.ViewModel
7import androidx.lifecycle.viewModelScope 7import androidx.lifecycle.viewModelScope
8import kotlinx.coroutines.Dispatchers 8import kotlinx.coroutines.Dispatchers
9import kotlinx.coroutines.flow.MutableStateFlow 9import kotlinx.coroutines.flow.MutableStateFlow
10import kotlinx.coroutines.flow.SharingStarted
10import kotlinx.coroutines.flow.StateFlow 11import kotlinx.coroutines.flow.StateFlow
12import kotlinx.coroutines.flow.combine
13import kotlinx.coroutines.flow.stateIn
11import kotlinx.coroutines.launch 14import kotlinx.coroutines.launch
12import kotlinx.coroutines.withContext 15import kotlinx.coroutines.withContext
13import org.yuzu.yuzu_emu.R 16import org.yuzu.yuzu_emu.R
14import org.yuzu.yuzu_emu.YuzuApplication 17import org.yuzu.yuzu_emu.YuzuApplication
18import org.yuzu.yuzu_emu.features.settings.model.StringSetting
19import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
15import org.yuzu.yuzu_emu.utils.FileUtil 20import org.yuzu.yuzu_emu.utils.FileUtil
16import org.yuzu.yuzu_emu.utils.GpuDriverHelper 21import org.yuzu.yuzu_emu.utils.GpuDriverHelper
17import org.yuzu.yuzu_emu.utils.GpuDriverMetadata 22import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
23import org.yuzu.yuzu_emu.utils.NativeConfig
18import java.io.BufferedOutputStream 24import java.io.BufferedOutputStream
19import java.io.File 25import java.io.File
20 26
21class DriverViewModel : ViewModel() { 27class DriverViewModel : ViewModel() {
22 private val _areDriversLoading = MutableStateFlow(false) 28 private val _areDriversLoading = MutableStateFlow(false)
23 val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
24
25 private val _isDriverReady = MutableStateFlow(true) 29 private val _isDriverReady = MutableStateFlow(true)
26 val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
27
28 private val _isDeletingDrivers = MutableStateFlow(false) 30 private val _isDeletingDrivers = MutableStateFlow(false)
29 val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
30 31
31 private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>()) 32 val isInteractionAllowed: StateFlow<Boolean> =
33 combine(
34 _areDriversLoading,
35 _isDriverReady,
36 _isDeletingDrivers
37 ) { loading, ready, deleting ->
38 !loading && ready && !deleting
39 }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false)
40
41 private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers())
32 val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList 42 val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
33 43
34 var previouslySelectedDriver = 0 44 var previouslySelectedDriver = 0
35 var selectedDriver = -1 45 var selectedDriver = -1
36 46
37 private val _selectedDriverMetadata = 47 // Used for showing which driver is currently installed within the driver manager card
38 MutableStateFlow( 48 private val _selectedDriverTitle = MutableStateFlow("")
39 GpuDriverHelper.customDriverData.name 49 val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle
40 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
41 )
42 val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
43 50
44 private val _newDriverInstalled = MutableStateFlow(false) 51 private val _newDriverInstalled = MutableStateFlow(false)
45 val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled 52 val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
46 53
47 val driversToDelete = mutableListOf<String>() 54 val driversToDelete = mutableListOf<String>()
48 55
49 val isInteractionAllowed
50 get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
51
52 init { 56 init {
53 _areDriversLoading.value = true 57 val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData
54 viewModelScope.launch { 58 findSelectedDriver(currentDriverMetadata)
55 withContext(Dispatchers.IO) { 59
56 val drivers = GpuDriverHelper.getDrivers() 60 // If a user had installed a driver before the manager was implemented, this zips
57 val currentDriverMetadata = GpuDriverHelper.customDriverData 61 // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
58 for (i in drivers.indices) { 62 // be indexed and exported as expected.
59 if (drivers[i].second == currentDriverMetadata) { 63 if (selectedDriver == -1) {
60 setSelectedDriverIndex(i) 64 val driverToSave =
61 break 65 File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
62 } 66 driverToSave.createNewFile()
63 } 67 FileUtil.zipFromInternalStorage(
64 68 File(GpuDriverHelper.driverInstallationPath!!),
65 // If a user had installed a driver before the manager was implemented, this zips 69 GpuDriverHelper.driverInstallationPath!!,
66 // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can 70 BufferedOutputStream(driverToSave.outputStream())
67 // be indexed and exported as expected. 71 )
68 if (selectedDriver == -1) { 72 _driverList.value.add(Pair(driverToSave.path, currentDriverMetadata))
69 val driverToSave = 73 setSelectedDriverIndex(_driverList.value.size - 1)
70 File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip") 74 }
71 driverToSave.createNewFile()
72 FileUtil.zipFromInternalStorage(
73 File(GpuDriverHelper.driverInstallationPath!!),
74 GpuDriverHelper.driverInstallationPath!!,
75 BufferedOutputStream(driverToSave.outputStream())
76 )
77 drivers.add(Pair(driverToSave.path, currentDriverMetadata))
78 setSelectedDriverIndex(drivers.size - 1)
79 }
80 75
81 _driverList.value = drivers 76 // If a user had installed a driver before the config was reworked to be multiplatform,
82 _areDriversLoading.value = false 77 // we have save the path of the previously selected driver to the new setting.
83 } 78 if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 &&
79 StringSetting.DRIVER_PATH.global
80 ) {
81 StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first)
82 NativeConfig.saveGlobalConfig()
83 } else {
84 findSelectedDriver(GpuDriverHelper.customDriverSettingData)
84 } 85 }
86 updateDriverNameForGame(null)
85 } 87 }
86 88
87 fun setSelectedDriverIndex(value: Int) { 89 fun setSelectedDriverIndex(value: Int) {
@@ -98,9 +100,9 @@ class DriverViewModel : ViewModel() {
98 fun addDriver(driverData: Pair<String, GpuDriverMetadata>) { 100 fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
99 val driverIndex = _driverList.value.indexOfFirst { it == driverData } 101 val driverIndex = _driverList.value.indexOfFirst { it == driverData }
100 if (driverIndex == -1) { 102 if (driverIndex == -1) {
101 setSelectedDriverIndex(_driverList.value.size)
102 _driverList.value.add(driverData) 103 _driverList.value.add(driverData)
103 _selectedDriverMetadata.value = driverData.second.name 104 setSelectedDriverIndex(_driverList.value.size - 1)
105 _selectedDriverTitle.value = driverData.second.name
104 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) 106 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
105 } else { 107 } else {
106 setSelectedDriverIndex(driverIndex) 108 setSelectedDriverIndex(driverIndex)
@@ -111,8 +113,31 @@ class DriverViewModel : ViewModel() {
111 _driverList.value.remove(driverData) 113 _driverList.value.remove(driverData)
112 } 114 }
113 115
114 fun onCloseDriverManager() { 116 fun onOpenDriverManager(game: Game?) {
117 if (game != null) {
118 SettingsFile.loadCustomConfig(game)
119 }
120
121 val driverPath = StringSetting.DRIVER_PATH.getString()
122 if (driverPath.isEmpty()) {
123 setSelectedDriverIndex(0)
124 } else {
125 findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath)))
126 }
127 }
128
129 fun onCloseDriverManager(game: Game?) {
115 _isDeletingDrivers.value = true 130 _isDeletingDrivers.value = true
131 StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first)
132 updateDriverNameForGame(game)
133 if (game == null) {
134 NativeConfig.saveGlobalConfig()
135 } else {
136 NativeConfig.savePerGameConfig()
137 NativeConfig.unloadPerGameConfig()
138 NativeConfig.reloadGlobalConfig()
139 }
140
116 viewModelScope.launch { 141 viewModelScope.launch {
117 withContext(Dispatchers.IO) { 142 withContext(Dispatchers.IO) {
118 driversToDelete.forEach { 143 driversToDelete.forEach {
@@ -125,23 +150,29 @@ class DriverViewModel : ViewModel() {
125 _isDeletingDrivers.value = false 150 _isDeletingDrivers.value = false
126 } 151 }
127 } 152 }
153 }
154
155 // It is the Emulation Fragment's responsibility to load per-game settings so that this function
156 // knows what driver to load.
157 fun onLaunchGame() {
158 _isDriverReady.value = false
128 159
129 if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) { 160 val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
161 val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
162 if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
130 return 163 return
131 } 164 }
132 165
133 _isDriverReady.value = false
134 viewModelScope.launch { 166 viewModelScope.launch {
135 withContext(Dispatchers.IO) { 167 withContext(Dispatchers.IO) {
136 if (selectedDriver == 0) { 168 if (selectedDriverMetadata.name == null) {
137 GpuDriverHelper.installDefaultDriver() 169 GpuDriverHelper.installDefaultDriver()
138 setDriverReady() 170 setDriverReady()
139 return@withContext 171 return@withContext
140 } 172 }
141 173
142 val driverToInstall = File(driverList.value[selectedDriver].first) 174 if (selectedDriverFile.exists()) {
143 if (driverToInstall.exists()) { 175 GpuDriverHelper.installCustomDriver(selectedDriverFile)
144 GpuDriverHelper.installCustomDriver(driverToInstall)
145 } else { 176 } else {
146 GpuDriverHelper.installDefaultDriver() 177 GpuDriverHelper.installDefaultDriver()
147 } 178 }
@@ -150,9 +181,43 @@ class DriverViewModel : ViewModel() {
150 } 181 }
151 } 182 }
152 183
184 private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) {
185 if (driverList.value.size == 1) {
186 setSelectedDriverIndex(0)
187 return
188 }
189
190 driverList.value.forEachIndexed { i: Int, driver: Pair<String, GpuDriverMetadata> ->
191 if (driver.second == currentDriverMetadata) {
192 setSelectedDriverIndex(i)
193 return
194 }
195 }
196 }
197
198 fun updateDriverNameForGame(game: Game?) {
199 if (!GpuDriverHelper.supportsCustomDriverLoading()) {
200 return
201 }
202
203 if (game == null || NativeConfig.isPerGameConfigLoaded()) {
204 updateName()
205 } else {
206 SettingsFile.loadCustomConfig(game)
207 updateName()
208 NativeConfig.unloadPerGameConfig()
209 NativeConfig.reloadGlobalConfig()
210 }
211 }
212
213 private fun updateName() {
214 _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name
215 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
216 }
217
153 private fun setDriverReady() { 218 private fun setDriverReady() {
154 _isDriverReady.value = true 219 _isDriverReady.value = true
155 _selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name 220 _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name
156 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) 221 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
157 } 222 }
158} 223}
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 2fa3ab31b..f1ea1e20f 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
@@ -3,10 +3,18 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import android.net.Uri
6import android.os.Parcelable 7import android.os.Parcelable
7import java.util.HashSet 8import java.util.HashSet
8import kotlinx.parcelize.Parcelize 9import kotlinx.parcelize.Parcelize
9import kotlinx.serialization.Serializable 10import kotlinx.serialization.Serializable
11import org.yuzu.yuzu_emu.NativeLibrary
12import org.yuzu.yuzu_emu.R
13import org.yuzu.yuzu_emu.YuzuApplication
14import org.yuzu.yuzu_emu.utils.DirectoryInitialization
15import org.yuzu.yuzu_emu.utils.FileUtil
16import java.time.LocalDateTime
17import java.time.format.DateTimeFormatter
10 18
11@Parcelize 19@Parcelize
12@Serializable 20@Serializable
@@ -15,12 +23,44 @@ class Game(
15 val path: String, 23 val path: String,
16 val programId: String = "", 24 val programId: String = "",
17 val developer: String = "", 25 val developer: String = "",
18 val version: String = "", 26 var version: String = "",
19 val isHomebrew: Boolean = false 27 val isHomebrew: Boolean = false
20) : Parcelable { 28) : Parcelable {
21 val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime" 29 val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
22 val keyLastPlayedTime get() = "${path}_LastPlayed" 30 val keyLastPlayedTime get() = "${path}_LastPlayed"
23 31
32 val settingsName: String
33 get() {
34 val programIdLong = programId.toLong()
35 return if (programIdLong == 0L) {
36 FileUtil.getFilename(Uri.parse(path))
37 } else {
38 "0" + programIdLong.toString(16).uppercase()
39 }
40 }
41
42 val programIdHex: String
43 get() {
44 val programIdLong = programId.toLong()
45 return if (programIdLong == 0L) {
46 "0"
47 } else {
48 "0" + programIdLong.toString(16).uppercase()
49 }
50 }
51
52 val saveZipName: String
53 get() = "$title ${YuzuApplication.appContext.getString(R.string.save_data).lowercase()} - ${
54 LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
55 }.zip"
56
57 val saveDir: String
58 get() = DirectoryInitialization.userDirectory + "/nand" +
59 NativeLibrary.getSavePath(programId)
60
61 val addonDir: String
62 get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/"
63
24 override fun equals(other: Any?): Boolean { 64 override fun equals(other: Any?): Boolean {
25 if (other !is Game) { 65 if (other !is Game) {
26 return false 66 return false
@@ -34,6 +74,7 @@ class Game(
34 result = 31 * result + path.hashCode() 74 result = 31 * result + path.hashCode()
35 result = 31 * result + programId.hashCode() 75 result = 31 * result + programId.hashCode()
36 result = 31 * result + developer.hashCode() 76 result = 31 * result + developer.hashCode()
77 result = 31 * result + version.hashCode()
37 result = 31 * result + isHomebrew.hashCode() 78 result = 31 * result + isHomebrew.hashCode()
38 return result 79 return result
39 } 80 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt
new file mode 100644
index 000000000..0135a95be
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import androidx.annotation.DrawableRes
7import androidx.annotation.StringRes
8import kotlinx.coroutines.flow.StateFlow
9
10interface GameProperty {
11 @get:StringRes
12 val titleId: Int
13
14 @get:StringRes
15 val descriptionId: Int
16
17 @get:DrawableRes
18 val iconId: Int
19}
20
21data class SubmenuProperty(
22 override val titleId: Int,
23 override val descriptionId: Int,
24 override val iconId: Int,
25 val details: (() -> String)? = null,
26 val detailsFlow: StateFlow<String>? = null,
27 val action: () -> Unit
28) : GameProperty
29
30data class InstallableProperty(
31 override val titleId: Int,
32 override val descriptionId: Int,
33 override val iconId: Int,
34 val install: (() -> Unit)? = null,
35 val export: (() -> Unit)? = null
36) : GameProperty
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 fd925235b..d19f20dc2 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
@@ -20,8 +20,8 @@ import kotlinx.serialization.json.Json
20import org.yuzu.yuzu_emu.NativeLibrary 20import org.yuzu.yuzu_emu.NativeLibrary
21import org.yuzu.yuzu_emu.YuzuApplication 21import org.yuzu.yuzu_emu.YuzuApplication
22import org.yuzu.yuzu_emu.utils.GameHelper 22import org.yuzu.yuzu_emu.utils.GameHelper
23import org.yuzu.yuzu_emu.utils.GameMetadata
24import org.yuzu.yuzu_emu.utils.NativeConfig 23import org.yuzu.yuzu_emu.utils.NativeConfig
24import java.util.concurrent.atomic.AtomicBoolean
25 25
26class GamesViewModel : ViewModel() { 26class GamesViewModel : ViewModel() {
27 val games: StateFlow<List<Game>> get() = _games 27 val games: StateFlow<List<Game>> get() = _games
@@ -33,6 +33,8 @@ class GamesViewModel : ViewModel() {
33 val isReloading: StateFlow<Boolean> get() = _isReloading 33 val isReloading: StateFlow<Boolean> get() = _isReloading
34 private val _isReloading = MutableStateFlow(false) 34 private val _isReloading = MutableStateFlow(false)
35 35
36 private val reloading = AtomicBoolean(false)
37
36 val shouldSwapData: StateFlow<Boolean> get() = _shouldSwapData 38 val shouldSwapData: StateFlow<Boolean> get() = _shouldSwapData
37 private val _shouldSwapData = MutableStateFlow(false) 39 private val _shouldSwapData = MutableStateFlow(false)
38 40
@@ -49,38 +51,8 @@ class GamesViewModel : ViewModel() {
49 // Ensure keys are loaded so that ROM metadata can be decrypted. 51 // Ensure keys are loaded so that ROM metadata can be decrypted.
50 NativeLibrary.reloadKeys() 52 NativeLibrary.reloadKeys()
51 53
52 // Retrieve list of cached games 54 getGameDirs()
53 val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 55 reloadGames(directoriesChanged = false, firstStartup = true)
54 .getStringSet(GameHelper.KEY_GAMES, emptySet())
55
56 viewModelScope.launch {
57 withContext(Dispatchers.IO) {
58 getGameDirs()
59 if (storedGames!!.isNotEmpty()) {
60 val deserializedGames = mutableSetOf<Game>()
61 storedGames.forEach {
62 val game: Game
63 try {
64 game = Json.decodeFromString(it)
65 } catch (e: Exception) {
66 // We don't care about any errors related to parsing the game cache
67 return@forEach
68 }
69
70 val gameExists =
71 DocumentFile.fromSingleUri(
72 YuzuApplication.appContext,
73 Uri.parse(game.path)
74 )?.exists()
75 if (gameExists == true) {
76 deserializedGames.add(game)
77 }
78 }
79 setGames(deserializedGames.toList())
80 }
81 reloadGames(false)
82 }
83 }
84 } 56 }
85 57
86 fun setGames(games: List<Game>) { 58 fun setGames(games: List<Game>) {
@@ -110,16 +82,46 @@ class GamesViewModel : ViewModel() {
110 _searchFocused.value = searchFocused 82 _searchFocused.value = searchFocused
111 } 83 }
112 84
113 fun reloadGames(directoriesChanged: Boolean) { 85 fun reloadGames(directoriesChanged: Boolean, firstStartup: Boolean = false) {
114 if (isReloading.value) { 86 if (reloading.get()) {
115 return 87 return
116 } 88 }
89 reloading.set(true)
117 _isReloading.value = true 90 _isReloading.value = true
118 91
119 viewModelScope.launch { 92 viewModelScope.launch {
120 withContext(Dispatchers.IO) { 93 withContext(Dispatchers.IO) {
121 GameMetadata.resetMetadata() 94 if (firstStartup) {
95 // Retrieve list of cached games
96 val storedGames =
97 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
98 .getStringSet(GameHelper.KEY_GAMES, emptySet())
99 if (storedGames!!.isNotEmpty()) {
100 val deserializedGames = mutableSetOf<Game>()
101 storedGames.forEach {
102 val game: Game
103 try {
104 game = Json.decodeFromString(it)
105 } catch (e: Exception) {
106 // We don't care about any errors related to parsing the game cache
107 return@forEach
108 }
109
110 val gameExists =
111 DocumentFile.fromSingleUri(
112 YuzuApplication.appContext,
113 Uri.parse(game.path)
114 )?.exists()
115 if (gameExists == true) {
116 deserializedGames.add(game)
117 }
118 }
119 setGames(deserializedGames.toList())
120 }
121 }
122
122 setGames(GameHelper.getGames()) 123 setGames(GameHelper.getGames())
124 reloading.set(false)
123 _isReloading.value = false 125 _isReloading.value = false
124 126
125 if (directoriesChanged) { 127 if (directoriesChanged) {
@@ -168,6 +170,7 @@ class GamesViewModel : ViewModel() {
168 fun onCloseGameFoldersFragment() = 170 fun onCloseGameFoldersFragment() =
169 viewModelScope.launch { 171 viewModelScope.launch {
170 withContext(Dispatchers.IO) { 172 withContext(Dispatchers.IO) {
173 NativeConfig.saveGlobalConfig()
171 getGameDirs(true) 174 getGameDirs(true)
172 } 175 }
173 } 176 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 07e65b028..513ac2fc5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -3,6 +3,7 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import android.net.Uri
6import androidx.lifecycle.ViewModel 7import androidx.lifecycle.ViewModel
7import kotlinx.coroutines.flow.MutableStateFlow 8import kotlinx.coroutines.flow.MutableStateFlow
8import kotlinx.coroutines.flow.StateFlow 9import kotlinx.coroutines.flow.StateFlow
@@ -21,6 +22,15 @@ class HomeViewModel : ViewModel() {
21 private val _gamesDirSelected = MutableStateFlow(false) 22 private val _gamesDirSelected = MutableStateFlow(false)
22 val gamesDirSelected get() = _gamesDirSelected.asStateFlow() 23 val gamesDirSelected get() = _gamesDirSelected.asStateFlow()
23 24
25 private val _openImportSaves = MutableStateFlow(false)
26 val openImportSaves get() = _openImportSaves.asStateFlow()
27
28 private val _contentToInstall = MutableStateFlow<List<Uri>?>(null)
29 val contentToInstall get() = _contentToInstall.asStateFlow()
30
31 private val _reloadPropertiesList = MutableStateFlow(false)
32 val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
33
24 var navigatedToSetup = false 34 var navigatedToSetup = false
25 35
26 fun setNavigationVisibility(visible: Boolean, animated: Boolean) { 36 fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@@ -44,4 +54,16 @@ class HomeViewModel : ViewModel() {
44 fun setGamesDirSelected(selected: Boolean) { 54 fun setGamesDirSelected(selected: Boolean) {
45 _gamesDirSelected.value = selected 55 _gamesDirSelected.value = selected
46 } 56 }
57
58 fun setOpenImportSaves(import: Boolean) {
59 _openImportSaves.value = import
60 }
61
62 fun setContentToInstall(documents: List<Uri>?) {
63 _contentToInstall.value = documents
64 }
65
66 fun reloadPropertiesList(reload: Boolean) {
67 _reloadPropertiesList.value = reload
68 }
47} 69}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt
index 36ffd08d2..641c5cb17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt
@@ -6,9 +6,9 @@ package org.yuzu.yuzu_emu.model
6import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
7 7
8class MessageDialogViewModel : ViewModel() { 8class MessageDialogViewModel : ViewModel() {
9 var dismissAction: () -> Unit = {} 9 var positiveAction: (() -> Unit)? = null
10 10
11 fun clear() { 11 fun clear() {
12 dismissAction = {} 12 positiveAction = null
13 } 13 }
14} 14}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
index ccc981e95..5cb6a5d57 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
@@ -68,8 +68,4 @@ class SettingsViewModel : ViewModel() {
68 fun setAdapterItemChanged(value: Int) { 68 fun setAdapterItemChanged(value: Int) {
69 _adapterItemChanged.value = value 69 _adapterItemChanged.value = value
70 } 70 }
71
72 fun clear() {
73 game = null
74 }
75} 71}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
index 16a794dee..e59c95733 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
@@ -23,7 +23,7 @@ class TaskViewModel : ViewModel() {
23 val cancelled: StateFlow<Boolean> get() = _cancelled 23 val cancelled: StateFlow<Boolean> get() = _cancelled
24 private val _cancelled = MutableStateFlow(false) 24 private val _cancelled = MutableStateFlow(false)
25 25
26 lateinit var task: () -> Any 26 lateinit var task: suspend () -> Any
27 27
28 fun clear() { 28 fun clear() {
29 _result.value = Any() 29 _result.value = Any()
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 805b89b31..fc0eeb9ad 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
@@ -19,7 +19,7 @@ import androidx.lifecycle.Lifecycle
19import androidx.lifecycle.lifecycleScope 19import androidx.lifecycle.lifecycleScope
20import androidx.lifecycle.repeatOnLifecycle 20import androidx.lifecycle.repeatOnLifecycle
21import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
22import com.google.android.material.transition.MaterialFadeThrough 22import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch 23import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.R 24import org.yuzu.yuzu_emu.R
25import org.yuzu.yuzu_emu.adapters.GameAdapter 25import org.yuzu.yuzu_emu.adapters.GameAdapter
@@ -35,11 +35,6 @@ class GamesFragment : Fragment() {
35 private val gamesViewModel: GamesViewModel by activityViewModels() 35 private val gamesViewModel: GamesViewModel by activityViewModels()
36 private val homeViewModel: HomeViewModel by activityViewModels() 36 private val homeViewModel: HomeViewModel by activityViewModels()
37 37
38 override fun onCreate(savedInstanceState: Bundle?) {
39 super.onCreate(savedInstanceState)
40 enterTransition = MaterialFadeThrough()
41 }
42
43 override fun onCreateView( 38 override fun onCreateView(
44 inflater: LayoutInflater, 39 inflater: LayoutInflater,
45 container: ViewGroup?, 40 container: ViewGroup?,
@@ -52,7 +47,9 @@ class GamesFragment : Fragment() {
52 // This is using the correct scope, lint is just acting up 47 // This is using the correct scope, lint is just acting up
53 @SuppressLint("UnsafeRepeatOnLifecycleDetector") 48 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
54 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 49 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
55 homeViewModel.setNavigationVisibility(visible = true, animated = false) 50 super.onViewCreated(view, savedInstanceState)
51 homeViewModel.setNavigationVisibility(visible = true, animated = true)
52 homeViewModel.setStatusBarShadeVisibility(true)
56 53
57 binding.gridGames.apply { 54 binding.gridGames.apply {
58 layoutManager = AutofitGridLayoutManager( 55 layoutManager = AutofitGridLayoutManager(
@@ -99,7 +96,7 @@ class GamesFragment : Fragment() {
99 } 96 }
100 launch { 97 launch {
101 repeatOnLifecycle(Lifecycle.State.RESUMED) { 98 repeatOnLifecycle(Lifecycle.State.RESUMED) {
102 gamesViewModel.games.collect { 99 gamesViewModel.games.collectLatest {
103 (binding.gridGames.adapter as GameAdapter).submitList(it) 100 (binding.gridGames.adapter as GameAdapter).submitList(it)
104 if (it.isEmpty()) { 101 if (it.isEmpty()) {
105 binding.noticeText.visibility = View.VISIBLE 102 binding.noticeText.visibility = View.VISIBLE
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 16323a316..b4117d761 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
@@ -28,12 +28,9 @@ import androidx.navigation.ui.setupWithNavController
28import androidx.preference.PreferenceManager 28import androidx.preference.PreferenceManager
29import com.google.android.material.color.MaterialColors 29import com.google.android.material.color.MaterialColors
30import com.google.android.material.navigation.NavigationBarView 30import com.google.android.material.navigation.NavigationBarView
31import kotlinx.coroutines.CoroutineScope
32import java.io.File 31import java.io.File
33import java.io.FilenameFilter 32import java.io.FilenameFilter
34import kotlinx.coroutines.Dispatchers
35import kotlinx.coroutines.launch 33import kotlinx.coroutines.launch
36import kotlinx.coroutines.withContext
37import org.yuzu.yuzu_emu.HomeNavigationDirections 34import org.yuzu.yuzu_emu.HomeNavigationDirections
38import org.yuzu.yuzu_emu.NativeLibrary 35import org.yuzu.yuzu_emu.NativeLibrary
39import org.yuzu.yuzu_emu.R 36import org.yuzu.yuzu_emu.R
@@ -43,7 +40,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
43import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment 40import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 41import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
45import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
46import org.yuzu.yuzu_emu.getPublicFilesDir 43import org.yuzu.yuzu_emu.model.AddonViewModel
47import org.yuzu.yuzu_emu.model.GamesViewModel 44import org.yuzu.yuzu_emu.model.GamesViewModel
48import org.yuzu.yuzu_emu.model.HomeViewModel 45import org.yuzu.yuzu_emu.model.HomeViewModel
49import org.yuzu.yuzu_emu.model.TaskState 46import org.yuzu.yuzu_emu.model.TaskState
@@ -60,15 +57,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
60 private val homeViewModel: HomeViewModel by viewModels() 57 private val homeViewModel: HomeViewModel by viewModels()
61 private val gamesViewModel: GamesViewModel by viewModels() 58 private val gamesViewModel: GamesViewModel by viewModels()
62 private val taskViewModel: TaskViewModel by viewModels() 59 private val taskViewModel: TaskViewModel by viewModels()
60 private val addonViewModel: AddonViewModel by viewModels()
63 61
64 override var themeId: Int = 0 62 override var themeId: Int = 0
65 63
66 private val savesFolder
67 get() = "${getPublicFilesDir().canonicalPath}/nand/user/save/0000000000000000"
68
69 // Get first subfolder in saves folder (should be the user folder)
70 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
71
72 override fun onCreate(savedInstanceState: Bundle?) { 64 override fun onCreate(savedInstanceState: Bundle?) {
73 val splashScreen = installSplashScreen() 65 val splashScreen = installSplashScreen()
74 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } 66 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -145,6 +137,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
145 homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) } 137 homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
146 } 138 }
147 } 139 }
140 launch {
141 repeatOnLifecycle(Lifecycle.State.CREATED) {
142 homeViewModel.contentToInstall.collect {
143 if (it != null) {
144 installContent(it)
145 homeViewModel.setContentToInstall(null)
146 }
147 }
148 }
149 }
148 } 150 }
149 151
150 // Dismiss previous notifications (should not happen unless a crash occurred) 152 // Dismiss previous notifications (should not happen unless a crash occurred)
@@ -253,13 +255,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
253 super.onResume() 255 super.onResume()
254 } 256 }
255 257
256 override fun onStop() {
257 super.onStop()
258 CoroutineScope(Dispatchers.IO).launch {
259 NativeConfig.saveSettings()
260 }
261 }
262
263 override fun onDestroy() { 258 override fun onDestroy() {
264 EmulationActivity.stopForegroundService(this) 259 EmulationActivity.stopForegroundService(this)
265 super.onDestroy() 260 super.onDestroy()
@@ -468,110 +463,150 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
468 val installGameUpdate = registerForActivityResult( 463 val installGameUpdate = registerForActivityResult(
469 ActivityResultContracts.OpenMultipleDocuments() 464 ActivityResultContracts.OpenMultipleDocuments()
470 ) { documents: List<Uri> -> 465 ) { documents: List<Uri> ->
471 if (documents.isNotEmpty()) { 466 if (documents.isEmpty()) {
472 IndeterminateProgressDialogFragment.newInstance( 467 return@registerForActivityResult
473 this@MainActivity, 468 }
474 R.string.installing_game_content
475 ) {
476 var installSuccess = 0
477 var installOverwrite = 0
478 var errorBaseGame = 0
479 var errorExtension = 0
480 var errorOther = 0
481 documents.forEach {
482 when (
483 NativeLibrary.installFileToNand(
484 it.toString(),
485 FileUtil.getExtension(it)
486 )
487 ) {
488 NativeLibrary.InstallFileToNandResult.Success -> {
489 installSuccess += 1
490 }
491 469
492 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { 470 if (addonViewModel.game == null) {
493 installOverwrite += 1 471 installContent(documents)
494 } 472 return@registerForActivityResult
473 }
495 474
496 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { 475 IndeterminateProgressDialogFragment.newInstance(
497 errorBaseGame += 1 476 this@MainActivity,
498 } 477 R.string.verifying_content,
478 false
479 ) {
480 var updatesMatchProgram = true
481 for (document in documents) {
482 val valid = NativeLibrary.doesUpdateMatchProgram(
483 addonViewModel.game!!.programId,
484 document.toString()
485 )
486 if (!valid) {
487 updatesMatchProgram = false
488 break
489 }
490 }
499 491
500 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { 492 if (updatesMatchProgram) {
501 errorExtension += 1 493 homeViewModel.setContentToInstall(documents)
502 } 494 } else {
495 MessageDialogFragment.newInstance(
496 this@MainActivity,
497 titleId = R.string.content_install_notice,
498 descriptionId = R.string.content_install_notice_description,
499 positiveAction = { homeViewModel.setContentToInstall(documents) }
500 )
501 }
502 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
503 }
503 504
504 else -> { 505 private fun installContent(documents: List<Uri>) {
505 errorOther += 1 506 IndeterminateProgressDialogFragment.newInstance(
506 } 507 this@MainActivity,
508 R.string.installing_game_content
509 ) {
510 var installSuccess = 0
511 var installOverwrite = 0
512 var errorBaseGame = 0
513 var errorExtension = 0
514 var errorOther = 0
515 documents.forEach {
516 when (
517 NativeLibrary.installFileToNand(
518 it.toString(),
519 FileUtil.getExtension(it)
520 )
521 ) {
522 NativeLibrary.InstallFileToNandResult.Success -> {
523 installSuccess += 1
524 }
525
526 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
527 installOverwrite += 1
528 }
529
530 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
531 errorBaseGame += 1
532 }
533
534 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
535 errorExtension += 1
536 }
537
538 else -> {
539 errorOther += 1
507 } 540 }
508 } 541 }
542 }
509 543
510 val separator = System.getProperty("line.separator") ?: "\n" 544 addonViewModel.refreshAddons()
511 val installResult = StringBuilder() 545
512 if (installSuccess > 0) { 546 val separator = System.getProperty("line.separator") ?: "\n"
513 installResult.append( 547 val installResult = StringBuilder()
514 getString( 548 if (installSuccess > 0) {
515 R.string.install_game_content_success_install, 549 installResult.append(
516 installSuccess 550 getString(
517 ) 551 R.string.install_game_content_success_install,
552 installSuccess
518 ) 553 )
554 )
555 installResult.append(separator)
556 }
557 if (installOverwrite > 0) {
558 installResult.append(
559 getString(
560 R.string.install_game_content_success_overwrite,
561 installOverwrite
562 )
563 )
564 installResult.append(separator)
565 }
566 val errorTotal: Int = errorBaseGame + errorExtension + errorOther
567 if (errorTotal > 0) {
568 installResult.append(separator)
569 installResult.append(
570 getString(
571 R.string.install_game_content_failed_count,
572 errorTotal
573 )
574 )
575 installResult.append(separator)
576 if (errorBaseGame > 0) {
519 installResult.append(separator) 577 installResult.append(separator)
520 }
521 if (installOverwrite > 0) {
522 installResult.append( 578 installResult.append(
523 getString( 579 getString(R.string.install_game_content_failure_base)
524 R.string.install_game_content_success_overwrite,
525 installOverwrite
526 )
527 ) 580 )
528 installResult.append(separator) 581 installResult.append(separator)
529 } 582 }
530 val errorTotal: Int = errorBaseGame + errorExtension + errorOther 583 if (errorExtension > 0) {
531 if (errorTotal > 0) {
532 installResult.append(separator) 584 installResult.append(separator)
533 installResult.append( 585 installResult.append(
534 getString( 586 getString(R.string.install_game_content_failure_file_extension)
535 R.string.install_game_content_failed_count,
536 errorTotal
537 )
538 ) 587 )
539 installResult.append(separator) 588 installResult.append(separator)
540 if (errorBaseGame > 0) { 589 }
541 installResult.append(separator) 590 if (errorOther > 0) {
542 installResult.append( 591 installResult.append(
543 getString(R.string.install_game_content_failure_base) 592 getString(R.string.install_game_content_failure_description)
544 )
545 installResult.append(separator)
546 }
547 if (errorExtension > 0) {
548 installResult.append(separator)
549 installResult.append(
550 getString(R.string.install_game_content_failure_file_extension)
551 )
552 installResult.append(separator)
553 }
554 if (errorOther > 0) {
555 installResult.append(
556 getString(R.string.install_game_content_failure_description)
557 )
558 installResult.append(separator)
559 }
560 return@newInstance MessageDialogFragment.newInstance(
561 this,
562 titleId = R.string.install_game_content_failure,
563 descriptionString = installResult.toString().trim(),
564 helpLinkId = R.string.install_game_content_help_link
565 )
566 } else {
567 return@newInstance MessageDialogFragment.newInstance(
568 this,
569 titleId = R.string.install_game_content_success,
570 descriptionString = installResult.toString().trim()
571 ) 593 )
594 installResult.append(separator)
572 } 595 }
573 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 596 return@newInstance MessageDialogFragment.newInstance(
574 } 597 this,
598 titleId = R.string.install_game_content_failure,
599 descriptionString = installResult.toString().trim(),
600 helpLinkId = R.string.install_game_content_help_link
601 )
602 } else {
603 return@newInstance MessageDialogFragment.newInstance(
604 this,
605 titleId = R.string.install_game_content_success,
606 descriptionString = installResult.toString().trim()
607 )
608 }
609 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
575 } 610 }
576 611
577 val exportUserData = registerForActivityResult( 612 val exportUserData = registerForActivityResult(
@@ -632,7 +667,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
632 } 667 }
633 668
634 // Clear existing user data 669 // Clear existing user data
635 NativeConfig.unloadConfig() 670 NativeConfig.unloadGlobalConfig()
636 File(DirectoryInitialization.userDirectory!!).deleteRecursively() 671 File(DirectoryInitialization.userDirectory!!).deleteRecursively()
637 672
638 // Copy archive to internal storage 673 // Copy archive to internal storage
@@ -651,108 +686,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
651 686
652 // Reinitialize relevant data 687 // Reinitialize relevant data
653 NativeLibrary.initializeSystem(true) 688 NativeLibrary.initializeSystem(true)
654 NativeConfig.initializeConfig() 689 NativeConfig.initializeGlobalConfig()
655 gamesViewModel.reloadGames(false) 690 gamesViewModel.reloadGames(false)
656 691
657 return@newInstance getString(R.string.user_data_import_success) 692 return@newInstance getString(R.string.user_data_import_success)
658 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 693 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
659 } 694 }
660
661 /**
662 * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
663 */
664 val exportSaves = registerForActivityResult(
665 ActivityResultContracts.CreateDocument("application/zip")
666 ) { result ->
667 if (result == null) {
668 return@registerForActivityResult
669 }
670
671 IndeterminateProgressDialogFragment.newInstance(
672 this,
673 R.string.save_files_exporting,
674 false
675 ) {
676 val zipResult = FileUtil.zipFromInternalStorage(
677 File(savesFolderRoot),
678 savesFolderRoot,
679 BufferedOutputStream(contentResolver.openOutputStream(result))
680 )
681 return@newInstance when (zipResult) {
682 TaskState.Completed -> getString(R.string.export_success)
683 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
684 }
685 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
686 }
687
688 private val startForResultExportSave =
689 registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { _ ->
690 File(getPublicFilesDir().canonicalPath, "temp").deleteRecursively()
691 }
692
693 val importSaves =
694 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
695 if (result == null) {
696 return@registerForActivityResult
697 }
698
699 NativeLibrary.initializeEmptyUserDirectory()
700
701 val inputZip = contentResolver.openInputStream(result)
702 // A zip needs to have at least one subfolder named after a TitleId in order to be considered valid.
703 var validZip = false
704 val savesFolder = File(savesFolderRoot)
705 val cacheSaveDir = File("${applicationContext.cacheDir.path}/saves/")
706 cacheSaveDir.mkdir()
707
708 if (inputZip == null) {
709 Toast.makeText(
710 applicationContext,
711 getString(R.string.fatal_error),
712 Toast.LENGTH_LONG
713 ).show()
714 return@registerForActivityResult
715 }
716
717 val filterTitleId =
718 FilenameFilter { _, dirName -> dirName.matches(Regex("^0100[\\dA-Fa-f]{12}$")) }
719
720 try {
721 CoroutineScope(Dispatchers.IO).launch {
722 FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
723 cacheSaveDir.list(filterTitleId)?.forEach { savePath ->
724 File(savesFolder, savePath).deleteRecursively()
725 File(cacheSaveDir, savePath).copyRecursively(
726 File(savesFolder, savePath),
727 true
728 )
729 validZip = true
730 }
731
732 withContext(Dispatchers.Main) {
733 if (!validZip) {
734 MessageDialogFragment.newInstance(
735 this@MainActivity,
736 titleId = R.string.save_file_invalid_zip_structure,
737 descriptionId = R.string.save_file_invalid_zip_structure_description
738 ).show(supportFragmentManager, MessageDialogFragment.TAG)
739 return@withContext
740 }
741 Toast.makeText(
742 applicationContext,
743 getString(R.string.save_file_imported_success),
744 Toast.LENGTH_LONG
745 ).show()
746 }
747
748 cacheSaveDir.deleteRecursively()
749 }
750 } catch (e: Exception) {
751 Toast.makeText(
752 applicationContext,
753 getString(R.string.fatal_error),
754 Toast.LENGTH_LONG
755 ).show()
756 }
757 }
758} 695}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/AddonUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/AddonUtil.kt
new file mode 100644
index 000000000..8cc5ea71f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/AddonUtil.kt
@@ -0,0 +1,8 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6object AddonUtil {
7 val validAddonDirectories = listOf("cheats", "exefs", "romfs")
8}
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 21270fc84..0197fd712 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
@@ -16,7 +16,7 @@ object DirectoryInitialization {
16 if (!areDirectoriesReady) { 16 if (!areDirectoriesReady) {
17 initializeInternalStorage() 17 initializeInternalStorage()
18 NativeLibrary.initializeSystem(false) 18 NativeLibrary.initializeSystem(false)
19 NativeConfig.initializeConfig() 19 NativeConfig.initializeGlobalConfig()
20 areDirectoriesReady = true 20 areDirectoriesReady = true
21 } 21 }
22 } 22 }
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 bbe7bfa92..00c6bf90e 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
@@ -22,6 +22,7 @@ import java.io.BufferedOutputStream
22import java.lang.NullPointerException 22import java.lang.NullPointerException
23import java.nio.charset.StandardCharsets 23import java.nio.charset.StandardCharsets
24import java.util.zip.ZipOutputStream 24import java.util.zip.ZipOutputStream
25import kotlin.IllegalStateException
25 26
26object FileUtil { 27object FileUtil {
27 const val PATH_TREE = "tree" 28 const val PATH_TREE = "tree"
@@ -342,6 +343,37 @@ object FileUtil {
342 return TaskState.Completed 343 return TaskState.Completed
343 } 344 }
344 345
346 /**
347 * Helper function that copies the contents of a DocumentFile folder into a [File]
348 * @param file [File] representation of the folder to copy into
349 * @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa
350 */
351 fun DocumentFile.copyFilesTo(file: File) {
352 file.mkdirs()
353 if (!this.isDirectory || !file.isDirectory) {
354 throw IllegalStateException(
355 "[FileUtil] Tried to copy a folder into a file or vice versa"
356 )
357 }
358
359 this.listFiles().forEach {
360 val newFile = File(file, it.name!!)
361 if (it.isDirectory) {
362 newFile.mkdirs()
363 DocumentFile.fromTreeUri(YuzuApplication.appContext, it.uri)?.copyFilesTo(newFile)
364 } else {
365 val inputStream =
366 YuzuApplication.appContext.contentResolver.openInputStream(it.uri)
367 BufferedInputStream(inputStream).use { bos ->
368 if (!newFile.exists()) {
369 newFile.createNewFile()
370 }
371 newFile.outputStream().use { os -> bos.copyTo(os) }
372 }
373 }
374 }
375 }
376
345 fun isRootTreeUri(uri: Uri): Boolean { 377 fun isRootTreeUri(uri: Uri): Boolean {
346 val paths = uri.pathSegments 378 val paths = uri.pathSegments
347 return paths.size == 2 && PATH_TREE == paths[0] 379 return paths.size == 2 && PATH_TREE == paths[0]
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 55010dc59..579b600f1 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
@@ -36,6 +36,12 @@ object GameHelper {
36 // Ensure keys are loaded so that ROM metadata can be decrypted. 36 // Ensure keys are loaded so that ROM metadata can be decrypted.
37 NativeLibrary.reloadKeys() 37 NativeLibrary.reloadKeys()
38 38
39 // Reset metadata so we don't use stale information
40 GameMetadata.resetMetadata()
41
42 // Remove previous filesystem provider information so we can get up to date version info
43 NativeLibrary.clearFilesystemProvider()
44
39 val badDirs = mutableListOf<Int>() 45 val badDirs = mutableListOf<Int>()
40 gameDirs.forEachIndexed { index: Int, gameDir: GameDir -> 46 gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
41 val gameDirUri = Uri.parse(gameDir.uriString) 47 val gameDirUri = Uri.parse(gameDir.uriString)
@@ -92,14 +98,24 @@ object GameHelper {
92 ) 98 )
93 } else { 99 } else {
94 if (Game.extensions.contains(FileUtil.getExtension(it.uri))) { 100 if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
95 games.add(getGame(it.uri, true)) 101 val game = getGame(it.uri, true)
102 if (game != null) {
103 games.add(game)
104 }
96 } 105 }
97 } 106 }
98 } 107 }
99 } 108 }
100 109
101 fun getGame(uri: Uri, addedToLibrary: Boolean): Game { 110 fun getGame(uri: Uri, addedToLibrary: Boolean): Game? {
102 val filePath = uri.toString() 111 val filePath = uri.toString()
112 if (!GameMetadata.getIsValid(filePath)) {
113 return null
114 }
115
116 // Needed to update installed content information
117 NativeLibrary.addFileToFilesystemProvider(filePath)
118
103 var name = GameMetadata.getTitle(filePath) 119 var name = GameMetadata.getTitle(filePath)
104 120
105 // If the game's title field is empty, use the filename. 121 // If the game's title field is empty, use the filename.
@@ -118,7 +134,7 @@ object GameHelper {
118 filePath, 134 filePath,
119 programId, 135 programId,
120 GameMetadata.getDeveloper(filePath), 136 GameMetadata.getDeveloper(filePath),
121 GameMetadata.getVersion(filePath), 137 GameMetadata.getVersion(filePath, false),
122 GameMetadata.getIsHomebrew(filePath) 138 GameMetadata.getIsHomebrew(filePath)
123 ) 139 )
124 140
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt
index 0f3542ac6..8e412482a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt
@@ -4,13 +4,15 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6object GameMetadata { 6object GameMetadata {
7 external fun getIsValid(path: String): Boolean
8
7 external fun getTitle(path: String): String 9 external fun getTitle(path: String): String
8 10
9 external fun getProgramId(path: String): String 11 external fun getProgramId(path: String): String
10 12
11 external fun getDeveloper(path: String): String 13 external fun getDeveloper(path: String): String
12 14
13 external fun getVersion(path: String): String 15 external fun getVersion(path: String, reload: Boolean): String
14 16
15 external fun getIcon(path: String): ByteArray 17 external fun getIcon(path: String): ByteArray
16 18
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 f6882ce6c..685272288 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
@@ -10,6 +10,8 @@ import java.io.File
10import java.io.IOException 10import java.io.IOException
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.features.settings.model.StringSetting
14import java.io.FileNotFoundException
13import java.util.zip.ZipException 15import java.util.zip.ZipException
14import java.util.zip.ZipFile 16import java.util.zip.ZipFile
15 17
@@ -44,7 +46,7 @@ object GpuDriverHelper {
44 NativeLibrary.initializeGpuDriver( 46 NativeLibrary.initializeGpuDriver(
45 hookLibPath, 47 hookLibPath,
46 driverInstallationPath, 48 driverInstallationPath,
47 customDriverData.libraryName, 49 installedCustomDriverData.libraryName,
48 fileRedirectionPath 50 fileRedirectionPath
49 ) 51 )
50 } 52 }
@@ -190,6 +192,7 @@ object GpuDriverHelper {
190 } 192 }
191 } 193 }
192 } catch (_: ZipException) { 194 } catch (_: ZipException) {
195 } catch (_: FileNotFoundException) {
193 } 196 }
194 return GpuDriverMetadata() 197 return GpuDriverMetadata()
195 } 198 }
@@ -197,9 +200,12 @@ object GpuDriverHelper {
197 external fun supportsCustomDriverLoading(): Boolean 200 external fun supportsCustomDriverLoading(): Boolean
198 201
199 // Parse the custom driver metadata to retrieve the name. 202 // Parse the custom driver metadata to retrieve the name.
200 val customDriverData: GpuDriverMetadata 203 val installedCustomDriverData: GpuDriverMetadata
201 get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME)) 204 get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
202 205
206 val customDriverSettingData: GpuDriverMetadata
207 get() = getMetadataFromZip(File(StringSetting.DRIVER_PATH.getString()))
208
203 fun initializeDirectories() { 209 fun initializeDirectories() {
204 // Ensure the file redirection directory exists. 210 // Ensure the file redirection directory exists.
205 val fileRedirectionDir = File(fileRedirectionPath!!) 211 val fileRedirectionDir = File(fileRedirectionPath!!)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
index 9076a86c4..0b94c73e5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
@@ -27,13 +27,13 @@ object MemoryUtil {
27 const val Pb = Tb * 1024 27 const val Pb = Tb * 1024
28 const val Eb = Pb * 1024 28 const val Eb = Pb * 1024
29 29
30 private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = 30 fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
31 when { 31 when {
32 size < Kb -> { 32 size < Kb -> {
33 context.getString( 33 context.getString(
34 R.string.memory_formatted, 34 R.string.memory_formatted,
35 size.hundredths, 35 size.hundredths,
36 context.getString(R.string.memory_byte) 36 context.getString(R.string.memory_byte_shorthand)
37 ) 37 )
38 } 38 }
39 size < Mb -> { 39 size < Mb -> {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
index f4e1bb13f..7512d5eed 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -7,56 +7,113 @@ import org.yuzu.yuzu_emu.model.GameDir
7 7
8object NativeConfig { 8object NativeConfig {
9 /** 9 /**
10 * Creates a Config object and opens the emulation config. 10 * Loads global config.
11 */ 11 */
12 @Synchronized 12 @Synchronized
13 external fun initializeConfig() 13 external fun initializeGlobalConfig()
14 14
15 /** 15 /**
16 * Destroys the stored config object. This automatically saves the existing config. 16 * Destroys the stored global config object. This does not save the existing config.
17 */ 17 */
18 @Synchronized 18 @Synchronized
19 external fun unloadConfig() 19 external fun unloadGlobalConfig()
20 20
21 /** 21 /**
22 * Reads values saved to the config file and saves them. 22 * Reads values in the global config file and saves them.
23 */ 23 */
24 @Synchronized 24 @Synchronized
25 external fun reloadSettings() 25 external fun reloadGlobalConfig()
26 26
27 /** 27 /**
28 * Saves settings values in memory to disk. 28 * Saves global settings values in memory to disk.
29 */ 29 */
30 @Synchronized 30 @Synchronized
31 external fun saveSettings() 31 external fun saveGlobalConfig()
32 32
33 external fun getBoolean(key: String, getDefault: Boolean): Boolean 33 /**
34 * Creates per-game config for the specified parameters. Must be unloaded once per-game config
35 * is closed with [unloadPerGameConfig]. All switchable values that [NativeConfig] gets/sets
36 * will follow the per-game config until the global config is reloaded.
37 *
38 * @param programId String representation of the u64 programId
39 * @param fileName Filename of the game, including its extension
40 */
41 @Synchronized
42 external fun initializePerGameConfig(programId: String, fileName: String)
43
44 @Synchronized
45 external fun isPerGameConfigLoaded(): Boolean
46
47 /**
48 * Saves per-game settings values in memory to disk.
49 */
50 @Synchronized
51 external fun savePerGameConfig()
52
53 /**
54 * Destroys the stored per-game config object. This does not save the config.
55 */
56 @Synchronized
57 external fun unloadPerGameConfig()
58
59 @Synchronized
60 external fun getBoolean(key: String, needsGlobal: Boolean): Boolean
61
62 @Synchronized
34 external fun setBoolean(key: String, value: Boolean) 63 external fun setBoolean(key: String, value: Boolean)
35 64
36 external fun getByte(key: String, getDefault: Boolean): Byte 65 @Synchronized
66 external fun getByte(key: String, needsGlobal: Boolean): Byte
67
68 @Synchronized
37 external fun setByte(key: String, value: Byte) 69 external fun setByte(key: String, value: Byte)
38 70
39 external fun getShort(key: String, getDefault: Boolean): Short 71 @Synchronized
72 external fun getShort(key: String, needsGlobal: Boolean): Short
73
74 @Synchronized
40 external fun setShort(key: String, value: Short) 75 external fun setShort(key: String, value: Short)
41 76
42 external fun getInt(key: String, getDefault: Boolean): Int 77 @Synchronized
78 external fun getInt(key: String, needsGlobal: Boolean): Int
79
80 @Synchronized
43 external fun setInt(key: String, value: Int) 81 external fun setInt(key: String, value: Int)
44 82
45 external fun getFloat(key: String, getDefault: Boolean): Float 83 @Synchronized
84 external fun getFloat(key: String, needsGlobal: Boolean): Float
85
86 @Synchronized
46 external fun setFloat(key: String, value: Float) 87 external fun setFloat(key: String, value: Float)
47 88
48 external fun getLong(key: String, getDefault: Boolean): Long 89 @Synchronized
90 external fun getLong(key: String, needsGlobal: Boolean): Long
91
92 @Synchronized
49 external fun setLong(key: String, value: Long) 93 external fun setLong(key: String, value: Long)
50 94
51 external fun getString(key: String, getDefault: Boolean): String 95 @Synchronized
96 external fun getString(key: String, needsGlobal: Boolean): String
97
98 @Synchronized
52 external fun setString(key: String, value: String) 99 external fun setString(key: String, value: String)
53 100
54 external fun getIsRuntimeModifiable(key: String): Boolean 101 external fun getIsRuntimeModifiable(key: String): Boolean
55 102
56 external fun getConfigHeader(category: Int): String
57
58 external fun getPairedSettingKey(key: String): String 103 external fun getPairedSettingKey(key: String): String
59 104
105 external fun getIsSwitchable(key: String): Boolean
106
107 @Synchronized
108 external fun usingGlobal(key: String): Boolean
109
110 @Synchronized
111 external fun setGlobal(key: String, global: Boolean)
112
113 external fun getIsSaveable(key: String): Boolean
114
115 external fun getDefaultToString(key: String): String
116
60 /** 117 /**
61 * Gets every [GameDir] in AndroidSettings::values.game_dirs 118 * Gets every [GameDir] in AndroidSettings::values.game_dirs
62 */ 119 */
@@ -74,4 +131,23 @@ object NativeConfig {
74 */ 131 */
75 @Synchronized 132 @Synchronized
76 external fun addGameDir(dir: GameDir) 133 external fun addGameDir(dir: GameDir)
134
135 /**
136 * Gets an array of the addons that are disabled for a given game
137 *
138 * @param programId String representation of a game's program ID
139 * @return An array of disabled addons
140 */
141 @Synchronized
142 external fun getDisabledAddons(programId: String): Array<String>
143
144 /**
145 * Clears the disabled addons array corresponding to [programId] and replaces them
146 * with [disabledAddons]
147 *
148 * @param programId String representation of a game's program ID
149 * @param disabledAddons Replacement array of disabled addons
150 */
151 @Synchronized
152 external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
77} 153}
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index 767d8ea83..9c3a5a9b2 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -36,6 +36,7 @@ void AndroidConfig::ReadAndroidValues() {
36 ReadAndroidUIValues(); 36 ReadAndroidUIValues();
37 ReadUIValues(); 37 ReadUIValues();
38 } 38 }
39 ReadDriverValues();
39} 40}
40 41
41void AndroidConfig::ReadAndroidUIValues() { 42void AndroidConfig::ReadAndroidUIValues() {
@@ -57,6 +58,7 @@ void AndroidConfig::ReadUIValues() {
57void AndroidConfig::ReadPathValues() { 58void AndroidConfig::ReadPathValues() {
58 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); 59 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
59 60
61 AndroidSettings::values.game_dirs.clear();
60 const int gamedirs_size = BeginArray(std::string("gamedirs")); 62 const int gamedirs_size = BeginArray(std::string("gamedirs"));
61 for (int i = 0; i < gamedirs_size; ++i) { 63 for (int i = 0; i < gamedirs_size; ++i) {
62 SetArrayIndex(i); 64 SetArrayIndex(i);
@@ -71,11 +73,20 @@ void AndroidConfig::ReadPathValues() {
71 EndGroup(); 73 EndGroup();
72} 74}
73 75
76void AndroidConfig::ReadDriverValues() {
77 BeginGroup(Settings::TranslateCategory(Settings::Category::GpuDriver));
78
79 ReadCategory(Settings::Category::GpuDriver);
80
81 EndGroup();
82}
83
74void AndroidConfig::SaveAndroidValues() { 84void AndroidConfig::SaveAndroidValues() {
75 if (global) { 85 if (global) {
76 SaveAndroidUIValues(); 86 SaveAndroidUIValues();
77 SaveUIValues(); 87 SaveUIValues();
78 } 88 }
89 SaveDriverValues();
79 90
80 WriteToIni(); 91 WriteToIni();
81} 92}
@@ -111,6 +122,14 @@ void AndroidConfig::SavePathValues() {
111 EndGroup(); 122 EndGroup();
112} 123}
113 124
125void AndroidConfig::SaveDriverValues() {
126 BeginGroup(Settings::TranslateCategory(Settings::Category::GpuDriver));
127
128 WriteCategory(Settings::Category::GpuDriver);
129
130 EndGroup();
131}
132
114std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { 133std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
115 auto& map = Settings::values.linkage.by_category; 134 auto& map = Settings::values.linkage.by_category;
116 if (map.contains(category)) { 135 if (map.contains(category)) {
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index f490be016..2c12874e1 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -17,6 +17,7 @@ public:
17protected: 17protected:
18 void ReadAndroidValues(); 18 void ReadAndroidValues();
19 void ReadAndroidUIValues(); 19 void ReadAndroidUIValues();
20 void ReadDriverValues();
20 void ReadHidbusValues() override {} 21 void ReadHidbusValues() override {}
21 void ReadDebugControlValues() override {} 22 void ReadDebugControlValues() override {}
22 void ReadPathValues() override; 23 void ReadPathValues() override;
@@ -28,6 +29,7 @@ protected:
28 29
29 void SaveAndroidValues(); 30 void SaveAndroidValues();
30 void SaveAndroidUIValues(); 31 void SaveAndroidUIValues();
32 void SaveDriverValues();
31 void SaveHidbusValues() override {} 33 void SaveHidbusValues() override {}
32 void SaveDebugControlValues() override {} 34 void SaveDebugControlValues() override {}
33 void SavePathValues() override; 35 void SavePathValues() override;
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index fc0523206..3733f5a3c 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -30,6 +30,9 @@ struct Values {
30 Settings::Specialization::Default, 30 Settings::Specialization::Default,
31 true, 31 true,
32 true}; 32 true};
33
34 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
35 Settings::Category::GpuDriver};
33}; 36};
34 37
35extern Values values; 38extern Values values;
diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp
index 24d9df702..78f604c70 100644
--- a/src/android/app/src/main/jni/game_metadata.cpp
+++ b/src/android/app/src/main/jni/game_metadata.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 <core/core.h> 4#include <core/core.h>
5#include <core/file_sys/mode.h>
5#include <core/file_sys/patch_manager.h> 6#include <core/file_sys/patch_manager.h>
6#include <core/loader/nro.h> 7#include <core/loader/nro.h>
7#include <jni.h> 8#include <jni.h>
@@ -61,7 +62,11 @@ RomMetadata CacheRomMetadata(const std::string& path) {
61 return entry; 62 return entry;
62} 63}
63 64
64RomMetadata GetRomMetadata(const std::string& path) { 65RomMetadata GetRomMetadata(const std::string& path, bool reload = false) {
66 if (reload) {
67 return CacheRomMetadata(path);
68 }
69
65 if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { 70 if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
66 return search->second; 71 return search->second;
67 } 72 }
@@ -71,6 +76,32 @@ RomMetadata GetRomMetadata(const std::string& path) {
71 76
72extern "C" { 77extern "C" {
73 78
79jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
80 jstring jpath) {
81 const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
82 GetJString(env, jpath), FileSys::Mode::Read);
83 if (!file) {
84 return false;
85 }
86
87 auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file);
88 if (!loader) {
89 return false;
90 }
91
92 const auto file_type = loader->GetFileType();
93 if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
94 return false;
95 }
96
97 u64 program_id = 0;
98 Loader::ResultStatus res = loader->ReadProgramId(program_id);
99 if (res != Loader::ResultStatus::Success) {
100 return false;
101 }
102 return true;
103}
104
74jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, 105jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
75 jstring jpath) { 106 jstring jpath) {
76 return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title); 107 return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title);
@@ -87,8 +118,8 @@ jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, job
87} 118}
88 119
89jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, 120jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
90 jstring jpath) { 121 jstring jpath, jboolean jreload) {
91 return ToJString(env, GetRomMetadata(GetJString(env, jpath)).version); 122 return ToJString(env, GetRomMetadata(GetJString(env, jpath), jreload).version);
92} 123}
93 124
94jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, 125jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
@@ -106,7 +137,7 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, j
106} 137}
107 138
108void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { 139void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
109 return m_rom_metadata_cache.clear(); 140 m_rom_metadata_cache.clear();
110} 141}
111 142
112} // extern "C" 143} // extern "C"
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index a56ed5662..e7a86d3fd 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -20,6 +20,21 @@ static jmethodID s_disk_cache_load_progress;
20static jmethodID s_on_emulation_started; 20static jmethodID s_on_emulation_started;
21static jmethodID s_on_emulation_stopped; 21static jmethodID s_on_emulation_stopped;
22 22
23static jclass s_game_class;
24static jmethodID s_game_constructor;
25static jfieldID s_game_title_field;
26static jfieldID s_game_path_field;
27static jfieldID s_game_program_id_field;
28static jfieldID s_game_developer_field;
29static jfieldID s_game_version_field;
30static jfieldID s_game_is_homebrew_field;
31
32static jclass s_string_class;
33static jclass s_pair_class;
34static jmethodID s_pair_constructor;
35static jfieldID s_pair_first_field;
36static jfieldID s_pair_second_field;
37
23static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 38static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
24 39
25namespace IDCache { 40namespace IDCache {
@@ -79,6 +94,58 @@ jmethodID GetOnEmulationStopped() {
79 return s_on_emulation_stopped; 94 return s_on_emulation_stopped;
80} 95}
81 96
97jclass GetGameClass() {
98 return s_game_class;
99}
100
101jmethodID GetGameConstructor() {
102 return s_game_constructor;
103}
104
105jfieldID GetGameTitleField() {
106 return s_game_title_field;
107}
108
109jfieldID GetGamePathField() {
110 return s_game_path_field;
111}
112
113jfieldID GetGameProgramIdField() {
114 return s_game_program_id_field;
115}
116
117jfieldID GetGameDeveloperField() {
118 return s_game_developer_field;
119}
120
121jfieldID GetGameVersionField() {
122 return s_game_version_field;
123}
124
125jfieldID GetGameIsHomebrewField() {
126 return s_game_is_homebrew_field;
127}
128
129jclass GetStringClass() {
130 return s_string_class;
131}
132
133jclass GetPairClass() {
134 return s_pair_class;
135}
136
137jmethodID GetPairConstructor() {
138 return s_pair_constructor;
139}
140
141jfieldID GetPairFirstField() {
142 return s_pair_first_field;
143}
144
145jfieldID GetPairSecondField() {
146 return s_pair_second_field;
147}
148
82} // namespace IDCache 149} // namespace IDCache
83 150
84#ifdef __cplusplus 151#ifdef __cplusplus
@@ -115,6 +182,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
115 s_on_emulation_stopped = 182 s_on_emulation_stopped =
116 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); 183 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
117 184
185 const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
186 s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
187 s_game_constructor = env->GetMethodID(game_class, "<init>",
188 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
189 "String;Ljava/lang/String;Ljava/lang/String;Z)V");
190 s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
191 s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
192 s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
193 s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
194 s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
195 s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
196 env->DeleteLocalRef(game_class);
197
198 const jclass string_class = env->FindClass("java/lang/String");
199 s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
200 env->DeleteLocalRef(string_class);
201
202 const jclass pair_class = env->FindClass("kotlin/Pair");
203 s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
204 s_pair_constructor =
205 env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
206 s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
207 s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
208 env->DeleteLocalRef(pair_class);
209
118 // Initialize Android Storage 210 // Initialize Android Storage
119 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 211 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
120 212
@@ -136,6 +228,9 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
136 env->DeleteGlobalRef(s_disk_cache_progress_class); 228 env->DeleteGlobalRef(s_disk_cache_progress_class);
137 env->DeleteGlobalRef(s_load_callback_stage_class); 229 env->DeleteGlobalRef(s_load_callback_stage_class);
138 env->DeleteGlobalRef(s_game_dir_class); 230 env->DeleteGlobalRef(s_game_dir_class);
231 env->DeleteGlobalRef(s_game_class);
232 env->DeleteGlobalRef(s_string_class);
233 env->DeleteGlobalRef(s_pair_class);
139 234
140 // UnInitialize applets 235 // UnInitialize applets
141 SoftwareKeyboard::CleanupJNI(env); 236 SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 855649efa..24030be42 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -20,4 +20,19 @@ jmethodID GetDiskCacheLoadProgress();
20jmethodID GetOnEmulationStarted(); 20jmethodID GetOnEmulationStarted();
21jmethodID GetOnEmulationStopped(); 21jmethodID GetOnEmulationStopped();
22 22
23jclass GetGameClass();
24jmethodID GetGameConstructor();
25jfieldID GetGameTitleField();
26jfieldID GetGamePathField();
27jfieldID GetGameProgramIdField();
28jfieldID GetGameDeveloperField();
29jfieldID GetGameVersionField();
30jfieldID GetGameIsHomebrewField();
31
32jclass GetStringClass();
33jclass GetPairClass();
34jmethodID GetPairConstructor();
35jfieldID GetPairFirstField();
36jfieldID GetPairSecondField();
37
23} // namespace IDCache 38} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index e5d3158c8..0c1db7d46 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 <common/fs/fs.h> 16#include <common/fs/fs.h>
17#include <core/file_sys/patch_manager.h>
17#include <core/file_sys/savedata_factory.h> 18#include <core/file_sys/savedata_factory.h>
18#include <core/loader/nro.h> 19#include <core/loader/nro.h>
19#include <jni.h> 20#include <jni.h>
@@ -79,6 +80,10 @@ Core::System& EmulationSession::System() {
79 return m_system; 80 return m_system;
80} 81}
81 82
83FileSys::ManualContentProvider* EmulationSession::GetContentProvider() {
84 return m_manual_provider.get();
85}
86
82const EmuWindow_Android& EmulationSession::Window() const { 87const EmuWindow_Android& EmulationSession::Window() const {
83 return *m_window; 88 return *m_window;
84} 89}
@@ -455,6 +460,15 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
455 static_cast<jint>(result)); 460 static_cast<jint>(result));
456} 461}
457 462
463u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
464 auto program_id_string = GetJString(env, jprogramId);
465 try {
466 return std::stoull(program_id_string);
467 } catch (...) {
468 return 0;
469 }
470}
471
458static Core::SystemResultStatus RunEmulation(const std::string& filepath) { 472static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
459 MicroProfileOnThreadCreate("EmuThread"); 473 MicroProfileOnThreadCreate("EmuThread");
460 SCOPE_EXIT({ MicroProfileShutdown(); }); 474 SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -504,6 +518,27 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
504 GetJString(env, j_file_extension)); 518 GetJString(env, j_file_extension));
505} 519}
506 520
521jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
522 jstring jprogramId,
523 jstring jupdatePath) {
524 u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
525 std::string updatePath = GetJString(env, jupdatePath);
526 std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
527 EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
528 FileSys::Mode::Read));
529 for (const auto& item : nsp->GetNCAs()) {
530 for (const auto& nca_details : item.second) {
531 if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
532 auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
533 if (update_id == program_id) {
534 return true;
535 }
536 }
537 }
538 }
539 return false;
540}
541
507void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz, 542void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
508 jstring hook_lib_dir, 543 jstring hook_lib_dir,
509 jstring custom_driver_dir, 544 jstring custom_driver_dir,
@@ -665,13 +700,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass
665 EmulationSession::GetInstance().InitializeSystem(reload); 700 EmulationSession::GetInstance().InitializeSystem(reload);
666} 701}
667 702
668jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
669 return {};
670}
671
672void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
673 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
674
675jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { 703jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
676 jdoubleArray j_stats = env->NewDoubleArray(4); 704 jdoubleArray j_stats = env->NewDoubleArray(4);
677 705
@@ -696,9 +724,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass
696 return ToJString(env, "JIT"); 724 return ToJString(env, "JIT");
697} 725}
698 726
699void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, 727void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
700 jclass clazz, 728 EmulationSession::GetInstance().System().ApplySettings();
701 jstring j_path) {} 729}
730
731void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj) {
732 Settings::LogSettings();
733}
702 734
703void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, 735void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
704 jstring j_path) { 736 jstring j_path) {
@@ -792,4 +824,69 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
792 return true; 824 return true;
793} 825}
794 826
827jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env, jobject jobj,
828 jstring jpath,
829 jstring jprogramId) {
830 const auto path = GetJString(env, jpath);
831 const auto vFile =
832 Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
833 if (vFile == nullptr) {
834 return nullptr;
835 }
836
837 auto& system = EmulationSession::GetInstance().System();
838 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
839 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
840 system.GetContentProvider()};
841 const auto loader = Loader::GetLoader(system, vFile);
842
843 FileSys::VirtualFile update_raw;
844 loader->ReadUpdateRaw(update_raw);
845
846 auto addons = pm.GetPatchVersionNames(update_raw);
847 auto jemptyString = ToJString(env, "");
848 auto jemptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
849 jemptyString, jemptyString);
850 jobjectArray jaddonsArray =
851 env->NewObjectArray(addons.size(), IDCache::GetPairClass(), jemptyStringPair);
852 int i = 0;
853 for (const auto& addon : addons) {
854 jobject jaddon = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
855 ToJString(env, addon.first), ToJString(env, addon.second));
856 env->SetObjectArrayElement(jaddonsArray, i, jaddon);
857 ++i;
858 }
859 return jaddonsArray;
860}
861
862jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
863 jstring jprogramId) {
864 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
865
866 auto& system = EmulationSession::GetInstance().System();
867
868 Service::Account::ProfileManager manager;
869 // TODO: Pass in a selected user once we get the relevant UI working
870 const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
871 ASSERT(user_id);
872
873 const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
874 auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
875 FileSys::Mode::Read);
876
877 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
878 system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
879 program_id, user_id->AsU128(), 0);
880 return ToJString(env, user_save_data_path);
881}
882
883void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
884 jstring jpath) {
885 EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath));
886}
887
888void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {
889 EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
890}
891
795} // extern "C" 892} // extern "C"
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index f1457bd1f..4a8049578 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -21,6 +21,7 @@ public:
21 static EmulationSession& GetInstance(); 21 static EmulationSession& GetInstance();
22 const Core::System& System() const; 22 const Core::System& System() const;
23 Core::System& System(); 23 Core::System& System();
24 FileSys::ManualContentProvider* GetContentProvider();
24 25
25 const EmuWindow_Android& Window() const; 26 const EmuWindow_Android& Window() const;
26 EmuWindow_Android& Window(); 27 EmuWindow_Android& Window();
@@ -54,6 +55,8 @@ public:
54 55
55 static void OnEmulationStarted(); 56 static void OnEmulationStarted();
56 57
58 static u64 GetProgramId(JNIEnv* env, jstring jprogramId);
59
57private: 60private:
58 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); 61 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
59 static void OnEmulationStopped(Core::SystemResultStatus result); 62 static void OnEmulationStopped(Core::SystemResultStatus result);
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 763b2164c..324d9e9cd 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -3,6 +3,7 @@
3 3
4#include <string> 4#include <string>
5 5
6#include <common/fs/fs_util.h>
6#include <jni.h> 7#include <jni.h>
7 8
8#include "android_config.h" 9#include "android_config.h"
@@ -12,19 +13,21 @@
12#include "frontend_common/config.h" 13#include "frontend_common/config.h"
13#include "jni/android_common/android_common.h" 14#include "jni/android_common/android_common.h"
14#include "jni/id_cache.h" 15#include "jni/id_cache.h"
16#include "native.h"
15 17
16std::unique_ptr<AndroidConfig> config; 18std::unique_ptr<AndroidConfig> global_config;
19std::unique_ptr<AndroidConfig> per_game_config;
17 20
18template <typename T> 21template <typename T>
19Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { 22Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
20 auto key = GetJString(env, jkey); 23 auto key = GetJString(env, jkey);
21 auto basicSetting = Settings::values.linkage.by_key[key]; 24 auto basic_setting = Settings::values.linkage.by_key[key];
22 auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key]; 25 if (basic_setting != 0) {
23 if (basicSetting != 0) { 26 return static_cast<Settings::Setting<T>*>(basic_setting);
24 return static_cast<Settings::Setting<T>*>(basicSetting);
25 } 27 }
26 if (basicAndroidSetting != 0) { 28 auto basic_android_setting = AndroidSettings::values.linkage.by_key[key];
27 return static_cast<Settings::Setting<T>*>(basicAndroidSetting); 29 if (basic_android_setting != 0) {
30 return static_cast<Settings::Setting<T>*>(basic_android_setting);
28 } 31 }
29 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key); 32 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
30 return nullptr; 33 return nullptr;
@@ -32,35 +35,52 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
32 35
33extern "C" { 36extern "C" {
34 37
35void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) { 38void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) {
36 config = std::make_unique<AndroidConfig>(); 39 global_config = std::make_unique<AndroidConfig>();
37} 40}
38 41
39void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) { 42void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) {
40 config.reset(); 43 global_config.reset();
41} 44}
42 45
43void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) { 46void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadGlobalConfig(JNIEnv* env, jobject obj) {
44 config->AndroidConfig::ReloadAllValues(); 47 global_config->AndroidConfig::ReloadAllValues();
45} 48}
46 49
47void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) { 50void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jobject obj) {
48 config->AndroidConfig::SaveAllValues(); 51 global_config->AndroidConfig::SaveAllValues();
52}
53
54void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
55 jstring jprogramId,
56 jstring jfileName) {
57 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
58 auto file_name = GetJString(env, jfileName);
59 const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
60 per_game_config =
61 std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig);
62}
63
64jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_isPerGameConfigLoaded(JNIEnv* env,
65 jobject obj) {
66 return per_game_config != nullptr;
67}
68
69void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_savePerGameConfig(JNIEnv* env, jobject obj) {
70 per_game_config->AndroidConfig::SaveAllValues();
71}
72
73void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadPerGameConfig(JNIEnv* env, jobject obj) {
74 per_game_config.reset();
49} 75}
50 76
51jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, 77jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
52 jstring jkey, jboolean getDefault) { 78 jstring jkey, jboolean needGlobal) {
53 auto setting = getSetting<bool>(env, jkey); 79 auto setting = getSetting<bool>(env, jkey);
54 if (setting == nullptr) { 80 if (setting == nullptr) {
55 return false; 81 return false;
56 } 82 }
57 setting->SetGlobal(true); 83 return setting->GetValue(static_cast<bool>(needGlobal));
58
59 if (static_cast<bool>(getDefault)) {
60 return setting->GetDefault();
61 }
62
63 return setting->GetValue();
64} 84}
65 85
66void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey, 86void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey,
@@ -69,23 +89,16 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject
69 if (setting == nullptr) { 89 if (setting == nullptr) {
70 return; 90 return;
71 } 91 }
72 setting->SetGlobal(true);
73 setting->SetValue(static_cast<bool>(value)); 92 setting->SetValue(static_cast<bool>(value));
74} 93}
75 94
76jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey, 95jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey,
77 jboolean getDefault) { 96 jboolean needGlobal) {
78 auto setting = getSetting<u8>(env, jkey); 97 auto setting = getSetting<u8>(env, jkey);
79 if (setting == nullptr) { 98 if (setting == nullptr) {
80 return -1; 99 return -1;
81 } 100 }
82 setting->SetGlobal(true); 101 return setting->GetValue(static_cast<bool>(needGlobal));
83
84 if (static_cast<bool>(getDefault)) {
85 return setting->GetDefault();
86 }
87
88 return setting->GetValue();
89} 102}
90 103
91void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey, 104void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey,
@@ -94,23 +107,16 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj
94 if (setting == nullptr) { 107 if (setting == nullptr) {
95 return; 108 return;
96 } 109 }
97 setting->SetGlobal(true);
98 setting->SetValue(value); 110 setting->SetValue(value);
99} 111}
100 112
101jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey, 113jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey,
102 jboolean getDefault) { 114 jboolean needGlobal) {
103 auto setting = getSetting<u16>(env, jkey); 115 auto setting = getSetting<u16>(env, jkey);
104 if (setting == nullptr) { 116 if (setting == nullptr) {
105 return -1; 117 return -1;
106 } 118 }
107 setting->SetGlobal(true); 119 return setting->GetValue(static_cast<bool>(needGlobal));
108
109 if (static_cast<bool>(getDefault)) {
110 return setting->GetDefault();
111 }
112
113 return setting->GetValue();
114} 120}
115 121
116void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey, 122void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey,
@@ -119,23 +125,16 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
119 if (setting == nullptr) { 125 if (setting == nullptr) {
120 return; 126 return;
121 } 127 }
122 setting->SetGlobal(true);
123 setting->SetValue(value); 128 setting->SetValue(value);
124} 129}
125 130
126jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey, 131jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
127 jboolean getDefault) { 132 jboolean needGlobal) {
128 auto setting = getSetting<int>(env, jkey); 133 auto setting = getSetting<int>(env, jkey);
129 if (setting == nullptr) { 134 if (setting == nullptr) {
130 return -1; 135 return -1;
131 } 136 }
132 setting->SetGlobal(true); 137 return setting->GetValue(needGlobal);
133
134 if (static_cast<bool>(getDefault)) {
135 return setting->GetDefault();
136 }
137
138 return setting->GetValue();
139} 138}
140 139
141void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey, 140void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey,
@@ -144,23 +143,16 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj,
144 if (setting == nullptr) { 143 if (setting == nullptr) {
145 return; 144 return;
146 } 145 }
147 setting->SetGlobal(true);
148 setting->SetValue(value); 146 setting->SetValue(value);
149} 147}
150 148
151jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey, 149jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey,
152 jboolean getDefault) { 150 jboolean needGlobal) {
153 auto setting = getSetting<float>(env, jkey); 151 auto setting = getSetting<float>(env, jkey);
154 if (setting == nullptr) { 152 if (setting == nullptr) {
155 return -1; 153 return -1;
156 } 154 }
157 setting->SetGlobal(true); 155 return setting->GetValue(static_cast<bool>(needGlobal));
158
159 if (static_cast<bool>(getDefault)) {
160 return setting->GetDefault();
161 }
162
163 return setting->GetValue();
164} 156}
165 157
166void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey, 158void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey,
@@ -169,23 +161,16 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject ob
169 if (setting == nullptr) { 161 if (setting == nullptr) {
170 return; 162 return;
171 } 163 }
172 setting->SetGlobal(true);
173 setting->SetValue(value); 164 setting->SetValue(value);
174} 165}
175 166
176jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey, 167jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey,
177 jboolean getDefault) { 168 jboolean needGlobal) {
178 auto setting = getSetting<long>(env, jkey); 169 auto setting = getSetting<s64>(env, jkey);
179 if (setting == nullptr) { 170 if (setting == nullptr) {
180 return -1; 171 return -1;
181 } 172 }
182 setting->SetGlobal(true); 173 return setting->GetValue(static_cast<bool>(needGlobal));
183
184 if (static_cast<bool>(getDefault)) {
185 return setting->GetDefault();
186 }
187
188 return setting->GetValue();
189} 174}
190 175
191void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey, 176void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey,
@@ -194,23 +179,16 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj
194 if (setting == nullptr) { 179 if (setting == nullptr) {
195 return; 180 return;
196 } 181 }
197 setting->SetGlobal(true);
198 setting->SetValue(value); 182 setting->SetValue(value);
199} 183}
200 184
201jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey, 185jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey,
202 jboolean getDefault) { 186 jboolean needGlobal) {
203 auto setting = getSetting<std::string>(env, jkey); 187 auto setting = getSetting<std::string>(env, jkey);
204 if (setting == nullptr) { 188 if (setting == nullptr) {
205 return ToJString(env, ""); 189 return ToJString(env, "");
206 } 190 }
207 setting->SetGlobal(true); 191 return ToJString(env, setting->GetValue(static_cast<bool>(needGlobal)));
208
209 if (static_cast<bool>(getDefault)) {
210 return ToJString(env, setting->GetDefault());
211 }
212
213 return ToJString(env, setting->GetValue());
214} 192}
215 193
216void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey, 194void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
@@ -220,27 +198,18 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject o
220 return; 198 return;
221 } 199 }
222 200
223 setting->SetGlobal(true);
224 setting->SetValue(GetJString(env, value)); 201 setting->SetValue(GetJString(env, value));
225} 202}
226 203
227jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj, 204jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
228 jstring jkey) { 205 jstring jkey) {
229 auto key = GetJString(env, jkey); 206 auto setting = getSetting<std::string>(env, jkey);
230 auto setting = Settings::values.linkage.by_key[key]; 207 if (setting != nullptr) {
231 if (setting != 0) {
232 return setting->RuntimeModfiable(); 208 return setting->RuntimeModfiable();
233 } 209 }
234 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
235 return true; 210 return true;
236} 211}
237 212
238jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj,
239 jint jcategory) {
240 auto category = static_cast<Settings::Category>(jcategory);
241 return ToJString(env, Settings::TranslateCategory(category));
242}
243
244jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* env, jobject obj, 213jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* env, jobject obj,
245 jstring jkey) { 214 jstring jkey) {
246 auto setting = getSetting<std::string>(env, jkey); 215 auto setting = getSetting<std::string>(env, jkey);
@@ -254,6 +223,50 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e
254 return ToJString(env, setting->PairedSetting()->GetLabel()); 223 return ToJString(env, setting->PairedSetting()->GetLabel());
255} 224}
256 225
226jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj,
227 jstring jkey) {
228 auto setting = getSetting<std::string>(env, jkey);
229 if (setting != nullptr) {
230 return setting->Switchable();
231 }
232 return false;
233}
234
235jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_usingGlobal(JNIEnv* env, jobject obj,
236 jstring jkey) {
237 auto setting = getSetting<std::string>(env, jkey);
238 if (setting != nullptr) {
239 return setting->UsingGlobal();
240 }
241 return true;
242}
243
244void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGlobal(JNIEnv* env, jobject obj, jstring jkey,
245 jboolean global) {
246 auto setting = getSetting<std::string>(env, jkey);
247 if (setting != nullptr) {
248 setting->SetGlobal(static_cast<bool>(global));
249 }
250}
251
252jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSaveable(JNIEnv* env, jobject obj,
253 jstring jkey) {
254 auto setting = getSetting<std::string>(env, jkey);
255 if (setting != nullptr) {
256 return setting->Save();
257 }
258 return false;
259}
260
261jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* env, jobject obj,
262 jstring jkey) {
263 auto setting = getSetting<std::string>(env, jkey);
264 if (setting != nullptr) {
265 return ToJString(env, setting->DefaultToString());
266 }
267 return ToJString(env, "");
268}
269
257jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) { 270jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
258 jclass gameDirClass = IDCache::GetGameDirClass(); 271 jclass gameDirClass = IDCache::GetGameDirClass();
259 jmethodID gameDirConstructor = IDCache::GetGameDirConstructor(); 272 jmethodID gameDirConstructor = IDCache::GetGameDirConstructor();
@@ -305,4 +318,30 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject
305 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); 318 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
306} 319}
307 320
321jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
322 jstring jprogramId) {
323 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
324 auto& disabledAddons = Settings::values.disabled_addons[program_id];
325 jobjectArray jdisabledAddonsArray =
326 env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, ""));
327 for (size_t i = 0; i < disabledAddons.size(); ++i) {
328 env->SetObjectArrayElement(jdisabledAddonsArray, i, ToJString(env, disabledAddons[i]));
329 }
330 return jdisabledAddonsArray;
331}
332
333void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
334 jstring jprogramId,
335 jobjectArray jdisabledAddons) {
336 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
337 Settings::values.disabled_addons[program_id].clear();
338 std::vector<std::string> disabled_addons;
339 const int size = env->GetArrayLength(jdisabledAddons);
340 for (int i = 0; i < size; ++i) {
341 auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i));
342 disabled_addons.push_back(GetJString(env, jaddon));
343 }
344 Settings::values.disabled_addons[program_id] = disabled_addons;
345}
346
308} // extern "C" 347} // extern "C"
diff --git a/src/android/app/src/main/res/drawable/ic_save.xml b/src/android/app/src/main/res/drawable/ic_save.xml
index a9af3d9cf..5acc2bbab 100644
--- a/src/android/app/src/main/res/drawable/ic_save.xml
+++ b/src/android/app/src/main/res/drawable/ic_save.xml
@@ -1,10 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android" 1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp" 2 android:width="24dp"
3 android:height="24dp" 3 android:height="24dp"
4 android:viewportWidth="960" 4 android:viewportWidth="24"
5 android:viewportHeight="960" 5 android:viewportHeight="24">
6 android:tint="?attr/colorControlNormal">
7 <path 6 <path
8 android:fillColor="@android:color/white" 7 android:fillColor="?attr/colorControlNormal"
9 android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L647,120Q663,120 677.5,126Q692,132 703,143L817,257Q828,268 834,282.5Q840,297 840,313L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM760,314L646,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L760,760Q760,760 760,760Q760,760 760,760L760,314ZM480,720Q530,720 565,685Q600,650 600,600Q600,550 565,515Q530,480 480,480Q430,480 395,515Q360,550 360,600Q360,650 395,685Q430,720 480,720ZM280,400L560,400Q577,400 588.5,388.5Q600,377 600,360L600,280Q600,263 588.5,251.5Q577,240 560,240L280,240Q263,240 251.5,251.5Q240,263 240,280L240,360Q240,377 251.5,388.5Q263,400 280,400ZM200,314L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200L200,200L200,314Z"/> 8 android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
10</vector> 9</vector>
diff --git a/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml b/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml
new file mode 100644
index 000000000..59ee1aad3
--- /dev/null
+++ b/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml
@@ -0,0 +1,82 @@
1<?xml version="1.0" encoding="utf-8"?>
2<com.google.android.material.card.MaterialCardView 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 style="?attr/materialCardViewOutlinedStyle"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:layout_marginHorizontal="16dp"
9 android:layout_marginVertical="12dp">
10
11 <LinearLayout
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:layout_gravity="center"
15 android:orientation="horizontal"
16 android:gravity="center_vertical"
17 android:paddingHorizontal="24dp"
18 android:paddingVertical="16dp">
19
20 <ImageView
21 android:id="@+id/icon"
22 android:layout_width="24dp"
23 android:layout_height="24dp"
24 android:layout_marginEnd="20dp"
25 android:layout_gravity="center_vertical"
26 app:tint="?attr/colorOnSurface"
27 tools:src="@drawable/ic_settings" />
28
29 <LinearLayout
30 android:layout_width="0dp"
31 android:layout_height="wrap_content"
32 android:layout_marginEnd="16dp"
33 android:layout_weight="1"
34 android:orientation="vertical">
35
36 <com.google.android.material.textview.MaterialTextView
37 android:id="@+id/title"
38 style="@style/TextAppearance.Material3.TitleMedium"
39 android:layout_width="match_parent"
40 android:layout_height="wrap_content"
41 android:text="@string/user_data"
42 android:textAlignment="viewStart" />
43
44 <com.google.android.material.textview.MaterialTextView
45 android:id="@+id/description"
46 style="@style/TextAppearance.Material3.BodyMedium"
47 android:layout_width="match_parent"
48 android:layout_height="wrap_content"
49 android:layout_marginTop="6dp"
50 android:text="@string/user_data_description"
51 android:textAlignment="viewStart" />
52
53 </LinearLayout>
54
55 <Button
56 android:id="@+id/button_export"
57 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
58 android:layout_width="wrap_content"
59 android:layout_height="wrap_content"
60 android:layout_gravity="center_vertical"
61 android:contentDescription="@string/export"
62 android:tooltipText="@string/export"
63 android:visibility="gone"
64 app:icon="@drawable/ic_export"
65 tools:visibility="visible" />
66
67 <Button
68 android:id="@+id/button_install"
69 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
70 android:layout_width="wrap_content"
71 android:layout_height="wrap_content"
72 android:layout_gravity="center_vertical"
73 android:layout_marginStart="12dp"
74 android:contentDescription="@string/string_import"
75 android:tooltipText="@string/string_import"
76 android:visibility="gone"
77 app:icon="@drawable/ic_import"
78 tools:visibility="visible" />
79
80 </LinearLayout>
81
82</com.google.android.material.card.MaterialCardView>
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml
new file mode 100644
index 000000000..0b9633855
--- /dev/null
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml
@@ -0,0 +1,99 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:background="?attr/colorSurface">
9
10 <androidx.core.widget.NestedScrollView
11 android:id="@+id/list_all"
12 android:layout_width="0dp"
13 android:layout_height="match_parent"
14 android:clipToPadding="false"
15 android:fadeScrollbars="false"
16 android:scrollbars="vertical"
17 app:layout_constraintEnd_toEndOf="parent"
18 app:layout_constraintStart_toEndOf="@+id/icon_layout"
19 app:layout_constraintTop_toTopOf="parent">
20
21 <LinearLayout
22 android:id="@+id/layout_all"
23 android:layout_width="match_parent"
24 android:layout_height="wrap_content"
25 android:gravity="center_horizontal"
26 android:orientation="horizontal">
27
28 <androidx.recyclerview.widget.RecyclerView
29 android:id="@+id/list_properties"
30 android:layout_width="match_parent"
31 android:layout_height="match_parent"
32 tools:listitem="@layout/card_simple_outlined" />
33
34 </LinearLayout>
35
36 </androidx.core.widget.NestedScrollView>
37
38 <LinearLayout
39 android:id="@+id/icon_layout"
40 android:layout_width="wrap_content"
41 android:layout_height="wrap_content"
42 android:orientation="vertical"
43 app:layout_constraintStart_toStartOf="parent"
44 app:layout_constraintTop_toTopOf="parent">
45
46 <Button
47 android:id="@+id/button_back"
48 style="?attr/materialIconButtonStyle"
49 android:layout_width="wrap_content"
50 android:layout_height="wrap_content"
51 android:layout_gravity="start"
52 android:layout_margin="8dp"
53 app:icon="@drawable/ic_back"
54 app:iconSize="24dp"
55 app:iconTint="?attr/colorOnSurface" />
56
57 <com.google.android.material.card.MaterialCardView
58 style="?attr/materialCardViewElevatedStyle"
59 android:layout_width="wrap_content"
60 android:layout_height="wrap_content"
61 android:layout_marginHorizontal="16dp"
62 android:layout_marginTop="8dp"
63 app:cardCornerRadius="4dp"
64 app:cardElevation="4dp">
65
66 <ImageView
67 android:id="@+id/image_game_screen"
68 android:layout_width="175dp"
69 android:layout_height="175dp"
70 tools:src="@drawable/default_icon" />
71
72 </com.google.android.material.card.MaterialCardView>
73
74 <com.google.android.material.textview.MaterialTextView
75 android:id="@+id/title"
76 style="@style/TextAppearance.Material3.TitleMedium"
77 android:layout_width="match_parent"
78 android:layout_height="wrap_content"
79 android:layout_marginHorizontal="16dp"
80 android:layout_marginTop="12dp"
81 android:ellipsize="none"
82 android:marqueeRepeatLimit="marquee_forever"
83 android:requiresFadingEdge="horizontal"
84 android:singleLine="true"
85 android:textAlignment="center"
86 tools:text="deko_basic" />
87
88 </LinearLayout>
89
90 <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
91 android:id="@+id/button_start"
92 android:layout_width="wrap_content"
93 android:layout_height="wrap_content"
94 android:text="@string/start"
95 app:icon="@drawable/ic_play"
96 app:layout_constraintBottom_toBottomOf="parent"
97 app:layout_constraintEnd_toEndOf="parent" />
98
99</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/card_installable.xml b/src/android/app/src/main/res/layout/card_installable.xml
index f5b0e3741..ce2402d7a 100644
--- a/src/android/app/src/main/res/layout/card_installable.xml
+++ b/src/android/app/src/main/res/layout/card_installable.xml
@@ -11,7 +11,8 @@
11 <LinearLayout 11 <LinearLayout
12 android:layout_width="match_parent" 12 android:layout_width="match_parent"
13 android:layout_height="wrap_content" 13 android:layout_height="wrap_content"
14 android:layout_margin="16dp" 14 android:paddingVertical="16dp"
15 android:paddingHorizontal="24dp"
15 android:orientation="horizontal" 16 android:orientation="horizontal"
16 android:layout_gravity="center"> 17 android:layout_gravity="center">
17 18
diff --git a/src/android/app/src/main/res/layout/card_installable_icon.xml b/src/android/app/src/main/res/layout/card_installable_icon.xml
new file mode 100644
index 000000000..4ae5423b1
--- /dev/null
+++ b/src/android/app/src/main/res/layout/card_installable_icon.xml
@@ -0,0 +1,89 @@
1<?xml version="1.0" encoding="utf-8"?>
2<com.google.android.material.card.MaterialCardView 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 style="?attr/materialCardViewOutlinedStyle"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:layout_marginHorizontal="16dp"
9 android:layout_marginVertical="12dp">
10
11 <LinearLayout
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:layout_gravity="center"
15 android:orientation="horizontal"
16 android:gravity="center_vertical"
17 android:paddingHorizontal="24dp"
18 android:paddingVertical="16dp">
19
20 <ImageView
21 android:id="@+id/icon"
22 android:layout_width="24dp"
23 android:layout_height="24dp"
24 android:layout_marginEnd="20dp"
25 android:layout_gravity="center_vertical"
26 app:tint="?attr/colorOnSurface"
27 tools:src="@drawable/ic_settings" />
28
29 <LinearLayout
30 android:layout_width="0dp"
31 android:layout_height="wrap_content"
32 android:layout_marginEnd="16dp"
33 android:layout_weight="1"
34 android:orientation="vertical">
35
36 <com.google.android.material.textview.MaterialTextView
37 android:id="@+id/title"
38 style="@style/TextAppearance.Material3.TitleMedium"
39 android:layout_width="match_parent"
40 android:layout_height="wrap_content"
41 android:text="@string/user_data"
42 android:textAlignment="viewStart" />
43
44 <com.google.android.material.textview.MaterialTextView
45 android:id="@+id/description"
46 style="@style/TextAppearance.Material3.BodyMedium"
47 android:layout_width="match_parent"
48 android:layout_height="wrap_content"
49 android:layout_marginTop="6dp"
50 android:text="@string/user_data_description"
51 android:textAlignment="viewStart" />
52
53 </LinearLayout>
54
55 <LinearLayout
56 android:layout_width="wrap_content"
57 android:layout_height="wrap_content"
58 android:orientation="vertical">
59
60 <Button
61 android:id="@+id/button_install"
62 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
63 android:layout_width="wrap_content"
64 android:layout_height="wrap_content"
65 android:layout_gravity="center_vertical"
66 android:contentDescription="@string/string_import"
67 android:tooltipText="@string/string_import"
68 android:visibility="gone"
69 app:icon="@drawable/ic_import"
70 tools:visibility="visible" />
71
72 <Button
73 android:id="@+id/button_export"
74 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
75 android:layout_width="wrap_content"
76 android:layout_height="wrap_content"
77 android:layout_gravity="center_vertical"
78 android:layout_marginTop="8dp"
79 android:contentDescription="@string/export"
80 android:tooltipText="@string/export"
81 android:visibility="gone"
82 app:icon="@drawable/ic_export"
83 tools:visibility="visible" />
84
85 </LinearLayout>
86
87 </LinearLayout>
88
89</com.google.android.material.card.MaterialCardView>
diff --git a/src/android/app/src/main/res/layout/card_applet_option.xml b/src/android/app/src/main/res/layout/card_simple_outlined.xml
index 19fbec9f1..b73930e7e 100644
--- a/src/android/app/src/main/res/layout/card_applet_option.xml
+++ b/src/android/app/src/main/res/layout/card_simple_outlined.xml
@@ -16,7 +16,8 @@
16 android:layout_height="wrap_content" 16 android:layout_height="wrap_content"
17 android:orientation="horizontal" 17 android:orientation="horizontal"
18 android:layout_gravity="center" 18 android:layout_gravity="center"
19 android:padding="24dp"> 19 android:paddingVertical="16dp"
20 android:paddingHorizontal="24dp">
20 21
21 <ImageView 22 <ImageView
22 android:id="@+id/icon" 23 android:id="@+id/icon"
@@ -50,6 +51,23 @@
50 android:textAlignment="viewStart" 51 android:textAlignment="viewStart"
51 tools:text="@string/applets_description" /> 52 tools:text="@string/applets_description" />
52 53
54 <com.google.android.material.textview.MaterialTextView
55 style="@style/TextAppearance.Material3.LabelMedium"
56 android:id="@+id/details"
57 android:layout_width="match_parent"
58 android:layout_height="wrap_content"
59 android:textAlignment="viewStart"
60 android:textSize="14sp"
61 android:textStyle="bold"
62 android:singleLine="true"
63 android:marqueeRepeatLimit="marquee_forever"
64 android:ellipsize="none"
65 android:requiresFadingEdge="horizontal"
66 android:layout_marginTop="6dp"
67 android:visibility="gone"
68 tools:visibility="visible"
69 tools:text="/tree/primary:Games" />
70
53 </LinearLayout> 71 </LinearLayout>
54 72
55 </LinearLayout> 73 </LinearLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_addons.xml b/src/android/app/src/main/res/layout/fragment_addons.xml
new file mode 100644
index 000000000..a25e82766
--- /dev/null
+++ b/src/android/app/src/main/res/layout/fragment_addons.xml
@@ -0,0 +1,47 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 android:id="@+id/coordinator_about"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:background="?attr/colorSurface">
8
9 <com.google.android.material.appbar.AppBarLayout
10 android:id="@+id/appbar_addons"
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:fitsSystemWindows="true"
14 app:layout_constraintEnd_toEndOf="parent"
15 app:layout_constraintStart_toStartOf="parent"
16 app:layout_constraintTop_toTopOf="parent">
17
18 <com.google.android.material.appbar.MaterialToolbar
19 android:id="@+id/toolbar_addons"
20 android:layout_width="match_parent"
21 android:layout_height="?attr/actionBarSize"
22 app:navigationIcon="@drawable/ic_back" />
23
24 </com.google.android.material.appbar.AppBarLayout>
25
26 <androidx.recyclerview.widget.RecyclerView
27 android:id="@+id/list_addons"
28 android:layout_width="match_parent"
29 android:layout_height="0dp"
30 android:clipToPadding="false"
31 app:layout_behavior="@string/appbar_scrolling_view_behavior"
32 app:layout_constraintBottom_toBottomOf="parent"
33 app:layout_constraintEnd_toEndOf="parent"
34 app:layout_constraintStart_toStartOf="parent"
35 app:layout_constraintTop_toBottomOf="@+id/appbar_addons" />
36
37 <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
38 android:id="@+id/button_install"
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:layout_gravity="bottom|end"
42 android:text="@string/install"
43 app:icon="@drawable/ic_add"
44 app:layout_constraintBottom_toBottomOf="parent"
45 app:layout_constraintEnd_toEndOf="parent" />
46
47</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_game_info.xml b/src/android/app/src/main/res/layout/fragment_game_info.xml
new file mode 100644
index 000000000..80ede8a8c
--- /dev/null
+++ b/src/android/app/src/main/res/layout/fragment_game_info.xml
@@ -0,0 +1,125 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:id="@+id/coordinator_about"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:background="?attr/colorSurface">
9
10 <com.google.android.material.appbar.AppBarLayout
11 android:id="@+id/appbar_info"
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:fitsSystemWindows="true">
15
16 <com.google.android.material.appbar.MaterialToolbar
17 android:id="@+id/toolbar_info"
18 android:layout_width="match_parent"
19 android:layout_height="?attr/actionBarSize"
20 app:navigationIcon="@drawable/ic_back" />
21
22 </com.google.android.material.appbar.AppBarLayout>
23
24 <androidx.core.widget.NestedScrollView
25 android:id="@+id/scroll_info"
26 android:layout_width="match_parent"
27 android:layout_height="wrap_content"
28 app:layout_behavior="@string/appbar_scrolling_view_behavior">
29
30 <LinearLayout
31 android:id="@+id/content_info"
32 android:layout_width="match_parent"
33 android:layout_height="wrap_content"
34 android:orientation="vertical"
35 android:paddingHorizontal="16dp">
36
37 <com.google.android.material.textfield.TextInputLayout
38 android:id="@+id/path"
39 android:layout_width="match_parent"
40 android:layout_height="wrap_content"
41 android:paddingTop="16dp">
42
43 <com.google.android.material.textfield.TextInputEditText
44 android:id="@+id/path_field"
45 android:layout_width="match_parent"
46 android:layout_height="wrap_content"
47 android:editable="false"
48 android:importantForAutofill="no"
49 android:inputType="none"
50 android:minHeight="48dp"
51 android:textAlignment="viewStart"
52 tools:text="1.0.0" />
53
54 </com.google.android.material.textfield.TextInputLayout>
55
56 <com.google.android.material.textfield.TextInputLayout
57 android:id="@+id/program_id"
58 android:layout_width="match_parent"
59 android:layout_height="wrap_content"
60 android:paddingTop="16dp">
61
62 <com.google.android.material.textfield.TextInputEditText
63 android:id="@+id/program_id_field"
64 android:layout_width="match_parent"
65 android:layout_height="wrap_content"
66 android:editable="false"
67 android:importantForAutofill="no"
68 android:inputType="none"
69 android:minHeight="48dp"
70 android:textAlignment="viewStart"
71 tools:text="1.0.0" />
72
73 </com.google.android.material.textfield.TextInputLayout>
74
75 <com.google.android.material.textfield.TextInputLayout
76 android:id="@+id/developer"
77 android:layout_width="match_parent"
78 android:layout_height="wrap_content"
79 android:paddingTop="16dp">
80
81 <com.google.android.material.textfield.TextInputEditText
82 android:id="@+id/developer_field"
83 android:layout_width="match_parent"
84 android:layout_height="wrap_content"
85 android:editable="false"
86 android:importantForAutofill="no"
87 android:inputType="none"
88 android:minHeight="48dp"
89 android:textAlignment="viewStart"
90 tools:text="1.0.0" />
91
92 </com.google.android.material.textfield.TextInputLayout>
93
94 <com.google.android.material.textfield.TextInputLayout
95 android:id="@+id/version"
96 android:layout_width="match_parent"
97 android:layout_height="wrap_content"
98 android:paddingTop="16dp">
99
100 <com.google.android.material.textfield.TextInputEditText
101 android:id="@+id/version_field"
102 android:layout_width="match_parent"
103 android:layout_height="wrap_content"
104 android:editable="false"
105 android:importantForAutofill="no"
106 android:inputType="none"
107 android:minHeight="48dp"
108 android:textAlignment="viewStart"
109 tools:text="1.0.0" />
110
111 </com.google.android.material.textfield.TextInputLayout>
112
113 <com.google.android.material.button.MaterialButton
114 android:id="@+id/button_copy"
115 style="@style/Widget.Material3.Button"
116 android:layout_width="wrap_content"
117 android:layout_height="wrap_content"
118 android:layout_marginTop="16dp"
119 android:text="@string/copy_details" />
120
121 </LinearLayout>
122
123 </androidx.core.widget.NestedScrollView>
124
125</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml
new file mode 100644
index 000000000..72ecbde30
--- /dev/null
+++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml
@@ -0,0 +1,86 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:background="?attr/colorSurface">
9
10 <androidx.core.widget.NestedScrollView
11 android:id="@+id/list_all"
12 android:layout_width="match_parent"
13 android:layout_height="match_parent"
14 android:scrollbars="vertical"
15 android:fadeScrollbars="false"
16 android:clipToPadding="false">
17
18 <LinearLayout
19 android:id="@+id/layout_all"
20 android:layout_width="match_parent"
21 android:layout_height="wrap_content"
22 android:orientation="vertical"
23 android:gravity="center_horizontal">
24
25 <Button
26 android:id="@+id/button_back"
27 style="?attr/materialIconButtonStyle"
28 android:layout_width="wrap_content"
29 android:layout_height="wrap_content"
30 android:layout_margin="8dp"
31 android:layout_gravity="start"
32 app:icon="@drawable/ic_back"
33 app:iconSize="24dp"
34 app:iconTint="?attr/colorOnSurface" />
35
36 <com.google.android.material.card.MaterialCardView
37 style="?attr/materialCardViewElevatedStyle"
38 android:layout_width="wrap_content"
39 android:layout_height="wrap_content"
40 android:layout_marginTop="8dp"
41 app:cardCornerRadius="4dp"
42 app:cardElevation="4dp">
43
44 <ImageView
45 android:id="@+id/image_game_screen"
46 android:layout_width="175dp"
47 android:layout_height="175dp"
48 tools:src="@drawable/default_icon"/>
49
50 </com.google.android.material.card.MaterialCardView>
51
52 <com.google.android.material.textview.MaterialTextView
53 android:id="@+id/title"
54 style="@style/TextAppearance.Material3.TitleMedium"
55 android:layout_width="wrap_content"
56 android:layout_height="wrap_content"
57 android:layout_marginTop="12dp"
58 android:layout_marginBottom="12dp"
59 android:layout_marginHorizontal="16dp"
60 android:ellipsize="none"
61 android:marqueeRepeatLimit="marquee_forever"
62 android:requiresFadingEdge="horizontal"
63 android:singleLine="true"
64 android:textAlignment="center"
65 tools:text="deko_basic" />
66
67 <androidx.recyclerview.widget.RecyclerView
68 android:id="@+id/list_properties"
69 android:layout_width="match_parent"
70 android:layout_height="match_parent"
71 tools:listitem="@layout/card_simple_outlined" />
72
73 </LinearLayout>
74
75 </androidx.core.widget.NestedScrollView>
76
77 <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
78 android:id="@+id/button_start"
79 android:layout_width="wrap_content"
80 android:layout_height="wrap_content"
81 android:text="@string/start"
82 app:icon="@drawable/ic_play"
83 app:layout_constraintBottom_toBottomOf="parent"
84 app:layout_constraintEnd_toEndOf="parent" />
85
86</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_addon.xml b/src/android/app/src/main/res/layout/list_item_addon.xml
new file mode 100644
index 000000000..74ca04ef1
--- /dev/null
+++ b/src/android/app/src/main/res/layout/list_item_addon.xml
@@ -0,0 +1,57 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout 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/addon_container"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:background="?attr/selectableItemBackground"
9 android:focusable="true"
10 android:paddingHorizontal="20dp"
11 android:paddingVertical="16dp">
12
13 <LinearLayout
14 android:id="@+id/text_container"
15 android:layout_width="0dp"
16 android:layout_height="wrap_content"
17 android:layout_marginEnd="16dp"
18 android:orientation="vertical"
19 app:layout_constraintBottom_toBottomOf="@+id/addon_switch"
20 app:layout_constraintEnd_toStartOf="@+id/addon_switch"
21 app:layout_constraintStart_toStartOf="parent"
22 app:layout_constraintTop_toTopOf="@+id/addon_switch">
23
24 <com.google.android.material.textview.MaterialTextView
25 android:id="@+id/title"
26 style="@style/TextAppearance.Material3.HeadlineMedium"
27 android:layout_width="wrap_content"
28 android:layout_height="wrap_content"
29 android:textAlignment="viewStart"
30 android:textSize="17sp"
31 app:lineHeight="28dp"
32 tools:text="1440p Resolution" />
33
34 <com.google.android.material.textview.MaterialTextView
35 android:id="@+id/version"
36 style="@style/TextAppearance.Material3.BodySmall"
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:layout_marginTop="@dimen/spacing_small"
40 android:textAlignment="viewStart"
41 tools:text="1.0.0" />
42
43 </LinearLayout>
44
45 <com.google.android.material.materialswitch.MaterialSwitch
46 android:id="@+id/addon_switch"
47 android:layout_width="wrap_content"
48 android:layout_height="wrap_content"
49 android:focusable="true"
50 android:gravity="center"
51 android:nextFocusLeft="@id/addon_container"
52 app:layout_constraintBottom_toBottomOf="parent"
53 app:layout_constraintEnd_toEndOf="parent"
54 app:layout_constraintStart_toEndOf="@id/text_container"
55 app:layout_constraintTop_toTopOf="parent" />
56
57</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml
index 544280e75..1f80682f1 100644
--- a/src/android/app/src/main/res/layout/list_item_setting.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting.xml
@@ -62,6 +62,16 @@
62 android:textSize="13sp" 62 android:textSize="13sp"
63 tools:text="1x" /> 63 tools:text="1x" />
64 64
65 <com.google.android.material.button.MaterialButton
66 android:id="@+id/button_clear"
67 style="@style/Widget.Material3.Button.TonalButton"
68 android:layout_width="wrap_content"
69 android:layout_height="wrap_content"
70 android:layout_marginTop="16dp"
71 android:visibility="gone"
72 android:text="@string/clear"
73 tools:visibility="visible" />
74
65 </LinearLayout> 75 </LinearLayout>
66 76
67 </LinearLayout> 77 </LinearLayout>
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 a8f5aff78..5cb84182e 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
@@ -10,41 +10,62 @@
10 android:minHeight="72dp" 10 android:minHeight="72dp"
11 android:padding="16dp"> 11 android:padding="16dp">
12 12
13 <com.google.android.material.materialswitch.MaterialSwitch
14 android:id="@+id/switch_widget"
15 android:layout_width="wrap_content"
16 android:layout_height="wrap_content"
17 android:layout_alignParentEnd="true"
18 android:layout_centerVertical="true" />
19
20 <LinearLayout 13 <LinearLayout
21 android:layout_width="match_parent" 14 android:layout_width="match_parent"
22 android:layout_height="wrap_content" 15 android:layout_height="wrap_content"
23 android:layout_alignParentTop="true"
24 android:layout_centerVertical="true"
25 android:layout_marginEnd="24dp"
26 android:layout_toStartOf="@+id/switch_widget"
27 android:gravity="center_vertical"
28 android:orientation="vertical"> 16 android:orientation="vertical">
29 17
30 <com.google.android.material.textview.MaterialTextView 18 <LinearLayout
31 android:id="@+id/text_setting_name" 19 android:layout_width="match_parent"
32 style="@style/TextAppearance.Material3.HeadlineMedium"
33 android:layout_width="wrap_content"
34 android:layout_height="wrap_content" 20 android:layout_height="wrap_content"
35 android:textAlignment="viewStart" 21 android:orientation="horizontal">
36 android:textSize="17sp" 22
37 app:lineHeight="28dp" 23 <LinearLayout
38 tools:text="@string/frame_limit_enable" /> 24 android:layout_width="0dp"
39 25 android:layout_height="wrap_content"
40 <com.google.android.material.textview.MaterialTextView 26 android:layout_marginEnd="24dp"
41 android:id="@+id/text_setting_description" 27 android:gravity="center_vertical"
42 style="@style/TextAppearance.Material3.BodySmall" 28 android:orientation="vertical"
29 android:layout_weight="1">
30
31 <com.google.android.material.textview.MaterialTextView
32 android:id="@+id/text_setting_name"
33 style="@style/TextAppearance.Material3.HeadlineMedium"
34 android:layout_width="wrap_content"
35 android:layout_height="wrap_content"
36 android:textAlignment="viewStart"
37 android:textSize="17sp"
38 app:lineHeight="28dp"
39 tools:text="@string/frame_limit_enable" />
40
41 <com.google.android.material.textview.MaterialTextView
42 android:id="@+id/text_setting_description"
43 style="@style/TextAppearance.Material3.BodySmall"
44 android:layout_width="wrap_content"
45 android:layout_height="wrap_content"
46 android:layout_marginTop="@dimen/spacing_small"
47 android:textAlignment="viewStart"
48 tools:text="@string/frame_limit_enable_description" />
49
50 </LinearLayout>
51
52 <com.google.android.material.materialswitch.MaterialSwitch
53 android:id="@+id/switch_widget"
54 android:layout_width="wrap_content"
55 android:layout_height="wrap_content"
56 android:layout_gravity="center_vertical"/>
57
58 </LinearLayout>
59
60 <com.google.android.material.button.MaterialButton
61 android:id="@+id/button_clear"
62 style="@style/Widget.Material3.Button.TonalButton"
43 android:layout_width="wrap_content" 63 android:layout_width="wrap_content"
44 android:layout_height="wrap_content" 64 android:layout_height="wrap_content"
45 android:layout_marginTop="@dimen/spacing_small" 65 android:layout_marginTop="16dp"
46 android:textAlignment="viewStart" 66 android:text="@string/clear"
47 tools:text="@string/frame_limit_enable_description" /> 67 android:visibility="gone"
68 tools:visibility="visible" />
48 69
49 </LinearLayout> 70 </LinearLayout>
50 71
diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml
index f98f727b6..ac6ab06ff 100644
--- a/src/android/app/src/main/res/menu/menu_in_game.xml
+++ b/src/android/app/src/main/res/menu/menu_in_game.xml
@@ -12,6 +12,11 @@
12 android:title="@string/preferences_settings" /> 12 android:title="@string/preferences_settings" />
13 13
14 <item 14 <item
15 android:id="@+id/menu_settings_per_game"
16 android:icon="@drawable/ic_settings_outline"
17 android:title="@string/per_game_settings" />
18
19 <item
15 android:id="@+id/menu_overlay_controls" 20 android:id="@+id/menu_overlay_controls"
16 android:icon="@drawable/ic_controller" 21 android:icon="@drawable/ic_controller"
17 android:title="@string/emulation_input_overlay" /> 22 android:title="@string/emulation_input_overlay" />
diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml
index cfc494b3f..2f8c3fa0d 100644
--- a/src/android/app/src/main/res/navigation/emulation_navigation.xml
+++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml
@@ -15,6 +15,10 @@
15 app:argType="org.yuzu.yuzu_emu.model.Game" 15 app:argType="org.yuzu.yuzu_emu.model.Game"
16 app:nullable="true" 16 app:nullable="true"
17 android:defaultValue="@null" /> 17 android:defaultValue="@null" />
18 <argument
19 android:name="custom"
20 app:argType="boolean"
21 android:defaultValue="false" />
18 </fragment> 22 </fragment>
19 23
20 <activity 24 <activity
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 cf70b4bc4..37a03a8d1 100644
--- a/src/android/app/src/main/res/navigation/home_navigation.xml
+++ b/src/android/app/src/main/res/navigation/home_navigation.xml
@@ -77,6 +77,10 @@
77 app:argType="org.yuzu.yuzu_emu.model.Game" 77 app:argType="org.yuzu.yuzu_emu.model.Game"
78 app:nullable="true" 78 app:nullable="true"
79 android:defaultValue="@null" /> 79 android:defaultValue="@null" />
80 <argument
81 android:name="custom"
82 app:argType="boolean"
83 android:defaultValue="false" />
80 </activity> 84 </activity>
81 85
82 <action 86 <action
@@ -107,7 +111,13 @@
107 <fragment 111 <fragment
108 android:id="@+id/driverManagerFragment" 112 android:id="@+id/driverManagerFragment"
109 android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment" 113 android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
110 android:label="DriverManagerFragment" /> 114 android:label="DriverManagerFragment" >
115 <argument
116 android:name="game"
117 app:argType="org.yuzu.yuzu_emu.model.Game"
118 app:nullable="true"
119 android:defaultValue="@null" />
120 </fragment>
111 <fragment 121 <fragment
112 android:id="@+id/appletLauncherFragment" 122 android:id="@+id/appletLauncherFragment"
113 android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment" 123 android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment"
@@ -124,5 +134,41 @@
124 android:id="@+id/gameFoldersFragment" 134 android:id="@+id/gameFoldersFragment"
125 android:name="org.yuzu.yuzu_emu.fragments.GameFoldersFragment" 135 android:name="org.yuzu.yuzu_emu.fragments.GameFoldersFragment"
126 android:label="GameFoldersFragment" /> 136 android:label="GameFoldersFragment" />
137 <fragment
138 android:id="@+id/perGamePropertiesFragment"
139 android:name="org.yuzu.yuzu_emu.fragments.GamePropertiesFragment"
140 android:label="PerGamePropertiesFragment" >
141 <argument
142 android:name="game"
143 app:argType="org.yuzu.yuzu_emu.model.Game" />
144 <action
145 android:id="@+id/action_perGamePropertiesFragment_to_gameInfoFragment"
146 app:destination="@id/gameInfoFragment" />
147 <action
148 android:id="@+id/action_perGamePropertiesFragment_to_addonsFragment"
149 app:destination="@id/addonsFragment" />
150 <action
151 android:id="@+id/action_perGamePropertiesFragment_to_driverManagerFragment"
152 app:destination="@id/driverManagerFragment" />
153 </fragment>
154 <action
155 android:id="@+id/action_global_perGamePropertiesFragment"
156 app:destination="@id/perGamePropertiesFragment" />
157 <fragment
158 android:id="@+id/gameInfoFragment"
159 android:name="org.yuzu.yuzu_emu.fragments.GameInfoFragment"
160 android:label="GameInfoFragment" >
161 <argument
162 android:name="game"
163 app:argType="org.yuzu.yuzu_emu.model.Game" />
164 </fragment>
165 <fragment
166 android:id="@+id/addonsFragment"
167 android:name="org.yuzu.yuzu_emu.fragments.AddonsFragment"
168 android:label="AddonsFragment" >
169 <argument
170 android:name="game"
171 app:argType="org.yuzu.yuzu_emu.model.Game" />
172 </fragment>
127 173
128</navigation> 174</navigation>
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml
index 380d14213..992b5ae44 100644
--- a/src/android/app/src/main/res/values/dimens.xml
+++ b/src/android/app/src/main/res/values/dimens.xml
@@ -13,7 +13,7 @@
13 <dimen name="menu_width">256dp</dimen> 13 <dimen name="menu_width">256dp</dimen>
14 <dimen name="card_width">165dp</dimen> 14 <dimen name="card_width">165dp</dimen>
15 <dimen name="icon_inset">24dp</dimen> 15 <dimen name="icon_inset">24dp</dimen>
16 <dimen name="spacing_bottom_list_fab">76dp</dimen> 16 <dimen name="spacing_bottom_list_fab">96dp</dimen>
17 <dimen name="spacing_fab">24dp</dimen> 17 <dimen name="spacing_fab">24dp</dimen>
18 18
19 <dimen name="dialog_margin">20dp</dimen> 19 <dimen name="dialog_margin">20dp</dimen>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index a6ccef8a1..c86c43df2 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -91,7 +91,10 @@
91 <string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string> 91 <string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string>
92 <string name="manage_save_data">Manage save data</string> 92 <string name="manage_save_data">Manage save data</string>
93 <string name="manage_save_data_description">Save data found. Please select an option below.</string> 93 <string name="manage_save_data_description">Save data found. Please select an option below.</string>
94 <string name="import_save_warning">Import save data</string>
95 <string name="import_save_warning_description">This will overwrite all existing save data with the provided file. Are you sure that you want to continue?</string>
94 <string name="import_export_saves_description">Import or export save files</string> 96 <string name="import_export_saves_description">Import or export save files</string>
97 <string name="save_files_importing">Importing save files…</string>
95 <string name="save_files_exporting">Exporting save files…</string> 98 <string name="save_files_exporting">Exporting save files…</string>
96 <string name="save_file_imported_success">Imported successfully</string> 99 <string name="save_file_imported_success">Imported successfully</string>
97 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> 100 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
@@ -266,6 +269,11 @@
266 <string name="delete">Delete</string> 269 <string name="delete">Delete</string>
267 <string name="edit">Edit</string> 270 <string name="edit">Edit</string>
268 <string name="export_success">Exported successfully</string> 271 <string name="export_success">Exported successfully</string>
272 <string name="start">Start</string>
273 <string name="clear">Clear</string>
274 <string name="global">Global</string>
275 <string name="custom">Custom</string>
276 <string name="notice">Notice</string>
269 277
270 <!-- GPU driver installation --> 278 <!-- GPU driver installation -->
271 <string name="select_gpu_driver">Select GPU driver</string> 279 <string name="select_gpu_driver">Select GPU driver</string>
@@ -291,6 +299,44 @@
291 <string name="preferences_debug">Debug</string> 299 <string name="preferences_debug">Debug</string>
292 <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string> 300 <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
293 301
302 <!-- Game properties -->
303 <string name="info">Info</string>
304 <string name="info_description">Program ID, developer, version</string>
305 <string name="per_game_settings">Per-game settings</string>
306 <string name="per_game_settings_description">Edit settings specific to this game</string>
307 <string name="launch_options">Launch config</string>
308 <string name="path">Path</string>
309 <string name="program_id">Program ID</string>
310 <string name="developer">Developer</string>
311 <string name="version">Version</string>
312 <string name="copy_details">Copy details</string>
313 <string name="add_ons">Add-ons</string>
314 <string name="add_ons_description">Toggle mods, updates and DLC</string>
315 <string name="clear_shader_cache">Clear shader cache</string>
316 <string name="clear_shader_cache_description">Removes all shaders built while playing this game</string>
317 <string name="clear_shader_cache_warning_description">You will experience more stuttering as the shader cache regenerates</string>
318 <string name="cleared_shaders_successfully">Cleared shaders successfully</string>
319 <string name="addons_game">Addons: %1$s</string>
320 <string name="save_data">Save data</string>
321 <string name="save_data_description">Manage save data specific to this game</string>
322 <string name="delete_save_data">Delete save data</string>
323 <string name="delete_save_data_description">Removes all save data specific to this game</string>
324 <string name="delete_save_data_warning_description">This irrecoverably removes all of this game\'s save data. Are you sure you want to continue?</string>
325 <string name="save_data_deleted_successfully">Save data deleted successfully</string>
326 <string name="select_content_type">Content type</string>
327 <string name="updates_and_dlc">Updates and DLC</string>
328 <string name="mods_and_cheats">Mods and cheats</string>
329 <string name="addon_notice">Important addon notice</string>
330 <!-- "cheats/" "romfs/" and "exefs/ should not be translated -->
331 <string name="addon_notice_description">In order to install mods and cheats, you must select a folder that contains a cheats/, romfs/, or exefs/ directory. We can\'t verify if these will be compatible with your game so be careful!</string>
332 <string name="invalid_directory">Invalid directory</string>
333 <!-- "cheats/" "romfs/" and "exefs/ should not be translated -->
334 <string name="invalid_directory_description">Please make sure that the directory you selected contains a cheats/, romfs/, or exefs/ folder and try again.</string>
335 <string name="addon_installed_successfully">Addon installed successfully</string>
336 <string name="verifying_content">Verifying content…</string>
337 <string name="content_install_notice">Content install notice</string>
338 <string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string>
339
294 <!-- ROM loading errors --> 340 <!-- ROM loading errors -->
295 <string name="loader_error_encrypted">Your ROM is encrypted</string> 341 <string name="loader_error_encrypted">Your ROM is encrypted</string>
296 <string name="loader_error_encrypted_roms_description"><![CDATA[Please follow the guides to redump your <a href="https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards">game cartidges</a> or <a href="https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop">installed titles</a>.]]></string> 342 <string name="loader_error_encrypted_roms_description"><![CDATA[Please follow the guides to redump your <a href="https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards">game cartidges</a> or <a href="https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop">installed titles</a>.]]></string>
@@ -369,6 +415,7 @@
369 415
370 <!-- Memory Sizes --> 416 <!-- Memory Sizes -->
371 <string name="memory_byte">Byte</string> 417 <string name="memory_byte">Byte</string>
418 <string name="memory_byte_shorthand">B</string>
372 <string name="memory_kilobyte">KB</string> 419 <string name="memory_kilobyte">KB</string>
373 <string name="memory_megabyte">MB</string> 420 <string name="memory_megabyte">MB</string>
374 <string name="memory_gigabyte">GB</string> 421 <string name="memory_gigabyte">GB</string>
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 51a23fe15..d97ca2a40 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -253,8 +253,9 @@ CubebSink::~CubebSink() {
253#endif 253#endif
254} 254}
255 255
256SinkStream* CubebSink::AcquireSinkStream(Core::System& system, u32 system_channels, 256SinkStream* CubebSink::AcquireSinkStream(Core::System& system, u32 system_channels_,
257 const std::string& name, StreamType type) { 257 const std::string& name, StreamType type) {
258 system_channels = system_channels_;
258 SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<CubebSinkStream>( 259 SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<CubebSinkStream>(
259 ctx, device_channels, system_channels, output_device, input_device, name, type, system)); 260 ctx, device_channels, system_channels, output_device, input_device, name, type, system));
260 261
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index 96e0efce2..7dd155ff0 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -168,8 +168,9 @@ SDLSink::SDLSink(std::string_view target_device_name) {
168 168
169SDLSink::~SDLSink() = default; 169SDLSink::~SDLSink() = default;
170 170
171SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels, 171SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels_,
172 const std::string&, StreamType type) { 172 const std::string&, StreamType type) {
173 system_channels = system_channels_;
173 SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>( 174 SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>(
174 device_channels, system_channels, output_device, input_device, type, system)); 175 device_channels, system_channels, output_device, input_device, type, system));
175 return stream.get(); 176 return stream.get();
diff --git a/src/audio_core/sink/sink.h b/src/audio_core/sink/sink.h
index f28c6d126..e22e8c3e5 100644
--- a/src/audio_core/sink/sink.h
+++ b/src/audio_core/sink/sink.h
@@ -85,9 +85,21 @@ public:
85 */ 85 */
86 virtual void SetSystemVolume(f32 volume) = 0; 86 virtual void SetSystemVolume(f32 volume) = 0;
87 87
88 /**
89 * Get the number of channels the game has set, can be different to the host hardware's support.
90 * Either 2 or 6.
91 *
92 * @return Number of device channels.
93 */
94 u32 GetSystemChannels() const {
95 return system_channels;
96 }
97
88protected: 98protected:
89 /// Number of device channels supported by the hardware 99 /// Number of device channels supported by the hardware
90 u32 device_channels{2}; 100 u32 device_channels{2};
101 /// Number of channels the game is sending
102 u32 system_channels{2};
91}; 103};
92 104
93using SinkPtr = std::unique_ptr<Sink>; 105using SinkPtr = std::unique_ptr<Sink>;
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 2a09db599..c047b0668 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -40,29 +40,36 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
40 40
41 if (system_channels == 6 && device_channels == 2) { 41 if (system_channels == 6 && device_channels == 2) {
42 // We're given 6 channels, but our device only outputs 2, so downmix. 42 // We're given 6 channels, but our device only outputs 2, so downmix.
43 static constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f}; 43 // Front = 1.0
44 // Center = 0.596
45 // LFE = 0.354
46 // Back = 0.707
47 static constexpr std::array<f32, 4> down_mix_coeff{1.0, 0.596f, 0.354f, 0.707f};
44 48
45 for (u32 read_index = 0, write_index = 0; read_index < samples.size(); 49 for (u32 read_index = 0, write_index = 0; read_index < samples.size();
46 read_index += system_channels, write_index += device_channels) { 50 read_index += system_channels, write_index += device_channels) {
51 const auto fl =
52 static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontLeft)]);
53 const auto fr =
54 static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontRight)]);
55 const auto c =
56 static_cast<f32>(samples[read_index + static_cast<u32>(Channels::Center)]);
57 const auto lfe =
58 static_cast<f32>(samples[read_index + static_cast<u32>(Channels::LFE)]);
59 const auto bl =
60 static_cast<f32>(samples[read_index + static_cast<u32>(Channels::BackLeft)]);
61 const auto br =
62 static_cast<f32>(samples[read_index + static_cast<u32>(Channels::BackRight)]);
63
47 const auto left_sample{ 64 const auto left_sample{
48 ((Common::FixedPoint<49, 15>( 65 static_cast<s32>((fl * down_mix_coeff[0] + c * down_mix_coeff[1] +
49 samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * 66 lfe * down_mix_coeff[2] + bl * down_mix_coeff[3]) *
50 down_mix_coeff[0] + 67 volume)};
51 samples[read_index + static_cast<u32>(Channels::Center)] * down_mix_coeff[1] +
52 samples[read_index + static_cast<u32>(Channels::LFE)] * down_mix_coeff[2] +
53 samples[read_index + static_cast<u32>(Channels::BackLeft)] * down_mix_coeff[3]) *
54 volume)
55 .to_int()};
56 68
57 const auto right_sample{ 69 const auto right_sample{
58 ((Common::FixedPoint<49, 15>( 70 static_cast<s32>((fr * down_mix_coeff[0] + c * down_mix_coeff[1] +
59 samples[read_index + static_cast<u32>(Channels::FrontRight)]) * 71 lfe * down_mix_coeff[2] + br * down_mix_coeff[3]) *
60 down_mix_coeff[0] + 72 volume)};
61 samples[read_index + static_cast<u32>(Channels::Center)] * down_mix_coeff[1] +
62 samples[read_index + static_cast<u32>(Channels::LFE)] * down_mix_coeff[2] +
63 samples[read_index + static_cast<u32>(Channels::BackRight)] * down_mix_coeff[3]) *
64 volume)
65 .to_int()};
66 73
67 samples[write_index + static_cast<u32>(Channels::FrontLeft)] = 74 samples[write_index + static_cast<u32>(Channels::FrontLeft)] =
68 static_cast<s16>(std::clamp(left_sample, min, max)); 75 static_cast<s16>(std::clamp(left_sample, min, max));
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 47d028d48..ba3081efb 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -123,6 +123,12 @@ namespace Common {
123 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; 123 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
124} 124}
125 125
126[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g,
127 char h) {
128 return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 |
129 u64(g) << 48 | u64(h) << 56;
130}
131
126// std::size() does not support zero-size C arrays. We're fixing that. 132// std::size() does not support zero-size C arrays. We're fixing that.
127template <class C> 133template <class C>
128constexpr auto Size(const C& c) -> decltype(c.size()) { 134constexpr auto Size(const C& c) -> decltype(c.size()) {
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index c3a81f9a9..4f69db6f5 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -354,18 +354,36 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
354 return path; 354 return path;
355} 355}
356 356
357std::vector<std::string> SplitPathComponents(std::string_view filename) { 357template <typename F>
358 std::string copy(filename); 358static void ForEachPathComponent(std::string_view filename, F&& cb) {
359 std::replace(copy.begin(), copy.end(), '\\', '/'); 359 const char* component_begin = filename.data();
360 std::vector<std::string> out; 360 const char* const end = component_begin + filename.size();
361 361 for (const char* it = component_begin; it != end; ++it) {
362 std::stringstream stream(copy); 362 const char c = *it;
363 std::string item; 363 if (c == '\\' || c == '/') {
364 while (std::getline(stream, item, '/')) { 364 if (component_begin != it) {
365 out.push_back(std::move(item)); 365 cb(std::string_view{component_begin, it});
366 }
367 component_begin = it + 1;
368 }
369 }
370 if (component_begin != end) {
371 cb(std::string_view{component_begin, end});
366 } 372 }
373}
374
375std::vector<std::string_view> SplitPathComponents(std::string_view filename) {
376 std::vector<std::string_view> components;
377 ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
367 378
368 return out; 379 return components;
380}
381
382std::vector<std::string> SplitPathComponentsCopy(std::string_view filename) {
383 std::vector<std::string> components;
384 ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
385
386 return components;
369} 387}
370 388
371std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { 389std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
@@ -400,9 +418,9 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
400 return std::string(RemoveTrailingSlash(path)); 418 return std::string(RemoveTrailingSlash(path));
401} 419}
402 420
403std::string_view GetParentPath(std::string_view path) { 421std::string GetParentPath(std::string_view path) {
404 if (path.empty()) { 422 if (path.empty()) {
405 return path; 423 return std::string(path);
406 } 424 }
407 425
408#ifdef ANDROID 426#ifdef ANDROID
@@ -421,7 +439,7 @@ std::string_view GetParentPath(std::string_view path) {
421 name_index = std::max(name_bck_index, name_fwd_index); 439 name_index = std::max(name_bck_index, name_fwd_index);
422 } 440 }
423 441
424 return path.substr(0, name_index); 442 return std::string(path.substr(0, name_index));
425} 443}
426 444
427std::string_view GetPathWithoutTop(std::string_view path) { 445std::string_view GetPathWithoutTop(std::string_view path) {
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index 2874ea738..59301e7ed 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -289,7 +289,11 @@ enum class DirectorySeparator {
289 289
290// Splits the path on '/' or '\' and put the components into a vector 290// Splits the path on '/' or '\' and put the components into a vector
291// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } 291// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
292[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename); 292[[nodiscard]] std::vector<std::string_view> SplitPathComponents(std::string_view filename);
293
294// Splits the path on '/' or '\' and put the components into a vector
295// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
296[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
293 297
294// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' 298// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
295// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows 299// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
@@ -298,7 +302,7 @@ enum class DirectorySeparator {
298 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); 302 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
299 303
300// Gets all of the text up to the last '/' or '\' in the path. 304// Gets all of the text up to the last '/' or '\' in the path.
301[[nodiscard]] std::string_view GetParentPath(std::string_view path); 305[[nodiscard]] std::string GetParentPath(std::string_view path);
302 306
303// Gets all of the text after the first '/' or '\' in the path. 307// Gets all of the text after the first '/' or '\' in the path.
304[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path); 308[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 4bfc64f2d..e540375b8 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -11,10 +11,6 @@
11 11
12#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv 12#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
13 13
14#ifdef ANDROID
15#include <android/sharedmem.h>
16#endif
17
18#ifndef _GNU_SOURCE 14#ifndef _GNU_SOURCE
19#define _GNU_SOURCE 15#define _GNU_SOURCE
20#endif 16#endif
@@ -193,6 +189,11 @@ public:
193 } 189 }
194 } 190 }
195 191
192 bool ClearBackingRegion(size_t physical_offset, size_t length) {
193 // TODO: This does not seem to be possible on Windows.
194 return false;
195 }
196
196 void EnableDirectMappedAddress() { 197 void EnableDirectMappedAddress() {
197 // TODO 198 // TODO
198 UNREACHABLE(); 199 UNREACHABLE();
@@ -442,9 +443,7 @@ public:
442 } 443 }
443 444
444 // Backing memory initialization 445 // Backing memory initialization
445#ifdef ANDROID 446#if defined(__FreeBSD__) && __FreeBSD__ < 13
446 fd = ASharedMemory_create("HostMemory", backing_size);
447#elif defined(__FreeBSD__) && __FreeBSD__ < 13
448 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 447 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
449 fd = shm_open(SHM_ANON, O_RDWR, 0600); 448 fd = shm_open(SHM_ANON, O_RDWR, 0600);
450#else 449#else
@@ -455,7 +454,6 @@ public:
455 throw std::bad_alloc{}; 454 throw std::bad_alloc{};
456 } 455 }
457 456
458#ifndef ANDROID
459 // Defined to extend the file with zeros 457 // Defined to extend the file with zeros
460 int ret = ftruncate(fd, backing_size); 458 int ret = ftruncate(fd, backing_size);
461 if (ret != 0) { 459 if (ret != 0) {
@@ -463,7 +461,6 @@ public:
463 strerror(errno)); 461 strerror(errno));
464 throw std::bad_alloc{}; 462 throw std::bad_alloc{};
465 } 463 }
466#endif
467 464
468 backing_base = static_cast<u8*>( 465 backing_base = static_cast<u8*>(
469 mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); 466 mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
@@ -552,6 +549,19 @@ public:
552 ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); 549 ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
553 } 550 }
554 551
552 bool ClearBackingRegion(size_t physical_offset, size_t length) {
553#ifdef __linux__
554 // Set MADV_REMOVE on backing map to destroy it instantly.
555 // This also deletes the area from the backing file.
556 int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
557 ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
558
559 return true;
560#else
561 return false;
562#endif
563 }
564
555 void EnableDirectMappedAddress() { 565 void EnableDirectMappedAddress() {
556 virtual_base = nullptr; 566 virtual_base = nullptr;
557 } 567 }
@@ -623,6 +633,10 @@ public:
623 633
624 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {} 634 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
625 635
636 bool ClearBackingRegion(size_t physical_offset, size_t length) {
637 return false;
638 }
639
626 void EnableDirectMappedAddress() {} 640 void EnableDirectMappedAddress() {}
627 641
628 u8* backing_base{nullptr}; 642 u8* backing_base{nullptr};
@@ -698,6 +712,12 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool w
698 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); 712 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
699} 713}
700 714
715void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) {
716 if (!impl || fill_value != 0 || !impl->ClearBackingRegion(physical_offset, length)) {
717 std::memset(backing_base + physical_offset, fill_value, length);
718 }
719}
720
701void HostMemory::EnableDirectMappedAddress() { 721void HostMemory::EnableDirectMappedAddress() {
702 if (impl) { 722 if (impl) {
703 impl->EnableDirectMappedAddress(); 723 impl->EnableDirectMappedAddress();
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index cebfacab2..747c5850c 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -48,6 +48,8 @@ public:
48 48
49 void EnableDirectMappedAddress(); 49 void EnableDirectMappedAddress();
50 50
51 void ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value);
52
51 [[nodiscard]] u8* BackingBasePointer() noexcept { 53 [[nodiscard]] u8* BackingBasePointer() noexcept {
52 return backing_base; 54 return backing_base;
53 } 55 }
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 88f509ba7..ea52bbfa6 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -211,6 +211,8 @@ const char* TranslateCategory(Category category) {
211 case Category::Debugging: 211 case Category::Debugging:
212 case Category::DebuggingGraphics: 212 case Category::DebuggingGraphics:
213 return "Debugging"; 213 return "Debugging";
214 case Category::GpuDriver:
215 return "GpuDriver";
214 case Category::Miscellaneous: 216 case Category::Miscellaneous:
215 return "Miscellaneous"; 217 return "Miscellaneous";
216 case Category::Network: 218 case Category::Network:
diff --git a/src/common/settings.h b/src/common/settings.h
index 7dc18fffe..07dba53ab 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -197,7 +197,7 @@ struct Values {
197 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, 197 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
198 CpuAccuracy::Auto, CpuAccuracy::Paranoid, 198 CpuAccuracy::Auto, CpuAccuracy::Paranoid,
199 "cpu_accuracy", Category::Cpu}; 199 "cpu_accuracy", Category::Cpu};
200 Setting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug}; 200 SwitchableSetting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug};
201 201
202 Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug}; 202 Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};
203 Setting<bool> cpuopt_block_linking{linkage, true, "cpuopt_block_linking", Category::CpuDebug}; 203 Setting<bool> cpuopt_block_linking{linkage, true, "cpuopt_block_linking", Category::CpuDebug};
@@ -211,9 +211,9 @@ struct Values {
211 Setting<bool> cpuopt_misc_ir{linkage, true, "cpuopt_misc_ir", Category::CpuDebug}; 211 Setting<bool> cpuopt_misc_ir{linkage, true, "cpuopt_misc_ir", Category::CpuDebug};
212 Setting<bool> cpuopt_reduce_misalign_checks{linkage, true, "cpuopt_reduce_misalign_checks", 212 Setting<bool> cpuopt_reduce_misalign_checks{linkage, true, "cpuopt_reduce_misalign_checks",
213 Category::CpuDebug}; 213 Category::CpuDebug};
214 Setting<bool> cpuopt_fastmem{linkage, true, "cpuopt_fastmem", Category::CpuDebug}; 214 SwitchableSetting<bool> cpuopt_fastmem{linkage, true, "cpuopt_fastmem", Category::CpuDebug};
215 Setting<bool> cpuopt_fastmem_exclusives{linkage, true, "cpuopt_fastmem_exclusives", 215 SwitchableSetting<bool> cpuopt_fastmem_exclusives{linkage, true, "cpuopt_fastmem_exclusives",
216 Category::CpuDebug}; 216 Category::CpuDebug};
217 Setting<bool> cpuopt_recompile_exclusives{linkage, true, "cpuopt_recompile_exclusives", 217 Setting<bool> cpuopt_recompile_exclusives{linkage, true, "cpuopt_recompile_exclusives",
218 Category::CpuDebug}; 218 Category::CpuDebug};
219 Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts", 219 Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts",
@@ -256,7 +256,7 @@ struct Values {
256 AstcDecodeMode::CpuAsynchronous, 256 AstcDecodeMode::CpuAsynchronous,
257 "accelerate_astc", 257 "accelerate_astc",
258 Category::Renderer}; 258 Category::Renderer};
259 Setting<VSyncMode, true> vsync_mode{ 259 SwitchableSetting<VSyncMode, true> vsync_mode{
260 linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed, 260 linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
261 "use_vsync", Category::Renderer, Specialization::RuntimeList, true, 261 "use_vsync", Category::Renderer, Specialization::RuntimeList, true,
262 true}; 262 true};
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 344c04439..c82e17495 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -26,6 +26,7 @@ enum class Category : u32 {
26 DataStorage, 26 DataStorage,
27 Debugging, 27 Debugging,
28 DebuggingGraphics, 28 DebuggingGraphics,
29 GpuDriver,
29 Miscellaneous, 30 Miscellaneous,
30 Network, 31 Network,
31 WebService, 32 WebService,
diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h
index 3175ab07d..0b18ca5ec 100644
--- a/src/common/settings_setting.h
+++ b/src/common/settings_setting.h
@@ -81,6 +81,9 @@ public:
81 [[nodiscard]] virtual const Type& GetValue() const { 81 [[nodiscard]] virtual const Type& GetValue() const {
82 return value; 82 return value;
83 } 83 }
84 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
85 return value;
86 }
84 87
85 /** 88 /**
86 * Sets the setting to the given value. 89 * Sets the setting to the given value.
@@ -353,7 +356,7 @@ public:
353 } 356 }
354 return custom; 357 return custom;
355 } 358 }
356 [[nodiscard]] const Type& GetValue(bool need_global) const { 359 [[nodiscard]] const Type& GetValue(bool need_global) const override final {
357 if (use_global || need_global) { 360 if (use_global || need_global) {
358 return this->value; 361 return this->value;
359 } 362 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 7b9ed856f..96ab39cb8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -549,6 +549,11 @@ add_library(core STATIC
549 hle/service/hid/xcd.cpp 549 hle/service/hid/xcd.cpp
550 hle/service/hid/xcd.h 550 hle/service/hid/xcd.h
551 hle/service/hid/errors.h 551 hle/service/hid/errors.h
552 hle/service/hid/controllers/types/debug_pad_types.h
553 hle/service/hid/controllers/types/keyboard_types.h
554 hle/service/hid/controllers/types/mouse_types.h
555 hle/service/hid/controllers/types/npad_types.h
556 hle/service/hid/controllers/types/touch_types.h
552 hle/service/hid/controllers/applet_resource.cpp 557 hle/service/hid/controllers/applet_resource.cpp
553 hle/service/hid/controllers/applet_resource.h 558 hle/service/hid/controllers/applet_resource.h
554 hle/service/hid/controllers/console_six_axis.cpp 559 hle/service/hid/controllers/console_six_axis.cpp
@@ -569,14 +574,15 @@ add_library(core STATIC
569 hle/service/hid/controllers/palma.h 574 hle/service/hid/controllers/palma.h
570 hle/service/hid/controllers/seven_six_axis.cpp 575 hle/service/hid/controllers/seven_six_axis.cpp
571 hle/service/hid/controllers/seven_six_axis.h 576 hle/service/hid/controllers/seven_six_axis.h
577 hle/service/hid/controllers/shared_memory_format.h
578 hle/service/hid/controllers/shared_memory_holder.cpp
579 hle/service/hid/controllers/shared_memory_holder.h
572 hle/service/hid/controllers/six_axis.cpp 580 hle/service/hid/controllers/six_axis.cpp
573 hle/service/hid/controllers/six_axis.h 581 hle/service/hid/controllers/six_axis.h
574 hle/service/hid/controllers/stubbed.cpp 582 hle/service/hid/controllers/stubbed.cpp
575 hle/service/hid/controllers/stubbed.h 583 hle/service/hid/controllers/stubbed.h
576 hle/service/hid/controllers/touchscreen.cpp 584 hle/service/hid/controllers/touchscreen.cpp
577 hle/service/hid/controllers/touchscreen.h 585 hle/service/hid/controllers/touchscreen.h
578 hle/service/hid/controllers/xpad.cpp
579 hle/service/hid/controllers/xpad.h
580 hle/service/hid/hidbus/hidbus_base.cpp 586 hle/service/hid/hidbus/hidbus_base.cpp
581 hle/service/hid/hidbus/hidbus_base.h 587 hle/service/hid/hidbus/hidbus_base.h
582 hle/service/hid/hidbus/ringcon.cpp 588 hle/service/hid/hidbus/ringcon.cpp
@@ -784,6 +790,12 @@ add_library(core STATIC
784 hle/service/service.h 790 hle/service/service.h
785 hle/service/set/set.cpp 791 hle/service/set/set.cpp
786 hle/service/set/set.h 792 hle/service/set/set.h
793 hle/service/set/appln_settings.cpp
794 hle/service/set/appln_settings.h
795 hle/service/set/device_settings.cpp
796 hle/service/set/device_settings.h
797 hle/service/set/private_settings.cpp
798 hle/service/set/private_settings.h
787 hle/service/set/set_cal.cpp 799 hle/service/set/set_cal.cpp
788 hle/service/set/set_cal.h 800 hle/service/set/set_cal.h
789 hle/service/set/set_fd.cpp 801 hle/service/set/set_fd.cpp
@@ -792,6 +804,8 @@ add_library(core STATIC
792 hle/service/set/set_sys.h 804 hle/service/set/set_sys.h
793 hle/service/set/settings.cpp 805 hle/service/set/settings.cpp
794 hle/service/set/settings.h 806 hle/service/set/settings.h
807 hle/service/set/system_settings.cpp
808 hle/service/set/system_settings.h
795 hle/service/sm/sm.cpp 809 hle/service/sm/sm.cpp
796 hle/service/sm/sm.h 810 hle/service/sm/sm.h
797 hle/service/sm/sm_controller.cpp 811 hle/service/sm/sm_controller.cpp
@@ -947,15 +961,19 @@ if (HAS_NCE)
947 set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp") 961 set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
948 962
949 target_sources(core PRIVATE 963 target_sources(core PRIVATE
964 arm/nce/arm_nce_asm_definitions.h
950 arm/nce/arm_nce.cpp 965 arm/nce/arm_nce.cpp
951 arm/nce/arm_nce.h 966 arm/nce/arm_nce.h
952 arm/nce/arm_nce.s 967 arm/nce/arm_nce.s
953 arm/nce/guest_context.h 968 arm/nce/guest_context.h
969 arm/nce/instructions.h
970 arm/nce/interpreter_visitor.cpp
971 arm/nce/interpreter_visitor.h
954 arm/nce/patcher.cpp 972 arm/nce/patcher.cpp
955 arm/nce/patcher.h 973 arm/nce/patcher.h
956 arm/nce/instructions.h 974 arm/nce/visitor_base.h
957 ) 975 )
958 target_link_libraries(core PRIVATE merry::oaknut) 976 target_link_libraries(core PRIVATE merry::mcl merry::oaknut)
959endif() 977endif()
960 978
961if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 979if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp
index b42a32a0b..1311e66a9 100644
--- a/src/core/arm/nce/arm_nce.cpp
+++ b/src/core/arm/nce/arm_nce.cpp
@@ -6,7 +6,7 @@
6 6
7#include "common/signal_chain.h" 7#include "common/signal_chain.h"
8#include "core/arm/nce/arm_nce.h" 8#include "core/arm/nce/arm_nce.h"
9#include "core/arm/nce/guest_context.h" 9#include "core/arm/nce/interpreter_visitor.h"
10#include "core/arm/nce/patcher.h" 10#include "core/arm/nce/patcher.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/memory.h" 12#include "core/memory.h"
@@ -21,7 +21,8 @@ namespace Core {
21 21
22namespace { 22namespace {
23 23
24struct sigaction g_orig_action; 24struct sigaction g_orig_bus_action;
25struct sigaction g_orig_segv_action;
25 26
26// Verify assembly offsets. 27// Verify assembly offsets.
27using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; 28using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
@@ -37,6 +38,9 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
37 return reinterpret_cast<fpsimd_context*>(header); 38 return reinterpret_cast<fpsimd_context*>(header);
38} 39}
39 40
41using namespace Common::Literals;
42constexpr u32 StackSize = 32_KiB;
43
40} // namespace 44} // namespace
41 45
42void* ArmNce::RestoreGuestContext(void* raw_context) { 46void* ArmNce::RestoreGuestContext(void* raw_context) {
@@ -104,19 +108,10 @@ void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
104 host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0); 108 host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
105} 109}
106 110
107bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { 111bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
108 auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext; 112 auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
109 auto* info = static_cast<siginfo_t*>(raw_info); 113 auto* info = static_cast<siginfo_t*>(raw_info);
110 114
111 // Try to handle an invalid access.
112 // TODO: handle accesses which split a page?
113 const Common::ProcessAddress addr =
114 (reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
115 if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
116 // We handled the access successfully and are returning to guest code.
117 return true;
118 }
119
120 // We can't handle the access, so determine why we crashed. 115 // We can't handle the access, so determine why we crashed.
121 const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr); 116 const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
122 117
@@ -143,8 +138,44 @@ bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw
143 return false; 138 return false;
144} 139}
145 140
146void ArmNce::HandleHostFault(int sig, void* raw_info, void* raw_context) { 141bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
147 return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context); 142 auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
143 auto* fpctx = GetFloatingPointState(host_ctx);
144 auto& memory = guest_ctx->system->ApplicationMemory();
145
146 // Match and execute an instruction.
147 auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
148 if (next_pc) {
149 host_ctx.pc = *next_pc;
150 return true;
151 }
152
153 // We couldn't handle the access.
154 return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
155}
156
157bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
158 auto* info = static_cast<siginfo_t*>(raw_info);
159
160 // Try to handle an invalid access.
161 // TODO: handle accesses which split a page?
162 const Common::ProcessAddress addr =
163 (reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
164 if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
165 // We handled the access successfully and are returning to guest code.
166 return true;
167 }
168
169 // We couldn't handle the access.
170 return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
171}
172
173void ArmNce::HandleHostAlignmentFault(int sig, void* raw_info, void* raw_context) {
174 return g_orig_bus_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
175}
176
177void ArmNce::HandleHostAccessFault(int sig, void* raw_info, void* raw_context) {
178 return g_orig_segv_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
148} 179}
149 180
150void ArmNce::LockThread(Kernel::KThread* thread) { 181void ArmNce::LockThread(Kernel::KThread* thread) {
@@ -225,18 +256,31 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
225ArmNce::~ArmNce() = default; 256ArmNce::~ArmNce() = default;
226 257
227void ArmNce::Initialize() { 258void ArmNce::Initialize() {
228 m_thread_id = gettid(); 259 if (m_thread_id == -1) {
260 m_thread_id = gettid();
261 }
262
263 // Configure signal stack.
264 if (!m_stack) {
265 m_stack = std::make_unique<u8[]>(StackSize);
266
267 stack_t ss{};
268 ss.ss_sp = m_stack.get();
269 ss.ss_size = StackSize;
270 sigaltstack(&ss, nullptr);
271 }
229 272
230 // Setup our signals 273 // Set up signals.
231 static std::once_flag signals; 274 static std::once_flag flag;
232 std::call_once(signals, [] { 275 std::call_once(flag, [] {
233 using HandlerType = decltype(sigaction::sa_sigaction); 276 using HandlerType = decltype(sigaction::sa_sigaction);
234 277
235 sigset_t signal_mask; 278 sigset_t signal_mask;
236 sigemptyset(&signal_mask); 279 sigemptyset(&signal_mask);
237 sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal); 280 sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
238 sigaddset(&signal_mask, BreakFromRunCodeSignal); 281 sigaddset(&signal_mask, BreakFromRunCodeSignal);
239 sigaddset(&signal_mask, GuestFaultSignal); 282 sigaddset(&signal_mask, GuestAlignmentFaultSignal);
283 sigaddset(&signal_mask, GuestAccessFaultSignal);
240 284
241 struct sigaction return_to_run_code_action {}; 285 struct sigaction return_to_run_code_action {};
242 return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK; 286 return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
@@ -253,18 +297,19 @@ void ArmNce::Initialize() {
253 break_from_run_code_action.sa_mask = signal_mask; 297 break_from_run_code_action.sa_mask = signal_mask;
254 Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr); 298 Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
255 299
256 struct sigaction fault_action {}; 300 struct sigaction alignment_fault_action {};
257 fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART; 301 alignment_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
258 fault_action.sa_sigaction = reinterpret_cast<HandlerType>(&ArmNce::GuestFaultSignalHandler); 302 alignment_fault_action.sa_sigaction =
259 fault_action.sa_mask = signal_mask; 303 reinterpret_cast<HandlerType>(&ArmNce::GuestAlignmentFaultSignalHandler);
260 Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action); 304 alignment_fault_action.sa_mask = signal_mask;
261 305 Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr);
262 // Simplify call for g_orig_action. 306
263 // These fields occupy the same space in memory, so this should be a no-op in practice. 307 struct sigaction access_fault_action {};
264 if (!(g_orig_action.sa_flags & SA_SIGINFO)) { 308 access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
265 g_orig_action.sa_sigaction = 309 access_fault_action.sa_sigaction =
266 reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler); 310 reinterpret_cast<HandlerType>(&ArmNce::GuestAccessFaultSignalHandler);
267 } 311 access_fault_action.sa_mask = signal_mask;
312 Common::SigAction(GuestAccessFaultSignal, &access_fault_action, &g_orig_segv_action);
268 }); 313 });
269} 314}
270 315
diff --git a/src/core/arm/nce/arm_nce.h b/src/core/arm/nce/arm_nce.h
index f55c10d1d..be9b304c4 100644
--- a/src/core/arm/nce/arm_nce.h
+++ b/src/core/arm/nce/arm_nce.h
@@ -61,7 +61,8 @@ private:
61 static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, 61 static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
62 void* raw_context); 62 void* raw_context);
63 static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context); 63 static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
64 static void GuestFaultSignalHandler(int sig, void* info, void* raw_context); 64 static void GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context);
65 static void GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context);
65 66
66 static void LockThreadParameters(void* tpidr); 67 static void LockThreadParameters(void* tpidr);
67 static void UnlockThreadParameters(void* tpidr); 68 static void UnlockThreadParameters(void* tpidr);
@@ -70,8 +71,11 @@ private:
70 // C++ implementation functions for assembly definitions. 71 // C++ implementation functions for assembly definitions.
71 static void* RestoreGuestContext(void* raw_context); 72 static void* RestoreGuestContext(void* raw_context);
72 static void SaveGuestContext(GuestContext* ctx, void* raw_context); 73 static void SaveGuestContext(GuestContext* ctx, void* raw_context);
73 static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context); 74 static bool HandleFailedGuestFault(GuestContext* ctx, void* info, void* raw_context);
74 static void HandleHostFault(int sig, void* info, void* raw_context); 75 static bool HandleGuestAlignmentFault(GuestContext* ctx, void* info, void* raw_context);
76 static bool HandleGuestAccessFault(GuestContext* ctx, void* info, void* raw_context);
77 static void HandleHostAlignmentFault(int sig, void* info, void* raw_context);
78 static void HandleHostAccessFault(int sig, void* info, void* raw_context);
75 79
76public: 80public:
77 Core::System& m_system; 81 Core::System& m_system;
@@ -83,6 +87,9 @@ public:
83 // Core context. 87 // Core context.
84 GuestContext m_guest_ctx{}; 88 GuestContext m_guest_ctx{};
85 Kernel::KThread* m_running_thread{}; 89 Kernel::KThread* m_running_thread{};
90
91 // Stack for signal processing.
92 std::unique_ptr<u8[]> m_stack{};
86}; 93};
87 94
88} // namespace Core 95} // namespace Core
diff --git a/src/core/arm/nce/arm_nce.s b/src/core/arm/nce/arm_nce.s
index 4aeda4740..c68c05949 100644
--- a/src/core/arm/nce/arm_nce.s
+++ b/src/core/arm/nce/arm_nce.s
@@ -130,11 +130,11 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
130 ret 130 ret
131 131
132 132
133/* static void Core::ArmNce::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */ 133/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
134.section .text._ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits 134.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
135.global _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_ 135.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
136.type _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, %function 136.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
137_ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_: 137_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
138 /* Check to see if we have the correct TLS magic. */ 138 /* Check to see if we have the correct TLS magic. */
139 mrs x8, tpidr_el0 139 mrs x8, tpidr_el0
140 ldr w9, [x8, #(TpidrEl0TlsMagic)] 140 ldr w9, [x8, #(TpidrEl0TlsMagic)]
@@ -146,7 +146,7 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
146 146
147 /* Incorrect TLS magic, so this is a host fault. */ 147 /* Incorrect TLS magic, so this is a host fault. */
148 /* Tail call the handler. */ 148 /* Tail call the handler. */
149 b _ZN4Core6ArmNce15HandleHostFaultEiPvS1_ 149 b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
150 150
1511: 1511:
152 /* Correct TLS magic, so this is a guest fault. */ 152 /* Correct TLS magic, so this is a guest fault. */
@@ -163,7 +163,53 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
163 msr tpidr_el0, x3 163 msr tpidr_el0, x3
164 164
165 /* Call the handler. */ 165 /* Call the handler. */
166 bl _ZN4Core6ArmNce16HandleGuestFaultEPNS_12GuestContextEPvS3_ 166 bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
167
168 /* If the handler returned false, we want to preserve the host tpidr_el0. */
169 cbz x0, 2f
170
171 /* Otherwise, restore guest tpidr_el0. */
172 msr tpidr_el0, x19
173
1742:
175 ldr x19, [sp, #0x10]
176 ldp x29, x30, [sp], #0x20
177 ret
178
179/* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */
180.section .text._ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, "ax", %progbits
181.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
182.type _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, %function
183_ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
184 /* Check to see if we have the correct TLS magic. */
185 mrs x8, tpidr_el0
186 ldr w9, [x8, #(TpidrEl0TlsMagic)]
187
188 LOAD_IMMEDIATE_32(w10, TlsMagic)
189
190 cmp w9, w10
191 b.eq 1f
192
193 /* Incorrect TLS magic, so this is a host fault. */
194 /* Tail call the handler. */
195 b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
196
1971:
198 /* Correct TLS magic, so this is a guest fault. */
199 stp x29, x30, [sp, #-0x20]!
200 str x19, [sp, #0x10]
201 mov x29, sp
202
203 /* Save the old tpidr_el0. */
204 mov x19, x8
205
206 /* Restore host tpidr_el0. */
207 ldr x0, [x8, #(TpidrEl0NativeContext)]
208 ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
209 msr tpidr_el0, x3
210
211 /* Call the handler. */
212 bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
167 213
168 /* If the handler returned false, we want to preserve the host tpidr_el0. */ 214 /* If the handler returned false, we want to preserve the host tpidr_el0. */
169 cbz x0, 2f 215 cbz x0, 2f
diff --git a/src/core/arm/nce/arm_nce_asm_definitions.h b/src/core/arm/nce/arm_nce_asm_definitions.h
index 8a9b285b5..8ea4383f7 100644
--- a/src/core/arm/nce/arm_nce_asm_definitions.h
+++ b/src/core/arm/nce/arm_nce_asm_definitions.h
@@ -10,7 +10,8 @@
10 10
11#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2 11#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
12#define BreakFromRunCodeSignal SIGURG 12#define BreakFromRunCodeSignal SIGURG
13#define GuestFaultSignal SIGSEGV 13#define GuestAccessFaultSignal SIGSEGV
14#define GuestAlignmentFaultSignal SIGBUS
14 15
15#define GuestContextSp 0xF8 16#define GuestContextSp 0xF8
16#define GuestContextHostContext 0x320 17#define GuestContextHostContext 0x320
diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp
new file mode 100644
index 000000000..8e81c66a5
--- /dev/null
+++ b/src/core/arm/nce/interpreter_visitor.cpp
@@ -0,0 +1,825 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#include "common/bit_cast.h"
6#include "core/arm/nce/interpreter_visitor.h"
7
8#include <dynarmic/frontend/A64/decoder/a64.h>
9
10namespace Core {
11
12template <u32 BitSize>
13u64 SignExtendToLong(u64 value) {
14 u64 mask = 1ULL << (BitSize - 1);
15 value &= (1ULL << BitSize) - 1;
16 return (value ^ mask) - mask;
17}
18
19static u64 SignExtendToLong(u64 value, u64 bitsize) {
20 switch (bitsize) {
21 case 8:
22 return SignExtendToLong<8>(value);
23 case 16:
24 return SignExtendToLong<16>(value);
25 case 32:
26 return SignExtendToLong<32>(value);
27 default:
28 return value;
29 }
30}
31
32template <u64 BitSize>
33u32 SignExtendToWord(u32 value) {
34 u32 mask = 1ULL << (BitSize - 1);
35 value &= (1ULL << BitSize) - 1;
36 return (value ^ mask) - mask;
37}
38
39static u32 SignExtendToWord(u32 value, u64 bitsize) {
40 switch (bitsize) {
41 case 8:
42 return SignExtendToWord<8>(value);
43 case 16:
44 return SignExtendToWord<16>(value);
45 default:
46 return value;
47 }
48}
49
50static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) {
51 if (regsize == 64) {
52 return SignExtendToLong(value, bitsize);
53 } else {
54 return SignExtendToWord(static_cast<u32>(value), bitsize);
55 }
56}
57
58static u128 VectorGetElement(u128 value, u64 bitsize) {
59 switch (bitsize) {
60 case 8:
61 return {value[0] & ((1ULL << 8) - 1), 0};
62 case 16:
63 return {value[0] & ((1ULL << 16) - 1), 0};
64 case 32:
65 return {value[0] & ((1ULL << 32) - 1), 0};
66 case 64:
67 return {value[0], 0};
68 default:
69 return value;
70 }
71}
72
73u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
74 ASSERT(shift <= 4);
75 ASSERT(bitsize == 32 || bitsize == 64);
76 u64 val = this->GetReg(reg);
77 size_t len;
78 u64 extended;
79 bool signed_extend;
80
81 switch (option.ZeroExtend()) {
82 case 0b000: { // UXTB
83 val &= ((1ULL << 8) - 1);
84 len = 8;
85 signed_extend = false;
86 break;
87 }
88 case 0b001: { // UXTH
89 val &= ((1ULL << 16) - 1);
90 len = 16;
91 signed_extend = false;
92 break;
93 }
94 case 0b010: { // UXTW
95 val &= ((1ULL << 32) - 1);
96 len = 32;
97 signed_extend = false;
98 break;
99 }
100 case 0b011: { // UXTX
101 len = 64;
102 signed_extend = false;
103 break;
104 }
105 case 0b100: { // SXTB
106 val &= ((1ULL << 8) - 1);
107 len = 8;
108 signed_extend = true;
109 break;
110 }
111 case 0b101: { // SXTH
112 val &= ((1ULL << 16) - 1);
113 len = 16;
114 signed_extend = true;
115 break;
116 }
117 case 0b110: { // SXTW
118 val &= ((1ULL << 32) - 1);
119 len = 32;
120 signed_extend = true;
121 break;
122 }
123 case 0b111: { // SXTX
124 len = 64;
125 signed_extend = true;
126 break;
127 }
128 default:
129 UNREACHABLE();
130 }
131
132 if (len < bitsize && signed_extend) {
133 extended = SignExtend(val, len, bitsize);
134 } else {
135 extended = val;
136 }
137
138 return extended << shift;
139}
140
141u128 InterpreterVisitor::GetVec(Vec v) {
142 return m_fpsimd_regs[static_cast<u32>(v)];
143}
144
145u64 InterpreterVisitor::GetReg(Reg r) {
146 return m_regs[static_cast<u32>(r)];
147}
148
149u64 InterpreterVisitor::GetSp() {
150 return m_sp;
151}
152
153u64 InterpreterVisitor::GetPc() {
154 return m_pc;
155}
156
157void InterpreterVisitor::SetVec(Vec v, u128 value) {
158 m_fpsimd_regs[static_cast<u32>(v)] = value;
159}
160
161void InterpreterVisitor::SetReg(Reg r, u64 value) {
162 m_regs[static_cast<u32>(r)] = value;
163}
164
165void InterpreterVisitor::SetSp(u64 value) {
166 m_sp = value;
167}
168
169bool InterpreterVisitor::Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
170 const auto memop = L ? MemOp::Load : MemOp::Store;
171 const size_t elsize = 8 << size;
172 const size_t datasize = elsize;
173
174 // Operation
175 const size_t dbytes = datasize / 8;
176
177 u64 address;
178 if (Rn == Reg::SP) {
179 address = this->GetSp();
180 } else {
181 address = this->GetReg(Rn);
182 }
183
184 switch (memop) {
185 case MemOp::Store: {
186 std::atomic_thread_fence(std::memory_order_seq_cst);
187 u64 value = this->GetReg(Rt);
188 m_memory.WriteBlock(address, &value, dbytes);
189 std::atomic_thread_fence(std::memory_order_seq_cst);
190 break;
191 }
192 case MemOp::Load: {
193 u64 value = 0;
194 m_memory.ReadBlock(address, &value, dbytes);
195 this->SetReg(Rt, value);
196 std::atomic_thread_fence(std::memory_order_seq_cst);
197 break;
198 }
199 default:
200 UNREACHABLE();
201 }
202
203 return true;
204}
205
206bool InterpreterVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
207 const size_t size = sz.ZeroExtend<size_t>();
208 const bool L = 0;
209 const bool o0 = 0;
210 return this->Ordered(size, L, o0, Rn, Rt);
211}
212
213bool InterpreterVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) {
214 const size_t size = sz.ZeroExtend<size_t>();
215 const bool L = 0;
216 const bool o0 = 1;
217 return this->Ordered(size, L, o0, Rn, Rt);
218}
219
220bool InterpreterVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) {
221 const size_t size = sz.ZeroExtend<size_t>();
222 const bool L = 1;
223 const bool o0 = 0;
224 return this->Ordered(size, L, o0, Rn, Rt);
225}
226
227bool InterpreterVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) {
228 const size_t size = sz.ZeroExtend<size_t>();
229 const bool L = 1;
230 const bool o0 = 1;
231 return this->Ordered(size, L, o0, Rn, Rt);
232}
233
234bool InterpreterVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
235 const size_t size = opc_0 == 0 ? 4 : 8;
236 const s64 offset = Dynarmic::concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
237 const u64 address = this->GetPc() + offset;
238
239 u64 data = 0;
240 m_memory.ReadBlock(address, &data, size);
241
242 this->SetReg(Rt, data);
243 return true;
244}
245
246bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
247 if (opc == 0b11) {
248 // Unallocated encoding
249 return false;
250 }
251
252 const u64 size = 4 << opc.ZeroExtend();
253 const u64 offset = imm19.SignExtend<u64>() << 2;
254 const u64 address = this->GetPc() + offset;
255
256 u128 data{};
257 m_memory.ReadBlock(address, &data, size);
258 this->SetVec(Vt, data);
259 return true;
260}
261
262bool InterpreterVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
263 Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
264 if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) {
265 // Unallocated encoding
266 return false;
267 }
268
269 const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
270 if (memop == MemOp::Load && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
271 // Unpredictable instruction
272 return false;
273 }
274 if (memop == MemOp::Store && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
275 // Unpredictable instruction
276 return false;
277 }
278 if (memop == MemOp::Load && Rt == Rt2) {
279 // Unpredictable instruction
280 return false;
281 }
282
283 u64 address;
284 if (Rn == Reg::SP) {
285 address = this->GetSp();
286 } else {
287 address = this->GetReg(Rn);
288 }
289
290 const bool postindex = !not_postindex;
291 const bool signed_ = opc.Bit<0>() != 0;
292 const size_t scale = 2 + opc.Bit<1>();
293 const size_t datasize = 8 << scale;
294 const u64 offset = imm7.SignExtend<u64>() << scale;
295
296 if (!postindex) {
297 address += offset;
298 }
299
300 const size_t dbytes = datasize / 8;
301 switch (memop) {
302 case MemOp::Store: {
303 u64 data1 = this->GetReg(Rt);
304 u64 data2 = this->GetReg(Rt2);
305 m_memory.WriteBlock(address, &data1, dbytes);
306 m_memory.WriteBlock(address + dbytes, &data2, dbytes);
307 break;
308 }
309 case MemOp::Load: {
310 u64 data1 = 0, data2 = 0;
311 m_memory.ReadBlock(address, &data1, dbytes);
312 m_memory.ReadBlock(address + dbytes, &data2, dbytes);
313 if (signed_) {
314 this->SetReg(Rt, SignExtend(data1, datasize, 64));
315 this->SetReg(Rt2, SignExtend(data2, datasize, 64));
316 } else {
317 this->SetReg(Rt, data1);
318 this->SetReg(Rt2, data2);
319 }
320 break;
321 }
322 default:
323 UNREACHABLE();
324 }
325
326 if (wback) {
327 if (postindex) {
328 address += offset;
329 }
330
331 if (Rn == Reg::SP) {
332 this->SetSp(address);
333 } else {
334 this->SetReg(Rn, address);
335 }
336 }
337
338 return true;
339}
340
341bool InterpreterVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
342 Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
343 if (opc == 0b11) {
344 // Unallocated encoding
345 return false;
346 }
347
348 const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
349 if (memop == MemOp::Load && Vt == Vt2) {
350 // Unpredictable instruction
351 return false;
352 }
353
354 u64 address;
355 if (Rn == Reg::SP) {
356 address = this->GetSp();
357 } else {
358 address = this->GetReg(Rn);
359 }
360
361 const bool postindex = !not_postindex;
362 const size_t scale = 2 + opc.ZeroExtend<size_t>();
363 const size_t datasize = 8 << scale;
364 const u64 offset = imm7.SignExtend<u64>() << scale;
365 const size_t dbytes = datasize / 8;
366
367 if (!postindex) {
368 address += offset;
369 }
370
371 switch (memop) {
372 case MemOp::Store: {
373 u128 data1 = VectorGetElement(this->GetVec(Vt), datasize);
374 u128 data2 = VectorGetElement(this->GetVec(Vt2), datasize);
375 m_memory.WriteBlock(address, &data1, dbytes);
376 m_memory.WriteBlock(address + dbytes, &data2, dbytes);
377 break;
378 }
379 case MemOp::Load: {
380 u128 data1{}, data2{};
381 m_memory.ReadBlock(address, &data1, dbytes);
382 m_memory.ReadBlock(address + dbytes, &data2, dbytes);
383 this->SetVec(Vt, data1);
384 this->SetVec(Vt2, data2);
385 break;
386 }
387 default:
388 UNREACHABLE();
389 }
390
391 if (wback) {
392 if (postindex) {
393 address += offset;
394 }
395
396 if (Rn == Reg::SP) {
397 this->SetSp(address);
398 } else {
399 this->SetReg(Rn, address);
400 }
401 }
402
403 return true;
404}
405
406bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset,
407 Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) {
408 MemOp memop;
409 bool signed_ = false;
410 size_t regsize = 0;
411
412 if (opc.Bit<1>() == 0) {
413 memop = opc.Bit<0>() ? MemOp::Load : MemOp::Store;
414 regsize = size == 0b11 ? 64 : 32;
415 signed_ = false;
416 } else if (size == 0b11) {
417 memop = MemOp::Prefetch;
418 ASSERT(!opc.Bit<0>());
419 } else {
420 memop = MemOp::Load;
421 ASSERT(!(size == 0b10 && opc.Bit<0>() == 1));
422 regsize = opc.Bit<0>() ? 32 : 64;
423 signed_ = true;
424 }
425
426 if (memop == MemOp::Load && wback && Rn == Rt && Rn != Reg::R31) {
427 // Unpredictable instruction
428 return false;
429 }
430 if (memop == MemOp::Store && wback && Rn == Rt && Rn != Reg::R31) {
431 // Unpredictable instruction
432 return false;
433 }
434
435 u64 address;
436 if (Rn == Reg::SP) {
437 address = this->GetSp();
438 } else {
439 address = this->GetReg(Rn);
440 }
441 if (!postindex) {
442 address += offset;
443 }
444
445 const size_t datasize = 8 << scale;
446 switch (memop) {
447 case MemOp::Store: {
448 u64 data = this->GetReg(Rt);
449 m_memory.WriteBlock(address, &data, datasize / 8);
450 break;
451 }
452 case MemOp::Load: {
453 u64 data = 0;
454 m_memory.ReadBlock(address, &data, datasize / 8);
455 if (signed_) {
456 this->SetReg(Rt, SignExtend(data, datasize, regsize));
457 } else {
458 this->SetReg(Rt, data);
459 }
460 break;
461 }
462 case MemOp::Prefetch:
463 // this->Prefetch(address, Rt)
464 break;
465 }
466
467 if (wback) {
468 if (postindex) {
469 address += offset;
470 }
471
472 if (Rn == Reg::SP) {
473 this->SetSp(address);
474 } else {
475 this->SetReg(Rn, address);
476 }
477 }
478
479 return true;
480}
481
482bool InterpreterVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex,
483 Reg Rn, Reg Rt) {
484 const bool wback = true;
485 const bool postindex = !not_postindex;
486 const size_t scale = size.ZeroExtend<size_t>();
487 const u64 offset = imm9.SignExtend<u64>();
488
489 return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
490}
491
492bool InterpreterVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
493 const bool wback = false;
494 const bool postindex = false;
495 const size_t scale = size.ZeroExtend<size_t>();
496 const u64 offset = imm12.ZeroExtend<u64>() << scale;
497
498 return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
499}
500
501bool InterpreterVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
502 const bool wback = false;
503 const bool postindex = false;
504 const size_t scale = size.ZeroExtend<size_t>();
505 const u64 offset = imm9.SignExtend<u64>();
506
507 return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
508}
509
510bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset,
511 MemOp memop, Reg Rn, Vec Vt) {
512 const size_t datasize = 8 << scale;
513
514 u64 address;
515 if (Rn == Reg::SP) {
516 address = this->GetSp();
517 } else {
518 address = this->GetReg(Rn);
519 }
520
521 if (!postindex) {
522 address += offset;
523 }
524
525 switch (memop) {
526 case MemOp::Store: {
527 u128 data = VectorGetElement(this->GetVec(Vt), datasize);
528 m_memory.WriteBlock(address, &data, datasize / 8);
529 break;
530 }
531 case MemOp::Load: {
532 u128 data{};
533 m_memory.ReadBlock(address, &data, datasize);
534 this->SetVec(Vt, data);
535 break;
536 }
537 default:
538 UNREACHABLE();
539 }
540
541 if (wback) {
542 if (postindex) {
543 address += offset;
544 }
545
546 if (Rn == Reg::SP) {
547 this->SetSp(address);
548 } else {
549 this->SetReg(Rn, address);
550 }
551 }
552
553 return true;
554}
555
556bool InterpreterVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
557 bool not_postindex, Reg Rn, Vec Vt) {
558 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
559 if (scale > 4) {
560 // Unallocated encoding
561 return false;
562 }
563
564 const bool wback = true;
565 const bool postindex = !not_postindex;
566 const u64 offset = imm9.SignExtend<u64>();
567
568 return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
569}
570
571bool InterpreterVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
572 Vec Vt) {
573 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
574 if (scale > 4) {
575 // Unallocated encoding
576 return false;
577 }
578
579 const bool wback = false;
580 const bool postindex = false;
581 const u64 offset = imm12.ZeroExtend<u64>() << scale;
582
583 return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
584}
585
586bool InterpreterVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
587 bool not_postindex, Reg Rn, Vec Vt) {
588 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
589 if (scale > 4) {
590 // Unallocated encoding
591 return false;
592 }
593
594 const bool wback = true;
595 const bool postindex = !not_postindex;
596 const u64 offset = imm9.SignExtend<u64>();
597
598 return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
599}
600
601bool InterpreterVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
602 Vec Vt) {
603 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
604 if (scale > 4) {
605 // Unallocated encoding
606 return false;
607 }
608
609 const bool wback = false;
610 const bool postindex = false;
611 const u64 offset = imm12.ZeroExtend<u64>() << scale;
612
613 return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
614}
615
616bool InterpreterVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
617 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
618 if (scale > 4) {
619 // Unallocated encoding
620 return false;
621 }
622
623 const bool wback = false;
624 const bool postindex = false;
625 const u64 offset = imm9.SignExtend<u64>();
626
627 return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
628}
629
630bool InterpreterVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
631 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
632 if (scale > 4) {
633 // Unallocated encoding
634 return false;
635 }
636
637 const bool wback = false;
638 const bool postindex = false;
639 const u64 offset = imm9.SignExtend<u64>();
640
641 return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
642}
643
644bool InterpreterVisitor::RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1,
645 Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) {
646 MemOp memop;
647 size_t regsize = 64;
648 bool signed_ = false;
649
650 if (opc_1 == 0) {
651 memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
652 regsize = size == 0b11 ? 64 : 32;
653 signed_ = false;
654 } else if (size == 0b11) {
655 memop = MemOp::Prefetch;
656 if (opc_0 == 1) {
657 // Unallocated encoding
658 return false;
659 }
660 } else {
661 memop = MemOp::Load;
662 if (size == 0b10 && opc_0 == 1) {
663 // Unallocated encoding
664 return false;
665 }
666 regsize = opc_0 == 1 ? 32 : 64;
667 signed_ = true;
668 }
669
670 const size_t datasize = 8 << scale;
671
672 // Operation
673 const u64 offset = this->ExtendReg(64, Rm, option, shift);
674
675 u64 address;
676 if (Rn == Reg::SP) {
677 address = this->GetSp();
678 } else {
679 address = this->GetReg(Rn);
680 }
681 address += offset;
682
683 switch (memop) {
684 case MemOp::Store: {
685 u64 data = this->GetReg(Rt);
686 m_memory.WriteBlock(address, &data, datasize / 8);
687 break;
688 }
689 case MemOp::Load: {
690 u64 data = 0;
691 m_memory.ReadBlock(address, &data, datasize / 8);
692 if (signed_) {
693 this->SetReg(Rt, SignExtend(data, datasize, regsize));
694 } else {
695 this->SetReg(Rt, data);
696 }
697 break;
698 }
699 case MemOp::Prefetch:
700 break;
701 }
702
703 return true;
704}
705
706bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
707 Reg Rt) {
708 const Imm<1> opc_0{0};
709 const size_t scale = size.ZeroExtend<size_t>();
710 const u8 shift = S ? static_cast<u8>(scale) : 0;
711 if (!option.Bit<1>()) {
712 // Unallocated encoding
713 return false;
714 }
715 return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
716}
717
718bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
719 Reg Rt) {
720 const Imm<1> opc_0{1};
721 const size_t scale = size.ZeroExtend<size_t>();
722 const u8 shift = S ? static_cast<u8>(scale) : 0;
723 if (!option.Bit<1>()) {
724 // Unallocated encoding
725 return false;
726 }
727 return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
728}
729
730bool InterpreterVisitor::SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option,
731 Reg Rn, Vec Vt) {
732 const auto memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
733 const size_t datasize = 8 << scale;
734
735 // Operation
736 const u64 offset = this->ExtendReg(64, Rm, option, shift);
737
738 u64 address;
739 if (Rn == Reg::SP) {
740 address = this->GetSp();
741 } else {
742 address = this->GetReg(Rn);
743 }
744 address += offset;
745
746 switch (memop) {
747 case MemOp::Store: {
748 u128 data = VectorGetElement(this->GetVec(Vt), datasize);
749 m_memory.WriteBlock(address, &data, datasize / 8);
750 break;
751 }
752 case MemOp::Load: {
753 u128 data{};
754 m_memory.ReadBlock(address, &data, datasize / 8);
755 this->SetVec(Vt, data);
756 break;
757 }
758 default:
759 UNREACHABLE();
760 }
761
762 return true;
763}
764
765bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
766 Reg Rn, Vec Vt) {
767 const Imm<1> opc_0{0};
768 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
769 if (scale > 4) {
770 // Unallocated encoding
771 return false;
772 }
773 const u8 shift = S ? static_cast<u8>(scale) : 0;
774 if (!option.Bit<1>()) {
775 // Unallocated encoding
776 return false;
777 }
778 return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
779}
780
781bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
782 Reg Rn, Vec Vt) {
783 const Imm<1> opc_0{1};
784 const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
785 if (scale > 4) {
786 // Unallocated encoding
787 return false;
788 }
789 const u8 shift = S ? static_cast<u8>(scale) : 0;
790 if (!option.Bit<1>()) {
791 // Unallocated encoding
792 return false;
793 }
794 return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
795}
796
797std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
798 fpsimd_context* fpsimd_context) {
799 // Construct the interpreter.
800 std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
801 std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
802 u64& sp = *reinterpret_cast<u64*>(&context->sp);
803 const u64& pc = *reinterpret_cast<u64*>(&context->pc);
804
805 InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
806
807 // Read the instruction at the program counter.
808 u32 instruction = memory.Read32(pc);
809 bool was_executed = false;
810
811 // Interpret the instruction.
812 if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
813 was_executed = decoder->get().call(visitor, instruction);
814 } else {
815 LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
816 }
817
818 if (was_executed) {
819 return pc + 4;
820 }
821
822 return std::nullopt;
823}
824
825} // namespace Core
diff --git a/src/core/arm/nce/interpreter_visitor.h b/src/core/arm/nce/interpreter_visitor.h
new file mode 100644
index 000000000..f90d876ab
--- /dev/null
+++ b/src/core/arm/nce/interpreter_visitor.h
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#pragma once
6
7#include <signal.h>
8#include <unistd.h>
9
10#include "core/arm/nce/visitor_base.h"
11
12namespace Core {
13
14namespace Memory {
15class Memory;
16}
17
18class InterpreterVisitor final : public VisitorBase {
19public:
20 explicit InterpreterVisitor(Core::Memory::Memory& memory, std::span<u64, 31> regs,
21 std::span<u128, 32> fpsimd_regs, u64& sp, const u64& pc)
22 : m_memory(memory), m_regs(regs), m_fpsimd_regs(fpsimd_regs), m_sp(sp), m_pc(pc) {}
23 ~InterpreterVisitor() override = default;
24
25 enum class MemOp {
26 Load,
27 Store,
28 Prefetch,
29 };
30
31 u128 GetVec(Vec v);
32 u64 GetReg(Reg r);
33 u64 GetSp();
34 u64 GetPc();
35
36 void SetVec(Vec v, u128 value);
37 void SetReg(Reg r, u64 value);
38 void SetSp(u64 value);
39
40 u64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift);
41
42 // Loads and stores - Load/Store Exclusive
43 bool Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt);
44 bool STLLR(Imm<2> size, Reg Rn, Reg Rt) override;
45 bool STLR(Imm<2> size, Reg Rn, Reg Rt) override;
46 bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) override;
47 bool LDAR(Imm<2> size, Reg Rn, Reg Rt) override;
48
49 // Loads and stores - Load register (literal)
50 bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) override;
51 bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) override;
52
53 // Loads and stores - Load/Store register pair
54 bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2,
55 Reg Rn, Reg Rt) override;
56 bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2,
57 Reg Rn, Vec Vt) override;
58
59 // Loads and stores - Load/Store register (immediate)
60 bool RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size,
61 Imm<2> opc, Reg Rn, Reg Rt);
62 bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn,
63 Reg Rt) override;
64 bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) override;
65 bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) override;
66
67 bool SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, MemOp memop, Reg Rn,
68 Vec Vt);
69 bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
70 Vec Vt) override;
71 bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
72 bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
73 Vec Vt) override;
74 bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
75 bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
76 bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
77
78 // Loads and stores - Load/Store register (register offset)
79 bool RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm,
80 Imm<3> option, Reg Rn, Reg Rt);
81 bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
82 Reg Rt) override;
83 bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
84 Reg Rt) override;
85
86 bool SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt);
87 bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
88 Vec Vt) override;
89 bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
90 Vec Vt) override;
91
92private:
93 Core::Memory::Memory& m_memory;
94 std::span<u64, 31> m_regs;
95 std::span<u128, 32> m_fpsimd_regs;
96 u64& m_sp;
97 const u64& m_pc;
98};
99
100std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
101 fpsimd_context* fpsimd_context);
102
103} // namespace Core
diff --git a/src/core/arm/nce/visitor_base.h b/src/core/arm/nce/visitor_base.h
new file mode 100644
index 000000000..8fb032912
--- /dev/null
+++ b/src/core/arm/nce/visitor_base.h
@@ -0,0 +1,2777 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#pragma once
6
7#include <dynarmic/frontend/A64/a64_types.h>
8#include <dynarmic/frontend/imm.h>
9
10namespace Core {
11
12class VisitorBase {
13public:
14 using instruction_return_type = bool;
15
16 template <size_t BitSize>
17 using Imm = Dynarmic::Imm<BitSize>;
18 using Reg = Dynarmic::A64::Reg;
19 using Vec = Dynarmic::A64::Vec;
20 using Cond = Dynarmic::A64::Cond;
21
22 virtual ~VisitorBase() {}
23
24 virtual bool UnallocatedEncoding() {
25 return false;
26 }
27
28 // Data processing - Immediate - PC relative addressing
29 virtual bool ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd) {
30 return false;
31 }
32 virtual bool ADRP(Imm<2> immlo, Imm<19> immhi, Reg Rd) {
33 return false;
34 }
35
36 // Data processing - Immediate - Add/Sub (with tag)
37 virtual bool ADDG(Imm<6> offset_imm, Imm<4> tag_offset, Reg Rn, Reg Rd) {
38 return false;
39 }
40 virtual bool SUBG(Imm<6> offset_imm, Imm<4> tag_offset, Reg Rn, Reg Rd) {
41 return false;
42 }
43
44 // Data processing - Immediate - Add/Sub
45 virtual bool ADD_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
46 return false;
47 }
48 virtual bool ADDS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
49 return false;
50 }
51 virtual bool SUB_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
52 return false;
53 }
54 virtual bool SUBS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
55 return false;
56 }
57
58 // Data processing - Immediate - Logical
59 virtual bool AND_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
60 return false;
61 }
62 virtual bool ORR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
63 return false;
64 }
65 virtual bool EOR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
66 return false;
67 }
68 virtual bool ANDS_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
69 return false;
70 }
71
72 // Data processing - Immediate - Move Wide
73 virtual bool MOVN(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
74 return false;
75 }
76 virtual bool MOVZ(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
77 return false;
78 }
79 virtual bool MOVK(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
80 return false;
81 }
82
83 // Data processing - Immediate - Bitfield
84 virtual bool SBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
85 return false;
86 }
87 virtual bool BFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
88 return false;
89 }
90 virtual bool UBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
91 return false;
92 }
93 virtual bool ASR_1(Imm<5> immr, Reg Rn, Reg Rd) {
94 return false;
95 }
96 virtual bool ASR_2(Imm<6> immr, Reg Rn, Reg Rd) {
97 return false;
98 }
99 virtual bool SXTB_1(Reg Rn, Reg Rd) {
100 return false;
101 }
102 virtual bool SXTB_2(Reg Rn, Reg Rd) {
103 return false;
104 }
105 virtual bool SXTH_1(Reg Rn, Reg Rd) {
106 return false;
107 }
108 virtual bool SXTH_2(Reg Rn, Reg Rd) {
109 return false;
110 }
111 virtual bool SXTW(Reg Rn, Reg Rd) {
112 return false;
113 }
114
115 // Data processing - Immediate - Extract
116 virtual bool EXTR(bool sf, bool N, Reg Rm, Imm<6> imms, Reg Rn, Reg Rd) {
117 return false;
118 }
119
120 // Conditional branch
121 virtual bool B_cond(Imm<19> imm19, Cond cond) {
122 return false;
123 }
124
125 // Exception generation
126 virtual bool SVC(Imm<16> imm16) {
127 return false;
128 }
129 virtual bool HVC(Imm<16> imm16) {
130 return false;
131 }
132 virtual bool SMC(Imm<16> imm16) {
133 return false;
134 }
135 virtual bool BRK(Imm<16> imm16) {
136 return false;
137 }
138 virtual bool HLT(Imm<16> imm16) {
139 return false;
140 }
141 virtual bool DCPS1(Imm<16> imm16) {
142 return false;
143 }
144 virtual bool DCPS2(Imm<16> imm16) {
145 return false;
146 }
147 virtual bool DCPS3(Imm<16> imm16) {
148 return false;
149 }
150
151 // System
152 virtual bool MSR_imm(Imm<3> op1, Imm<4> CRm, Imm<3> op2) {
153 return false;
154 }
155 virtual bool HINT(Imm<4> CRm, Imm<3> op2) {
156 return false;
157 }
158 virtual bool NOP() {
159 return false;
160 }
161 virtual bool YIELD() {
162 return false;
163 }
164 virtual bool WFE() {
165 return false;
166 }
167 virtual bool WFI() {
168 return false;
169 }
170 virtual bool SEV() {
171 return false;
172 }
173 virtual bool SEVL() {
174 return false;
175 }
176 virtual bool XPAC_1(bool D, Reg Rd) {
177 return false;
178 }
179 virtual bool XPAC_2() {
180 return false;
181 }
182 virtual bool PACIA_1(bool Z, Reg Rn, Reg Rd) {
183 return false;
184 }
185 virtual bool PACIA_2() {
186 return false;
187 }
188 virtual bool PACIB_1(bool Z, Reg Rn, Reg Rd) {
189 return false;
190 }
191 virtual bool PACIB_2() {
192 return false;
193 }
194 virtual bool AUTIA_1(bool Z, Reg Rn, Reg Rd) {
195 return false;
196 }
197 virtual bool AUTIA_2() {
198 return false;
199 }
200 virtual bool AUTIB_1(bool Z, Reg Rn, Reg Rd) {
201 return false;
202 }
203 virtual bool AUTIB_2() {
204 return false;
205 }
206 virtual bool BTI(Imm<2> upper_op2) {
207 return false;
208 }
209 virtual bool ESB() {
210 return false;
211 }
212 virtual bool PSB() {
213 return false;
214 }
215 virtual bool TSB() {
216 return false;
217 }
218 virtual bool CSDB() {
219 return false;
220 }
221 virtual bool CLREX(Imm<4> CRm) {
222 return false;
223 }
224 virtual bool DSB(Imm<4> CRm) {
225 return false;
226 }
227 virtual bool SSBB() {
228 return false;
229 }
230 virtual bool PSSBB() {
231 return false;
232 }
233 virtual bool DMB(Imm<4> CRm) {
234 return false;
235 }
236 virtual bool ISB(Imm<4> CRm) {
237 return false;
238 }
239 virtual bool SYS(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
240 return false;
241 }
242 virtual bool SB() {
243 return false;
244 }
245 virtual bool MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
246 return false;
247 }
248 virtual bool SYSL(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
249 return false;
250 }
251 virtual bool MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
252 return false;
253 }
254
255 // System - Flag manipulation instructions
256 virtual bool CFINV() {
257 return false;
258 }
259 virtual bool RMIF(Imm<6> lsb, Reg Rn, Imm<4> mask) {
260 return false;
261 }
262 virtual bool SETF8(Reg Rn) {
263 return false;
264 }
265 virtual bool SETF16(Reg Rn) {
266 return false;
267 }
268
269 // System - Flag format instructions
270 virtual bool XAFlag() {
271 return false;
272 }
273 virtual bool AXFlag() {
274 return false;
275 }
276
277 // SYS: Data Cache
278 virtual bool DC_IVAC(Reg Rt) {
279 return false;
280 }
281 virtual bool DC_ISW(Reg Rt) {
282 return false;
283 }
284 virtual bool DC_CSW(Reg Rt) {
285 return false;
286 }
287 virtual bool DC_CISW(Reg Rt) {
288 return false;
289 }
290 virtual bool DC_ZVA(Reg Rt) {
291 return false;
292 }
293 virtual bool DC_CVAC(Reg Rt) {
294 return false;
295 }
296 virtual bool DC_CVAU(Reg Rt) {
297 return false;
298 }
299 virtual bool DC_CVAP(Reg Rt) {
300 return false;
301 }
302 virtual bool DC_CIVAC(Reg Rt) {
303 return false;
304 }
305
306 // SYS: Instruction Cache
307 virtual bool IC_IALLU() {
308 return false;
309 }
310 virtual bool IC_IALLUIS() {
311 return false;
312 }
313 virtual bool IC_IVAU(Reg Rt) {
314 return false;
315 }
316
317 // Unconditional branch (Register)
318 virtual bool BR(Reg Rn) {
319 return false;
320 }
321 virtual bool BRA(bool Z, bool M, Reg Rn, Reg Rm) {
322 return false;
323 }
324 virtual bool BLR(Reg Rn) {
325 return false;
326 }
327 virtual bool BLRA(bool Z, bool M, Reg Rn, Reg Rm) {
328 return false;
329 }
330 virtual bool RET(Reg Rn) {
331 return false;
332 }
333 virtual bool RETA(bool M) {
334 return false;
335 }
336 virtual bool ERET() {
337 return false;
338 }
339 virtual bool ERETA(bool M) {
340 return false;
341 }
342 virtual bool DRPS() {
343 return false;
344 }
345
346 // Unconditional branch (immediate)
347 virtual bool B_uncond(Imm<26> imm26) {
348 return false;
349 }
350 virtual bool BL(Imm<26> imm26) {
351 return false;
352 }
353
354 // Compare and branch (immediate)
355 virtual bool CBZ(bool sf, Imm<19> imm19, Reg Rt) {
356 return false;
357 }
358 virtual bool CBNZ(bool sf, Imm<19> imm19, Reg Rt) {
359 return false;
360 }
361 virtual bool TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
362 return false;
363 }
364 virtual bool TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
365 return false;
366 }
367
368 // Loads and stores - Advanced SIMD Load/Store multiple structures
369 virtual bool STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
370 return false;
371 }
372 virtual bool STx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
373 return false;
374 }
375 virtual bool LDx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
376 return false;
377 }
378 virtual bool LDx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
379 return false;
380 }
381
382 // Loads and stores - Advanced SIMD Load/Store single structures
383 virtual bool ST1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
384 return false;
385 }
386 virtual bool ST1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
387 Vec Vt) {
388 return false;
389 }
390 virtual bool ST3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
391 return false;
392 }
393 virtual bool ST3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
394 Vec Vt) {
395 return false;
396 }
397 virtual bool ST2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
398 return false;
399 }
400 virtual bool ST2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
401 Vec Vt) {
402 return false;
403 }
404 virtual bool ST4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
405 return false;
406 }
407 virtual bool ST4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
408 Vec Vt) {
409 return false;
410 }
411 virtual bool LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
412 return false;
413 }
414 virtual bool LD1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
415 Vec Vt) {
416 return false;
417 }
418 virtual bool LD3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
419 return false;
420 }
421 virtual bool LD3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
422 Vec Vt) {
423 return false;
424 }
425 virtual bool LD1R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
426 return false;
427 }
428 virtual bool LD1R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
429 return false;
430 }
431 virtual bool LD3R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
432 return false;
433 }
434 virtual bool LD3R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
435 return false;
436 }
437 virtual bool LD2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
438 return false;
439 }
440 virtual bool LD2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
441 Vec Vt) {
442 return false;
443 }
444 virtual bool LD4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
445 return false;
446 }
447 virtual bool LD4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn,
448 Vec Vt) {
449 return false;
450 }
451 virtual bool LD2R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
452 return false;
453 }
454 virtual bool LD2R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
455 return false;
456 }
457 virtual bool LD4R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
458 return false;
459 }
460 virtual bool LD4R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
461 return false;
462 }
463
464 // Loads and stores - Load/Store Exclusive
465 virtual bool STXR(Imm<2> size, Reg Rs, Reg Rn, Reg Rt) {
466 return false;
467 }
468 virtual bool STLXR(Imm<2> size, Reg Rs, Reg Rn, Reg Rt) {
469 return false;
470 }
471 virtual bool STXP(Imm<1> size, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) {
472 return false;
473 }
474 virtual bool STLXP(Imm<1> size, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) {
475 return false;
476 }
477 virtual bool LDXR(Imm<2> size, Reg Rn, Reg Rt) {
478 return false;
479 }
480 virtual bool LDAXR(Imm<2> size, Reg Rn, Reg Rt) {
481 return false;
482 }
483 virtual bool LDXP(Imm<1> size, Reg Rt2, Reg Rn, Reg Rt) {
484 return false;
485 }
486 virtual bool LDAXP(Imm<1> size, Reg Rt2, Reg Rn, Reg Rt) {
487 return false;
488 }
489 virtual bool STLLR(Imm<2> size, Reg Rn, Reg Rt) {
490 return false;
491 }
492 virtual bool STLR(Imm<2> size, Reg Rn, Reg Rt) {
493 return false;
494 }
495 virtual bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) {
496 return false;
497 }
498 virtual bool LDAR(Imm<2> size, Reg Rn, Reg Rt) {
499 return false;
500 }
501 virtual bool CASP(bool sz, bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) {
502 return false;
503 }
504 virtual bool CASB(bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) {
505 return false;
506 }
507 virtual bool CASH(bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) {
508 return false;
509 }
510 virtual bool CAS(bool sz, bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) {
511 return false;
512 }
513
514 // Loads and stores - Load register (literal)
515 virtual bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
516 return false;
517 }
518 virtual bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
519 return false;
520 }
521 virtual bool LDRSW_lit(Imm<19> imm19, Reg Rt) {
522 return false;
523 }
524 virtual bool PRFM_lit(Imm<19> imm19, Imm<5> prfop) {
525 return false;
526 }
527
528 // Loads and stores - Load/Store no-allocate pair
529 virtual bool STNP_LDNP_gen(Imm<1> upper_opc, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
530 return false;
531 }
532 virtual bool STNP_LDNP_fpsimd(Imm<2> opc, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
533 return false;
534 }
535
536 // Loads and stores - Load/Store register pair
537 virtual bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7,
538 Reg Rt2, Reg Rn, Reg Rt) {
539 return false;
540 }
541 virtual bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7,
542 Vec Vt2, Reg Rn, Vec Vt) {
543 return false;
544 }
545 virtual bool STGP_1(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) {
546 return false;
547 }
548 virtual bool STGP_2(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) {
549 return false;
550 }
551 virtual bool STGP_3(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) {
552 return false;
553 }
554
555 // Loads and stores - Load/Store register (immediate)
556 virtual bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn,
557 Reg Rt) {
558 return false;
559 }
560 virtual bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
561 return false;
562 }
563 virtual bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
564 return false;
565 }
566 virtual bool PRFM_imm(Imm<12> imm12, Reg Rn, Reg Rt) {
567 return false;
568 }
569 virtual bool PRFM_unscaled_imm(Imm<9> imm9, Reg Rn, Reg Rt) {
570 return false;
571 }
572 virtual bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex,
573 Reg Rn, Vec Vt) {
574 return false;
575 }
576 virtual bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) {
577 return false;
578 }
579 virtual bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex,
580 Reg Rn, Vec Vt) {
581 return false;
582 }
583 virtual bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) {
584 return false;
585 }
586 virtual bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
587 return false;
588 }
589 virtual bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
590 return false;
591 }
592
593 // Loads and stores - Load/Store register (unprivileged)
594 virtual bool STTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
595 return false;
596 }
597 virtual bool LDTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
598 return false;
599 }
600 virtual bool LDTRSB(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
601 return false;
602 }
603 virtual bool STTRH(Imm<9> imm9, Reg Rn, Reg Rt) {
604 return false;
605 }
606 virtual bool LDTRH(Imm<9> imm9, Reg Rn, Reg Rt) {
607 return false;
608 }
609 virtual bool LDTRSH(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
610 return false;
611 }
612 virtual bool STTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) {
613 return false;
614 }
615 virtual bool LDTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) {
616 return false;
617 }
618 virtual bool LDTRSW(Imm<9> imm9, Reg Rn, Reg Rt) {
619 return false;
620 }
621
622 // Loads and stores - Atomic memory options
623 virtual bool LDADDB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
624 return false;
625 }
626 virtual bool LDCLRB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
627 return false;
628 }
629 virtual bool LDEORB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
630 return false;
631 }
632 virtual bool LDSETB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
633 return false;
634 }
635 virtual bool LDSMAXB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
636 return false;
637 }
638 virtual bool LDSMINB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
639 return false;
640 }
641 virtual bool LDUMAXB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
642 return false;
643 }
644 virtual bool LDUMINB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
645 return false;
646 }
647 virtual bool SWPB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
648 return false;
649 }
650 virtual bool LDAPRB(Reg Rn, Reg Rt) {
651 return false;
652 }
653 virtual bool LDADDH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
654 return false;
655 }
656 virtual bool LDCLRH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
657 return false;
658 }
659 virtual bool LDEORH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
660 return false;
661 }
662 virtual bool LDSETH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
663 return false;
664 }
665 virtual bool LDSMAXH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
666 return false;
667 }
668 virtual bool LDSMINH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
669 return false;
670 }
671 virtual bool LDUMAXH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
672 return false;
673 }
674 virtual bool LDUMINH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
675 return false;
676 }
677 virtual bool SWPH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
678 return false;
679 }
680 virtual bool LDAPRH(Reg Rn, Reg Rt) {
681 return false;
682 }
683 virtual bool LDADD(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
684 return false;
685 }
686 virtual bool LDCLR(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
687 return false;
688 }
689 virtual bool LDEOR(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
690 return false;
691 }
692 virtual bool LDSET(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
693 return false;
694 }
695 virtual bool LDSMAX(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
696 return false;
697 }
698 virtual bool LDSMIN(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
699 return false;
700 }
701 virtual bool LDUMAX(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
702 return false;
703 }
704 virtual bool LDUMIN(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
705 return false;
706 }
707 virtual bool SWP(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) {
708 return false;
709 }
710 virtual bool LDAPR(Reg Rn, Reg Rt) {
711 return false;
712 }
713
714 // Loads and stores - Load/Store register (register offset)
715 virtual bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
716 Reg Rt) {
717 return false;
718 }
719 virtual bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
720 Reg Rt) {
721 return false;
722 }
723 virtual bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
724 Vec Vt) {
725 return false;
726 }
727 virtual bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
728 Vec Vt) {
729 return false;
730 }
731
732 // Loads and stores - Load/Store memory tags
733 virtual bool STG_1(Imm<9> imm9, Reg Rn) {
734 return false;
735 }
736 virtual bool STG_2(Imm<9> imm9, Reg Rn) {
737 return false;
738 }
739 virtual bool STG_3(Imm<9> imm9, Reg Rn) {
740 return false;
741 }
742 virtual bool LDG(Imm<9> offset_imm, Reg Rn, Reg Rt) {
743 return false;
744 }
745 virtual bool STZG_1(Imm<9> offset_imm, Reg Rn) {
746 return false;
747 }
748 virtual bool STZG_2(Imm<9> offset_imm, Reg Rn) {
749 return false;
750 }
751 virtual bool STZG_3(Imm<9> offset_imm, Reg Rn) {
752 return false;
753 }
754 virtual bool ST2G_1(Imm<9> offset_imm, Reg Rn) {
755 return false;
756 }
757 virtual bool ST2G_2(Imm<9> offset_imm, Reg Rn) {
758 return false;
759 }
760 virtual bool ST2G_3(Imm<9> offset_imm, Reg Rn) {
761 return false;
762 }
763 virtual bool STGV(Reg Rn, Reg Rt) {
764 return false;
765 }
766 virtual bool STZ2G_1(Imm<9> offset_imm, Reg Rn) {
767 return false;
768 }
769 virtual bool STZ2G_2(Imm<9> offset_imm, Reg Rn) {
770 return false;
771 }
772 virtual bool STZ2G_3(Imm<9> offset_imm, Reg Rn) {
773 return false;
774 }
775 virtual bool LDGV(Reg Rn, Reg Rt) {
776 return false;
777 }
778
779 // Loads and stores - Load/Store register (pointer authentication)
780 virtual bool LDRA(bool M, bool S, Imm<9> imm9, bool W, Reg Rn, Reg Rt) {
781 return false;
782 }
783
784 // Data Processing - Register - 2 source
785 virtual bool UDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
786 return false;
787 }
788 virtual bool SDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
789 return false;
790 }
791 virtual bool LSLV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
792 return false;
793 }
794 virtual bool LSRV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
795 return false;
796 }
797 virtual bool ASRV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
798 return false;
799 }
800 virtual bool RORV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
801 return false;
802 }
803 virtual bool CRC32(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) {
804 return false;
805 }
806 virtual bool CRC32C(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) {
807 return false;
808 }
809 virtual bool PACGA(Reg Rm, Reg Rn, Reg Rd) {
810 return false;
811 }
812 virtual bool SUBP(Reg Rm, Reg Rn, Reg Rd) {
813 return false;
814 }
815 virtual bool IRG(Reg Rm, Reg Rn, Reg Rd) {
816 return false;
817 }
818 virtual bool GMI(Reg Rm, Reg Rn, Reg Rd) {
819 return false;
820 }
821 virtual bool SUBPS(Reg Rm, Reg Rn, Reg Rd) {
822 return false;
823 }
824
825 // Data Processing - Register - 1 source
826 virtual bool RBIT_int(bool sf, Reg Rn, Reg Rd) {
827 return false;
828 }
829 virtual bool REV16_int(bool sf, Reg Rn, Reg Rd) {
830 return false;
831 }
832 virtual bool REV(bool sf, bool opc_0, Reg Rn, Reg Rd) {
833 return false;
834 }
835 virtual bool CLZ_int(bool sf, Reg Rn, Reg Rd) {
836 return false;
837 }
838 virtual bool CLS_int(bool sf, Reg Rn, Reg Rd) {
839 return false;
840 }
841 virtual bool REV32_int(Reg Rn, Reg Rd) {
842 return false;
843 }
844 virtual bool PACDA(bool Z, Reg Rn, Reg Rd) {
845 return false;
846 }
847 virtual bool PACDB(bool Z, Reg Rn, Reg Rd) {
848 return false;
849 }
850 virtual bool AUTDA(bool Z, Reg Rn, Reg Rd) {
851 return false;
852 }
853 virtual bool AUTDB(bool Z, Reg Rn, Reg Rd) {
854 return false;
855 }
856
857 // Data Processing - Register - Logical (shifted register)
858 virtual bool AND_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
859 return false;
860 }
861 virtual bool BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
862 return false;
863 }
864 virtual bool ORR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
865 return false;
866 }
867 virtual bool ORN_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
868 return false;
869 }
870 virtual bool EOR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
871 return false;
872 }
873 virtual bool EON(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
874 return false;
875 }
876 virtual bool ANDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
877 return false;
878 }
879 virtual bool BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
880 return false;
881 }
882
883 // Data Processing - Register - Add/Sub (shifted register)
884 virtual bool ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
885 return false;
886 }
887 virtual bool ADDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
888 return false;
889 }
890 virtual bool SUB_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
891 return false;
892 }
893 virtual bool SUBS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
894 return false;
895 }
896
897 // Data Processing - Register - Add/Sub (shifted register)
898 virtual bool ADD_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
899 return false;
900 }
901 virtual bool ADDS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
902 return false;
903 }
904 virtual bool SUB_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
905 return false;
906 }
907 virtual bool SUBS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
908 return false;
909 }
910
911 // Data Processing - Register - Add/Sub (with carry)
912 virtual bool ADC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
913 return false;
914 }
915 virtual bool ADCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
916 return false;
917 }
918 virtual bool SBC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
919 return false;
920 }
921 virtual bool SBCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
922 return false;
923 }
924
925 // Data Processing - Register - Conditional compare
926 virtual bool CCMN_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) {
927 return false;
928 }
929 virtual bool CCMP_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) {
930 return false;
931 }
932 virtual bool CCMN_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) {
933 return false;
934 }
935 virtual bool CCMP_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) {
936 return false;
937 }
938
939 // Data Processing - Register - Conditional select
940 virtual bool CSEL(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
941 return false;
942 }
943 virtual bool CSINC(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
944 return false;
945 }
946 virtual bool CSINV(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
947 return false;
948 }
949 virtual bool CSNEG(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
950 return false;
951 }
952
953 // Data Processing - Register - 3 source
954 virtual bool MADD(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
955 return false;
956 }
957 virtual bool MSUB(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
958 return false;
959 }
960 virtual bool SMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
961 return false;
962 }
963 virtual bool SMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
964 return false;
965 }
966 virtual bool SMULH(Reg Rm, Reg Rn, Reg Rd) {
967 return false;
968 }
969 virtual bool UMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
970 return false;
971 }
972 virtual bool UMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
973 return false;
974 }
975 virtual bool UMULH(Reg Rm, Reg Rn, Reg Rd) {
976 return false;
977 }
978
979 // Data Processing - FP and SIMD - AES
980 virtual bool AESE(Vec Vn, Vec Vd) {
981 return false;
982 }
983 virtual bool AESD(Vec Vn, Vec Vd) {
984 return false;
985 }
986 virtual bool AESMC(Vec Vn, Vec Vd) {
987 return false;
988 }
989 virtual bool AESIMC(Vec Vn, Vec Vd) {
990 return false;
991 }
992
993 // Data Processing - FP and SIMD - SHA
994 virtual bool SHA1C(Vec Vm, Vec Vn, Vec Vd) {
995 return false;
996 }
997 virtual bool SHA1P(Vec Vm, Vec Vn, Vec Vd) {
998 return false;
999 }
1000 virtual bool SHA1M(Vec Vm, Vec Vn, Vec Vd) {
1001 return false;
1002 }
1003 virtual bool SHA1SU0(Vec Vm, Vec Vn, Vec Vd) {
1004 return false;
1005 }
1006 virtual bool SHA256H(Vec Vm, Vec Vn, Vec Vd) {
1007 return false;
1008 }
1009 virtual bool SHA256H2(Vec Vm, Vec Vn, Vec Vd) {
1010 return false;
1011 }
1012 virtual bool SHA256SU1(Vec Vm, Vec Vn, Vec Vd) {
1013 return false;
1014 }
1015 virtual bool SHA1H(Vec Vn, Vec Vd) {
1016 return false;
1017 }
1018 virtual bool SHA1SU1(Vec Vn, Vec Vd) {
1019 return false;
1020 }
1021 virtual bool SHA256SU0(Vec Vn, Vec Vd) {
1022 return false;
1023 }
1024
1025 // Data Processing - FP and SIMD - Scalar copy
1026 virtual bool DUP_elt_1(Imm<5> imm5, Vec Vn, Vec Vd) {
1027 return false;
1028 }
1029
1030 // Data Processing - FP and SIMD - Scalar three
1031 virtual bool FMULX_vec_1(Vec Vm, Vec Vn, Vec Vd) {
1032 return false;
1033 }
1034 virtual bool FMULX_vec_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1035 return false;
1036 }
1037 virtual bool FCMEQ_reg_1(Vec Vm, Vec Vn, Vec Vd) {
1038 return false;
1039 }
1040 virtual bool FCMEQ_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1041 return false;
1042 }
1043 virtual bool FRECPS_1(Vec Vm, Vec Vn, Vec Vd) {
1044 return false;
1045 }
1046 virtual bool FRECPS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1047 return false;
1048 }
1049 virtual bool FRSQRTS_1(Vec Vm, Vec Vn, Vec Vd) {
1050 return false;
1051 }
1052 virtual bool FRSQRTS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1053 return false;
1054 }
1055 virtual bool FCMGE_reg_1(Vec Vm, Vec Vn, Vec Vd) {
1056 return false;
1057 }
1058 virtual bool FCMGE_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1059 return false;
1060 }
1061 virtual bool FACGE_1(Vec Vm, Vec Vn, Vec Vd) {
1062 return false;
1063 }
1064 virtual bool FACGE_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1065 return false;
1066 }
1067 virtual bool FABD_1(Vec Vm, Vec Vn, Vec Vd) {
1068 return false;
1069 }
1070 virtual bool FABD_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1071 return false;
1072 }
1073 virtual bool FCMGT_reg_1(Vec Vm, Vec Vn, Vec Vd) {
1074 return false;
1075 }
1076 virtual bool FCMGT_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1077 return false;
1078 }
1079 virtual bool FACGT_1(Vec Vm, Vec Vn, Vec Vd) {
1080 return false;
1081 }
1082 virtual bool FACGT_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
1083 return false;
1084 }
1085
1086 // Data Processing - FP and SIMD - Two register misc FP16
1087 virtual bool FCVTNS_1(Vec Vn, Vec Vd) {
1088 return false;
1089 }
1090 virtual bool FCVTMS_1(Vec Vn, Vec Vd) {
1091 return false;
1092 }
1093 virtual bool FCVTAS_1(Vec Vn, Vec Vd) {
1094 return false;
1095 }
1096 virtual bool SCVTF_int_1(Vec Vn, Vec Vd) {
1097 return false;
1098 }
1099 virtual bool FCMGT_zero_1(Vec Vn, Vec Vd) {
1100 return false;
1101 }
1102 virtual bool FCMEQ_zero_1(Vec Vn, Vec Vd) {
1103 return false;
1104 }
1105 virtual bool FCMLT_1(Vec Vn, Vec Vd) {
1106 return false;
1107 }
1108 virtual bool FCVTPS_1(Vec Vn, Vec Vd) {
1109 return false;
1110 }
1111 virtual bool FCVTZS_int_1(Vec Vn, Vec Vd) {
1112 return false;
1113 }
1114 virtual bool FRECPE_1(Vec Vn, Vec Vd) {
1115 return false;
1116 }
1117 virtual bool FRECPX_1(Vec Vn, Vec Vd) {
1118 return false;
1119 }
1120 virtual bool FCVTNU_1(Vec Vn, Vec Vd) {
1121 return false;
1122 }
1123 virtual bool FCVTMU_1(Vec Vn, Vec Vd) {
1124 return false;
1125 }
1126 virtual bool FCVTAU_1(Vec Vn, Vec Vd) {
1127 return false;
1128 }
1129 virtual bool UCVTF_int_1(Vec Vn, Vec Vd) {
1130 return false;
1131 }
1132 virtual bool FCMGE_zero_1(Vec Vn, Vec Vd) {
1133 return false;
1134 }
1135 virtual bool FCMLE_1(Vec Vn, Vec Vd) {
1136 return false;
1137 }
1138 virtual bool FCVTPU_1(Vec Vn, Vec Vd) {
1139 return false;
1140 }
1141 virtual bool FCVTZU_int_1(Vec Vn, Vec Vd) {
1142 return false;
1143 }
1144 virtual bool FRSQRTE_1(Vec Vn, Vec Vd) {
1145 return false;
1146 }
1147
1148 // Data Processing - FP and SIMD - Two register misc
1149 virtual bool FCVTNS_2(bool sz, Vec Vn, Vec Vd) {
1150 return false;
1151 }
1152 virtual bool FCVTMS_2(bool sz, Vec Vn, Vec Vd) {
1153 return false;
1154 }
1155 virtual bool FCVTAS_2(bool sz, Vec Vn, Vec Vd) {
1156 return false;
1157 }
1158 virtual bool SCVTF_int_2(bool sz, Vec Vn, Vec Vd) {
1159 return false;
1160 }
1161 virtual bool FCMGT_zero_2(bool sz, Vec Vn, Vec Vd) {
1162 return false;
1163 }
1164 virtual bool FCMEQ_zero_2(bool sz, Vec Vn, Vec Vd) {
1165 return false;
1166 }
1167 virtual bool FCMLT_2(bool sz, Vec Vn, Vec Vd) {
1168 return false;
1169 }
1170 virtual bool FCVTPS_2(bool sz, Vec Vn, Vec Vd) {
1171 return false;
1172 }
1173 virtual bool FCVTZS_int_2(bool sz, Vec Vn, Vec Vd) {
1174 return false;
1175 }
1176 virtual bool FRECPE_2(bool sz, Vec Vn, Vec Vd) {
1177 return false;
1178 }
1179 virtual bool FRECPX_2(bool sz, Vec Vn, Vec Vd) {
1180 return false;
1181 }
1182 virtual bool FCVTNU_2(bool sz, Vec Vn, Vec Vd) {
1183 return false;
1184 }
1185 virtual bool FCVTMU_2(bool sz, Vec Vn, Vec Vd) {
1186 return false;
1187 }
1188 virtual bool FCVTAU_2(bool sz, Vec Vn, Vec Vd) {
1189 return false;
1190 }
1191 virtual bool UCVTF_int_2(bool sz, Vec Vn, Vec Vd) {
1192 return false;
1193 }
1194 virtual bool FCMGE_zero_2(bool sz, Vec Vn, Vec Vd) {
1195 return false;
1196 }
1197 virtual bool FCMLE_2(bool sz, Vec Vn, Vec Vd) {
1198 return false;
1199 }
1200 virtual bool FCVTPU_2(bool sz, Vec Vn, Vec Vd) {
1201 return false;
1202 }
1203 virtual bool FCVTZU_int_2(bool sz, Vec Vn, Vec Vd) {
1204 return false;
1205 }
1206 virtual bool FRSQRTE_2(bool sz, Vec Vn, Vec Vd) {
1207 return false;
1208 }
1209
1210 // Data Processing - FP and SIMD - Scalar two register misc FP16
1211 virtual bool FCVTNS_3(bool Q, Vec Vn, Vec Vd) {
1212 return false;
1213 }
1214 virtual bool FCVTMS_3(bool Q, Vec Vn, Vec Vd) {
1215 return false;
1216 }
1217 virtual bool FCVTAS_3(bool Q, Vec Vn, Vec Vd) {
1218 return false;
1219 }
1220 virtual bool SCVTF_int_3(bool Q, Vec Vn, Vec Vd) {
1221 return false;
1222 }
1223 virtual bool FCMGT_zero_3(bool Q, Vec Vn, Vec Vd) {
1224 return false;
1225 }
1226 virtual bool FCMEQ_zero_3(bool Q, Vec Vn, Vec Vd) {
1227 return false;
1228 }
1229 virtual bool FCMLT_3(bool Q, Vec Vn, Vec Vd) {
1230 return false;
1231 }
1232 virtual bool FCVTPS_3(bool Q, Vec Vn, Vec Vd) {
1233 return false;
1234 }
1235 virtual bool FCVTZS_int_3(bool Q, Vec Vn, Vec Vd) {
1236 return false;
1237 }
1238 virtual bool FRECPE_3(bool Q, Vec Vn, Vec Vd) {
1239 return false;
1240 }
1241 virtual bool FCVTNU_3(bool Q, Vec Vn, Vec Vd) {
1242 return false;
1243 }
1244 virtual bool FCVTMU_3(bool Q, Vec Vn, Vec Vd) {
1245 return false;
1246 }
1247 virtual bool FCVTAU_3(bool Q, Vec Vn, Vec Vd) {
1248 return false;
1249 }
1250 virtual bool UCVTF_int_3(bool Q, Vec Vn, Vec Vd) {
1251 return false;
1252 }
1253 virtual bool FCMGE_zero_3(bool Q, Vec Vn, Vec Vd) {
1254 return false;
1255 }
1256 virtual bool FCMLE_3(bool Q, Vec Vn, Vec Vd) {
1257 return false;
1258 }
1259 virtual bool FCVTPU_3(bool Q, Vec Vn, Vec Vd) {
1260 return false;
1261 }
1262 virtual bool FCVTZU_int_3(bool Q, Vec Vn, Vec Vd) {
1263 return false;
1264 }
1265 virtual bool FRSQRTE_3(bool Q, Vec Vn, Vec Vd) {
1266 return false;
1267 }
1268
1269 // Data Processing - FP and SIMD - Scalar two register misc
1270 virtual bool FCVTNS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1271 return false;
1272 }
1273 virtual bool FCVTMS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1274 return false;
1275 }
1276 virtual bool FCVTAS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1277 return false;
1278 }
1279 virtual bool SCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1280 return false;
1281 }
1282 virtual bool FCMGT_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1283 return false;
1284 }
1285 virtual bool FCMEQ_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1286 return false;
1287 }
1288 virtual bool FCMLT_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1289 return false;
1290 }
1291 virtual bool FCVTPS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1292 return false;
1293 }
1294 virtual bool FCVTZS_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1295 return false;
1296 }
1297 virtual bool FRECPE_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1298 return false;
1299 }
1300 virtual bool FCVTNU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1301 return false;
1302 }
1303 virtual bool FCVTMU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1304 return false;
1305 }
1306 virtual bool FCVTAU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1307 return false;
1308 }
1309 virtual bool UCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1310 return false;
1311 }
1312 virtual bool FCMGE_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1313 return false;
1314 }
1315 virtual bool FCMLE_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1316 return false;
1317 }
1318 virtual bool FCVTPU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1319 return false;
1320 }
1321 virtual bool FCVTZU_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1322 return false;
1323 }
1324 virtual bool FRSQRTE_4(bool Q, bool sz, Vec Vn, Vec Vd) {
1325 return false;
1326 }
1327
1328 // Data Processing - FP and SIMD - Scalar three same extra
1329 virtual bool SQRDMLAH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1330 return false;
1331 }
1332 virtual bool SQRDMLAH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1333 return false;
1334 }
1335 virtual bool SQRDMLSH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1336 return false;
1337 }
1338 virtual bool SQRDMLSH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1339 return false;
1340 }
1341
1342 // Data Processing - FP and SIMD - Scalar two-register misc
1343 virtual bool SUQADD_1(Imm<2> size, Vec Vn, Vec Vd) {
1344 return false;
1345 }
1346 virtual bool SQABS_1(Imm<2> size, Vec Vn, Vec Vd) {
1347 return false;
1348 }
1349 virtual bool CMGT_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
1350 return false;
1351 }
1352 virtual bool CMEQ_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
1353 return false;
1354 }
1355 virtual bool CMLT_1(Imm<2> size, Vec Vn, Vec Vd) {
1356 return false;
1357 }
1358 virtual bool ABS_1(Imm<2> size, Vec Vn, Vec Vd) {
1359 return false;
1360 }
1361 virtual bool SQXTN_1(Imm<2> size, Vec Vn, Vec Vd) {
1362 return false;
1363 }
1364 virtual bool USQADD_1(Imm<2> size, Vec Vn, Vec Vd) {
1365 return false;
1366 }
1367 virtual bool SQNEG_1(Imm<2> size, Vec Vn, Vec Vd) {
1368 return false;
1369 }
1370 virtual bool CMGE_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
1371 return false;
1372 }
1373 virtual bool CMLE_1(Imm<2> size, Vec Vn, Vec Vd) {
1374 return false;
1375 }
1376 virtual bool NEG_1(Imm<2> size, Vec Vn, Vec Vd) {
1377 return false;
1378 }
1379 virtual bool SQXTUN_1(Imm<2> size, Vec Vn, Vec Vd) {
1380 return false;
1381 }
1382 virtual bool UQXTN_1(Imm<2> size, Vec Vn, Vec Vd) {
1383 return false;
1384 }
1385 virtual bool FCVTXN_1(bool sz, Vec Vn, Vec Vd) {
1386 return false;
1387 }
1388
1389 // Data Processing - FP and SIMD - SIMD Scalar pairwise
1390 virtual bool ADDP_pair(Imm<2> size, Vec Vn, Vec Vd) {
1391 return false;
1392 }
1393 virtual bool FMAXNMP_pair_1(Vec Vn, Vec Vd) {
1394 return false;
1395 }
1396 virtual bool FMAXNMP_pair_2(bool sz, Vec Vn, Vec Vd) {
1397 return false;
1398 }
1399 virtual bool FADDP_pair_1(Vec Vn, Vec Vd) {
1400 return false;
1401 }
1402 virtual bool FADDP_pair_2(bool sz, Vec Vn, Vec Vd) {
1403 return false;
1404 }
1405 virtual bool FMAXP_pair_1(Vec Vn, Vec Vd) {
1406 return false;
1407 }
1408 virtual bool FMAXP_pair_2(bool sz, Vec Vn, Vec Vd) {
1409 return false;
1410 }
1411 virtual bool FMINNMP_pair_1(Vec Vn, Vec Vd) {
1412 return false;
1413 }
1414 virtual bool FMINNMP_pair_2(bool sz, Vec Vn, Vec Vd) {
1415 return false;
1416 }
1417 virtual bool FMINP_pair_1(Vec Vn, Vec Vd) {
1418 return false;
1419 }
1420 virtual bool FMINP_pair_2(bool sz, Vec Vn, Vec Vd) {
1421 return false;
1422 }
1423
1424 // Data Processing - FP and SIMD - SIMD Scalar three different
1425 virtual bool SQDMLAL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) {
1426 return false;
1427 }
1428 virtual bool SQDMLSL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) {
1429 return false;
1430 }
1431 virtual bool SQDMULL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) {
1432 return false;
1433 }
1434
1435 // Data Processing - FP and SIMD - SIMD Scalar three same
1436 virtual bool SQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1437 return false;
1438 }
1439 virtual bool SQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1440 return false;
1441 }
1442 virtual bool CMGT_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1443 return false;
1444 }
1445 virtual bool CMGE_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1446 return false;
1447 }
1448 virtual bool SSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1449 return false;
1450 }
1451 virtual bool SQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1452 return false;
1453 }
1454 virtual bool SRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1455 return false;
1456 }
1457 virtual bool SQRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1458 return false;
1459 }
1460 virtual bool ADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1461 return false;
1462 }
1463 virtual bool CMTST_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1464 return false;
1465 }
1466 virtual bool SQDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1467 return false;
1468 }
1469 virtual bool UQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1470 return false;
1471 }
1472 virtual bool UQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1473 return false;
1474 }
1475 virtual bool CMHI_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1476 return false;
1477 }
1478 virtual bool CMHS_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1479 return false;
1480 }
1481 virtual bool USHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1482 return false;
1483 }
1484 virtual bool UQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1485 return false;
1486 }
1487 virtual bool URSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1488 return false;
1489 }
1490 virtual bool UQRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1491 return false;
1492 }
1493 virtual bool SUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1494 return false;
1495 }
1496 virtual bool CMEQ_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1497 return false;
1498 }
1499 virtual bool SQRDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1500 return false;
1501 }
1502
1503 // Data Processing - FP and SIMD - SIMD Scalar shift by immediate
1504 virtual bool SSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1505 return false;
1506 }
1507 virtual bool SSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1508 return false;
1509 }
1510 virtual bool SRSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1511 return false;
1512 }
1513 virtual bool SRSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1514 return false;
1515 }
1516 virtual bool SHL_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1517 return false;
1518 }
1519 virtual bool SQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1520 return false;
1521 }
1522 virtual bool SQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1523 return false;
1524 }
1525 virtual bool SQRSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1526 return false;
1527 }
1528 virtual bool SCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1529 return false;
1530 }
1531 virtual bool FCVTZS_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1532 return false;
1533 }
1534 virtual bool USHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1535 return false;
1536 }
1537 virtual bool USRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1538 return false;
1539 }
1540 virtual bool URSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1541 return false;
1542 }
1543 virtual bool URSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1544 return false;
1545 }
1546 virtual bool SRI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1547 return false;
1548 }
1549 virtual bool SLI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1550 return false;
1551 }
1552 virtual bool SQSHLU_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1553 return false;
1554 }
1555 virtual bool UQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1556 return false;
1557 }
1558 virtual bool SQSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1559 return false;
1560 }
1561 virtual bool SQRSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1562 return false;
1563 }
1564 virtual bool UQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1565 return false;
1566 }
1567 virtual bool UQRSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1568 return false;
1569 }
1570 virtual bool UCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1571 return false;
1572 }
1573 virtual bool FCVTZU_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
1574 return false;
1575 }
1576
1577 // Data Processing - FP and SIMD - SIMD Scalar x indexed element
1578 virtual bool SQDMLAL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1579 Vec Vd) {
1580 return false;
1581 }
1582 virtual bool SQDMLSL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1583 Vec Vd) {
1584 return false;
1585 }
1586 virtual bool SQDMULL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1587 Vec Vd) {
1588 return false;
1589 }
1590 virtual bool SQDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1591 Vec Vd) {
1592 return false;
1593 }
1594 virtual bool SQRDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1595 Vec Vd) {
1596 return false;
1597 }
1598 virtual bool FMLA_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1599 return false;
1600 }
1601 virtual bool FMLA_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1602 return false;
1603 }
1604 virtual bool FMLS_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1605 return false;
1606 }
1607 virtual bool FMLS_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1608 return false;
1609 }
1610 virtual bool FMUL_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1611 return false;
1612 }
1613 virtual bool FMUL_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1614 return false;
1615 }
1616 virtual bool SQRDMLAH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1617 Vec Vd) {
1618 return false;
1619 }
1620 virtual bool SQRDMLSH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
1621 Vec Vd) {
1622 return false;
1623 }
1624 virtual bool FMULX_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1625 return false;
1626 }
1627 virtual bool FMULX_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
1628 return false;
1629 }
1630
1631 // Data Processing - FP and SIMD - SIMD Table Lookup
1632 virtual bool TBL(bool Q, Vec Vm, Imm<2> len, size_t Vn, Vec Vd) {
1633 return false;
1634 }
1635 virtual bool TBX(bool Q, Vec Vm, Imm<2> len, size_t Vn, Vec Vd) {
1636 return false;
1637 }
1638
1639 // Data Processing - FP and SIMD - SIMD Permute
1640 virtual bool UZP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1641 return false;
1642 }
1643 virtual bool TRN1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1644 return false;
1645 }
1646 virtual bool ZIP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1647 return false;
1648 }
1649 virtual bool UZP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1650 return false;
1651 }
1652 virtual bool TRN2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1653 return false;
1654 }
1655 virtual bool ZIP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1656 return false;
1657 }
1658
1659 // Data Processing - FP and SIMD - SIMD Extract
1660 virtual bool EXT(bool Q, Vec Vm, Imm<4> imm4, Vec Vn, Vec Vd) {
1661 return false;
1662 }
1663
1664 // Data Processing - FP and SIMD - SIMD Copy
1665 virtual bool DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) {
1666 return false;
1667 }
1668 virtual bool DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) {
1669 return false;
1670 }
1671 virtual bool SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) {
1672 return false;
1673 }
1674 virtual bool UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) {
1675 return false;
1676 }
1677 virtual bool INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) {
1678 return false;
1679 }
1680 virtual bool INS_elt(Imm<5> imm5, Imm<4> imm4, Vec Vn, Vec Vd) {
1681 return false;
1682 }
1683
1684 // Data Processing - FP and SIMD - SIMD Three same
1685 virtual bool FMULX_vec_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1686 return false;
1687 }
1688 virtual bool FCMEQ_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1689 return false;
1690 }
1691 virtual bool FRECPS_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1692 return false;
1693 }
1694 virtual bool FRSQRTS_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1695 return false;
1696 }
1697 virtual bool FCMGE_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1698 return false;
1699 }
1700 virtual bool FACGE_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1701 return false;
1702 }
1703 virtual bool FABD_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1704 return false;
1705 }
1706 virtual bool FCMGT_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1707 return false;
1708 }
1709 virtual bool FACGT_3(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1710 return false;
1711 }
1712 virtual bool FMAXNM_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1713 return false;
1714 }
1715 virtual bool FMLA_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1716 return false;
1717 }
1718 virtual bool FADD_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1719 return false;
1720 }
1721 virtual bool FMAX_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1722 return false;
1723 }
1724 virtual bool FMINNM_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1725 return false;
1726 }
1727 virtual bool FMLS_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1728 return false;
1729 }
1730 virtual bool FSUB_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1731 return false;
1732 }
1733 virtual bool FMIN_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1734 return false;
1735 }
1736 virtual bool FMAXNMP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1737 return false;
1738 }
1739 virtual bool FADDP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1740 return false;
1741 }
1742 virtual bool FMUL_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1743 return false;
1744 }
1745 virtual bool FMAXP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1746 return false;
1747 }
1748 virtual bool FDIV_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1749 return false;
1750 }
1751 virtual bool FMINNMP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1752 return false;
1753 }
1754 virtual bool FMINP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) {
1755 return false;
1756 }
1757
1758 // Data Processing - FP and SIMD - SIMD Three same extra
1759 virtual bool SDOT_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1760 return false;
1761 }
1762 virtual bool UDOT_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1763 return false;
1764 }
1765 virtual bool FCMLA_vec(bool Q, Imm<2> size, Vec Vm, Imm<2> rot, Vec Vn, Vec Vd) {
1766 return false;
1767 }
1768 virtual bool FCADD_vec(bool Q, Imm<2> size, Vec Vm, Imm<1> rot, Vec Vn, Vec Vd) {
1769 return false;
1770 }
1771
1772 // Data Processing - FP and SIMD - SIMD Two register misc
1773 virtual bool REV64_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1774 return false;
1775 }
1776 virtual bool REV16_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1777 return false;
1778 }
1779 virtual bool SADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1780 return false;
1781 }
1782 virtual bool CLS_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1783 return false;
1784 }
1785 virtual bool CNT(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1786 return false;
1787 }
1788 virtual bool SADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1789 return false;
1790 }
1791 virtual bool XTN(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1792 return false;
1793 }
1794 virtual bool FCVTN(bool Q, bool sz, Vec Vn, Vec Vd) {
1795 return false;
1796 }
1797 virtual bool FCVTL(bool Q, bool sz, Vec Vn, Vec Vd) {
1798 return false;
1799 }
1800 virtual bool URECPE(bool Q, bool sz, Vec Vn, Vec Vd) {
1801 return false;
1802 }
1803 virtual bool REV32_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1804 return false;
1805 }
1806 virtual bool UADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1807 return false;
1808 }
1809 virtual bool CLZ_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1810 return false;
1811 }
1812 virtual bool UADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1813 return false;
1814 }
1815 virtual bool SHLL(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1816 return false;
1817 }
1818 virtual bool NOT(bool Q, Vec Vn, Vec Vd) {
1819 return false;
1820 }
1821 virtual bool RBIT_asimd(bool Q, Vec Vn, Vec Vd) {
1822 return false;
1823 }
1824 virtual bool URSQRTE(bool Q, bool sz, Vec Vn, Vec Vd) {
1825 return false;
1826 }
1827 virtual bool SUQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1828 return false;
1829 }
1830 virtual bool SQABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1831 return false;
1832 }
1833 virtual bool CMGT_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1834 return false;
1835 }
1836 virtual bool CMEQ_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1837 return false;
1838 }
1839 virtual bool CMLT_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1840 return false;
1841 }
1842 virtual bool ABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1843 return false;
1844 }
1845 virtual bool SQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1846 return false;
1847 }
1848 virtual bool USQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1849 return false;
1850 }
1851 virtual bool SQNEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1852 return false;
1853 }
1854 virtual bool CMGE_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1855 return false;
1856 }
1857 virtual bool CMLE_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1858 return false;
1859 }
1860 virtual bool NEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1861 return false;
1862 }
1863 virtual bool SQXTUN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1864 return false;
1865 }
1866 virtual bool UQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1867 return false;
1868 }
1869 virtual bool FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1870 return false;
1871 }
1872 virtual bool FRINTN_1(bool Q, Vec Vn, Vec Vd) {
1873 return false;
1874 }
1875 virtual bool FRINTN_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1876 return false;
1877 }
1878 virtual bool FRINTM_1(bool Q, Vec Vn, Vec Vd) {
1879 return false;
1880 }
1881 virtual bool FRINTM_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1882 return false;
1883 }
1884 virtual bool FABS_1(bool Q, Vec Vn, Vec Vd) {
1885 return false;
1886 }
1887 virtual bool FABS_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1888 return false;
1889 }
1890 virtual bool FRINTP_1(bool Q, Vec Vn, Vec Vd) {
1891 return false;
1892 }
1893 virtual bool FRINTP_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1894 return false;
1895 }
1896 virtual bool FRINTZ_1(bool Q, Vec Vn, Vec Vd) {
1897 return false;
1898 }
1899 virtual bool FRINTZ_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1900 return false;
1901 }
1902 virtual bool FRINTA_1(bool Q, Vec Vn, Vec Vd) {
1903 return false;
1904 }
1905 virtual bool FRINTA_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1906 return false;
1907 }
1908 virtual bool FRINTX_1(bool Q, Vec Vn, Vec Vd) {
1909 return false;
1910 }
1911 virtual bool FRINTX_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1912 return false;
1913 }
1914 virtual bool FNEG_1(bool Q, Vec Vn, Vec Vd) {
1915 return false;
1916 }
1917 virtual bool FNEG_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1918 return false;
1919 }
1920 virtual bool FRINTI_1(bool Q, Vec Vn, Vec Vd) {
1921 return false;
1922 }
1923 virtual bool FRINTI_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1924 return false;
1925 }
1926 virtual bool FSQRT_1(bool Q, Vec Vn, Vec Vd) {
1927 return false;
1928 }
1929 virtual bool FSQRT_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1930 return false;
1931 }
1932 virtual bool FRINT32X_1(bool Q, bool sz, Vec Vn, Vec Vd) {
1933 return false;
1934 }
1935 virtual bool FRINT64X_1(bool Q, bool sz, Vec Vn, Vec Vd) {
1936 return false;
1937 }
1938 virtual bool FRINT32Z_1(bool Q, bool sz, Vec Vn, Vec Vd) {
1939 return false;
1940 }
1941 virtual bool FRINT64Z_1(bool Q, bool sz, Vec Vn, Vec Vd) {
1942 return false;
1943 }
1944
1945 // Data Processing - FP and SIMD - SIMD across lanes
1946 virtual bool SADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1947 return false;
1948 }
1949 virtual bool SMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1950 return false;
1951 }
1952 virtual bool SMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1953 return false;
1954 }
1955 virtual bool ADDV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1956 return false;
1957 }
1958 virtual bool FMAXNMV_1(bool Q, Vec Vn, Vec Vd) {
1959 return false;
1960 }
1961 virtual bool FMAXNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1962 return false;
1963 }
1964 virtual bool FMAXV_1(bool Q, Vec Vn, Vec Vd) {
1965 return false;
1966 }
1967 virtual bool FMAXV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1968 return false;
1969 }
1970 virtual bool FMINNMV_1(bool Q, Vec Vn, Vec Vd) {
1971 return false;
1972 }
1973 virtual bool FMINNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1974 return false;
1975 }
1976 virtual bool FMINV_1(bool Q, Vec Vn, Vec Vd) {
1977 return false;
1978 }
1979 virtual bool FMINV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
1980 return false;
1981 }
1982 virtual bool UADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1983 return false;
1984 }
1985 virtual bool UMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1986 return false;
1987 }
1988 virtual bool UMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
1989 return false;
1990 }
1991
1992 // Data Processing - FP and SIMD - SIMD three different
1993 virtual bool SADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1994 return false;
1995 }
1996 virtual bool SADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
1997 return false;
1998 }
1999 virtual bool SSUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2000 return false;
2001 }
2002 virtual bool SSUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2003 return false;
2004 }
2005 virtual bool ADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2006 return false;
2007 }
2008 virtual bool SABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2009 return false;
2010 }
2011 virtual bool SUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2012 return false;
2013 }
2014 virtual bool SABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2015 return false;
2016 }
2017 virtual bool SMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2018 return false;
2019 }
2020 virtual bool SMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2021 return false;
2022 }
2023 virtual bool SMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2024 return false;
2025 }
2026 virtual bool PMULL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2027 return false;
2028 }
2029 virtual bool UADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2030 return false;
2031 }
2032 virtual bool UADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2033 return false;
2034 }
2035 virtual bool USUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2036 return false;
2037 }
2038 virtual bool USUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2039 return false;
2040 }
2041 virtual bool RADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2042 return false;
2043 }
2044 virtual bool UABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2045 return false;
2046 }
2047 virtual bool RSUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2048 return false;
2049 }
2050 virtual bool UABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2051 return false;
2052 }
2053 virtual bool UMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2054 return false;
2055 }
2056 virtual bool UMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2057 return false;
2058 }
2059 virtual bool UMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2060 return false;
2061 }
2062 virtual bool SQDMLAL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2063 return false;
2064 }
2065 virtual bool SQDMLSL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2066 return false;
2067 }
2068 virtual bool SQDMULL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2069 return false;
2070 }
2071
2072 // Data Processing - FP and SIMD - SIMD three same
2073 virtual bool SHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2074 return false;
2075 }
2076 virtual bool SRHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2077 return false;
2078 }
2079 virtual bool SHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2080 return false;
2081 }
2082 virtual bool SMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2083 return false;
2084 }
2085 virtual bool SMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2086 return false;
2087 }
2088 virtual bool SABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2089 return false;
2090 }
2091 virtual bool SABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2092 return false;
2093 }
2094 virtual bool MLA_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2095 return false;
2096 }
2097 virtual bool MUL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2098 return false;
2099 }
2100 virtual bool SMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2101 return false;
2102 }
2103 virtual bool SMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2104 return false;
2105 }
2106 virtual bool ADDP_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2107 return false;
2108 }
2109 virtual bool FMLAL_vec_1(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2110 return false;
2111 }
2112 virtual bool FMLAL_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2113 return false;
2114 }
2115 virtual bool AND_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2116 return false;
2117 }
2118 virtual bool BIC_asimd_reg(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2119 return false;
2120 }
2121 virtual bool FMLSL_vec_1(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2122 return false;
2123 }
2124 virtual bool FMLSL_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2125 return false;
2126 }
2127 virtual bool ORR_asimd_reg(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2128 return false;
2129 }
2130 virtual bool ORN_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2131 return false;
2132 }
2133 virtual bool UHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2134 return false;
2135 }
2136 virtual bool URHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2137 return false;
2138 }
2139 virtual bool UHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2140 return false;
2141 }
2142 virtual bool UMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2143 return false;
2144 }
2145 virtual bool UMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2146 return false;
2147 }
2148 virtual bool UABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2149 return false;
2150 }
2151 virtual bool UABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2152 return false;
2153 }
2154 virtual bool MLS_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2155 return false;
2156 }
2157 virtual bool PMUL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2158 return false;
2159 }
2160 virtual bool UMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2161 return false;
2162 }
2163 virtual bool UMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2164 return false;
2165 }
2166 virtual bool EOR_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2167 return false;
2168 }
2169 virtual bool BSL(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2170 return false;
2171 }
2172 virtual bool BIT(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2173 return false;
2174 }
2175 virtual bool BIF(bool Q, Vec Vm, Vec Vn, Vec Vd) {
2176 return false;
2177 }
2178 virtual bool FMAXNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2179 return false;
2180 }
2181 virtual bool FMLA_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2182 return false;
2183 }
2184 virtual bool FADD_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2185 return false;
2186 }
2187 virtual bool FMAX_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2188 return false;
2189 }
2190 virtual bool FMINNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2191 return false;
2192 }
2193 virtual bool FMLS_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2194 return false;
2195 }
2196 virtual bool FSUB_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2197 return false;
2198 }
2199 virtual bool FMIN_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2200 return false;
2201 }
2202 virtual bool FMAXNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2203 return false;
2204 }
2205 virtual bool FADDP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2206 return false;
2207 }
2208 virtual bool FMUL_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2209 return false;
2210 }
2211 virtual bool FMAXP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2212 return false;
2213 }
2214 virtual bool FDIV_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2215 return false;
2216 }
2217 virtual bool FMINNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2218 return false;
2219 }
2220 virtual bool FMINP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2221 return false;
2222 }
2223 virtual bool FMULX_vec_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2224 return false;
2225 }
2226 virtual bool FCMEQ_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2227 return false;
2228 }
2229 virtual bool FRECPS_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2230 return false;
2231 }
2232 virtual bool FRSQRTS_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2233 return false;
2234 }
2235 virtual bool FCMGE_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2236 return false;
2237 }
2238 virtual bool FACGE_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2239 return false;
2240 }
2241 virtual bool FABD_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2242 return false;
2243 }
2244 virtual bool FCMGT_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2245 return false;
2246 }
2247 virtual bool FACGT_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) {
2248 return false;
2249 }
2250 virtual bool SQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2251 return false;
2252 }
2253 virtual bool SQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2254 return false;
2255 }
2256 virtual bool CMGT_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2257 return false;
2258 }
2259 virtual bool CMGE_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2260 return false;
2261 }
2262 virtual bool SSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2263 return false;
2264 }
2265 virtual bool SQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2266 return false;
2267 }
2268 virtual bool SRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2269 return false;
2270 }
2271 virtual bool SQRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2272 return false;
2273 }
2274 virtual bool ADD_vector(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2275 return false;
2276 }
2277 virtual bool CMTST_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2278 return false;
2279 }
2280 virtual bool SQDMULH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2281 return false;
2282 }
2283 virtual bool UQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2284 return false;
2285 }
2286 virtual bool UQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2287 return false;
2288 }
2289 virtual bool CMHI_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2290 return false;
2291 }
2292 virtual bool CMHS_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2293 return false;
2294 }
2295 virtual bool USHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2296 return false;
2297 }
2298 virtual bool UQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2299 return false;
2300 }
2301 virtual bool URSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2302 return false;
2303 }
2304 virtual bool UQRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2305 return false;
2306 }
2307 virtual bool SUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2308 return false;
2309 }
2310 virtual bool CMEQ_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2311 return false;
2312 }
2313 virtual bool SQRDMULH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
2314 return false;
2315 }
2316
2317 // Data Processing - FP and SIMD - SIMD modified immediate
2318 virtual bool MOVI(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<4> cmode, Imm<1> d,
2319 Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
2320 return false;
2321 }
2322 virtual bool FMOV_2(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f,
2323 Imm<1> g, Imm<1> h, Vec Vd) {
2324 return false;
2325 }
2326 virtual bool FMOV_3(bool Q, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f,
2327 Imm<1> g, Imm<1> h, Vec Vd) {
2328 return false;
2329 }
2330
2331 // Data Processing - FP and SIMD - SIMD Shift by immediate
2332 virtual bool SSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2333 return false;
2334 }
2335 virtual bool SSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2336 return false;
2337 }
2338 virtual bool SRSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2339 return false;
2340 }
2341 virtual bool SRSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2342 return false;
2343 }
2344 virtual bool SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2345 return false;
2346 }
2347 virtual bool SQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2348 return false;
2349 }
2350 virtual bool SHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2351 return false;
2352 }
2353 virtual bool RSHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2354 return false;
2355 }
2356 virtual bool SQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2357 return false;
2358 }
2359 virtual bool SQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2360 return false;
2361 }
2362 virtual bool SSHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2363 return false;
2364 }
2365 virtual bool SCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2366 return false;
2367 }
2368 virtual bool FCVTZS_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2369 return false;
2370 }
2371 virtual bool USHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2372 return false;
2373 }
2374 virtual bool USRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2375 return false;
2376 }
2377 virtual bool URSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2378 return false;
2379 }
2380 virtual bool URSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2381 return false;
2382 }
2383 virtual bool SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2384 return false;
2385 }
2386 virtual bool SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2387 return false;
2388 }
2389 virtual bool SQSHLU_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2390 return false;
2391 }
2392 virtual bool UQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2393 return false;
2394 }
2395 virtual bool SQSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2396 return false;
2397 }
2398 virtual bool SQRSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2399 return false;
2400 }
2401 virtual bool UQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2402 return false;
2403 }
2404 virtual bool UQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2405 return false;
2406 }
2407 virtual bool USHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2408 return false;
2409 }
2410 virtual bool UCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2411 return false;
2412 }
2413 virtual bool FCVTZU_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
2414 return false;
2415 }
2416
2417 // Data Processing - FP and SIMD - SIMD vector x indexed element
2418 virtual bool SMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2419 Vec Vd) {
2420 return false;
2421 }
2422 virtual bool SQDMLAL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2423 Vec Vn, Vec Vd) {
2424 return false;
2425 }
2426 virtual bool SMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2427 Vec Vd) {
2428 return false;
2429 }
2430 virtual bool SQDMLSL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2431 Vec Vn, Vec Vd) {
2432 return false;
2433 }
2434 virtual bool MUL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2435 Vec Vd) {
2436 return false;
2437 }
2438 virtual bool SMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm, Imm<1> H, Vec Vn,
2439 Vec Vd) {
2440 return false;
2441 }
2442 virtual bool SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2443 Vec Vn, Vec Vd) {
2444 return false;
2445 }
2446 virtual bool SQDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2447 Vec Vn, Vec Vd) {
2448 return false;
2449 }
2450 virtual bool SQRDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2451 Vec Vn, Vec Vd) {
2452 return false;
2453 }
2454 virtual bool SDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2455 Vec Vd) {
2456 return false;
2457 }
2458 virtual bool FMLA_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
2459 return false;
2460 }
2461 virtual bool FMLA_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2462 Vec Vd) {
2463 return false;
2464 }
2465 virtual bool FMLS_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
2466 return false;
2467 }
2468 virtual bool FMLS_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2469 Vec Vd) {
2470 return false;
2471 }
2472 virtual bool FMUL_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
2473 return false;
2474 }
2475 virtual bool FMUL_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2476 Vec Vd) {
2477 return false;
2478 }
2479 virtual bool FMLAL_elt_1(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2480 Vec Vd) {
2481 return false;
2482 }
2483 virtual bool FMLAL_elt_2(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2484 Vec Vd) {
2485 return false;
2486 }
2487 virtual bool FMLSL_elt_1(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2488 Vec Vd) {
2489 return false;
2490 }
2491 virtual bool FMLSL_elt_2(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2492 Vec Vd) {
2493 return false;
2494 }
2495 virtual bool MLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2496 Vec Vd) {
2497 return false;
2498 }
2499 virtual bool UMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2500 Vec Vd) {
2501 return false;
2502 }
2503 virtual bool MLS_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2504 Vec Vd) {
2505 return false;
2506 }
2507 virtual bool UMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2508 Vec Vd) {
2509 return false;
2510 }
2511 virtual bool UMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2512 Vec Vd) {
2513 return false;
2514 }
2515 virtual bool SQRDMLAH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2516 Vec Vn, Vec Vd) {
2517 return false;
2518 }
2519 virtual bool UDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2520 Vec Vd) {
2521 return false;
2522 }
2523 virtual bool SQRDMLSH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
2524 Vec Vn, Vec Vd) {
2525 return false;
2526 }
2527 virtual bool FMULX_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
2528 return false;
2529 }
2530 virtual bool FMULX_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn,
2531 Vec Vd) {
2532 return false;
2533 }
2534 virtual bool FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<2> rot,
2535 Imm<1> H, Vec Vn, Vec Vd) {
2536 return false;
2537 }
2538
2539 // Data Processing - FP and SIMD - Cryptographic three register
2540 virtual bool SM3TT1A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
2541 return false;
2542 }
2543 virtual bool SM3TT1B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
2544 return false;
2545 }
2546 virtual bool SM3TT2A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
2547 return false;
2548 }
2549 virtual bool SM3TT2B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
2550 return false;
2551 }
2552
2553 // Data Processing - FP and SIMD - SHA512 three register
2554 virtual bool SHA512H(Vec Vm, Vec Vn, Vec Vd) {
2555 return false;
2556 }
2557 virtual bool SHA512H2(Vec Vm, Vec Vn, Vec Vd) {
2558 return false;
2559 }
2560 virtual bool SHA512SU1(Vec Vm, Vec Vn, Vec Vd) {
2561 return false;
2562 }
2563 virtual bool RAX1(Vec Vm, Vec Vn, Vec Vd) {
2564 return false;
2565 }
2566 virtual bool XAR(Vec Vm, Imm<6> imm6, Vec Vn, Vec Vd) {
2567 return false;
2568 }
2569 virtual bool SM3PARTW1(Vec Vm, Vec Vn, Vec Vd) {
2570 return false;
2571 }
2572 virtual bool SM3PARTW2(Vec Vm, Vec Vn, Vec Vd) {
2573 return false;
2574 }
2575 virtual bool SM4EKEY(Vec Vm, Vec Vn, Vec Vd) {
2576 return false;
2577 }
2578
2579 // Data Processing - FP and SIMD - Cryptographic four register
2580 virtual bool EOR3(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2581 return false;
2582 }
2583 virtual bool BCAX(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2584 return false;
2585 }
2586 virtual bool SM3SS1(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2587 return false;
2588 }
2589
2590 // Data Processing - FP and SIMD - SHA512 two register
2591 virtual bool SHA512SU0(Vec Vn, Vec Vd) {
2592 return false;
2593 }
2594 virtual bool SM4E(Vec Vn, Vec Vd) {
2595 return false;
2596 }
2597
2598 // Data Processing - FP and SIMD - Conversion between floating point and fixed point
2599 virtual bool SCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) {
2600 return false;
2601 }
2602 virtual bool UCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) {
2603 return false;
2604 }
2605 virtual bool FCVTZS_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) {
2606 return false;
2607 }
2608 virtual bool FCVTZU_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) {
2609 return false;
2610 }
2611
2612 // Data Processing - FP and SIMD - Conversion between floating point and integer
2613 virtual bool FCVTNS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2614 return false;
2615 }
2616 virtual bool FCVTNU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2617 return false;
2618 }
2619 virtual bool SCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) {
2620 return false;
2621 }
2622 virtual bool UCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) {
2623 return false;
2624 }
2625 virtual bool FCVTAS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2626 return false;
2627 }
2628 virtual bool FCVTAU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2629 return false;
2630 }
2631 virtual bool FMOV_float_gen(bool sf, Imm<2> type, Imm<1> rmode_0, Imm<1> opc_0, size_t n,
2632 size_t d) {
2633 return false;
2634 }
2635 virtual bool FCVTPS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2636 return false;
2637 }
2638 virtual bool FCVTPU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2639 return false;
2640 }
2641 virtual bool FCVTMS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2642 return false;
2643 }
2644 virtual bool FCVTMU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2645 return false;
2646 }
2647 virtual bool FCVTZS_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2648 return false;
2649 }
2650 virtual bool FCVTZU_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
2651 return false;
2652 }
2653 virtual bool FJCVTZS(Vec Vn, Reg Rd) {
2654 return false;
2655 }
2656
2657 // Data Processing - FP and SIMD - Floating point data processing
2658 virtual bool FMOV_float(Imm<2> type, Vec Vn, Vec Vd) {
2659 return false;
2660 }
2661 virtual bool FABS_float(Imm<2> type, Vec Vn, Vec Vd) {
2662 return false;
2663 }
2664 virtual bool FNEG_float(Imm<2> type, Vec Vn, Vec Vd) {
2665 return false;
2666 }
2667 virtual bool FSQRT_float(Imm<2> type, Vec Vn, Vec Vd) {
2668 return false;
2669 }
2670 virtual bool FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) {
2671 return false;
2672 }
2673 virtual bool FRINTN_float(Imm<2> type, Vec Vn, Vec Vd) {
2674 return false;
2675 }
2676 virtual bool FRINTP_float(Imm<2> type, Vec Vn, Vec Vd) {
2677 return false;
2678 }
2679 virtual bool FRINTM_float(Imm<2> type, Vec Vn, Vec Vd) {
2680 return false;
2681 }
2682 virtual bool FRINTZ_float(Imm<2> type, Vec Vn, Vec Vd) {
2683 return false;
2684 }
2685 virtual bool FRINTA_float(Imm<2> type, Vec Vn, Vec Vd) {
2686 return false;
2687 }
2688 virtual bool FRINTX_float(Imm<2> type, Vec Vn, Vec Vd) {
2689 return false;
2690 }
2691 virtual bool FRINTI_float(Imm<2> type, Vec Vn, Vec Vd) {
2692 return false;
2693 }
2694 virtual bool FRINT32X_float(Imm<2> type, Vec Vn, Vec Vd) {
2695 return false;
2696 }
2697 virtual bool FRINT64X_float(Imm<2> type, Vec Vn, Vec Vd) {
2698 return false;
2699 }
2700 virtual bool FRINT32Z_float(Imm<2> type, Vec Vn, Vec Vd) {
2701 return false;
2702 }
2703 virtual bool FRINT64Z_float(Imm<2> type, Vec Vn, Vec Vd) {
2704 return false;
2705 }
2706
2707 // Data Processing - FP and SIMD - Floating point compare
2708 virtual bool FCMP_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) {
2709 return false;
2710 }
2711 virtual bool FCMPE_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) {
2712 return false;
2713 }
2714
2715 // Data Processing - FP and SIMD - Floating point immediate
2716 virtual bool FMOV_float_imm(Imm<2> type, Imm<8> imm8, Vec Vd) {
2717 return false;
2718 }
2719
2720 // Data Processing - FP and SIMD - Floating point conditional compare
2721 virtual bool FCCMP_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) {
2722 return false;
2723 }
2724 virtual bool FCCMPE_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) {
2725 return false;
2726 }
2727
2728 // Data Processing - FP and SIMD - Floating point data processing two register
2729 virtual bool FMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2730 return false;
2731 }
2732 virtual bool FDIV_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2733 return false;
2734 }
2735 virtual bool FADD_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2736 return false;
2737 }
2738 virtual bool FSUB_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2739 return false;
2740 }
2741 virtual bool FMAX_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2742 return false;
2743 }
2744 virtual bool FMIN_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2745 return false;
2746 }
2747 virtual bool FMAXNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2748 return false;
2749 }
2750 virtual bool FMINNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2751 return false;
2752 }
2753 virtual bool FNMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
2754 return false;
2755 }
2756
2757 // Data Processing - FP and SIMD - Floating point conditional select
2758 virtual bool FCSEL_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Vec Vd) {
2759 return false;
2760 }
2761
2762 // Data Processing - FP and SIMD - Floating point data processing three register
2763 virtual bool FMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2764 return false;
2765 }
2766 virtual bool FMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2767 return false;
2768 }
2769 virtual bool FNMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2770 return false;
2771 }
2772 virtual bool FNMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
2773 return false;
2774 }
2775};
2776
2777} // namespace Core
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 639842401..b7105c8ff 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -201,8 +201,6 @@ std::string VfsFile::GetFullPath() const {
201 201
202VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { 202VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
203 auto vec = Common::FS::SplitPathComponents(path); 203 auto vec = Common::FS::SplitPathComponents(path);
204 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
205 vec.end());
206 if (vec.empty()) { 204 if (vec.empty()) {
207 return nullptr; 205 return nullptr;
208 } 206 }
@@ -237,8 +235,6 @@ VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
237 235
238VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { 236VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const {
239 auto vec = Common::FS::SplitPathComponents(path); 237 auto vec = Common::FS::SplitPathComponents(path);
240 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
241 vec.end());
242 if (vec.empty()) { 238 if (vec.empty()) {
243 // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently 239 // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
244 // because of const-ness 240 // because of const-ness
@@ -303,8 +299,6 @@ std::size_t VfsDirectory::GetSize() const {
303 299
304VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { 300VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) {
305 auto vec = Common::FS::SplitPathComponents(path); 301 auto vec = Common::FS::SplitPathComponents(path);
306 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
307 vec.end());
308 if (vec.empty()) { 302 if (vec.empty()) {
309 return nullptr; 303 return nullptr;
310 } 304 }
@@ -334,8 +328,6 @@ VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) {
334 328
335VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { 329VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) {
336 auto vec = Common::FS::SplitPathComponents(path); 330 auto vec = Common::FS::SplitPathComponents(path);
337 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
338 vec.end());
339 if (vec.empty()) { 331 if (vec.empty()) {
340 return nullptr; 332 return nullptr;
341 } 333 }
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 1c706e4d8..cd9b79786 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -268,7 +268,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference)
268RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, 268RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
269 const std::string& path_, Mode perms_, std::optional<u64> size_) 269 const std::string& path_, Mode perms_, std::optional<u64> size_)
270 : base(base_), reference(std::move(reference_)), path(path_), 270 : base(base_), reference(std::move(reference_)), path(path_),
271 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)), 271 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
272 size(size_), perms(perms_) {} 272 size(size_), perms(perms_) {}
273 273
274RealVfsFile::~RealVfsFile() { 274RealVfsFile::~RealVfsFile() {
@@ -276,7 +276,7 @@ RealVfsFile::~RealVfsFile() {
276} 276}
277 277
278std::string RealVfsFile::GetName() const { 278std::string RealVfsFile::GetName() const {
279 return path_components.back(); 279 return path_components.empty() ? "" : std::string(path_components.back());
280} 280}
281 281
282std::size_t RealVfsFile::GetSize() const { 282std::size_t RealVfsFile::GetSize() const {
@@ -375,7 +375,7 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
375 375
376RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) 376RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
377 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), 377 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
378 path_components(FS::SplitPathComponents(path)), perms(perms_) { 378 path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
379 if (!FS::Exists(path) && True(perms & Mode::Write)) { 379 if (!FS::Exists(path) && True(perms & Mode::Write)) {
380 void(FS::CreateDirs(path)); 380 void(FS::CreateDirs(path));
381 } 381 }
@@ -464,7 +464,7 @@ bool RealVfsDirectory::IsReadable() const {
464} 464}
465 465
466std::string RealVfsDirectory::GetName() const { 466std::string RealVfsDirectory::GetName() const {
467 return path_components.back(); 467 return path_components.empty() ? "" : std::string(path_components.back());
468} 468}
469 469
470VirtualDir RealVfsDirectory::GetParentDirectory() const { 470VirtualDir RealVfsDirectory::GetParentDirectory() const {
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index a6bdd28f2..072f38a68 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -20,6 +20,9 @@ InputInterpreter::InputInterpreter(Core::System& system)
20InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
21 21
22void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
23 if (npad == nullptr) {
24 return;
25 }
23 const auto button_state = npad->GetAndResetPressState(); 26 const auto button_state = npad->GetAndResetPressState();
24 27
25 previous_index = current_index; 28 previous_index = current_index;
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 0a973ec8c..d6bd27296 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -421,8 +421,9 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
421 } else { 421 } else {
422 // Set all the allocated memory. 422 // Set all the allocated memory.
423 for (const auto& block : *out) { 423 for (const auto& block : *out) {
424 std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern, 424 m_system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(block.GetAddress()) -
425 block.GetSize()); 425 Core::DramMemoryMap::Base,
426 block.GetSize(), fill_pattern);
426 } 427 }
427 } 428 }
428 429
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 4c416d809..423289145 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -81,6 +81,11 @@ void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size)
81 } 81 }
82} 82}
83 83
84void ClearBackingRegion(Core::System& system, KPhysicalAddress addr, u64 size, u32 fill_value) {
85 system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(addr) - Core::DramMemoryMap::Base,
86 size, fill_value);
87}
88
84template <typename AddressType> 89template <typename AddressType>
85Result InvalidateDataCache(AddressType addr, u64 size) { 90Result InvalidateDataCache(AddressType addr, u64 size) {
86 R_SUCCEED(); 91 R_SUCCEED();
@@ -1363,8 +1368,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
1363 1368
1364 // Clear all the newly allocated pages. 1369 // Clear all the newly allocated pages.
1365 for (const auto& it : pg) { 1370 for (const auto& it : pg) {
1366 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), 1371 ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
1367 static_cast<u32>(m_heap_fill_value), it.GetSize());
1368 } 1372 }
1369 1373
1370 // Lock the table. 1374 // Lock the table.
@@ -1570,8 +1574,7 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
1570 1574
1571 // Clear all pages. 1575 // Clear all pages.
1572 for (const auto& it : pg) { 1576 for (const auto& it : pg) {
1573 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), 1577 ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
1574 static_cast<u32>(m_heap_fill_value), it.GetSize());
1575 } 1578 }
1576 1579
1577 // Map the pages. 1580 // Map the pages.
@@ -2159,8 +2162,7 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
2159 2162
2160 // Clear all the newly allocated pages. 2163 // Clear all the newly allocated pages.
2161 for (const auto& it : pg) { 2164 for (const auto& it : pg) {
2162 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value, 2165 ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
2163 it.GetSize());
2164 } 2166 }
2165 2167
2166 // Map the pages. 2168 // Map the pages.
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 8cb05ca0b..e479dacde 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -135,7 +135,6 @@ struct KernelCore::Impl {
135 obj = nullptr; 135 obj = nullptr;
136 } 136 }
137 }; 137 };
138 CleanupObject(hid_shared_mem);
139 CleanupObject(font_shared_mem); 138 CleanupObject(font_shared_mem);
140 CleanupObject(irs_shared_mem); 139 CleanupObject(irs_shared_mem);
141 CleanupObject(time_shared_mem); 140 CleanupObject(time_shared_mem);
@@ -744,22 +743,16 @@ struct KernelCore::Impl {
744 void InitializeHackSharedMemory(KernelCore& kernel) { 743 void InitializeHackSharedMemory(KernelCore& kernel) {
745 // Setup memory regions for emulated processes 744 // Setup memory regions for emulated processes
746 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel 745 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel
747 constexpr std::size_t hid_size{0x40000};
748 constexpr std::size_t font_size{0x1100000}; 746 constexpr std::size_t font_size{0x1100000};
749 constexpr std::size_t irs_size{0x8000}; 747 constexpr std::size_t irs_size{0x8000};
750 constexpr std::size_t time_size{0x1000}; 748 constexpr std::size_t time_size{0x1000};
751 constexpr std::size_t hidbus_size{0x1000}; 749 constexpr std::size_t hidbus_size{0x1000};
752 750
753 hid_shared_mem = KSharedMemory::Create(system.Kernel());
754 font_shared_mem = KSharedMemory::Create(system.Kernel()); 751 font_shared_mem = KSharedMemory::Create(system.Kernel());
755 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 752 irs_shared_mem = KSharedMemory::Create(system.Kernel());
756 time_shared_mem = KSharedMemory::Create(system.Kernel()); 753 time_shared_mem = KSharedMemory::Create(system.Kernel());
757 hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); 754 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
758 755
759 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
760 Svc::MemoryPermission::Read, hid_size);
761 KSharedMemory::Register(kernel, hid_shared_mem);
762
763 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 756 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
764 Svc::MemoryPermission::Read, font_size); 757 Svc::MemoryPermission::Read, font_size);
765 KSharedMemory::Register(kernel, font_shared_mem); 758 KSharedMemory::Register(kernel, font_shared_mem);
@@ -1190,14 +1183,6 @@ const KSystemResource& KernelCore::GetSystemSystemResource() const {
1190 return *impl->sys_system_resource; 1183 return *impl->sys_system_resource;
1191} 1184}
1192 1185
1193Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
1194 return *impl->hid_shared_mem;
1195}
1196
1197const Kernel::KSharedMemory& KernelCore::GetHidSharedMem() const {
1198 return *impl->hid_shared_mem;
1199}
1200
1201Kernel::KSharedMemory& KernelCore::GetFontSharedMem() { 1186Kernel::KSharedMemory& KernelCore::GetFontSharedMem() {
1202 return *impl->font_shared_mem; 1187 return *impl->font_shared_mem;
1203} 1188}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 69b5bbd6c..78c88902c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -239,12 +239,6 @@ public:
239 /// Gets the system resource manager. 239 /// Gets the system resource manager.
240 const KSystemResource& GetSystemSystemResource() const; 240 const KSystemResource& GetSystemSystemResource() const;
241 241
242 /// Gets the shared memory object for HID services.
243 Kernel::KSharedMemory& GetHidSharedMem();
244
245 /// Gets the shared memory object for HID services.
246 const Kernel::KSharedMemory& GetHidSharedMem() const;
247
248 /// Gets the shared memory object for font services. 242 /// Gets the shared memory object for font services.
249 Kernel::KSharedMemory& GetFontSharedMem(); 243 Kernel::KSharedMemory& GetFontSharedMem();
250 244
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 2f09cade5..23e56c77a 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -359,7 +359,7 @@ private:
359 359
360 void GetActiveChannelCount(HLERequestContext& ctx) { 360 void GetActiveChannelCount(HLERequestContext& ctx) {
361 const auto& sink{system.AudioCore().GetOutputSink()}; 361 const auto& sink{system.AudioCore().GetOutputSink()};
362 u32 channel_count{sink.GetDeviceChannels()}; 362 u32 channel_count{sink.GetSystemChannels()};
363 363
364 LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count); 364 LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
365 365
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 508db7360..780f8c74d 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -104,11 +104,7 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
104 const auto components = Common::FS::SplitPathComponents(path); 104 const auto components = Common::FS::SplitPathComponents(path);
105 std::string relative_path; 105 std::string relative_path;
106 for (const auto& component : components) { 106 for (const auto& component : components) {
107 // Skip empty path components 107 relative_path = Common::FS::SanitizePath(fmt::format("{}/{}", relative_path, component));
108 if (component.empty()) {
109 continue;
110 }
111 relative_path = Common::FS::SanitizePath(relative_path + '/' + component);
112 auto new_dir = backing->CreateSubdirectory(relative_path); 108 auto new_dir = backing->CreateSubdirectory(relative_path);
113 if (new_dir == nullptr) { 109 if (new_dir == nullptr) {
114 // TODO(DarkLordZach): Find a better error code for this 110 // TODO(DarkLordZach): Find a better error code for this
diff --git a/src/core/hle/service/hid/controllers/applet_resource.cpp b/src/core/hle/service/hid/controllers/applet_resource.cpp
index 435b86233..c8e74c764 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.cpp
+++ b/src/core/hle/service/hid/controllers/applet_resource.cpp
@@ -4,6 +4,7 @@
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h" 5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/applet_resource.h" 6#include "core/hle/service/hid/controllers/applet_resource.h"
7#include "core/hle/service/hid/controllers/shared_memory_format.h"
7#include "core/hle/service/hid/errors.h" 8#include "core/hle/service/hid/errors.h"
8 9
9namespace Service::HID { 10namespace Service::HID {
@@ -23,11 +24,24 @@ Result AppletResource::CreateAppletResource(u64 aruid) {
23 return ResultAruidAlreadyRegistered; 24 return ResultAruidAlreadyRegistered;
24 } 25 }
25 26
26 // TODO: Here shared memory is created for the process we don't quite emulate this part so 27 auto& shared_memory = shared_memory_holder[index];
27 // obtain this pointer from system 28 if (!shared_memory.IsMapped()) {
28 auto& shared_memory = system.Kernel().GetHidSharedMem(); 29 const Result result = shared_memory.Initialize(system);
30 if (result.IsError()) {
31 return result;
32 }
33 if (shared_memory.GetAddress() == nullptr) {
34 shared_memory.Finalize();
35 return ResultSharedMemoryNotInitialized;
36 }
37 }
38
39 auto* shared_memory_format = shared_memory.GetAddress();
40 if (shared_memory_format != nullptr) {
41 shared_memory_format->Initialize();
42 }
29 43
30 data[index].shared_memory_handle = &shared_memory; 44 data[index].shared_memory_format = shared_memory_format;
31 data[index].flag.is_assigned.Assign(true); 45 data[index].flag.is_assigned.Assign(true);
32 // TODO: InitializeSixAxisControllerConfig(false); 46 // TODO: InitializeSixAxisControllerConfig(false);
33 active_aruid = aruid; 47 active_aruid = aruid;
@@ -94,7 +108,7 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
94 108
95 if (index < AruidIndexMax) { 109 if (index < AruidIndexMax) {
96 if (data[index].flag.is_assigned) { 110 if (data[index].flag.is_assigned) {
97 data[index].shared_memory_handle = nullptr; 111 data[index].shared_memory_format = nullptr;
98 data[index].flag.is_assigned.Assign(false); 112 data[index].flag.is_assigned.Assign(false);
99 } 113 }
100 } 114 }
@@ -120,7 +134,7 @@ void AppletResource::FreeAppletResourceId(u64 aruid) {
120 134
121 auto& aruid_data = data[index]; 135 auto& aruid_data = data[index];
122 if (aruid_data.flag.is_assigned) { 136 if (aruid_data.flag.is_assigned) {
123 aruid_data.shared_memory_handle = nullptr; 137 aruid_data.shared_memory_format = nullptr;
124 aruid_data.flag.is_assigned.Assign(false); 138 aruid_data.flag.is_assigned.Assign(false);
125 } 139 }
126} 140}
@@ -135,7 +149,18 @@ Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle,
135 return ResultAruidNotRegistered; 149 return ResultAruidNotRegistered;
136 } 150 }
137 151
138 *out_handle = data[index].shared_memory_handle; 152 *out_handle = shared_memory_holder[index].GetHandle();
153 return ResultSuccess;
154}
155
156Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format,
157 u64 aruid) {
158 u64 index = GetIndexFromAruid(aruid);
159 if (index >= AruidIndexMax) {
160 return ResultAruidNotRegistered;
161 }
162
163 *out_shared_memory_format = data[index].shared_memory_format;
139 return ResultSuccess; 164 return ResultSuccess;
140} 165}
141 166
diff --git a/src/core/hle/service/hid/controllers/applet_resource.h b/src/core/hle/service/hid/controllers/applet_resource.h
index 62137db13..e7991f93a 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.h
+++ b/src/core/hle/service/hid/controllers/applet_resource.h
@@ -8,6 +8,7 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11#include "core/hle/service/hid/controllers/shared_memory_holder.h"
11 12
12namespace Core { 13namespace Core {
13class System; 14class System;
@@ -18,6 +19,8 @@ class KSharedMemory;
18} 19}
19 20
20namespace Service::HID { 21namespace Service::HID {
22struct SharedMemoryFormat;
23
21class AppletResource { 24class AppletResource {
22public: 25public:
23 explicit AppletResource(Core::System& system_); 26 explicit AppletResource(Core::System& system_);
@@ -32,6 +35,7 @@ public:
32 35
33 u64 GetActiveAruid(); 36 u64 GetActiveAruid();
34 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid); 37 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
38 Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
35 39
36 u64 GetIndexFromAruid(u64 aruid); 40 u64 GetIndexFromAruid(u64 aruid);
37 41
@@ -80,12 +84,13 @@ private:
80 struct AruidData { 84 struct AruidData {
81 DataStatusFlag flag{}; 85 DataStatusFlag flag{};
82 u64 aruid{}; 86 u64 aruid{};
83 Kernel::KSharedMemory* shared_memory_handle{nullptr}; 87 SharedMemoryFormat* shared_memory_format{nullptr};
84 }; 88 };
85 89
86 u64 active_aruid{}; 90 u64 active_aruid{};
87 AruidRegisterList registration_list{}; 91 AruidRegisterList registration_list{};
88 std::array<AruidData, AruidIndexMax> data{}; 92 std::array<AruidData, AruidIndexMax> data{};
93 std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
89 s32 ref_counter{}; 94 s32 ref_counter{};
90 95
91 Core::System& system; 96 Core::System& system;
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
index b2bf1d78d..3961d2b5f 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -1,23 +1,18 @@
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#include "core/core.h"
5#include "core/core_timing.h" 4#include "core/core_timing.h"
6#include "core/hid/emulated_console.h" 5#include "core/hid/emulated_console.h"
7#include "core/hid/hid_core.h" 6#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_six_axis.h" 7#include "core/hle/service/hid/controllers/console_six_axis.h"
9#include "core/memory.h" 8#include "core/hle/service/hid/controllers/shared_memory_format.h"
10 9
11namespace Service::HID { 10namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
13 11
14ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 12ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
15 : ControllerBase{hid_core_} { 13 ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory)
14 : ControllerBase{hid_core_}, shared_memory{console_shared_memory} {
16 console = hid_core.GetEmulatedConsole(); 15 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21} 16}
22 17
23ConsoleSixAxis::~ConsoleSixAxis() = default; 18ConsoleSixAxis::~ConsoleSixAxis() = default;
@@ -33,10 +28,10 @@ void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
33 28
34 const auto motion_status = console->GetMotion(); 29 const auto motion_status = console->GetMotion();
35 30
36 shared_memory->sampling_number++; 31 shared_memory.sampling_number++;
37 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; 32 shared_memory.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
38 shared_memory->verticalization_error = motion_status.verticalization_error; 33 shared_memory.verticalization_error = motion_status.verticalization_error;
39 shared_memory->gyro_bias = motion_status.gyro_bias; 34 shared_memory.gyro_bias = motion_status.gyro_bias;
40} 35}
41 36
42} // namespace Service::HID 37} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
index 5b7c6a29a..3d1c9ce23 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.h
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -3,7 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/vector_math.h"
7#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
8 7
9namespace Core::HID { 8namespace Core::HID {
@@ -11,9 +10,12 @@ class EmulatedConsole;
11} // namespace Core::HID 10} // namespace Core::HID
12 11
13namespace Service::HID { 12namespace Service::HID {
13struct ConsoleSixAxisSensorSharedMemoryFormat;
14
14class ConsoleSixAxis final : public ControllerBase { 15class ConsoleSixAxis final : public ControllerBase {
15public: 16public:
16 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 17 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
18 ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory);
17 ~ConsoleSixAxis() override; 19 ~ConsoleSixAxis() override;
18 20
19 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -26,18 +28,7 @@ public:
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
27 29
28private: 30private:
29 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat 31 ConsoleSixAxisSensorSharedMemoryFormat& shared_memory;
30 struct ConsoleSharedMemory {
31 u64 sampling_number{};
32 bool is_seven_six_axis_sensor_at_rest{};
33 INSERT_PADDING_BYTES(3); // padding
34 f32 verticalization_error{};
35 Common::Vec3f gyro_bias{};
36 INSERT_PADDING_BYTES(4); // padding
37 };
38 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
39
40 ConsoleSharedMemory* shared_memory = nullptr;
41 Core::HID::EmulatedConsole* console = nullptr; 32 Core::HID::EmulatedConsole* console = nullptr;
42}; 33};
43} // namespace Service::HID 34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 9a44ee41e..4326c7821 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -39,9 +39,6 @@ public:
39 39
40 bool IsControllerActivated() const; 40 bool IsControllerActivated() const;
41 41
42 static const std::size_t hid_entry_count = 17;
43 static const std::size_t shared_memory_size = 0x40000;
44
45protected: 42protected:
46 bool is_activated{false}; 43 bool is_activated{false};
47 44
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 9de19ebfc..7d2370b4f 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -1,24 +1,19 @@
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 <cstring>
5#include "common/common_types.h"
6#include "common/settings.h" 4#include "common/settings.h"
7#include "core/core_timing.h" 5#include "core/core_timing.h"
8#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
9#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
10#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
11#include "core/hle/service/hid/controllers/debug_pad.h" 9#include "core/hle/service/hid/controllers/debug_pad.h"
10#include "core/hle/service/hid/controllers/shared_memory_format.h"
12 11
13namespace Service::HID { 12namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; 13
15 14DebugPad::DebugPad(Core::HID::HIDCore& hid_core_,
16DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15 DebugPadSharedMemoryFormat& debug_pad_shared_memory)
17 : ControllerBase{hid_core_} { 16 : ControllerBase{hid_core_}, shared_memory{debug_pad_shared_memory} {
18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
19 "DebugPadSharedMemory is bigger than the shared memory");
20 shared_memory = std::construct_at(
21 reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 17 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
23} 18}
24 19
@@ -30,12 +25,12 @@ void DebugPad::OnRelease() {}
30 25
31void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 26void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
32 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
33 shared_memory->debug_pad_lifo.buffer_count = 0; 28 shared_memory.debug_pad_lifo.buffer_count = 0;
34 shared_memory->debug_pad_lifo.buffer_tail = 0; 29 shared_memory.debug_pad_lifo.buffer_tail = 0;
35 return; 30 return;
36 } 31 }
37 32
38 const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state; 33 const auto& last_entry = shared_memory.debug_pad_lifo.ReadCurrentEntry().state;
39 next_state.sampling_number = last_entry.sampling_number + 1; 34 next_state.sampling_number = last_entry.sampling_number + 1;
40 35
41 if (Settings::values.debug_pad_enabled) { 36 if (Settings::values.debug_pad_enabled) {
@@ -49,7 +44,7 @@ void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
49 next_state.r_stick = stick_state.right; 44 next_state.r_stick = stick_state.right;
50 } 45 }
51 46
52 shared_memory->debug_pad_lifo.WriteNextEntry(next_state); 47 shared_memory.debug_pad_lifo.WriteNextEntry(next_state);
53} 48}
54 49
55} // namespace Service::HID 50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 5566dba77..8ab29eca8 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -3,21 +3,24 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/hid/ring_lifo.h" 7#include "core/hle/service/hid/controllers/types/debug_pad_types.h"
10 8
11namespace Core::HID { 9namespace Core::HID {
12class EmulatedController; 10class HIDCore;
13struct DebugPadButton; 11}
14struct AnalogStickState; 12
15} // namespace Core::HID 13namespace Core::Timing {
14class CoreTiming;
15}
16 16
17namespace Service::HID { 17namespace Service::HID {
18struct DebugPadSharedMemoryFormat;
19
18class DebugPad final : public ControllerBase { 20class DebugPad final : public ControllerBase {
19public: 21public:
20 explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 22 explicit DebugPad(Core::HID::HIDCore& hid_core_,
23 DebugPadSharedMemoryFormat& debug_pad_shared_memory);
21 ~DebugPad() override; 24 ~DebugPad() override;
22 25
23 // Called when the controller is initialized 26 // Called when the controller is initialized
@@ -30,35 +33,8 @@ public:
30 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 33 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
31 34
32private: 35private:
33 // This is nn::hid::DebugPadAttribute
34 struct DebugPadAttribute {
35 union {
36 u32 raw{};
37 BitField<0, 1, u32> connected;
38 };
39 };
40 static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
41
42 // This is nn::hid::DebugPadState
43 struct DebugPadState {
44 s64 sampling_number{};
45 DebugPadAttribute attribute{};
46 Core::HID::DebugPadButton pad_state{};
47 Core::HID::AnalogStickState r_stick{};
48 Core::HID::AnalogStickState l_stick{};
49 };
50 static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
51
52 struct DebugPadSharedMemory {
53 // This is nn::hid::detail::DebugPadLifo
54 Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
55 static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
56 INSERT_PADDING_WORDS(0x4E);
57 };
58 static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
59
60 DebugPadState next_state{}; 36 DebugPadState next_state{};
61 DebugPadSharedMemory* shared_memory = nullptr; 37 DebugPadSharedMemoryFormat& shared_memory;
62 Core::HID::EmulatedController* controller = nullptr; 38 Core::HID::EmulatedController* controller = nullptr;
63}; 39};
64} // namespace Service::HID 40} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 59b2ec73c..f658005f6 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -1,17 +1,15 @@
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#include "common/logging/log.h"
5#include "common/math_util.h" 4#include "common/math_util.h"
6#include "common/settings.h" 5#include "common/settings.h"
7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
7#include "core/hid/emulated_console.h"
9#include "core/hid/hid_core.h" 8#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/gesture.h" 9#include "core/hle/service/hid/controllers/gesture.h"
10#include "core/hle/service/hid/controllers/shared_memory_format.h"
11 11
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
14
15// HW is around 700, value is set to 400 to make it easier to trigger with mouse 13// HW is around 700, value is set to 400 to make it easier to trigger with mouse
16constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s 14constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
17constexpr f32 angle_threshold = 0.015f; // Threshold in radians 15constexpr f32 angle_threshold = 0.015f; // Threshold in radians
@@ -23,19 +21,15 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 21 return static_cast<f32>(num * num);
24} 22}
25 23
26Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 24Gesture::Gesture(Core::HID::HIDCore& hid_core_, GestureSharedMemoryFormat& gesture_shared_memory)
27 : ControllerBase(hid_core_) { 25 : ControllerBase(hid_core_), shared_memory{gesture_shared_memory} {
28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
29 "GestureSharedMemory is bigger than the shared memory");
30 shared_memory = std::construct_at(
31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
32 console = hid_core.GetEmulatedConsole(); 26 console = hid_core.GetEmulatedConsole();
33} 27}
34Gesture::~Gesture() = default; 28Gesture::~Gesture() = default;
35 29
36void Gesture::OnInit() { 30void Gesture::OnInit() {
37 shared_memory->gesture_lifo.buffer_count = 0; 31 shared_memory.gesture_lifo.buffer_count = 0;
38 shared_memory->gesture_lifo.buffer_tail = 0; 32 shared_memory.gesture_lifo.buffer_tail = 0;
39 force_update = true; 33 force_update = true;
40} 34}
41 35
@@ -43,8 +37,8 @@ void Gesture::OnRelease() {}
43 37
44void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 38void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
45 if (!IsControllerActivated()) { 39 if (!IsControllerActivated()) {
46 shared_memory->gesture_lifo.buffer_count = 0; 40 shared_memory.gesture_lifo.buffer_count = 0;
47 shared_memory->gesture_lifo.buffer_tail = 0; 41 shared_memory.gesture_lifo.buffer_tail = 0;
48 return; 42 return;
49 } 43 }
50 44
@@ -52,7 +46,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
52 46
53 GestureProperties gesture = GetGestureProperties(); 47 GestureProperties gesture = GetGestureProperties();
54 f32 time_difference = 48 f32 time_difference =
55 static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / 49 static_cast<f32>(shared_memory.gesture_lifo.timestamp - last_update_timestamp) /
56 (1000 * 1000 * 1000); 50 (1000 * 1000 * 1000);
57 51
58 // Only update if necessary 52 // Only update if necessary
@@ -60,7 +54,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
60 return; 54 return;
61 } 55 }
62 56
63 last_update_timestamp = shared_memory->gesture_lifo.timestamp; 57 last_update_timestamp = shared_memory.gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(gesture, time_difference); 58 UpdateGestureSharedMemory(gesture, time_difference);
65} 59}
66 60
@@ -103,7 +97,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
103 GestureType type = GestureType::Idle; 97 GestureType type = GestureType::Idle;
104 GestureAttribute attributes{}; 98 GestureAttribute attributes{};
105 99
106 const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; 100 const auto& last_entry = shared_memory.gesture_lifo.ReadCurrentEntry().state;
107 101
108 // Reset next state to default 102 // Reset next state to default
109 next_state.sampling_number = last_entry.sampling_number + 1; 103 next_state.sampling_number = last_entry.sampling_number + 1;
@@ -133,7 +127,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
133 next_state.points = gesture.points; 127 next_state.points = gesture.points;
134 last_gesture = gesture; 128 last_gesture = gesture;
135 129
136 shared_memory->gesture_lifo.WriteNextEntry(next_state); 130 shared_memory.gesture_lifo.WriteNextEntry(next_state);
137} 131}
138 132
139void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, 133void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
@@ -305,11 +299,11 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
305 next_state.direction = GestureDirection::Up; 299 next_state.direction = GestureDirection::Up;
306} 300}
307 301
308const Gesture::GestureState& Gesture::GetLastGestureEntry() const { 302const GestureState& Gesture::GetLastGestureEntry() const {
309 return shared_memory->gesture_lifo.ReadCurrentEntry().state; 303 return shared_memory.gesture_lifo.ReadCurrentEntry().state;
310} 304}
311 305
312Gesture::GestureProperties Gesture::GetGestureProperties() { 306GestureProperties Gesture::GetGestureProperties() {
313 GestureProperties gesture; 307 GestureProperties gesture;
314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; 308 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 309 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 4c6f8ee07..41fdfcd03 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -4,17 +4,22 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include "common/bit_field.h" 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/point.h"
10#include "core/hid/emulated_console.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/controllers/types/touch_types.h"
11
12namespace Core::HID {
13class EmulatedConsole;
14}
13 15
14namespace Service::HID { 16namespace Service::HID {
17struct GestureSharedMemoryFormat;
18
15class Gesture final : public ControllerBase { 19class Gesture final : public ControllerBase {
16public: 20public:
17 explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 21 explicit Gesture(Core::HID::HIDCore& hid_core_,
22 GestureSharedMemoryFormat& gesture_shared_memory);
18 ~Gesture() override; 23 ~Gesture() override;
19 24
20 // Called when the controller is initialized 25 // Called when the controller is initialized
@@ -27,79 +32,6 @@ public:
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 32 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
28 33
29private: 34private:
30 static constexpr size_t MAX_FINGERS = 16;
31 static constexpr size_t MAX_POINTS = 4;
32
33 // This is nn::hid::GestureType
34 enum class GestureType : u32 {
35 Idle, // Nothing touching the screen
36 Complete, // Set at the end of a touch event
37 Cancel, // Set when the number of fingers change
38 Touch, // A finger just touched the screen
39 Press, // Set if last type is touch and the finger hasn't moved
40 Tap, // Fast press then release
41 Pan, // All points moving together across the screen
42 Swipe, // Fast press movement and release of a single point
43 Pinch, // All points moving away/closer to the midpoint
44 Rotate, // All points rotating from the midpoint
45 };
46
47 // This is nn::hid::GestureDirection
48 enum class GestureDirection : u32 {
49 None,
50 Left,
51 Up,
52 Right,
53 Down,
54 };
55
56 // This is nn::hid::GestureAttribute
57 struct GestureAttribute {
58 union {
59 u32 raw{};
60
61 BitField<4, 1, u32> is_new_touch;
62 BitField<8, 1, u32> is_double_tap;
63 };
64 };
65 static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
66
67 // This is nn::hid::GestureState
68 struct GestureState {
69 s64 sampling_number{};
70 s64 detection_count{};
71 GestureType type{GestureType::Idle};
72 GestureDirection direction{GestureDirection::None};
73 Common::Point<s32> pos{};
74 Common::Point<s32> delta{};
75 f32 vel_x{};
76 f32 vel_y{};
77 GestureAttribute attributes{};
78 f32 scale{};
79 f32 rotation_angle{};
80 s32 point_count{};
81 std::array<Common::Point<s32>, 4> points{};
82 };
83 static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
84
85 struct GestureProperties {
86 std::array<Common::Point<s32>, MAX_POINTS> points{};
87 std::size_t active_points{};
88 Common::Point<s32> mid_point{};
89 s64 detection_count{};
90 u64 delta_time{};
91 f32 average_distance{};
92 f32 angle{};
93 };
94
95 struct GestureSharedMemory {
96 // This is nn::hid::detail::GestureLifo
97 Lifo<GestureState, hid_entry_count> gesture_lifo{};
98 static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
99 INSERT_PADDING_WORDS(0x3E);
100 };
101 static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
102
103 // Reads input from all available input engines 35 // Reads input from all available input engines
104 void ReadTouchInput(); 36 void ReadTouchInput();
105 37
@@ -142,7 +74,7 @@ private:
142 GestureProperties GetGestureProperties(); 74 GestureProperties GetGestureProperties();
143 75
144 GestureState next_state{}; 76 GestureState next_state{};
145 GestureSharedMemory* shared_memory = nullptr; 77 GestureSharedMemoryFormat& shared_memory;
146 Core::HID::EmulatedConsole* console = nullptr; 78 Core::HID::EmulatedConsole* console = nullptr;
147 79
148 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; 80 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index ddb1b0ba4..871e5036a 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -1,23 +1,18 @@
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 <cstring>
5#include "common/common_types.h"
6#include "common/settings.h" 4#include "common/settings.h"
7#include "core/core_timing.h" 5#include "core/core_timing.h"
8#include "core/hid/emulated_devices.h" 6#include "core/hid/emulated_devices.h"
9#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/keyboard.h" 8#include "core/hle/service/hid/controllers/keyboard.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h"
11 10
12namespace Service::HID { 11namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12
14 13Keyboard::Keyboard(Core::HID::HIDCore& hid_core_,
15Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 14 KeyboardSharedMemoryFormat& keyboard_shared_memory)
16 : ControllerBase{hid_core_} { 15 : ControllerBase{hid_core_}, shared_memory{keyboard_shared_memory} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
18 "KeyboardSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21 emulated_devices = hid_core.GetEmulatedDevices(); 16 emulated_devices = hid_core.GetEmulatedDevices();
22} 17}
23 18
@@ -29,12 +24,12 @@ void Keyboard::OnRelease() {}
29 24
30void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 25void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
32 shared_memory->keyboard_lifo.buffer_count = 0; 27 shared_memory.keyboard_lifo.buffer_count = 0;
33 shared_memory->keyboard_lifo.buffer_tail = 0; 28 shared_memory.keyboard_lifo.buffer_tail = 0;
34 return; 29 return;
35 } 30 }
36 31
37 const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state; 32 const auto& last_entry = shared_memory.keyboard_lifo.ReadCurrentEntry().state;
38 next_state.sampling_number = last_entry.sampling_number + 1; 33 next_state.sampling_number = last_entry.sampling_number + 1;
39 34
40 if (Settings::values.keyboard_enabled) { 35 if (Settings::values.keyboard_enabled) {
@@ -46,7 +41,7 @@ void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
46 next_state.attribute.is_connected.Assign(1); 41 next_state.attribute.is_connected.Assign(1);
47 } 42 }
48 43
49 shared_memory->keyboard_lifo.WriteNextEntry(next_state); 44 shared_memory.keyboard_lifo.WriteNextEntry(next_state);
50} 45}
51 46
52} // namespace Service::HID 47} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 172ec1309..4d72171b9 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -3,20 +3,16 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h"
7#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
8#include "core/hle/service/hid/ring_lifo.h" 7#include "core/hle/service/hid/controllers/types/keyboard_types.h"
9
10namespace Core::HID {
11class EmulatedDevices;
12struct KeyboardModifier;
13struct KeyboardKey;
14} // namespace Core::HID
15 8
16namespace Service::HID { 9namespace Service::HID {
10struct KeyboardSharedMemoryFormat;
11
17class Keyboard final : public ControllerBase { 12class Keyboard final : public ControllerBase {
18public: 13public:
19 explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 14 explicit Keyboard(Core::HID::HIDCore& hid_core_,
15 KeyboardSharedMemoryFormat& keyboard_shared_memory);
20 ~Keyboard() override; 16 ~Keyboard() override;
21 17
22 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -29,25 +25,8 @@ public:
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 25 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
30 26
31private: 27private:
32 // This is nn::hid::detail::KeyboardState
33 struct KeyboardState {
34 s64 sampling_number{};
35 Core::HID::KeyboardModifier modifier{};
36 Core::HID::KeyboardAttribute attribute{};
37 Core::HID::KeyboardKey key{};
38 };
39 static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
40
41 struct KeyboardSharedMemory {
42 // This is nn::hid::detail::KeyboardLifo
43 Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
44 static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
45 INSERT_PADDING_WORDS(0xA);
46 };
47 static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
48
49 KeyboardState next_state{}; 28 KeyboardState next_state{};
50 KeyboardSharedMemory* shared_memory = nullptr; 29 KeyboardSharedMemoryFormat& shared_memory;
51 Core::HID::EmulatedDevices* emulated_devices = nullptr; 30 Core::HID::EmulatedDevices* emulated_devices = nullptr;
52}; 31};
53} // namespace Service::HID 32} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 6e5a04e34..de5b2c804 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -1,22 +1,17 @@
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 <cstring>
5#include "common/common_types.h"
6#include "core/core_timing.h" 4#include "core/core_timing.h"
7#include "core/frontend/emu_window.h" 5#include "core/frontend/emu_window.h"
8#include "core/hid/emulated_devices.h" 6#include "core/hid/emulated_devices.h"
9#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/mouse.h" 8#include "core/hle/service/hid/controllers/mouse.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h"
11 10
12namespace Service::HID { 11namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
14 12
15Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { 13Mouse::Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory)
16 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, 14 : ControllerBase{hid_core_}, shared_memory{mouse_shared_memory} {
17 "MouseSharedMemory is bigger than the shared memory");
18 shared_memory = std::construct_at(
19 reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
20 emulated_devices = hid_core.GetEmulatedDevices(); 15 emulated_devices = hid_core.GetEmulatedDevices();
21} 16}
22 17
@@ -27,14 +22,14 @@ void Mouse::OnRelease() {}
27 22
28void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 23void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
29 if (!IsControllerActivated()) { 24 if (!IsControllerActivated()) {
30 shared_memory->mouse_lifo.buffer_count = 0; 25 shared_memory.mouse_lifo.buffer_count = 0;
31 shared_memory->mouse_lifo.buffer_tail = 0; 26 shared_memory.mouse_lifo.buffer_tail = 0;
32 return; 27 return;
33 } 28 }
34 29
35 next_state = {}; 30 next_state = {};
36 31
37 const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state; 32 const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
38 next_state.sampling_number = last_entry.sampling_number + 1; 33 next_state.sampling_number = last_entry.sampling_number + 1;
39 34
40 if (Settings::values.mouse_enabled) { 35 if (Settings::values.mouse_enabled) {
@@ -53,7 +48,7 @@ void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
53 next_state.button = mouse_button_state; 48 next_state.button = mouse_button_state;
54 } 49 }
55 50
56 shared_memory->mouse_lifo.WriteNextEntry(next_state); 51 shared_memory.mouse_lifo.WriteNextEntry(next_state);
57} 52}
58 53
59} // namespace Service::HID 54} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index a80f3823f..363f316a5 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -3,9 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h"
7#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
8#include "core/hle/service/hid/ring_lifo.h"
9 7
10namespace Core::HID { 8namespace Core::HID {
11class EmulatedDevices; 9class EmulatedDevices;
@@ -14,9 +12,11 @@ struct AnalogStickState;
14} // namespace Core::HID 12} // namespace Core::HID
15 13
16namespace Service::HID { 14namespace Service::HID {
15struct MouseSharedMemoryFormat;
16
17class Mouse final : public ControllerBase { 17class Mouse final : public ControllerBase {
18public: 18public:
19 explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory);
20 ~Mouse() override; 20 ~Mouse() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
@@ -29,17 +29,9 @@ public:
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 29 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
30 30
31private: 31private:
32 struct MouseSharedMemory {
33 // This is nn::hid::detail::MouseLifo
34 Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
35 static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
36 INSERT_PADDING_WORDS(0x2C);
37 };
38 static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
39
40 Core::HID::MouseState next_state{}; 32 Core::HID::MouseState next_state{};
41 Core::HID::AnalogStickState last_mouse_wheel_state{}; 33 Core::HID::AnalogStickState last_mouse_wheel_state{};
42 MouseSharedMemory* shared_memory = nullptr; 34 MouseSharedMemoryFormat& shared_memory;
43 Core::HID::EmulatedDevices* emulated_devices = nullptr; 35 Core::HID::EmulatedDevices* emulated_devices = nullptr;
44}; 36};
45} // namespace Service::HID 37} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 08ee9de9c..53a737cf5 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,12 +17,12 @@
17#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
18#include "core/hle/kernel/k_readable_event.h" 18#include "core/hle/kernel/k_readable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/controllers/shared_memory_format.h"
20#include "core/hle/service/hid/errors.h" 21#include "core/hle/service/hid/errors.h"
21#include "core/hle/service/hid/hid_util.h" 22#include "core/hle/service/hid/hid_util.h"
22#include "core/hle/service/kernel_helpers.h" 23#include "core/hle/service/kernel_helpers.h"
23 24
24namespace Service::HID { 25namespace Service::HID {
25constexpr std::size_t NPAD_OFFSET = 0x9A00;
26constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{ 26constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
27 Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3, 27 Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
28 Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6, 28 Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
@@ -30,14 +30,12 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
30 Core::HID::NpadIdType::Handheld, 30 Core::HID::NpadIdType::Handheld,
31}; 31};
32 32
33NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 33NPad::NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
34 KernelHelpers::ServiceContext& service_context_) 34 KernelHelpers::ServiceContext& service_context_)
35 : ControllerBase{hid_core_}, service_context{service_context_} { 35 : ControllerBase{hid_core_}, service_context{service_context_} {
36 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
37 for (std::size_t i = 0; i < controller_data.size(); ++i) { 36 for (std::size_t i = 0; i < controller_data.size(); ++i) {
38 auto& controller = controller_data[i]; 37 auto& controller = controller_data[i];
39 controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>( 38 controller.shared_memory = &npad_shared_memory_format.npad_entry[i].internal_state;
40 raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState))));
41 controller.device = hid_core.GetEmulatedControllerByIndex(i); 39 controller.device = hid_core.GetEmulatedControllerByIndex(i);
42 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = 40 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
43 Core::HID::DEFAULT_VIBRATION_VALUE; 41 Core::HID::DEFAULT_VIBRATION_VALUE;
@@ -617,7 +615,7 @@ void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
617 hold_type = joy_hold_type; 615 hold_type = joy_hold_type;
618} 616}
619 617
620NPad::NpadJoyHoldType NPad::GetHoldType() const { 618NpadJoyHoldType NPad::GetHoldType() const {
621 return hold_type; 619 return hold_type;
622} 620}
623 621
@@ -630,7 +628,7 @@ void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_m
630 handheld_activation_mode = activation_mode; 628 handheld_activation_mode = activation_mode;
631} 629}
632 630
633NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { 631NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
634 return handheld_activation_mode; 632 return handheld_activation_mode;
635} 633}
636 634
@@ -638,7 +636,7 @@ void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
638 communication_mode = communication_mode_; 636 communication_mode = communication_mode_;
639} 637}
640 638
641NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const { 639NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
642 return communication_mode; 640 return communication_mode;
643} 641}
644 642
@@ -978,27 +976,27 @@ Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
978 return ResultSuccess; 976 return ResultSuccess;
979} 977}
980 978
981NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) { 979NpadSixAxisSensorLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
982 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo; 980 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
983} 981}
984 982
985NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) { 983NpadSixAxisSensorLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
986 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo; 984 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
987} 985}
988 986
989NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) { 987NpadSixAxisSensorLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
990 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo; 988 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
991} 989}
992 990
993NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) { 991NpadSixAxisSensorLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
994 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo; 992 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
995} 993}
996 994
997NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) { 995NpadSixAxisSensorLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
998 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo; 996 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
999} 997}
1000 998
1001NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) { 999NpadSixAxisSensorLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1002 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo; 1000 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1003} 1001}
1004 1002
@@ -1343,7 +1341,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1343 } 1341 }
1344} 1342}
1345 1343
1346NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { 1344AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
1347 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; 1345 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
1348 1346
1349 return { 1347 return {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9167c93f0..4e2412356 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -8,12 +8,10 @@
8#include <mutex> 8#include <mutex>
9#include <span> 9#include <span>
10 10
11#include "common/bit_field.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13
14#include "core/hid/hid_types.h" 12#include "core/hid/hid_types.h"
15#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
16#include "core/hle/service/hid/ring_lifo.h" 14#include "core/hle/service/hid/controllers/types/npad_types.h"
17 15
18namespace Core::HID { 16namespace Core::HID {
19class EmulatedController; 17class EmulatedController;
@@ -32,10 +30,13 @@ class ServiceContext;
32union Result; 30union Result;
33 31
34namespace Service::HID { 32namespace Service::HID {
33struct NpadInternalState;
34struct NpadSixAxisSensorLifo;
35struct NpadSharedMemoryFormat;
35 36
36class NPad final : public ControllerBase { 37class NPad final : public ControllerBase {
37public: 38public:
38 explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 39 explicit NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
39 KernelHelpers::ServiceContext& service_context_); 40 KernelHelpers::ServiceContext& service_context_);
40 ~NPad() override; 41 ~NPad() override;
41 42
@@ -48,89 +49,6 @@ public:
48 // When the controller is requesting an update for the shared memory 49 // When the controller is requesting an update for the shared memory
49 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 50 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
50 51
51 // This is nn::hid::NpadJoyHoldType
52 enum class NpadJoyHoldType : u64 {
53 Vertical = 0,
54 Horizontal = 1,
55 };
56
57 // This is nn::hid::NpadJoyAssignmentMode
58 enum class NpadJoyAssignmentMode : u32 {
59 Dual = 0,
60 Single = 1,
61 };
62
63 // This is nn::hid::NpadJoyDeviceType
64 enum class NpadJoyDeviceType : s64 {
65 Left = 0,
66 Right = 1,
67 };
68
69 // This is nn::hid::NpadHandheldActivationMode
70 enum class NpadHandheldActivationMode : u64 {
71 Dual = 0,
72 Single = 1,
73 None = 2,
74 MaxActivationMode = 3,
75 };
76
77 // This is nn::hid::system::AppletFooterUiAttributesSet
78 struct AppletFooterUiAttributes {
79 INSERT_PADDING_BYTES(0x4);
80 };
81
82 // This is nn::hid::system::AppletFooterUiType
83 enum class AppletFooterUiType : u8 {
84 None = 0,
85 HandheldNone = 1,
86 HandheldJoyConLeftOnly = 2,
87 HandheldJoyConRightOnly = 3,
88 HandheldJoyConLeftJoyConRight = 4,
89 JoyDual = 5,
90 JoyDualLeftOnly = 6,
91 JoyDualRightOnly = 7,
92 JoyLeftHorizontal = 8,
93 JoyLeftVertical = 9,
94 JoyRightHorizontal = 10,
95 JoyRightVertical = 11,
96 SwitchProController = 12,
97 CompatibleProController = 13,
98 CompatibleJoyCon = 14,
99 LarkHvc1 = 15,
100 LarkHvc2 = 16,
101 LarkNesLeft = 17,
102 LarkNesRight = 18,
103 Lucia = 19,
104 Verification = 20,
105 Lagon = 21,
106 };
107
108 using AppletFooterUiVariant = u8;
109
110 // This is "nn::hid::system::AppletDetailedUiType".
111 struct AppletDetailedUiType {
112 AppletFooterUiVariant ui_variant;
113 INSERT_PADDING_BYTES(0x2);
114 AppletFooterUiType footer;
115 };
116 static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
117 // This is nn::hid::NpadCommunicationMode
118 enum class NpadCommunicationMode : u64 {
119 Mode_5ms = 0,
120 Mode_10ms = 1,
121 Mode_15ms = 2,
122 Default = 3,
123 };
124
125 enum class NpadRevision : u32 {
126 Revision0 = 0,
127 Revision1 = 1,
128 Revision2 = 2,
129 Revision3 = 3,
130 };
131
132 using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
133
134 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 52 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
135 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 53 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
136 54
@@ -188,12 +106,12 @@ public:
188 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 106 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
189 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 107 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
190 108
191 SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id); 109 NpadSixAxisSensorLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
192 SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id); 110 NpadSixAxisSensorLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
193 SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id); 111 NpadSixAxisSensorLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
194 SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id); 112 NpadSixAxisSensorLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
195 SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id); 113 NpadSixAxisSensorLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
196 SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id); 114 NpadSixAxisSensorLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
197 115
198 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; 116 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
199 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 117 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
@@ -221,214 +139,6 @@ public:
221 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); 139 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
222 140
223private: 141private:
224 static constexpr std::size_t NPAD_COUNT = 10;
225
226 // This is nn::hid::detail::ColorAttribute
227 enum class ColorAttribute : u32 {
228 Ok = 0,
229 ReadError = 1,
230 NoController = 2,
231 };
232 static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
233
234 // This is nn::hid::detail::NpadFullKeyColorState
235 struct NpadFullKeyColorState {
236 ColorAttribute attribute{ColorAttribute::NoController};
237 Core::HID::NpadControllerColor fullkey{};
238 };
239 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
240
241 // This is nn::hid::detail::NpadJoyColorState
242 struct NpadJoyColorState {
243 ColorAttribute attribute{ColorAttribute::NoController};
244 Core::HID::NpadControllerColor left{};
245 Core::HID::NpadControllerColor right{};
246 };
247 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
248
249 // This is nn::hid::NpadAttribute
250 struct NpadAttribute {
251 union {
252 u32 raw{};
253 BitField<0, 1, u32> is_connected;
254 BitField<1, 1, u32> is_wired;
255 BitField<2, 1, u32> is_left_connected;
256 BitField<3, 1, u32> is_left_wired;
257 BitField<4, 1, u32> is_right_connected;
258 BitField<5, 1, u32> is_right_wired;
259 };
260 };
261 static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
262
263 // This is nn::hid::NpadFullKeyState
264 // This is nn::hid::NpadHandheldState
265 // This is nn::hid::NpadJoyDualState
266 // This is nn::hid::NpadJoyLeftState
267 // This is nn::hid::NpadJoyRightState
268 // This is nn::hid::NpadPalmaState
269 // This is nn::hid::NpadSystemExtState
270 struct NPadGenericState {
271 s64_le sampling_number{};
272 Core::HID::NpadButtonState npad_buttons{};
273 Core::HID::AnalogStickState l_stick{};
274 Core::HID::AnalogStickState r_stick{};
275 NpadAttribute connection_status{};
276 INSERT_PADDING_BYTES(4); // Reserved
277 };
278 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
279
280 // This is nn::hid::server::NpadGcTriggerState
281 struct NpadGcTriggerState {
282 s64 sampling_number{};
283 s32 l_analog{};
284 s32 r_analog{};
285 };
286 static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
287
288 // This is nn::hid::NpadSystemProperties
289 struct NPadSystemProperties {
290 union {
291 s64 raw{};
292 BitField<0, 1, s64> is_charging_joy_dual;
293 BitField<1, 1, s64> is_charging_joy_left;
294 BitField<2, 1, s64> is_charging_joy_right;
295 BitField<3, 1, s64> is_powered_joy_dual;
296 BitField<4, 1, s64> is_powered_joy_left;
297 BitField<5, 1, s64> is_powered_joy_right;
298 BitField<9, 1, s64> is_system_unsupported_button;
299 BitField<10, 1, s64> is_system_ext_unsupported_button;
300 BitField<11, 1, s64> is_vertical;
301 BitField<12, 1, s64> is_horizontal;
302 BitField<13, 1, s64> use_plus;
303 BitField<14, 1, s64> use_minus;
304 BitField<15, 1, s64> use_directional_buttons;
305 };
306 };
307 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
308
309 // This is nn::hid::NpadSystemButtonProperties
310 struct NpadSystemButtonProperties {
311 union {
312 s32 raw{};
313 BitField<0, 1, s32> is_home_button_protection_enabled;
314 };
315 };
316 static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
317 "NPadButtonProperties is an invalid size");
318
319 // This is nn::hid::system::DeviceType
320 struct DeviceType {
321 union {
322 u32 raw{};
323 BitField<0, 1, s32> fullkey;
324 BitField<1, 1, s32> debug_pad;
325 BitField<2, 1, s32> handheld_left;
326 BitField<3, 1, s32> handheld_right;
327 BitField<4, 1, s32> joycon_left;
328 BitField<5, 1, s32> joycon_right;
329 BitField<6, 1, s32> palma;
330 BitField<7, 1, s32> lark_hvc_left;
331 BitField<8, 1, s32> lark_hvc_right;
332 BitField<9, 1, s32> lark_nes_left;
333 BitField<10, 1, s32> lark_nes_right;
334 BitField<11, 1, s32> handheld_lark_hvc_left;
335 BitField<12, 1, s32> handheld_lark_hvc_right;
336 BitField<13, 1, s32> handheld_lark_nes_left;
337 BitField<14, 1, s32> handheld_lark_nes_right;
338 BitField<15, 1, s32> lucia;
339 BitField<16, 1, s32> lagon;
340 BitField<17, 1, s32> lager;
341 BitField<31, 1, s32> system;
342 };
343 };
344
345 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
346 struct NfcXcdDeviceHandleStateImpl {
347 u64 handle{};
348 bool is_available{};
349 bool is_activated{};
350 INSERT_PADDING_BYTES(0x6); // Reserved
351 u64 sampling_number{};
352 };
353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
354 "NfcXcdDeviceHandleStateImpl is an invalid size");
355
356 // This is nn::hid::NpadLarkType
357 enum class NpadLarkType : u32 {
358 Invalid,
359 H1,
360 H2,
361 NL,
362 NR,
363 };
364
365 // This is nn::hid::NpadLuciaType
366 enum class NpadLuciaType : u32 {
367 Invalid,
368 J,
369 E,
370 U,
371 };
372
373 // This is nn::hid::NpadLagonType
374 enum class NpadLagonType : u32 {
375 Invalid,
376 };
377
378 // This is nn::hid::NpadLagerType
379 enum class NpadLagerType : u32 {
380 Invalid,
381 J,
382 E,
383 U,
384 };
385
386 // This is nn::hid::detail::NpadInternalState
387 struct NpadInternalState {
388 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
389 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
390 NpadFullKeyColorState fullkey_color{};
391 NpadJoyColorState joycon_color{};
392 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
393 Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
394 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
395 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
396 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
397 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
398 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
399 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
400 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
401 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
402 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
403 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
404 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
405 DeviceType device_type{};
406 INSERT_PADDING_BYTES(0x4); // Reserved
407 NPadSystemProperties system_properties{};
408 NpadSystemButtonProperties button_properties{};
409 Core::HID::NpadBatteryLevel battery_level_dual{};
410 Core::HID::NpadBatteryLevel battery_level_left{};
411 Core::HID::NpadBatteryLevel battery_level_right{};
412 AppletFooterUiAttributes applet_footer_attributes{};
413 AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
414 INSERT_PADDING_BYTES(0x5B); // Reserved
415 INSERT_PADDING_BYTES(0x20); // Unknown
416 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
417 NpadLarkType lark_type_l_and_main{};
418 NpadLarkType lark_type_r{};
419 NpadLuciaType lucia_type{};
420 NpadLagonType lagon_type{};
421 NpadLagerType lager_type{};
422 Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
423 Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
424 Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
425 Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
426 Core::HID::SixAxisSensorProperties sixaxis_left_properties;
427 Core::HID::SixAxisSensorProperties sixaxis_right_properties;
428 INSERT_PADDING_BYTES(0xc06); // Unknown
429 };
430 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
431
432 struct VibrationData { 142 struct VibrationData {
433 bool device_mounted{}; 143 bool device_mounted{};
434 Core::HID::VibrationValue latest_vibration_value{}; 144 Core::HID::VibrationValue latest_vibration_value{};
@@ -479,7 +189,7 @@ private:
479 189
480 std::atomic<u64> press_state{}; 190 std::atomic<u64> press_state{};
481 191
482 std::array<NpadControllerData, NPAD_COUNT> controller_data{}; 192 std::array<NpadControllerData, NpadCount> controller_data{};
483 KernelHelpers::ServiceContext& service_context; 193 KernelHelpers::ServiceContext& service_context;
484 std::mutex mutex; 194 std::mutex mutex;
485 std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; 195 std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 588ff9d62..aa0454b5e 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -12,8 +12,7 @@
12 12
13namespace Service::HID { 13namespace Service::HID {
14 14
15Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 15Palma::Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
16 KernelHelpers::ServiceContext& service_context_)
17 : ControllerBase{hid_core_}, service_context{service_context_} { 16 : ControllerBase{hid_core_}, service_context{service_context_} {
18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 17 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); 18 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h
index a6047f36a..73884230d 100644
--- a/src/core/hle/service/hid/controllers/palma.h
+++ b/src/core/hle/service/hid/controllers/palma.h
@@ -97,8 +97,7 @@ public:
97 static_assert(sizeof(PalmaConnectionHandle) == 0x8, 97 static_assert(sizeof(PalmaConnectionHandle) == 0x8,
98 "PalmaConnectionHandle has incorrect size."); 98 "PalmaConnectionHandle has incorrect size.");
99 99
100 explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 100 explicit Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
101 KernelHelpers::ServiceContext& service_context_);
102 ~Palma() override; 101 ~Palma() override;
103 102
104 // Called when the controller is initialized 103 // Called when the controller is initialized
diff --git a/src/core/hle/service/hid/controllers/shared_memory_format.h b/src/core/hle/service/hid/controllers/shared_memory_format.h
new file mode 100644
index 000000000..2986c113e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/shared_memory_format.h
@@ -0,0 +1,240 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "common/vector_math.h"
9#include "core/hid/hid_types.h"
10#include "core/hle/service/hid//controllers/types/debug_pad_types.h"
11#include "core/hle/service/hid//controllers/types/keyboard_types.h"
12#include "core/hle/service/hid//controllers/types/mouse_types.h"
13#include "core/hle/service/hid//controllers/types/npad_types.h"
14#include "core/hle/service/hid//controllers/types/touch_types.h"
15#include "core/hle/service/hid/ring_lifo.h"
16
17namespace Service::HID {
18static const std::size_t HidEntryCount = 17;
19
20struct CommonHeader {
21 s64 timestamp{};
22 s64 total_entry_count{};
23 s64 last_entry_index{};
24 s64 entry_count{};
25};
26static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
27
28// This is nn::hid::detail::DebugPadSharedMemoryFormat
29struct DebugPadSharedMemoryFormat {
30 // This is nn::hid::detail::DebugPadLifo
31 Lifo<DebugPadState, HidEntryCount> debug_pad_lifo{};
32 static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
33 INSERT_PADDING_WORDS(0x4E);
34};
35static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400,
36 "DebugPadSharedMemoryFormat is an invalid size");
37
38// This is nn::hid::detail::TouchScreenSharedMemoryFormat
39struct TouchScreenSharedMemoryFormat {
40 // This is nn::hid::detail::TouchScreenLifo
41 Lifo<TouchScreenState, HidEntryCount> touch_screen_lifo{};
42 static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
43 INSERT_PADDING_WORDS(0xF2);
44};
45static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000,
46 "TouchScreenSharedMemoryFormat is an invalid size");
47
48// This is nn::hid::detail::MouseSharedMemoryFormat
49struct MouseSharedMemoryFormat {
50 // This is nn::hid::detail::MouseLifo
51 Lifo<Core::HID::MouseState, HidEntryCount> mouse_lifo{};
52 static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
53 INSERT_PADDING_WORDS(0x2C);
54};
55static_assert(sizeof(MouseSharedMemoryFormat) == 0x400,
56 "MouseSharedMemoryFormat is an invalid size");
57
58// This is nn::hid::detail::KeyboardSharedMemoryFormat
59struct KeyboardSharedMemoryFormat {
60 // This is nn::hid::detail::KeyboardLifo
61 Lifo<KeyboardState, HidEntryCount> keyboard_lifo{};
62 static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
63 INSERT_PADDING_WORDS(0xA);
64};
65static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400,
66 "KeyboardSharedMemoryFormat is an invalid size");
67
68// This is nn::hid::detail::DigitizerSharedMemoryFormat
69struct DigitizerSharedMemoryFormat {
70 CommonHeader header;
71 INSERT_PADDING_BYTES(0xFE0);
72};
73static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000,
74 "DigitizerSharedMemoryFormat is an invalid size");
75
76// This is nn::hid::detail::HomeButtonSharedMemoryFormat
77struct HomeButtonSharedMemoryFormat {
78 CommonHeader header;
79 INSERT_PADDING_BYTES(0x1E0);
80};
81static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200,
82 "HomeButtonSharedMemoryFormat is an invalid size");
83
84// This is nn::hid::detail::SleepButtonSharedMemoryFormat
85struct SleepButtonSharedMemoryFormat {
86 CommonHeader header;
87 INSERT_PADDING_BYTES(0x1E0);
88};
89static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200,
90 "SleepButtonSharedMemoryFormat is an invalid size");
91
92// This is nn::hid::detail::CaptureButtonSharedMemoryFormat
93struct CaptureButtonSharedMemoryFormat {
94 CommonHeader header;
95 INSERT_PADDING_BYTES(0x1E0);
96};
97static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200,
98 "CaptureButtonSharedMemoryFormat is an invalid size");
99
100// This is nn::hid::detail::InputDetectorSharedMemoryFormat
101struct InputDetectorSharedMemoryFormat {
102 CommonHeader header;
103 INSERT_PADDING_BYTES(0x7E0);
104};
105static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800,
106 "InputDetectorSharedMemoryFormat is an invalid size");
107
108// This is nn::hid::detail::UniquePadSharedMemoryFormat
109struct UniquePadSharedMemoryFormat {
110 CommonHeader header;
111 INSERT_PADDING_BYTES(0x3FE0);
112};
113static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000,
114 "UniquePadSharedMemoryFormat is an invalid size");
115
116// This is nn::hid::detail::NpadSixAxisSensorLifo
117struct NpadSixAxisSensorLifo {
118 Lifo<Core::HID::SixAxisSensorState, HidEntryCount> lifo;
119};
120
121// This is nn::hid::detail::NpadInternalState
122struct NpadInternalState {
123 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
124 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
125 NpadFullKeyColorState fullkey_color{};
126 NpadJoyColorState joycon_color{};
127 Lifo<NPadGenericState, HidEntryCount> fullkey_lifo{};
128 Lifo<NPadGenericState, HidEntryCount> handheld_lifo{};
129 Lifo<NPadGenericState, HidEntryCount> joy_dual_lifo{};
130 Lifo<NPadGenericState, HidEntryCount> joy_left_lifo{};
131 Lifo<NPadGenericState, HidEntryCount> joy_right_lifo{};
132 Lifo<NPadGenericState, HidEntryCount> palma_lifo{};
133 Lifo<NPadGenericState, HidEntryCount> system_ext_lifo{};
134 NpadSixAxisSensorLifo sixaxis_fullkey_lifo{};
135 NpadSixAxisSensorLifo sixaxis_handheld_lifo{};
136 NpadSixAxisSensorLifo sixaxis_dual_left_lifo{};
137 NpadSixAxisSensorLifo sixaxis_dual_right_lifo{};
138 NpadSixAxisSensorLifo sixaxis_left_lifo{};
139 NpadSixAxisSensorLifo sixaxis_right_lifo{};
140 DeviceType device_type{};
141 INSERT_PADDING_BYTES(0x4); // Reserved
142 NPadSystemProperties system_properties{};
143 NpadSystemButtonProperties button_properties{};
144 Core::HID::NpadBatteryLevel battery_level_dual{};
145 Core::HID::NpadBatteryLevel battery_level_left{};
146 Core::HID::NpadBatteryLevel battery_level_right{};
147 AppletFooterUiAttributes applet_footer_attributes{};
148 AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
149 INSERT_PADDING_BYTES(0x5B); // Reserved
150 INSERT_PADDING_BYTES(0x20); // Unknown
151 Lifo<NpadGcTriggerState, HidEntryCount> gc_trigger_lifo{};
152 NpadLarkType lark_type_l_and_main{};
153 NpadLarkType lark_type_r{};
154 NpadLuciaType lucia_type{};
155 NpadLagerType lager_type{};
156 Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
157 Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
158 Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
159 Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
160 Core::HID::SixAxisSensorProperties sixaxis_left_properties;
161 Core::HID::SixAxisSensorProperties sixaxis_right_properties;
162};
163static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size");
164
165// This is nn::hid::detail::NpadSharedMemoryEntry
166struct NpadSharedMemoryEntry {
167 NpadInternalState internal_state;
168 INSERT_PADDING_BYTES(0xC08);
169};
170static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size");
171
172// This is nn::hid::detail::NpadSharedMemoryFormat
173struct NpadSharedMemoryFormat {
174 std::array<NpadSharedMemoryEntry, NpadCount> npad_entry;
175};
176static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
177 "NpadSharedMemoryFormat is an invalid size");
178
179// This is nn::hid::detail::GestureSharedMemoryFormat
180struct GestureSharedMemoryFormat {
181 // This is nn::hid::detail::GestureLifo
182 Lifo<GestureState, HidEntryCount> gesture_lifo{};
183 static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
184 INSERT_PADDING_WORDS(0x3E);
185};
186static_assert(sizeof(GestureSharedMemoryFormat) == 0x800,
187 "GestureSharedMemoryFormat is an invalid size");
188
189// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
190struct ConsoleSixAxisSensorSharedMemoryFormat {
191 u64 sampling_number{};
192 bool is_seven_six_axis_sensor_at_rest{};
193 INSERT_PADDING_BYTES(3); // padding
194 f32 verticalization_error{};
195 Common::Vec3f gyro_bias{};
196 INSERT_PADDING_BYTES(4); // padding
197};
198static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
199 "ConsoleSixAxisSensorSharedMemoryFormat is an invalid size");
200
201// This is nn::hid::detail::SharedMemoryFormat
202struct SharedMemoryFormat {
203 void Initialize() {}
204
205 DebugPadSharedMemoryFormat debug_pad;
206 TouchScreenSharedMemoryFormat touch_screen;
207 MouseSharedMemoryFormat mouse;
208 KeyboardSharedMemoryFormat keyboard;
209 DigitizerSharedMemoryFormat digitizer;
210 HomeButtonSharedMemoryFormat home_button;
211 SleepButtonSharedMemoryFormat sleep_button;
212 CaptureButtonSharedMemoryFormat capture_button;
213 InputDetectorSharedMemoryFormat input_detector;
214 UniquePadSharedMemoryFormat unique_pad;
215 NpadSharedMemoryFormat npad;
216 GestureSharedMemoryFormat gesture;
217 ConsoleSixAxisSensorSharedMemoryFormat console;
218 INSERT_PADDING_BYTES(0x19E0);
219 MouseSharedMemoryFormat debug_mouse;
220 INSERT_PADDING_BYTES(0x2000);
221};
222static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
223static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
224static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset");
225static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset");
226static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset");
227static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset");
228static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00,
229 "sleep_button has wrong offset");
230static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000,
231 "capture_button has wrong offset");
232static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200,
233 "input_detector has wrong offset");
234static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset");
235static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
236static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
237static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
238static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");
239
240} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
new file mode 100644
index 000000000..51581188e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/shared_memory_format.h"
7#include "core/hle/service/hid/controllers/shared_memory_holder.h"
8#include "core/hle/service/hid/errors.h"
9
10namespace Service::HID {
11SharedMemoryHolder::SharedMemoryHolder() {}
12
13SharedMemoryHolder::~SharedMemoryHolder() {
14 Finalize();
15}
16
17Result SharedMemoryHolder::Initialize(Core::System& system) {
18 shared_memory = Kernel::KSharedMemory::Create(system.Kernel());
19 const Result result = shared_memory->Initialize(
20 system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None,
21 Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat));
22 if (result.IsError()) {
23 return result;
24 }
25 Kernel::KSharedMemory::Register(system.Kernel(), shared_memory);
26
27 is_created = true;
28 is_mapped = true;
29 address = std::construct_at(reinterpret_cast<SharedMemoryFormat*>(shared_memory->GetPointer()));
30 return ResultSuccess;
31}
32
33void SharedMemoryHolder::Finalize() {
34 if (address != nullptr) {
35 shared_memory->Close();
36 }
37 is_created = false;
38 is_mapped = false;
39 address = nullptr;
40}
41
42bool SharedMemoryHolder::IsMapped() {
43 return is_mapped;
44}
45
46SharedMemoryFormat* SharedMemoryHolder::GetAddress() {
47 return address;
48}
49
50Kernel::KSharedMemory* SharedMemoryHolder::GetHandle() {
51 return shared_memory;
52}
53} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.h b/src/core/hle/service/hid/controllers/shared_memory_holder.h
new file mode 100644
index 000000000..943407c00
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/shared_memory_holder.h
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Core {
9class System;
10}
11
12namespace Kernel {
13class KSharedMemory;
14}
15
16namespace Service::HID {
17struct SharedMemoryFormat;
18
19// This is nn::hid::detail::SharedMemoryHolder
20class SharedMemoryHolder {
21public:
22 SharedMemoryHolder();
23 ~SharedMemoryHolder();
24
25 Result Initialize(Core::System& system);
26 void Finalize();
27
28 bool IsMapped();
29 SharedMemoryFormat* GetAddress();
30 Kernel::KSharedMemory* GetHandle();
31
32private:
33 bool is_owner{};
34 bool is_created{};
35 bool is_mapped{};
36 INSERT_PADDING_BYTES(0x5);
37 Kernel::KSharedMemory* shared_memory;
38 INSERT_PADDING_BYTES(0x38);
39 SharedMemoryFormat* address = nullptr;
40};
41// Correct size is 0x50 bytes
42static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
43
44} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
index 3d24a5c04..36b72f9ea 100644
--- a/src/core/hle/service/hid/controllers/six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -6,6 +6,7 @@
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h"
9#include "core/hle/service/hid/controllers/six_axis.h" 10#include "core/hle/service/hid/controllers/six_axis.h"
10#include "core/hle/service/hid/errors.h" 11#include "core/hle/service/hid/errors.h"
11#include "core/hle/service/hid/hid_util.h" 12#include "core/hle/service/hid/hid_util.h"
@@ -132,30 +133,30 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
132 } 133 }
133 134
134 sixaxis_fullkey_state.sampling_number = 135 sixaxis_fullkey_state.sampling_number =
135 sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; 136 sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
136 sixaxis_handheld_state.sampling_number = 137 sixaxis_handheld_state.sampling_number =
137 sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; 138 sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
138 sixaxis_dual_left_state.sampling_number = 139 sixaxis_dual_left_state.sampling_number =
139 sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; 140 sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
140 sixaxis_dual_right_state.sampling_number = 141 sixaxis_dual_right_state.sampling_number =
141 sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; 142 sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
142 sixaxis_left_lifo_state.sampling_number = 143 sixaxis_left_lifo_state.sampling_number =
143 sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; 144 sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
144 sixaxis_right_lifo_state.sampling_number = 145 sixaxis_right_lifo_state.sampling_number =
145 sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; 146 sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
146 147
147 if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { 148 if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
148 // This buffer only is updated on handheld on HW 149 // This buffer only is updated on handheld on HW
149 sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); 150 sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
150 } else { 151 } else {
151 // Handheld doesn't update this buffer on HW 152 // Handheld doesn't update this buffer on HW
152 sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); 153 sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
153 } 154 }
154 155
155 sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); 156 sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
156 sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); 157 sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
157 sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); 158 sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
158 sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); 159 sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
159 } 160 }
160} 161}
161 162
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9e2f3ab21..e2a5f5d79 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -1,18 +1,15 @@
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 <cstring>
5#include "common/common_types.h"
6#include "core/core_timing.h" 4#include "core/core_timing.h"
7#include "core/hid/hid_core.h" 5#include "core/hle/service/hid/controllers/shared_memory_format.h"
8#include "core/hle/service/hid/controllers/stubbed.h" 6#include "core/hle/service/hid/controllers/stubbed.h"
9 7
10namespace Service::HID { 8namespace Service::HID {
11 9
12Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 10Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_,
13 : ControllerBase{hid_core_} { 11 CommonHeader& ring_lifo_header)
14 raw_shared_memory = raw_shared_memory_; 12 : ControllerBase{hid_core_}, header{ring_lifo_header} {}
15}
16 13
17Controller_Stubbed::~Controller_Stubbed() = default; 14Controller_Stubbed::~Controller_Stubbed() = default;
18 15
@@ -25,18 +22,10 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
25 return; 22 return;
26 } 23 }
27 24
28 CommonHeader header{};
29 header.timestamp = core_timing.GetGlobalTimeNs().count(); 25 header.timestamp = core_timing.GetGlobalTimeNs().count();
30 header.total_entry_count = 17; 26 header.total_entry_count = 17;
31 header.entry_count = 0; 27 header.entry_count = 0;
32 header.last_entry_index = 0; 28 header.last_entry_index = 0;
33
34 std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
35}
36
37void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
38 common_offset = off;
39 smart_update = true;
40} 29}
41 30
42} // namespace Service::HID 31} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 1483a968e..d2052fb17 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -3,13 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h"
7#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
8 7
9namespace Service::HID { 8namespace Service::HID {
9struct CommonHeader;
10
10class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
11public: 12public:
12 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 13 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, CommonHeader& ring_lifo_header);
13 ~Controller_Stubbed() override; 14 ~Controller_Stubbed() override;
14 15
15 // Called when the controller is initialized 16 // Called when the controller is initialized
@@ -21,19 +22,8 @@ public:
21 // When the controller is requesting an update for the shared memory 22 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 23 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23 24
24 void SetCommonHeaderOffset(std::size_t off);
25
26private: 25private:
27 struct CommonHeader { 26 CommonHeader& header;
28 s64 timestamp{};
29 s64 total_entry_count{};
30 s64 last_entry_index{};
31 s64 entry_count{};
32 };
33 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
34
35 u8* raw_shared_memory = nullptr;
36 bool smart_update{}; 27 bool smart_update{};
37 std::size_t common_offset{};
38}; 28};
39} // namespace Service::HID 29} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index fcd973414..469750006 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -2,26 +2,22 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <cstring>
6#include "common/common_types.h" 5#include "common/common_types.h"
7#include "common/settings.h" 6#include "common/settings.h"
8#include "core/core.h"
9#include "core/core_timing.h" 7#include "core/core_timing.h"
10#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
11#include "core/hid/emulated_console.h" 9#include "core/hid/emulated_console.h"
12#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
11#include "core/hle/service/hid/controllers/shared_memory_format.h"
13#include "core/hle/service/hid/controllers/touchscreen.h" 12#include "core/hle/service/hid/controllers/touchscreen.h"
14 13
15namespace Service::HID { 14namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 15
18TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 16TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_,
19 : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), 17 TouchScreenSharedMemoryFormat& touch_shared_memory)
18 : ControllerBase{hid_core_}, shared_memory{touch_shared_memory},
19 touchscreen_width(Layout::ScreenUndocked::Width),
20 touchscreen_height(Layout::ScreenUndocked::Height) { 20 touchscreen_height(Layout::ScreenUndocked::Height) {
21 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
22 "TouchSharedMemory is bigger than the shared memory");
23 shared_memory = std::construct_at(
24 reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
25 console = hid_core.GetEmulatedConsole(); 21 console = hid_core.GetEmulatedConsole();
26} 22}
27 23
@@ -32,11 +28,11 @@ void TouchScreen::OnInit() {}
32void TouchScreen::OnRelease() {} 28void TouchScreen::OnRelease() {}
33 29
34void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
35 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 31 shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
36 32
37 if (!IsControllerActivated()) { 33 if (!IsControllerActivated()) {
38 shared_memory->touch_screen_lifo.buffer_count = 0; 34 shared_memory.touch_screen_lifo.buffer_count = 0;
39 shared_memory->touch_screen_lifo.buffer_tail = 0; 35 shared_memory.touch_screen_lifo.buffer_tail = 0;
40 return; 36 return;
41 } 37 }
42 38
@@ -86,7 +82,7 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
86 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 82 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
87 83
88 const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); 84 const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
89 const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state; 85 const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state;
90 86
91 next_state.sampling_number = last_entry.sampling_number + 1; 87 next_state.sampling_number = last_entry.sampling_number + 1;
92 next_state.entry_count = static_cast<s32>(active_fingers_count); 88 next_state.entry_count = static_cast<s32>(active_fingers_count);
@@ -118,7 +114,7 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
118 } 114 }
119 } 115 }
120 116
121 shared_memory->touch_screen_lifo.WriteNextEntry(next_state); 117 shared_memory.touch_screen_lifo.WriteNextEntry(next_state);
122} 118}
123 119
124void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { 120void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 79f026a81..5b6305bfc 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -3,20 +3,23 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_funcs.h" 6#include <array>
7#include "common/common_types.h" 7
8#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
10#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/controllers/types/touch_types.h"
11 11
12namespace Core::HID { 12namespace Core::HID {
13class EmulatedConsole; 13class EmulatedConsole;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17struct TouchScreenSharedMemoryFormat;
18
17class TouchScreen final : public ControllerBase { 19class TouchScreen final : public ControllerBase {
18public: 20public:
19 explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 21 explicit TouchScreen(Core::HID::HIDCore& hid_core_,
22 TouchScreenSharedMemoryFormat& touch_shared_memory);
20 ~TouchScreen() override; 23 ~TouchScreen() override;
21 24
22 // Called when the controller is initialized 25 // Called when the controller is initialized
@@ -31,27 +34,8 @@ public:
31 void SetTouchscreenDimensions(u32 width, u32 height); 34 void SetTouchscreenDimensions(u32 width, u32 height);
32 35
33private: 36private:
34 static constexpr std::size_t MAX_FINGERS = 16;
35
36 // This is nn::hid::TouchScreenState
37 struct TouchScreenState {
38 s64 sampling_number{};
39 s32 entry_count{};
40 INSERT_PADDING_BYTES(4); // Reserved
41 std::array<Core::HID::TouchState, MAX_FINGERS> states{};
42 };
43 static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
44
45 struct TouchSharedMemory {
46 // This is nn::hid::detail::TouchScreenLifo
47 Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
48 static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
49 INSERT_PADDING_WORDS(0xF2);
50 };
51 static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
52
53 TouchScreenState next_state{}; 37 TouchScreenState next_state{};
54 TouchSharedMemory* shared_memory = nullptr; 38 TouchScreenSharedMemoryFormat& shared_memory;
55 Core::HID::EmulatedConsole* console = nullptr; 39 Core::HID::EmulatedConsole* console = nullptr;
56 40
57 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; 41 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/types/debug_pad_types.h b/src/core/hle/service/hid/controllers/types/debug_pad_types.h
new file mode 100644
index 000000000..a96171b62
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/types/debug_pad_types.h
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "core/hid/hid_types.h"
9
10namespace Service::HID {
11
12// This is nn::hid::DebugPadAttribute
13struct DebugPadAttribute {
14 union {
15 u32 raw{};
16 BitField<0, 1, u32> connected;
17 };
18};
19static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
20
21// This is nn::hid::DebugPadState
22struct DebugPadState {
23 s64 sampling_number{};
24 DebugPadAttribute attribute{};
25 Core::HID::DebugPadButton pad_state{};
26 Core::HID::AnalogStickState r_stick{};
27 Core::HID::AnalogStickState l_stick{};
28};
29static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
30
31} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/types/gesture_types.h b/src/core/hle/service/hid/controllers/types/gesture_types.h
new file mode 100644
index 000000000..b4f034cd3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/types/gesture_types.h
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include "common/bit_field.h"
8#include "common/common_types.h"
9#include "common/point.h"
10
11namespace Service::HID {
12static constexpr size_t MAX_FINGERS = 16;
13static constexpr size_t MAX_POINTS = 4;
14
15// This is nn::hid::GestureType
16enum class GestureType : u32 {
17 Idle, // Nothing touching the screen
18 Complete, // Set at the end of a touch event
19 Cancel, // Set when the number of fingers change
20 Touch, // A finger just touched the screen
21 Press, // Set if last type is touch and the finger hasn't moved
22 Tap, // Fast press then release
23 Pan, // All points moving together across the screen
24 Swipe, // Fast press movement and release of a single point
25 Pinch, // All points moving away/closer to the midpoint
26 Rotate, // All points rotating from the midpoint
27};
28
29// This is nn::hid::GestureDirection
30enum class GestureDirection : u32 {
31 None,
32 Left,
33 Up,
34 Right,
35 Down,
36};
37
38// This is nn::hid::GestureAttribute
39struct GestureAttribute {
40 union {
41 u32 raw{};
42
43 BitField<4, 1, u32> is_new_touch;
44 BitField<8, 1, u32> is_double_tap;
45 };
46};
47static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
48
49// This is nn::hid::GestureState
50struct GestureState {
51 s64 sampling_number{};
52 s64 detection_count{};
53 GestureType type{GestureType::Idle};
54 GestureDirection direction{GestureDirection::None};
55 Common::Point<s32> pos{};
56 Common::Point<s32> delta{};
57 f32 vel_x{};
58 f32 vel_y{};
59 GestureAttribute attributes{};
60 f32 scale{};
61 f32 rotation_angle{};
62 s32 point_count{};
63 std::array<Common::Point<s32>, 4> points{};
64};
65static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
66
67struct GestureProperties {
68 std::array<Common::Point<s32>, MAX_POINTS> points{};
69 std::size_t active_points{};
70 Common::Point<s32> mid_point{};
71 s64 detection_count{};
72 u64 delta_time{};
73 f32 average_distance{};
74 f32 angle{};
75};
76
77} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/types/keyboard_types.h b/src/core/hle/service/hid/controllers/types/keyboard_types.h
new file mode 100644
index 000000000..f44a536b9
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/types/keyboard_types.h
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/hid_types.h"
8
9namespace Service::HID {
10
11// This is nn::hid::detail::KeyboardState
12struct KeyboardState {
13 s64 sampling_number{};
14 Core::HID::KeyboardModifier modifier{};
15 Core::HID::KeyboardAttribute attribute{};
16 Core::HID::KeyboardKey key{};
17};
18static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
19
20} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/types/mouse_types.h b/src/core/hle/service/hid/controllers/types/mouse_types.h
new file mode 100644
index 000000000..8bd6e167c
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/types/mouse_types.h
@@ -0,0 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::HID {} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/types/npad_types.h b/src/core/hle/service/hid/controllers/types/npad_types.h
new file mode 100644
index 000000000..a5ce2562b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/types/npad_types.h
@@ -0,0 +1,254 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "core/hid/hid_types.h"
10
11namespace Service::HID {
12static constexpr std::size_t NpadCount = 10;
13
14// This is nn::hid::NpadJoyHoldType
15enum class NpadJoyHoldType : u64 {
16 Vertical = 0,
17 Horizontal = 1,
18};
19
20// This is nn::hid::NpadJoyAssignmentMode
21enum class NpadJoyAssignmentMode : u32 {
22 Dual = 0,
23 Single = 1,
24};
25
26// This is nn::hid::NpadJoyDeviceType
27enum class NpadJoyDeviceType : s64 {
28 Left = 0,
29 Right = 1,
30};
31
32// This is nn::hid::NpadHandheldActivationMode
33enum class NpadHandheldActivationMode : u64 {
34 Dual = 0,
35 Single = 1,
36 None = 2,
37 MaxActivationMode = 3,
38};
39
40// This is nn::hid::system::AppletFooterUiAttributesSet
41struct AppletFooterUiAttributes {
42 INSERT_PADDING_BYTES(0x4);
43};
44
45// This is nn::hid::system::AppletFooterUiType
46enum class AppletFooterUiType : u8 {
47 None = 0,
48 HandheldNone = 1,
49 HandheldJoyConLeftOnly = 2,
50 HandheldJoyConRightOnly = 3,
51 HandheldJoyConLeftJoyConRight = 4,
52 JoyDual = 5,
53 JoyDualLeftOnly = 6,
54 JoyDualRightOnly = 7,
55 JoyLeftHorizontal = 8,
56 JoyLeftVertical = 9,
57 JoyRightHorizontal = 10,
58 JoyRightVertical = 11,
59 SwitchProController = 12,
60 CompatibleProController = 13,
61 CompatibleJoyCon = 14,
62 LarkHvc1 = 15,
63 LarkHvc2 = 16,
64 LarkNesLeft = 17,
65 LarkNesRight = 18,
66 Lucia = 19,
67 Verification = 20,
68 Lagon = 21,
69};
70
71using AppletFooterUiVariant = u8;
72
73// This is "nn::hid::system::AppletDetailedUiType".
74struct AppletDetailedUiType {
75 AppletFooterUiVariant ui_variant;
76 INSERT_PADDING_BYTES(0x2);
77 AppletFooterUiType footer;
78};
79static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
80// This is nn::hid::NpadCommunicationMode
81enum class NpadCommunicationMode : u64 {
82 Mode_5ms = 0,
83 Mode_10ms = 1,
84 Mode_15ms = 2,
85 Default = 3,
86};
87
88enum class NpadRevision : u32 {
89 Revision0 = 0,
90 Revision1 = 1,
91 Revision2 = 2,
92 Revision3 = 3,
93};
94
95// This is nn::hid::detail::ColorAttribute
96enum class ColorAttribute : u32 {
97 Ok = 0,
98 ReadError = 1,
99 NoController = 2,
100};
101static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
102
103// This is nn::hid::detail::NpadFullKeyColorState
104struct NpadFullKeyColorState {
105 ColorAttribute attribute{ColorAttribute::NoController};
106 Core::HID::NpadControllerColor fullkey{};
107};
108static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
109
110// This is nn::hid::detail::NpadJoyColorState
111struct NpadJoyColorState {
112 ColorAttribute attribute{ColorAttribute::NoController};
113 Core::HID::NpadControllerColor left{};
114 Core::HID::NpadControllerColor right{};
115};
116static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
117
118// This is nn::hid::NpadAttribute
119struct NpadAttribute {
120 union {
121 u32 raw{};
122 BitField<0, 1, u32> is_connected;
123 BitField<1, 1, u32> is_wired;
124 BitField<2, 1, u32> is_left_connected;
125 BitField<3, 1, u32> is_left_wired;
126 BitField<4, 1, u32> is_right_connected;
127 BitField<5, 1, u32> is_right_wired;
128 };
129};
130static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
131
132// This is nn::hid::NpadFullKeyState
133// This is nn::hid::NpadHandheldState
134// This is nn::hid::NpadJoyDualState
135// This is nn::hid::NpadJoyLeftState
136// This is nn::hid::NpadJoyRightState
137// This is nn::hid::NpadPalmaState
138// This is nn::hid::NpadSystemExtState
139struct NPadGenericState {
140 s64_le sampling_number{};
141 Core::HID::NpadButtonState npad_buttons{};
142 Core::HID::AnalogStickState l_stick{};
143 Core::HID::AnalogStickState r_stick{};
144 NpadAttribute connection_status{};
145 INSERT_PADDING_BYTES(4); // Reserved
146};
147static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
148
149// This is nn::hid::server::NpadGcTriggerState
150struct NpadGcTriggerState {
151 s64 sampling_number{};
152 s32 l_analog{};
153 s32 r_analog{};
154};
155static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
156
157// This is nn::hid::NpadSystemProperties
158struct NPadSystemProperties {
159 union {
160 s64 raw{};
161 BitField<0, 1, s64> is_charging_joy_dual;
162 BitField<1, 1, s64> is_charging_joy_left;
163 BitField<2, 1, s64> is_charging_joy_right;
164 BitField<3, 1, s64> is_powered_joy_dual;
165 BitField<4, 1, s64> is_powered_joy_left;
166 BitField<5, 1, s64> is_powered_joy_right;
167 BitField<9, 1, s64> is_system_unsupported_button;
168 BitField<10, 1, s64> is_system_ext_unsupported_button;
169 BitField<11, 1, s64> is_vertical;
170 BitField<12, 1, s64> is_horizontal;
171 BitField<13, 1, s64> use_plus;
172 BitField<14, 1, s64> use_minus;
173 BitField<15, 1, s64> use_directional_buttons;
174 };
175};
176static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
177
178// This is nn::hid::NpadSystemButtonProperties
179struct NpadSystemButtonProperties {
180 union {
181 s32 raw{};
182 BitField<0, 1, s32> is_home_button_protection_enabled;
183 };
184};
185static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
186
187// This is nn::hid::system::DeviceType
188struct DeviceType {
189 union {
190 u32 raw{};
191 BitField<0, 1, s32> fullkey;
192 BitField<1, 1, s32> debug_pad;
193 BitField<2, 1, s32> handheld_left;
194 BitField<3, 1, s32> handheld_right;
195 BitField<4, 1, s32> joycon_left;
196 BitField<5, 1, s32> joycon_right;
197 BitField<6, 1, s32> palma;
198 BitField<7, 1, s32> lark_hvc_left;
199 BitField<8, 1, s32> lark_hvc_right;
200 BitField<9, 1, s32> lark_nes_left;
201 BitField<10, 1, s32> lark_nes_right;
202 BitField<11, 1, s32> handheld_lark_hvc_left;
203 BitField<12, 1, s32> handheld_lark_hvc_right;
204 BitField<13, 1, s32> handheld_lark_nes_left;
205 BitField<14, 1, s32> handheld_lark_nes_right;
206 BitField<15, 1, s32> lucia;
207 BitField<16, 1, s32> lagon;
208 BitField<17, 1, s32> lager;
209 BitField<31, 1, s32> system;
210 };
211};
212
213// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
214struct NfcXcdDeviceHandleStateImpl {
215 u64 handle{};
216 bool is_available{};
217 bool is_activated{};
218 INSERT_PADDING_BYTES(0x6); // Reserved
219 u64 sampling_number{};
220};
221static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
222 "NfcXcdDeviceHandleStateImpl is an invalid size");
223
224// This is nn::hid::NpadLarkType
225enum class NpadLarkType : u32 {
226 Invalid,
227 H1,
228 H2,
229 NL,
230 NR,
231};
232
233// This is nn::hid::NpadLuciaType
234enum class NpadLuciaType : u32 {
235 Invalid,
236 J,
237 E,
238 U,
239};
240
241// This is nn::hid::NpadLagonType
242enum class NpadLagonType : u32 {
243 Invalid,
244};
245
246// This is nn::hid::NpadLagerType
247enum class NpadLagerType : u32 {
248 Invalid,
249 J,
250 E,
251 U,
252};
253
254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/types/touch_types.h b/src/core/hle/service/hid/controllers/types/touch_types.h
new file mode 100644
index 000000000..efeaa796d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/types/touch_types.h
@@ -0,0 +1,90 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include <array>
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/point.h"
13#include "core/hid/hid_types.h"
14
15namespace Service::HID {
16static constexpr std::size_t MAX_FINGERS = 16;
17static constexpr size_t MAX_POINTS = 4;
18
19// This is nn::hid::GestureType
20enum class GestureType : u32 {
21 Idle, // Nothing touching the screen
22 Complete, // Set at the end of a touch event
23 Cancel, // Set when the number of fingers change
24 Touch, // A finger just touched the screen
25 Press, // Set if last type is touch and the finger hasn't moved
26 Tap, // Fast press then release
27 Pan, // All points moving together across the screen
28 Swipe, // Fast press movement and release of a single point
29 Pinch, // All points moving away/closer to the midpoint
30 Rotate, // All points rotating from the midpoint
31};
32
33// This is nn::hid::GestureDirection
34enum class GestureDirection : u32 {
35 None,
36 Left,
37 Up,
38 Right,
39 Down,
40};
41
42// This is nn::hid::GestureAttribute
43struct GestureAttribute {
44 union {
45 u32 raw{};
46
47 BitField<4, 1, u32> is_new_touch;
48 BitField<8, 1, u32> is_double_tap;
49 };
50};
51static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
52
53// This is nn::hid::GestureState
54struct GestureState {
55 s64 sampling_number{};
56 s64 detection_count{};
57 GestureType type{GestureType::Idle};
58 GestureDirection direction{GestureDirection::None};
59 Common::Point<s32> pos{};
60 Common::Point<s32> delta{};
61 f32 vel_x{};
62 f32 vel_y{};
63 GestureAttribute attributes{};
64 f32 scale{};
65 f32 rotation_angle{};
66 s32 point_count{};
67 std::array<Common::Point<s32>, 4> points{};
68};
69static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
70
71struct GestureProperties {
72 std::array<Common::Point<s32>, MAX_POINTS> points{};
73 std::size_t active_points{};
74 Common::Point<s32> mid_point{};
75 s64 detection_count{};
76 u64 delta_time{};
77 f32 average_distance{};
78 f32 angle{};
79};
80
81// This is nn::hid::TouchScreenState
82struct TouchScreenState {
83 s64 sampling_number{};
84 s32 entry_count{};
85 INSERT_PADDING_BYTES(4); // Reserved
86 std::array<Core::HID::TouchState, MAX_FINGERS> states{};
87};
88static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
89
90} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
deleted file mode 100644
index 0aaed1fa7..000000000
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5#include "common/common_types.h"
6#include "core/core_timing.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/xpad.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12
13XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
14 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
15 "XpadSharedMemory is bigger than the shared memory");
16 shared_memory = std::construct_at(
17 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
18}
19XPad::~XPad() = default;
20
21void XPad::OnInit() {}
22
23void XPad::OnRelease() {}
24
25void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
26 if (!IsControllerActivated()) {
27 shared_memory->basic_xpad_lifo.buffer_count = 0;
28 shared_memory->basic_xpad_lifo.buffer_tail = 0;
29 return;
30 }
31
32 const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
33 next_state.sampling_number = last_entry.sampling_number + 1;
34 // TODO(ogniK): Update xpad states
35
36 shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
37}
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
deleted file mode 100644
index 9e63a317a..000000000
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ /dev/null
@@ -1,112 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/controller_base.h"
10#include "core/hle/service/hid/ring_lifo.h"
11
12namespace Service::HID {
13class XPad final : public ControllerBase {
14public:
15 explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
16 ~XPad() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
26
27private:
28 // This is nn::hid::BasicXpadAttributeSet
29 struct BasicXpadAttributeSet {
30 union {
31 u32 raw{};
32 BitField<0, 1, u32> is_connected;
33 BitField<1, 1, u32> is_wired;
34 BitField<2, 1, u32> is_left_connected;
35 BitField<3, 1, u32> is_left_wired;
36 BitField<4, 1, u32> is_right_connected;
37 BitField<5, 1, u32> is_right_wired;
38 };
39 };
40 static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
41
42 // This is nn::hid::BasicXpadButtonSet
43 struct BasicXpadButtonSet {
44 union {
45 u32 raw{};
46 // Button states
47 BitField<0, 1, u32> a;
48 BitField<1, 1, u32> b;
49 BitField<2, 1, u32> x;
50 BitField<3, 1, u32> y;
51 BitField<4, 1, u32> l_stick;
52 BitField<5, 1, u32> r_stick;
53 BitField<6, 1, u32> l;
54 BitField<7, 1, u32> r;
55 BitField<8, 1, u32> zl;
56 BitField<9, 1, u32> zr;
57 BitField<10, 1, u32> plus;
58 BitField<11, 1, u32> minus;
59
60 // D-Pad
61 BitField<12, 1, u32> d_left;
62 BitField<13, 1, u32> d_up;
63 BitField<14, 1, u32> d_right;
64 BitField<15, 1, u32> d_down;
65
66 // Left JoyStick
67 BitField<16, 1, u32> l_stick_left;
68 BitField<17, 1, u32> l_stick_up;
69 BitField<18, 1, u32> l_stick_right;
70 BitField<19, 1, u32> l_stick_down;
71
72 // Right JoyStick
73 BitField<20, 1, u32> r_stick_left;
74 BitField<21, 1, u32> r_stick_up;
75 BitField<22, 1, u32> r_stick_right;
76 BitField<23, 1, u32> r_stick_down;
77
78 // Not always active?
79 BitField<24, 1, u32> left_sl;
80 BitField<25, 1, u32> left_sr;
81
82 BitField<26, 1, u32> right_sl;
83 BitField<27, 1, u32> right_sr;
84
85 BitField<28, 1, u32> palma;
86 BitField<30, 1, u32> handheld_left_b;
87 };
88 };
89 static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
90
91 // This is nn::hid::detail::BasicXpadState
92 struct BasicXpadState {
93 s64 sampling_number{};
94 BasicXpadAttributeSet attributes{};
95 BasicXpadButtonSet pad_states{};
96 Core::HID::AnalogStickState l_stick{};
97 Core::HID::AnalogStickState r_stick{};
98 };
99 static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
100
101 struct XpadSharedMemory {
102 // This is nn::hid::detail::BasicXpadLifo
103 Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
104 static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
105 INSERT_PADDING_WORDS(0x4E);
106 };
107 static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
108
109 BasicXpadState next_state{};
110 XpadSharedMemory* shared_memory = nullptr;
111};
112} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index b06ea467e..de24b0401 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -28,6 +28,7 @@
28#include "core/hle/service/hid/controllers/seven_six_axis.h" 28#include "core/hle/service/hid/controllers/seven_six_axis.h"
29#include "core/hle/service/hid/controllers/six_axis.h" 29#include "core/hle/service/hid/controllers/six_axis.h"
30#include "core/hle/service/hid/controllers/touchscreen.h" 30#include "core/hle/service/hid/controllers/touchscreen.h"
31#include "core/hle/service/hid/controllers/types/npad_types.h"
31 32
32namespace Service::HID { 33namespace Service::HID {
33 34
@@ -1099,7 +1100,7 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1099void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { 1100void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1100 IPC::RequestParser rp{ctx}; 1101 IPC::RequestParser rp{ctx};
1101 struct Parameters { 1102 struct Parameters {
1102 NPad::NpadRevision revision; 1103 NpadRevision revision;
1103 INSERT_PADDING_WORDS_NOINIT(1); 1104 INSERT_PADDING_WORDS_NOINIT(1);
1104 u64 applet_resource_user_id; 1105 u64 applet_resource_user_id;
1105 }; 1106 };
@@ -1122,7 +1123,7 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1122void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { 1123void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
1123 IPC::RequestParser rp{ctx}; 1124 IPC::RequestParser rp{ctx};
1124 const auto applet_resource_user_id{rp.Pop<u64>()}; 1125 const auto applet_resource_user_id{rp.Pop<u64>()};
1125 const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()}; 1126 const auto hold_type{rp.PopEnum<NpadJoyHoldType>()};
1126 1127
1127 GetResourceManager()->GetNpad()->SetHoldType(hold_type); 1128 GetResourceManager()->GetNpad()->SetHoldType(hold_type);
1128 1129
@@ -1157,8 +1158,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx)
1157 1158
1158 Core::HID::NpadIdType new_npad_id{}; 1159 Core::HID::NpadIdType new_npad_id{};
1159 auto controller = GetResourceManager()->GetNpad(); 1160 auto controller = GetResourceManager()->GetNpad();
1160 controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left, 1161 controller->SetNpadMode(new_npad_id, parameters.npad_id, NpadJoyDeviceType::Left,
1161 NPad::NpadJoyAssignmentMode::Single); 1162 NpadJoyAssignmentMode::Single);
1162 1163
1163 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1164 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1164 parameters.applet_resource_user_id); 1165 parameters.applet_resource_user_id);
@@ -1173,7 +1174,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1173 Core::HID::NpadIdType npad_id; 1174 Core::HID::NpadIdType npad_id;
1174 INSERT_PADDING_WORDS_NOINIT(1); 1175 INSERT_PADDING_WORDS_NOINIT(1);
1175 u64 applet_resource_user_id; 1176 u64 applet_resource_user_id;
1176 NPad::NpadJoyDeviceType npad_joy_device_type; 1177 NpadJoyDeviceType npad_joy_device_type;
1177 }; 1178 };
1178 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1179 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1179 1180
@@ -1182,7 +1183,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1182 Core::HID::NpadIdType new_npad_id{}; 1183 Core::HID::NpadIdType new_npad_id{};
1183 auto controller = GetResourceManager()->GetNpad(); 1184 auto controller = GetResourceManager()->GetNpad();
1184 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, 1185 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1185 NPad::NpadJoyAssignmentMode::Single); 1186 NpadJoyAssignmentMode::Single);
1186 1187
1187 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1188 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1188 parameters.npad_id, parameters.applet_resource_user_id, 1189 parameters.npad_id, parameters.applet_resource_user_id,
@@ -1205,7 +1206,7 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1205 1206
1206 Core::HID::NpadIdType new_npad_id{}; 1207 Core::HID::NpadIdType new_npad_id{};
1207 auto controller = GetResourceManager()->GetNpad(); 1208 auto controller = GetResourceManager()->GetNpad();
1208 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual); 1209 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NpadJoyAssignmentMode::Dual);
1209 1210
1210 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1211 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1211 parameters.applet_resource_user_id); // Spams a lot when controller applet is open 1212 parameters.applet_resource_user_id); // Spams a lot when controller applet is open
@@ -1257,7 +1258,7 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1257void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { 1258void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1258 IPC::RequestParser rp{ctx}; 1259 IPC::RequestParser rp{ctx};
1259 const auto applet_resource_user_id{rp.Pop<u64>()}; 1260 const auto applet_resource_user_id{rp.Pop<u64>()};
1260 const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()}; 1261 const auto activation_mode{rp.PopEnum<NpadHandheldActivationMode>()};
1261 1262
1262 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode); 1263 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
1263 1264
@@ -1349,7 +1350,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
1349 Core::HID::NpadIdType npad_id; 1350 Core::HID::NpadIdType npad_id;
1350 INSERT_PADDING_WORDS_NOINIT(1); 1351 INSERT_PADDING_WORDS_NOINIT(1);
1351 u64 applet_resource_user_id; 1352 u64 applet_resource_user_id;
1352 NPad::NpadJoyDeviceType npad_joy_device_type; 1353 NpadJoyDeviceType npad_joy_device_type;
1353 }; 1354 };
1354 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1355 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1355 1356
@@ -1359,7 +1360,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
1359 auto controller = GetResourceManager()->GetNpad(); 1360 auto controller = GetResourceManager()->GetNpad();
1360 const auto is_reassigned = 1361 const auto is_reassigned =
1361 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, 1362 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1362 NPad::NpadJoyAssignmentMode::Single); 1363 NpadJoyAssignmentMode::Single);
1363 1364
1364 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1365 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1365 parameters.npad_id, parameters.applet_resource_user_id, 1366 parameters.npad_id, parameters.applet_resource_user_id,
@@ -2315,7 +2316,7 @@ void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2315void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { 2316void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2316 IPC::RequestParser rp{ctx}; 2317 IPC::RequestParser rp{ctx};
2317 const auto applet_resource_user_id{rp.Pop<u64>()}; 2318 const auto applet_resource_user_id{rp.Pop<u64>()};
2318 const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()}; 2319 const auto communication_mode{rp.PopEnum<NpadCommunicationMode>()};
2319 2320
2320 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode); 2321 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);
2321 2322
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 4d33456a3..5cc88c4a1 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -5,6 +5,7 @@
5#include "core/hle/service/hid/controllers/npad.h" 5#include "core/hle/service/hid/controllers/npad.h"
6#include "core/hle/service/hid/controllers/palma.h" 6#include "core/hle/service/hid/controllers/palma.h"
7#include "core/hle/service/hid/controllers/touchscreen.h" 7#include "core/hle/service/hid/controllers/touchscreen.h"
8#include "core/hle/service/hid/controllers/types/npad_types.h"
8#include "core/hle/service/hid/errors.h" 9#include "core/hle/service/hid/errors.h"
9#include "core/hle/service/hid/hid_system_server.h" 10#include "core/hle/service/hid/hid_system_server.h"
10#include "core/hle/service/hid/resource_manager.h" 11#include "core/hle/service/hid/resource_manager.h"
@@ -328,7 +329,7 @@ void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
328 LOG_DEBUG(Service_HID, "called, npad_id_type={}", 329 LOG_DEBUG(Service_HID, "called, npad_id_type={}",
329 npad_id_type); // Spams a lot when controller applet is running 330 npad_id_type); // Spams a lot when controller applet is running
330 331
331 const NPad::AppletDetailedUiType detailed_ui_type = 332 const AppletDetailedUiType detailed_ui_type =
332 GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type); 333 GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type);
333 334
334 IPC::ResponseBuilder rb{ctx, 3}; 335 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index 89cdc19cc..6c6cbd802 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -18,10 +18,10 @@
18#include "core/hle/service/hid/controllers/npad.h" 18#include "core/hle/service/hid/controllers/npad.h"
19#include "core/hle/service/hid/controllers/palma.h" 19#include "core/hle/service/hid/controllers/palma.h"
20#include "core/hle/service/hid/controllers/seven_six_axis.h" 20#include "core/hle/service/hid/controllers/seven_six_axis.h"
21#include "core/hle/service/hid/controllers/shared_memory_format.h"
21#include "core/hle/service/hid/controllers/six_axis.h" 22#include "core/hle/service/hid/controllers/six_axis.h"
22#include "core/hle/service/hid/controllers/stubbed.h" 23#include "core/hle/service/hid/controllers/stubbed.h"
23#include "core/hle/service/hid/controllers/touchscreen.h" 24#include "core/hle/service/hid/controllers/touchscreen.h"
24#include "core/hle/service/hid/controllers/xpad.h"
25 25
26namespace Service::HID { 26namespace Service::HID {
27 27
@@ -45,40 +45,43 @@ void ResourceManager::Initialize() {
45 return; 45 return;
46 } 46 }
47 47
48 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); 48 system.HIDCore().ReloadInputDevices();
49 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory); 49 is_initialized = true;
50 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory); 50}
51 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory); 51
52 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory); 52void ResourceManager::InitializeController(u64 aruid) {
53 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory); 53 SharedMemoryFormat* shared_memory = nullptr;
54 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context); 54 const auto result = applet_resource->GetSharedMemoryFormat(&shared_memory, aruid);
55 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory); 55 if (result.IsError()) {
56 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory); 56 return;
57 xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory); 57 }
58
59 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory->debug_pad);
60 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory->mouse);
61 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory->debug_mouse);
62 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory->keyboard);
63 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory->unique_pad.header);
64 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory->npad, service_context);
65 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory->gesture);
66 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory->touch_screen);
58 67
59 palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context); 68 palma = std::make_shared<Palma>(system.HIDCore(), service_context);
60 69
61 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory); 70 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory->home_button.header);
62 sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory); 71 sleep_button =
63 capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory); 72 std::make_shared<SleepButton>(system.HIDCore(), shared_memory->sleep_button.header);
73 capture_button =
74 std::make_shared<CaptureButton>(system.HIDCore(), shared_memory->capture_button.header);
75 digitizer = std::make_shared<Digitizer>(system.HIDCore(), shared_memory->digitizer.header);
64 76
65 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad); 77 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
66 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory); 78 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory->console);
67 seven_six_axis = std::make_shared<SevenSixAxis>(system); 79 seven_six_axis = std::make_shared<SevenSixAxis>(system);
68 80
69 home_button->SetCommonHeaderOffset(0x4C00);
70 sleep_button->SetCommonHeaderOffset(0x4E00);
71 capture_button->SetCommonHeaderOffset(0x5000);
72 unique_pad->SetCommonHeaderOffset(0x5A00);
73 debug_mouse->SetCommonHeaderOffset(0x3DC00);
74
75 // Homebrew doesn't try to activate some controllers, so we activate them by default 81 // Homebrew doesn't try to activate some controllers, so we activate them by default
76 npad->Activate(); 82 npad->Activate();
77 six_axis->Activate(); 83 six_axis->Activate();
78 touch_screen->Activate(); 84 touch_screen->Activate();
79
80 system.HIDCore().ReloadInputDevices();
81 is_initialized = true;
82} 85}
83 86
84std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const { 87std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const {
@@ -101,6 +104,10 @@ std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const {
101 return debug_pad; 104 return debug_pad;
102} 105}
103 106
107std::shared_ptr<Digitizer> ResourceManager::GetDigitizer() const {
108 return digitizer;
109}
110
104std::shared_ptr<Gesture> ResourceManager::GetGesture() const { 111std::shared_ptr<Gesture> ResourceManager::GetGesture() const {
105 return gesture; 112 return gesture;
106} 113}
@@ -163,7 +170,11 @@ Result ResourceManager::CreateAppletResource(u64 aruid) {
163 170
164Result ResourceManager::CreateAppletResourceImpl(u64 aruid) { 171Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
165 std::scoped_lock lock{shared_mutex}; 172 std::scoped_lock lock{shared_mutex};
166 return applet_resource->CreateAppletResource(aruid); 173 const auto result = applet_resource->CreateAppletResource(aruid);
174 if (result.IsSuccess()) {
175 InitializeController(aruid);
176 }
177 return result;
167} 178}
168 179
169Result ResourceManager::RegisterCoreAppletResource() { 180Result ResourceManager::RegisterCoreAppletResource() {
@@ -220,6 +231,7 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
220 std::chrono::nanoseconds ns_late) { 231 std::chrono::nanoseconds ns_late) {
221 auto& core_timing = system.CoreTiming(); 232 auto& core_timing = system.CoreTiming();
222 debug_pad->OnUpdate(core_timing); 233 debug_pad->OnUpdate(core_timing);
234 digitizer->OnUpdate(core_timing);
223 unique_pad->OnUpdate(core_timing); 235 unique_pad->OnUpdate(core_timing);
224 gesture->OnUpdate(core_timing); 236 gesture->OnUpdate(core_timing);
225 touch_screen->OnUpdate(core_timing); 237 touch_screen->OnUpdate(core_timing);
@@ -227,7 +239,6 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
227 home_button->OnUpdate(core_timing); 239 home_button->OnUpdate(core_timing);
228 sleep_button->OnUpdate(core_timing); 240 sleep_button->OnUpdate(core_timing);
229 capture_button->OnUpdate(core_timing); 241 capture_button->OnUpdate(core_timing);
230 xpad->OnUpdate(core_timing);
231} 242}
232 243
233void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 244void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 15c1beb1a..5ad7cb564 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -31,10 +31,10 @@ class Palma;
31class SevenSixAxis; 31class SevenSixAxis;
32class SixAxis; 32class SixAxis;
33class TouchScreen; 33class TouchScreen;
34class XPad;
35 34
36using CaptureButton = Controller_Stubbed; 35using CaptureButton = Controller_Stubbed;
37using DebugMouse = Controller_Stubbed; 36using DebugMouse = Mouse;
37using Digitizer = Controller_Stubbed;
38using HomeButton = Controller_Stubbed; 38using HomeButton = Controller_Stubbed;
39using SleepButton = Controller_Stubbed; 39using SleepButton = Controller_Stubbed;
40using UniquePad = Controller_Stubbed; 40using UniquePad = Controller_Stubbed;
@@ -46,12 +46,14 @@ public:
46 ~ResourceManager(); 46 ~ResourceManager();
47 47
48 void Initialize(); 48 void Initialize();
49 void InitializeController(u64 aruid);
49 50
50 std::shared_ptr<AppletResource> GetAppletResource() const; 51 std::shared_ptr<AppletResource> GetAppletResource() const;
51 std::shared_ptr<CaptureButton> GetCaptureButton() const; 52 std::shared_ptr<CaptureButton> GetCaptureButton() const;
52 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const; 53 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const;
53 std::shared_ptr<DebugMouse> GetDebugMouse() const; 54 std::shared_ptr<DebugMouse> GetDebugMouse() const;
54 std::shared_ptr<DebugPad> GetDebugPad() const; 55 std::shared_ptr<DebugPad> GetDebugPad() const;
56 std::shared_ptr<Digitizer> GetDigitizer() const;
55 std::shared_ptr<Gesture> GetGesture() const; 57 std::shared_ptr<Gesture> GetGesture() const;
56 std::shared_ptr<HomeButton> GetHomeButton() const; 58 std::shared_ptr<HomeButton> GetHomeButton() const;
57 std::shared_ptr<Keyboard> GetKeyboard() const; 59 std::shared_ptr<Keyboard> GetKeyboard() const;
@@ -96,6 +98,7 @@ private:
96 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; 98 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
97 std::shared_ptr<DebugMouse> debug_mouse = nullptr; 99 std::shared_ptr<DebugMouse> debug_mouse = nullptr;
98 std::shared_ptr<DebugPad> debug_pad = nullptr; 100 std::shared_ptr<DebugPad> debug_pad = nullptr;
101 std::shared_ptr<Digitizer> digitizer = nullptr;
99 std::shared_ptr<Gesture> gesture = nullptr; 102 std::shared_ptr<Gesture> gesture = nullptr;
100 std::shared_ptr<HomeButton> home_button = nullptr; 103 std::shared_ptr<HomeButton> home_button = nullptr;
101 std::shared_ptr<Keyboard> keyboard = nullptr; 104 std::shared_ptr<Keyboard> keyboard = nullptr;
@@ -107,7 +110,6 @@ private:
107 std::shared_ptr<SleepButton> sleep_button = nullptr; 110 std::shared_ptr<SleepButton> sleep_button = nullptr;
108 std::shared_ptr<TouchScreen> touch_screen = nullptr; 111 std::shared_ptr<TouchScreen> touch_screen = nullptr;
109 std::shared_ptr<UniquePad> unique_pad = nullptr; 112 std::shared_ptr<UniquePad> unique_pad = nullptr;
110 std::shared_ptr<XPad> xpad = nullptr;
111 113
112 // TODO: Create these resources 114 // TODO: Create these resources
113 // std::shared_ptr<AudioControl> audio_control = nullptr; 115 // std::shared_ptr<AudioControl> audio_control = nullptr;
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 75bf31e32..2fef6cc1a 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -204,8 +204,9 @@ Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u
204 // Record the display id. 204 // Record the display id.
205 m_display_id = display_id; 205 m_display_id = display_id;
206 206
207 // Create a layer for the display. 207 // Create and open a layer for the display.
208 m_layer_id = m_flinger.CreateLayer(m_display_id).value(); 208 m_layer_id = m_flinger.CreateLayer(m_display_id).value();
209 m_flinger.OpenLayer(m_layer_id);
209 210
210 // Set up the buffer. 211 // Set up the buffer.
211 m_buffer_id = m_next_buffer_id++; 212 m_buffer_id = m_next_buffer_id++;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 0745434c5..6352b09a9 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -176,17 +176,37 @@ void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
176 display.CreateLayer(layer_id, buffer_id, nvdrv->container); 176 display.CreateLayer(layer_id, buffer_id, nvdrv->container);
177} 177}
178 178
179void Nvnflinger::OpenLayer(u64 layer_id) {
180 const auto lock_guard = Lock();
181
182 for (auto& display : displays) {
183 if (auto* layer = display.FindLayer(layer_id); layer) {
184 layer->Open();
185 }
186 }
187}
188
179void Nvnflinger::CloseLayer(u64 layer_id) { 189void Nvnflinger::CloseLayer(u64 layer_id) {
180 const auto lock_guard = Lock(); 190 const auto lock_guard = Lock();
181 191
182 for (auto& display : displays) { 192 for (auto& display : displays) {
183 display.CloseLayer(layer_id); 193 if (auto* layer = display.FindLayer(layer_id); layer) {
194 layer->Close();
195 }
196 }
197}
198
199void Nvnflinger::DestroyLayer(u64 layer_id) {
200 const auto lock_guard = Lock();
201
202 for (auto& display : displays) {
203 display.DestroyLayer(layer_id);
184 } 204 }
185} 205}
186 206
187std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) { 207std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
188 const auto lock_guard = Lock(); 208 const auto lock_guard = Lock();
189 const auto* const layer = FindOrCreateLayer(display_id, layer_id); 209 const auto* const layer = FindLayer(display_id, layer_id);
190 210
191 if (layer == nullptr) { 211 if (layer == nullptr) {
192 return std::nullopt; 212 return std::nullopt;
@@ -240,24 +260,6 @@ VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) {
240 return display->FindLayer(layer_id); 260 return display->FindLayer(layer_id);
241} 261}
242 262
243VI::Layer* Nvnflinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
244 auto* const display = FindDisplay(display_id);
245
246 if (display == nullptr) {
247 return nullptr;
248 }
249
250 auto* layer = display->FindLayer(layer_id);
251
252 if (layer == nullptr) {
253 LOG_DEBUG(Service_Nvnflinger, "Layer at id {} not found. Trying to create it.", layer_id);
254 CreateLayerAtId(*display, layer_id);
255 return display->FindLayer(layer_id);
256 }
257
258 return layer;
259}
260
261void Nvnflinger::Compose() { 263void Nvnflinger::Compose() {
262 for (auto& display : displays) { 264 for (auto& display : displays) {
263 // Trigger vsync for this display at the end of drawing 265 // Trigger vsync for this display at the end of drawing
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index f5d73acdb..871285764 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -73,9 +73,15 @@ public:
73 /// If an invalid display ID is specified, then an empty optional is returned. 73 /// If an invalid display ID is specified, then an empty optional is returned.
74 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); 74 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
75 75
76 /// Opens a layer on all displays for the given layer ID.
77 void OpenLayer(u64 layer_id);
78
76 /// Closes a layer on all displays for the given layer ID. 79 /// Closes a layer on all displays for the given layer ID.
77 void CloseLayer(u64 layer_id); 80 void CloseLayer(u64 layer_id);
78 81
82 /// Destroys the given layer ID.
83 void DestroyLayer(u64 layer_id);
84
79 /// Finds the buffer queue ID of the specified layer in the specified display. 85 /// Finds the buffer queue ID of the specified layer in the specified display.
80 /// 86 ///
81 /// If an invalid display ID or layer ID is provided, then an empty optional is returned. 87 /// If an invalid display ID or layer ID is provided, then an empty optional is returned.
@@ -117,11 +123,6 @@ private:
117 /// Finds the layer identified by the specified ID in the desired display. 123 /// Finds the layer identified by the specified ID in the desired display.
118 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); 124 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
119 125
120 /// Finds the layer identified by the specified ID in the desired display,
121 /// or creates the layer if it is not found.
122 /// To be used when the system expects the specified ID to already exist.
123 [[nodiscard]] VI::Layer* FindOrCreateLayer(u64 display_id, u64 layer_id);
124
125 /// Creates a layer with the specified layer ID in the desired display. 126 /// Creates a layer with the specified layer ID in the desired display.
126 void CreateLayerAtId(VI::Display& display, u64 layer_id); 127 void CreateLayerAtId(VI::Display& display, u64 layer_id);
127 128
diff --git a/src/core/hle/service/set/appln_settings.cpp b/src/core/hle/service/set/appln_settings.cpp
new file mode 100644
index 000000000..a5d802757
--- /dev/null
+++ b/src/core/hle/service/set/appln_settings.cpp
@@ -0,0 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/set/appln_settings.h"
5
6namespace Service::Set {
7
8ApplnSettings DefaultApplnSettings() {
9 return {};
10}
11
12} // namespace Service::Set
diff --git a/src/core/hle/service/set/appln_settings.h b/src/core/hle/service/set/appln_settings.h
new file mode 100644
index 000000000..126375860
--- /dev/null
+++ b/src/core/hle/service/set/appln_settings.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8
9#include "common/common_types.h"
10
11namespace Service::Set {
12struct ApplnSettings {
13 std::array<u8, 0x10> reserved_000;
14
15 // nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0
16 std::array<u8, 0x10> mii_author_id;
17
18 std::array<u8, 0x30> reserved_020;
19
20 // nn::settings::system::ServiceDiscoveryControlSettings
21 std::array<u8, 0x4> service_discovery_control_settings;
22
23 std::array<u8, 0x20> reserved_054;
24
25 bool in_repair_process_enable_flag;
26
27 std::array<u8, 0x3> pad_075;
28};
29static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10);
30static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50);
31static_assert(offsetof(ApplnSettings, in_repair_process_enable_flag) == 0x74);
32static_assert(sizeof(ApplnSettings) == 0x78, "ApplnSettings has the wrong size!");
33
34ApplnSettings DefaultApplnSettings();
35
36} // namespace Service::Set
diff --git a/src/core/hle/service/set/device_settings.cpp b/src/core/hle/service/set/device_settings.cpp
new file mode 100644
index 000000000..e423ce38a
--- /dev/null
+++ b/src/core/hle/service/set/device_settings.cpp
@@ -0,0 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/set/device_settings.h"
5
6namespace Service::Set {
7
8DeviceSettings DefaultDeviceSettings() {
9 return {};
10}
11
12} // namespace Service::Set
diff --git a/src/core/hle/service/set/device_settings.h b/src/core/hle/service/set/device_settings.h
new file mode 100644
index 000000000..f291d0ebe
--- /dev/null
+++ b/src/core/hle/service/set/device_settings.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8
9#include "common/common_types.h"
10
11namespace Service::Set {
12struct DeviceSettings {
13 std::array<u8, 0x10> reserved_000;
14
15 // nn::settings::BatteryLot
16 std::array<u8, 0x18> ptm_battery_lot;
17 // nn::settings::system::PtmFuelGaugeParameter
18 std::array<u8, 0x18> ptm_fuel_gauge_parameter;
19 u8 ptm_battery_version;
20 // nn::settings::system::PtmCycleCountReliability
21 u32 ptm_cycle_count_reliability;
22
23 std::array<u8, 0x48> reserved_048;
24
25 // nn::settings::system::AnalogStickUserCalibration L
26 std::array<u8, 0x10> analog_user_stick_calibration_l;
27 // nn::settings::system::AnalogStickUserCalibration R
28 std::array<u8, 0x10> analog_user_stick_calibration_r;
29
30 std::array<u8, 0x20> reserved_0B0;
31
32 // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
33 std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias;
34 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
35 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias;
36 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
37 std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
38 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
39 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
40 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
41 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias;
42 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
43 std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
44};
45static_assert(offsetof(DeviceSettings, ptm_battery_lot) == 0x10);
46static_assert(offsetof(DeviceSettings, ptm_cycle_count_reliability) == 0x44);
47static_assert(offsetof(DeviceSettings, analog_user_stick_calibration_l) == 0x90);
48static_assert(offsetof(DeviceSettings, console_six_axis_sensor_acceleration_bias) == 0xD0);
49static_assert(offsetof(DeviceSettings, console_six_axis_sensor_angular_acceleration) == 0x13C);
50static_assert(sizeof(DeviceSettings) == 0x160, "DeviceSettings has the wrong size!");
51
52DeviceSettings DefaultDeviceSettings();
53
54} // namespace Service::Set
diff --git a/src/core/hle/service/set/private_settings.cpp b/src/core/hle/service/set/private_settings.cpp
new file mode 100644
index 000000000..70bf65727
--- /dev/null
+++ b/src/core/hle/service/set/private_settings.cpp
@@ -0,0 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/set/private_settings.h"
5
6namespace Service::Set {
7
8PrivateSettings DefaultPrivateSettings() {
9 return {};
10}
11
12} // namespace Service::Set
diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h
new file mode 100644
index 000000000..b63eaf45c
--- /dev/null
+++ b/src/core/hle/service/set/private_settings.h
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/uuid.h"
12#include "core/hle/service/time/clock_types.h"
13
14namespace Service::Set {
15
16/// This is nn::settings::system::InitialLaunchFlag
17struct InitialLaunchFlag {
18 union {
19 u32 raw{};
20
21 BitField<0, 1, u32> InitialLaunchCompletionFlag;
22 BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
23 BitField<16, 1, u32> InitialLaunchTimestampFlag;
24 };
25};
26static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
27
28/// This is nn::settings::system::InitialLaunchSettings
29struct InitialLaunchSettings {
30 InitialLaunchFlag flags;
31 INSERT_PADDING_BYTES(0x4);
32 Service::Time::Clock::SteadyClockTimePoint timestamp;
33};
34static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
35
36#pragma pack(push, 4)
37struct InitialLaunchSettingsPacked {
38 InitialLaunchFlag flags;
39 Service::Time::Clock::SteadyClockTimePoint timestamp;
40};
41#pragma pack(pop)
42static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
43 "InitialLaunchSettingsPacked is incorrect size");
44
45struct PrivateSettings {
46 std::array<u8, 0x10> reserved_00;
47
48 // nn::settings::system::InitialLaunchSettings
49 InitialLaunchSettings initial_launch_settings;
50
51 std::array<u8, 0x20> reserved_30;
52
53 Common::UUID external_clock_source_id;
54 s64 shutdown_rtc_value;
55 s64 external_steady_clock_internal_offset;
56
57 std::array<u8, 0x60> reserved_70;
58
59 // nn::settings::system::PlatformRegion
60 std::array<u8, 0x4> platform_region;
61
62 std::array<u8, 0x4> reserved_D4;
63};
64static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10);
65static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50);
66static_assert(offsetof(PrivateSettings, reserved_70) == 0x70);
67static_assert(offsetof(PrivateSettings, platform_region) == 0xD0);
68static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!");
69
70PrivateSettings DefaultPrivateSettings();
71
72} // namespace Service::Set
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index b61a3560d..6ef3da410 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -4,35 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
7#include "core/hle/service/set/system_settings.h"
7 8
8namespace Core { 9namespace Core {
9class System; 10class System;
10} 11}
11 12
12namespace Service::Set { 13namespace Service::Set {
13
14/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
15enum class LanguageCode : u64 {
16 JA = 0x000000000000616A,
17 EN_US = 0x00000053552D6E65,
18 FR = 0x0000000000007266,
19 DE = 0x0000000000006564,
20 IT = 0x0000000000007469,
21 ES = 0x0000000000007365,
22 ZH_CN = 0x0000004E432D687A,
23 KO = 0x0000000000006F6B,
24 NL = 0x0000000000006C6E,
25 PT = 0x0000000000007470,
26 RU = 0x0000000000007572,
27 ZH_TW = 0x00000057542D687A,
28 EN_GB = 0x00000042472D6E65,
29 FR_CA = 0x00000041432D7266,
30 ES_419 = 0x00003931342D7365,
31 ZH_HANS = 0x00736E61482D687A,
32 ZH_HANT = 0x00746E61482D687A,
33 PT_BR = 0x00000052422D7470,
34};
35
36enum class KeyboardLayout : u64 { 14enum class KeyboardLayout : u64 {
37 Japanese = 0, 15 Japanese = 0,
38 EnglishUs = 1, 16 EnglishUs = 1,
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 48304e6d1..0653779d5 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -1,7 +1,12 @@
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 <fstream>
5
4#include "common/assert.h" 6#include "common/assert.h"
7#include "common/fs/file.h"
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
5#include "common/logging/log.h" 10#include "common/logging/log.h"
6#include "common/settings.h" 11#include "common/settings.h"
7#include "common/string_util.h" 12#include "common/string_util.h"
@@ -19,6 +24,16 @@
19 24
20namespace Service::Set { 25namespace Service::Set {
21 26
27namespace {
28constexpr u32 SETTINGS_VERSION{1u};
29constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't');
30struct SettingsHeader {
31 u64 magic;
32 u32 version;
33 u32 reserved;
34};
35} // Anonymous namespace
36
22Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, 37Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
23 GetFirmwareVersionType type) { 38 GetFirmwareVersionType type) {
24 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; 39 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809;
@@ -72,11 +87,120 @@ Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System&
72 return ResultSuccess; 87 return ResultSuccess;
73} 88}
74 89
90bool SET_SYS::LoadSettingsFile(std::filesystem::path& path, auto&& default_func) {
91 using settings_type = decltype(default_func());
92
93 if (!Common::FS::CreateDirs(path)) {
94 return false;
95 }
96
97 auto settings_file = path / "settings.dat";
98 auto exists = std::filesystem::exists(settings_file);
99 auto file_size_ok = exists && std::filesystem::file_size(settings_file) ==
100 sizeof(SettingsHeader) + sizeof(settings_type);
101
102 auto ResetToDefault = [&]() {
103 auto default_settings{default_func()};
104
105 SettingsHeader hdr{
106 .magic = SETTINGS_MAGIC,
107 .version = SETTINGS_VERSION,
108 .reserved = 0u,
109 };
110
111 std::ofstream out_settings_file(settings_file, std::ios::out | std::ios::binary);
112 out_settings_file.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr));
113 out_settings_file.write(reinterpret_cast<const char*>(&default_settings),
114 sizeof(settings_type));
115 out_settings_file.flush();
116 out_settings_file.close();
117 };
118
119 constexpr auto IsHeaderValid = [](std::ifstream& file) -> bool {
120 if (!file.is_open()) {
121 return false;
122 }
123 SettingsHeader hdr{};
124 file.read(reinterpret_cast<char*>(&hdr), sizeof(hdr));
125 return hdr.magic == SETTINGS_MAGIC && hdr.version == SETTINGS_VERSION;
126 };
127
128 if (!exists || !file_size_ok) {
129 ResetToDefault();
130 }
131
132 std::ifstream file(settings_file, std::ios::binary | std::ios::in);
133 if (!IsHeaderValid(file)) {
134 file.close();
135 ResetToDefault();
136 file = std::ifstream(settings_file, std::ios::binary | std::ios::in);
137 if (!IsHeaderValid(file)) {
138 return false;
139 }
140 }
141
142 if constexpr (std::is_same_v<settings_type, PrivateSettings>) {
143 file.read(reinterpret_cast<char*>(&m_private_settings), sizeof(settings_type));
144 } else if constexpr (std::is_same_v<settings_type, DeviceSettings>) {
145 file.read(reinterpret_cast<char*>(&m_device_settings), sizeof(settings_type));
146 } else if constexpr (std::is_same_v<settings_type, ApplnSettings>) {
147 file.read(reinterpret_cast<char*>(&m_appln_settings), sizeof(settings_type));
148 } else if constexpr (std::is_same_v<settings_type, SystemSettings>) {
149 file.read(reinterpret_cast<char*>(&m_system_settings), sizeof(settings_type));
150 } else {
151 UNREACHABLE();
152 }
153 file.close();
154
155 return true;
156}
157
158bool SET_SYS::StoreSettingsFile(std::filesystem::path& path, auto& settings) {
159 using settings_type = std::decay_t<decltype(settings)>;
160
161 if (!Common::FS::IsDir(path)) {
162 return false;
163 }
164
165 auto settings_base = path / "settings";
166 auto settings_tmp_file = settings_base;
167 settings_tmp_file = settings_tmp_file.replace_extension("tmp");
168 std::ofstream file(settings_tmp_file, std::ios::binary | std::ios::out);
169 if (!file.is_open()) {
170 return false;
171 }
172
173 SettingsHeader hdr{
174 .magic = SETTINGS_MAGIC,
175 .version = SETTINGS_VERSION,
176 .reserved = 0u,
177 };
178 file.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr));
179
180 if constexpr (std::is_same_v<settings_type, PrivateSettings>) {
181 file.write(reinterpret_cast<const char*>(&m_private_settings), sizeof(settings_type));
182 } else if constexpr (std::is_same_v<settings_type, DeviceSettings>) {
183 file.write(reinterpret_cast<const char*>(&m_device_settings), sizeof(settings_type));
184 } else if constexpr (std::is_same_v<settings_type, ApplnSettings>) {
185 file.write(reinterpret_cast<const char*>(&m_appln_settings), sizeof(settings_type));
186 } else if constexpr (std::is_same_v<settings_type, SystemSettings>) {
187 file.write(reinterpret_cast<const char*>(&m_system_settings), sizeof(settings_type));
188 } else {
189 UNREACHABLE();
190 }
191 file.close();
192
193 std::filesystem::rename(settings_tmp_file, settings_base.replace_extension("dat"));
194
195 return true;
196}
197
75void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { 198void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
76 IPC::RequestParser rp{ctx}; 199 IPC::RequestParser rp{ctx};
77 language_code_setting = rp.PopEnum<LanguageCode>(); 200 m_system_settings.language_code = rp.PopEnum<LanguageCode>();
201 SetSaveNeeded();
78 202
79 LOG_INFO(Service_SET, "called, language_code={}", language_code_setting); 203 LOG_INFO(Service_SET, "called, language_code={}", m_system_settings.language_code);
80 204
81 IPC::ResponseBuilder rb{ctx, 2}; 205 IPC::ResponseBuilder rb{ctx, 2};
82 rb.Push(ResultSuccess); 206 rb.Push(ResultSuccess);
@@ -112,19 +236,68 @@ void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) {
112 rb.Push(result); 236 rb.Push(result);
113} 237}
114 238
239void SET_SYS::GetExternalSteadyClockSourceId(HLERequestContext& ctx) {
240 LOG_INFO(Service_SET, "called");
241
242 Common::UUID id{};
243 auto res = GetExternalSteadyClockSourceId(id);
244
245 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)};
246 rb.Push(res);
247 rb.PushRaw(id);
248}
249
250void SET_SYS::SetExternalSteadyClockSourceId(HLERequestContext& ctx) {
251 LOG_INFO(Service_SET, "called");
252
253 IPC::RequestParser rp{ctx};
254 auto id{rp.PopRaw<Common::UUID>()};
255
256 auto res = SetExternalSteadyClockSourceId(id);
257
258 IPC::ResponseBuilder rb{ctx, 2};
259 rb.Push(res);
260}
261
262void SET_SYS::GetUserSystemClockContext(HLERequestContext& ctx) {
263 LOG_INFO(Service_SET, "called");
264
265 Service::Time::Clock::SystemClockContext context{};
266 auto res = GetUserSystemClockContext(context);
267
268 IPC::ResponseBuilder rb{ctx,
269 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)};
270 rb.Push(res);
271 rb.PushRaw(context);
272}
273
274void SET_SYS::SetUserSystemClockContext(HLERequestContext& ctx) {
275 LOG_INFO(Service_SET, "called");
276
277 IPC::RequestParser rp{ctx};
278 auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()};
279
280 auto res = SetUserSystemClockContext(context);
281
282 IPC::ResponseBuilder rb{ctx, 2};
283 rb.Push(res);
284}
285
115void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { 286void SET_SYS::GetAccountSettings(HLERequestContext& ctx) {
116 LOG_INFO(Service_SET, "called"); 287 LOG_INFO(Service_SET, "called");
117 288
118 IPC::ResponseBuilder rb{ctx, 3}; 289 IPC::ResponseBuilder rb{ctx, 3};
119 rb.Push(ResultSuccess); 290 rb.Push(ResultSuccess);
120 rb.PushRaw(account_settings); 291 rb.PushRaw(m_system_settings.account_settings);
121} 292}
122 293
123void SET_SYS::SetAccountSettings(HLERequestContext& ctx) { 294void SET_SYS::SetAccountSettings(HLERequestContext& ctx) {
124 IPC::RequestParser rp{ctx}; 295 IPC::RequestParser rp{ctx};
125 account_settings = rp.PopRaw<AccountSettings>(); 296 m_system_settings.account_settings = rp.PopRaw<AccountSettings>();
297 SetSaveNeeded();
126 298
127 LOG_INFO(Service_SET, "called, account_settings_flags={}", account_settings.flags); 299 LOG_INFO(Service_SET, "called, account_settings_flags={}",
300 m_system_settings.account_settings.flags);
128 301
129 IPC::ResponseBuilder rb{ctx, 2}; 302 IPC::ResponseBuilder rb{ctx, 2};
130 rb.Push(ResultSuccess); 303 rb.Push(ResultSuccess);
@@ -133,11 +306,11 @@ void SET_SYS::SetAccountSettings(HLERequestContext& ctx) {
133void SET_SYS::GetEulaVersions(HLERequestContext& ctx) { 306void SET_SYS::GetEulaVersions(HLERequestContext& ctx) {
134 LOG_INFO(Service_SET, "called"); 307 LOG_INFO(Service_SET, "called");
135 308
136 ctx.WriteBuffer(eula_versions); 309 ctx.WriteBuffer(m_system_settings.eula_versions);
137 310
138 IPC::ResponseBuilder rb{ctx, 3}; 311 IPC::ResponseBuilder rb{ctx, 3};
139 rb.Push(ResultSuccess); 312 rb.Push(ResultSuccess);
140 rb.Push(static_cast<u32>(eula_versions.size())); 313 rb.Push(m_system_settings.eula_version_count);
141} 314}
142 315
143void SET_SYS::SetEulaVersions(HLERequestContext& ctx) { 316void SET_SYS::SetEulaVersions(HLERequestContext& ctx) {
@@ -145,13 +318,12 @@ void SET_SYS::SetEulaVersions(HLERequestContext& ctx) {
145 const auto buffer_data = ctx.ReadBuffer(); 318 const auto buffer_data = ctx.ReadBuffer();
146 319
147 LOG_INFO(Service_SET, "called, elements={}", elements); 320 LOG_INFO(Service_SET, "called, elements={}", elements);
321 ASSERT(elements <= m_system_settings.eula_versions.size());
148 322
149 eula_versions.resize(elements); 323 m_system_settings.eula_version_count = static_cast<u32>(elements);
150 for (std::size_t index = 0; index < elements; index++) { 324 std::memcpy(&m_system_settings.eula_versions, buffer_data.data(),
151 const std::size_t start_index = index * sizeof(EulaVersion); 325 sizeof(EulaVersion) * elements);
152 memcpy(eula_versions.data() + start_index, buffer_data.data() + start_index, 326 SetSaveNeeded();
153 sizeof(EulaVersion));
154 }
155 327
156 IPC::ResponseBuilder rb{ctx, 2}; 328 IPC::ResponseBuilder rb{ctx, 2};
157 rb.Push(ResultSuccess); 329 rb.Push(ResultSuccess);
@@ -162,14 +334,15 @@ void SET_SYS::GetColorSetId(HLERequestContext& ctx) {
162 334
163 IPC::ResponseBuilder rb{ctx, 3}; 335 IPC::ResponseBuilder rb{ctx, 3};
164 rb.Push(ResultSuccess); 336 rb.Push(ResultSuccess);
165 rb.PushEnum(color_set); 337 rb.PushEnum(m_system_settings.color_set_id);
166} 338}
167 339
168void SET_SYS::SetColorSetId(HLERequestContext& ctx) { 340void SET_SYS::SetColorSetId(HLERequestContext& ctx) {
169 IPC::RequestParser rp{ctx}; 341 IPC::RequestParser rp{ctx};
170 color_set = rp.PopEnum<ColorSet>(); 342 m_system_settings.color_set_id = rp.PopEnum<ColorSet>();
343 SetSaveNeeded();
171 344
172 LOG_DEBUG(Service_SET, "called, color_set={}", color_set); 345 LOG_DEBUG(Service_SET, "called, color_set={}", m_system_settings.color_set_id);
173 346
174 IPC::ResponseBuilder rb{ctx, 2}; 347 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(ResultSuccess); 348 rb.Push(ResultSuccess);
@@ -180,17 +353,21 @@ void SET_SYS::GetNotificationSettings(HLERequestContext& ctx) {
180 353
181 IPC::ResponseBuilder rb{ctx, 8}; 354 IPC::ResponseBuilder rb{ctx, 8};
182 rb.Push(ResultSuccess); 355 rb.Push(ResultSuccess);
183 rb.PushRaw(notification_settings); 356 rb.PushRaw(m_system_settings.notification_settings);
184} 357}
185 358
186void SET_SYS::SetNotificationSettings(HLERequestContext& ctx) { 359void SET_SYS::SetNotificationSettings(HLERequestContext& ctx) {
187 IPC::RequestParser rp{ctx}; 360 IPC::RequestParser rp{ctx};
188 notification_settings = rp.PopRaw<NotificationSettings>(); 361 m_system_settings.notification_settings = rp.PopRaw<NotificationSettings>();
362 SetSaveNeeded();
189 363
190 LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}", 364 LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}",
191 notification_settings.flags.raw, notification_settings.volume, 365 m_system_settings.notification_settings.flags.raw,
192 notification_settings.start_time.hour, notification_settings.start_time.minute, 366 m_system_settings.notification_settings.volume,
193 notification_settings.stop_time.hour, notification_settings.stop_time.minute); 367 m_system_settings.notification_settings.start_time.hour,
368 m_system_settings.notification_settings.start_time.minute,
369 m_system_settings.notification_settings.stop_time.hour,
370 m_system_settings.notification_settings.stop_time.minute);
194 371
195 IPC::ResponseBuilder rb{ctx, 2}; 372 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ResultSuccess); 373 rb.Push(ResultSuccess);
@@ -199,11 +376,11 @@ void SET_SYS::SetNotificationSettings(HLERequestContext& ctx) {
199void SET_SYS::GetAccountNotificationSettings(HLERequestContext& ctx) { 376void SET_SYS::GetAccountNotificationSettings(HLERequestContext& ctx) {
200 LOG_INFO(Service_SET, "called"); 377 LOG_INFO(Service_SET, "called");
201 378
202 ctx.WriteBuffer(account_notifications); 379 ctx.WriteBuffer(m_system_settings.account_notification_settings);
203 380
204 IPC::ResponseBuilder rb{ctx, 3}; 381 IPC::ResponseBuilder rb{ctx, 3};
205 rb.Push(ResultSuccess); 382 rb.Push(ResultSuccess);
206 rb.Push(static_cast<u32>(account_notifications.size())); 383 rb.Push(m_system_settings.account_notification_settings_count);
207} 384}
208 385
209void SET_SYS::SetAccountNotificationSettings(HLERequestContext& ctx) { 386void SET_SYS::SetAccountNotificationSettings(HLERequestContext& ctx) {
@@ -212,12 +389,12 @@ void SET_SYS::SetAccountNotificationSettings(HLERequestContext& ctx) {
212 389
213 LOG_INFO(Service_SET, "called, elements={}", elements); 390 LOG_INFO(Service_SET, "called, elements={}", elements);
214 391
215 account_notifications.resize(elements); 392 ASSERT(elements <= m_system_settings.account_notification_settings.size());
216 for (std::size_t index = 0; index < elements; index++) { 393
217 const std::size_t start_index = index * sizeof(AccountNotificationSettings); 394 m_system_settings.account_notification_settings_count = static_cast<u32>(elements);
218 memcpy(account_notifications.data() + start_index, buffer_data.data() + start_index, 395 std::memcpy(&m_system_settings.account_notification_settings, buffer_data.data(),
219 sizeof(AccountNotificationSettings)); 396 elements * sizeof(AccountNotificationSettings));
220 } 397 SetSaveNeeded();
221 398
222 IPC::ResponseBuilder rb{ctx, 2}; 399 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ResultSuccess); 400 rb.Push(ResultSuccess);
@@ -244,6 +421,14 @@ static Settings GetSettings() {
244 ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); 421 ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0});
245 ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); 422 ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000});
246 423
424 // Time
425 ret["time"]["notify_time_to_fs_interval_seconds"] = ToBytes(s32{600});
426 ret["time"]["standard_network_clock_sufficient_accuracy_minutes"] =
427 ToBytes(s32{43200}); // 30 days
428 ret["time"]["standard_steady_clock_rtc_update_interval_minutes"] = ToBytes(s32{5});
429 ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0});
430 ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023});
431
247 return ret; 432 return ret;
248} 433}
249 434
@@ -273,8 +458,6 @@ void SET_SYS::GetSettingsItemValueSize(HLERequestContext& ctx) {
273} 458}
274 459
275void SET_SYS::GetSettingsItemValue(HLERequestContext& ctx) { 460void SET_SYS::GetSettingsItemValue(HLERequestContext& ctx) {
276 LOG_DEBUG(Service_SET, "called");
277
278 // The category of the setting. This corresponds to the top-level keys of 461 // The category of the setting. This corresponds to the top-level keys of
279 // system_settings.ini. 462 // system_settings.ini.
280 const auto setting_category_buf{ctx.ReadBuffer(0)}; 463 const auto setting_category_buf{ctx.ReadBuffer(0)};
@@ -285,14 +468,13 @@ void SET_SYS::GetSettingsItemValue(HLERequestContext& ctx) {
285 const auto setting_name_buf{ctx.ReadBuffer(1)}; 468 const auto setting_name_buf{ctx.ReadBuffer(1)};
286 const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; 469 const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
287 470
288 auto settings{GetSettings()}; 471 std::vector<u8> value;
289 Result response{ResultUnknown}; 472 auto response = GetSettingsItemValue(value, setting_category, setting_name);
290 473
291 if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { 474 LOG_INFO(Service_SET, "called. category={}, name={} -- res=0x{:X}", setting_category,
292 auto setting_value = settings[setting_category][setting_name]; 475 setting_name, response.raw);
293 ctx.WriteBuffer(setting_value.data(), setting_value.size()); 476
294 response = ResultSuccess; 477 ctx.WriteBuffer(value.data(), value.size());
295 }
296 478
297 IPC::ResponseBuilder rb{ctx, 2}; 479 IPC::ResponseBuilder rb{ctx, 2};
298 rb.Push(response); 480 rb.Push(response);
@@ -303,19 +485,23 @@ void SET_SYS::GetTvSettings(HLERequestContext& ctx) {
303 485
304 IPC::ResponseBuilder rb{ctx, 10}; 486 IPC::ResponseBuilder rb{ctx, 10};
305 rb.Push(ResultSuccess); 487 rb.Push(ResultSuccess);
306 rb.PushRaw(tv_settings); 488 rb.PushRaw(m_system_settings.tv_settings);
307} 489}
308 490
309void SET_SYS::SetTvSettings(HLERequestContext& ctx) { 491void SET_SYS::SetTvSettings(HLERequestContext& ctx) {
310 IPC::RequestParser rp{ctx}; 492 IPC::RequestParser rp{ctx};
311 tv_settings = rp.PopRaw<TvSettings>(); 493 m_system_settings.tv_settings = rp.PopRaw<TvSettings>();
494 SetSaveNeeded();
312 495
313 LOG_INFO(Service_SET, 496 LOG_INFO(Service_SET,
314 "called, flags={}, cmu_mode={}, constrast_ratio={}, hdmi_content_type={}, " 497 "called, flags={}, cmu_mode={}, constrast_ratio={}, hdmi_content_type={}, "
315 "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}", 498 "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}",
316 tv_settings.flags.raw, tv_settings.cmu_mode, tv_settings.constrast_ratio, 499 m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode,
317 tv_settings.hdmi_content_type, tv_settings.rgb_range, tv_settings.tv_gama, 500 m_system_settings.tv_settings.constrast_ratio,
318 tv_settings.tv_resolution, tv_settings.tv_underscan); 501 m_system_settings.tv_settings.hdmi_content_type,
502 m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama,
503 m_system_settings.tv_settings.tv_resolution,
504 m_system_settings.tv_settings.tv_underscan);
319 505
320 IPC::ResponseBuilder rb{ctx, 2}; 506 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(ResultSuccess); 507 rb.Push(ResultSuccess);
@@ -329,16 +515,87 @@ void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
329 rb.PushEnum(QuestFlag::Retail); 515 rb.PushEnum(QuestFlag::Retail);
330} 516}
331 517
518void SET_SYS::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
519 LOG_WARNING(Service_SET, "called");
520
521 Service::Time::TimeZone::LocationName name{};
522 auto res = GetDeviceTimeZoneLocationName(name);
523
524 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)};
525 rb.Push(res);
526 rb.PushRaw<Service::Time::TimeZone::LocationName>(name);
527}
528
529void SET_SYS::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
530 LOG_WARNING(Service_SET, "called");
531
532 IPC::RequestParser rp{ctx};
533 auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()};
534
535 auto res = SetDeviceTimeZoneLocationName(name);
536
537 IPC::ResponseBuilder rb{ctx, 2};
538 rb.Push(res);
539}
540
332void SET_SYS::SetRegionCode(HLERequestContext& ctx) { 541void SET_SYS::SetRegionCode(HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx}; 542 IPC::RequestParser rp{ctx};
334 region_code = rp.PopEnum<RegionCode>(); 543 m_system_settings.region_code = rp.PopEnum<RegionCode>();
544 SetSaveNeeded();
335 545
336 LOG_INFO(Service_SET, "called, region_code={}", region_code); 546 LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code);
337 547
338 IPC::ResponseBuilder rb{ctx, 2}; 548 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(ResultSuccess); 549 rb.Push(ResultSuccess);
340} 550}
341 551
552void SET_SYS::GetNetworkSystemClockContext(HLERequestContext& ctx) {
553 LOG_INFO(Service_SET, "called");
554
555 Service::Time::Clock::SystemClockContext context{};
556 auto res = GetNetworkSystemClockContext(context);
557
558 IPC::ResponseBuilder rb{ctx,
559 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)};
560 rb.Push(res);
561 rb.PushRaw(context);
562}
563
564void SET_SYS::SetNetworkSystemClockContext(HLERequestContext& ctx) {
565 LOG_INFO(Service_SET, "called");
566
567 IPC::RequestParser rp{ctx};
568 auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()};
569
570 auto res = SetNetworkSystemClockContext(context);
571
572 IPC::ResponseBuilder rb{ctx, 2};
573 rb.Push(res);
574}
575
576void SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) {
577 LOG_INFO(Service_SET, "called");
578
579 bool enabled{};
580 auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled);
581
582 IPC::ResponseBuilder rb{ctx, 3};
583 rb.Push(res);
584 rb.PushRaw(enabled);
585}
586
587void SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) {
588 LOG_INFO(Service_SET, "called");
589
590 IPC::RequestParser rp{ctx};
591 auto enabled{rp.Pop<bool>()};
592
593 auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled);
594
595 IPC::ResponseBuilder rb{ctx, 2};
596 rb.Push(res);
597}
598
342void SET_SYS::GetPrimaryAlbumStorage(HLERequestContext& ctx) { 599void SET_SYS::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
343 LOG_WARNING(Service_SET, "(STUBBED) called"); 600 LOG_WARNING(Service_SET, "(STUBBED) called");
344 601
@@ -352,16 +609,18 @@ void SET_SYS::GetSleepSettings(HLERequestContext& ctx) {
352 609
353 IPC::ResponseBuilder rb{ctx, 5}; 610 IPC::ResponseBuilder rb{ctx, 5};
354 rb.Push(ResultSuccess); 611 rb.Push(ResultSuccess);
355 rb.PushRaw(sleep_settings); 612 rb.PushRaw(m_system_settings.sleep_settings);
356} 613}
357 614
358void SET_SYS::SetSleepSettings(HLERequestContext& ctx) { 615void SET_SYS::SetSleepSettings(HLERequestContext& ctx) {
359 IPC::RequestParser rp{ctx}; 616 IPC::RequestParser rp{ctx};
360 sleep_settings = rp.PopRaw<SleepSettings>(); 617 m_system_settings.sleep_settings = rp.PopRaw<SleepSettings>();
618 SetSaveNeeded();
361 619
362 LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", 620 LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}",
363 sleep_settings.flags.raw, sleep_settings.handheld_sleep_plan, 621 m_system_settings.sleep_settings.flags.raw,
364 sleep_settings.console_sleep_plan); 622 m_system_settings.sleep_settings.handheld_sleep_plan,
623 m_system_settings.sleep_settings.console_sleep_plan);
365 624
366 IPC::ResponseBuilder rb{ctx, 2}; 625 IPC::ResponseBuilder rb{ctx, 2};
367 rb.Push(ResultSuccess); 626 rb.Push(ResultSuccess);
@@ -371,15 +630,20 @@ void SET_SYS::GetInitialLaunchSettings(HLERequestContext& ctx) {
371 LOG_INFO(Service_SET, "called"); 630 LOG_INFO(Service_SET, "called");
372 IPC::ResponseBuilder rb{ctx, 10}; 631 IPC::ResponseBuilder rb{ctx, 10};
373 rb.Push(ResultSuccess); 632 rb.Push(ResultSuccess);
374 rb.PushRaw(launch_settings); 633 rb.PushRaw(m_system_settings.initial_launch_settings_packed);
375} 634}
376 635
377void SET_SYS::SetInitialLaunchSettings(HLERequestContext& ctx) { 636void SET_SYS::SetInitialLaunchSettings(HLERequestContext& ctx) {
378 IPC::RequestParser rp{ctx}; 637 IPC::RequestParser rp{ctx};
379 launch_settings = rp.PopRaw<InitialLaunchSettings>(); 638 auto inital_launch_settings = rp.PopRaw<InitialLaunchSettings>();
380 639
381 LOG_INFO(Service_SET, "called, flags={}, timestamp={}", launch_settings.flags.raw, 640 m_system_settings.initial_launch_settings_packed.flags = inital_launch_settings.flags;
382 launch_settings.timestamp.time_point); 641 m_system_settings.initial_launch_settings_packed.timestamp = inital_launch_settings.timestamp;
642 SetSaveNeeded();
643
644 LOG_INFO(Service_SET, "called, flags={}, timestamp={}",
645 m_system_settings.initial_launch_settings_packed.flags.raw,
646 m_system_settings.initial_launch_settings_packed.timestamp.time_point);
383 647
384 IPC::ResponseBuilder rb{ctx, 2}; 648 IPC::ResponseBuilder rb{ctx, 2};
385 rb.Push(ResultSuccess); 649 rb.Push(ResultSuccess);
@@ -437,13 +701,37 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
437void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { 701void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
438 u8 battery_percentage_flag{1}; 702 u8 battery_percentage_flag{1};
439 703
440 LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag); 704 LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}",
705 battery_percentage_flag);
441 706
442 IPC::ResponseBuilder rb{ctx, 3}; 707 IPC::ResponseBuilder rb{ctx, 3};
443 rb.Push(ResultSuccess); 708 rb.Push(ResultSuccess);
444 rb.Push(battery_percentage_flag); 709 rb.Push(battery_percentage_flag);
445} 710}
446 711
712void SET_SYS::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) {
713 LOG_DEBUG(Service_SET, "called.");
714
715 IPC::RequestParser rp{ctx};
716 auto offset{rp.Pop<s64>()};
717
718 auto res = SetExternalSteadyClockInternalOffset(offset);
719
720 IPC::ResponseBuilder rb{ctx, 2};
721 rb.Push(res);
722}
723
724void SET_SYS::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) {
725 LOG_DEBUG(Service_SET, "called.");
726
727 s64 offset{};
728 auto res = GetExternalSteadyClockInternalOffset(offset);
729
730 IPC::ResponseBuilder rb{ctx, 4};
731 rb.Push(res);
732 rb.Push(offset);
733}
734
447void SET_SYS::GetErrorReportSharePermission(HLERequestContext& ctx) { 735void SET_SYS::GetErrorReportSharePermission(HLERequestContext& ctx) {
448 LOG_WARNING(Service_SET, "(STUBBED) called"); 736 LOG_WARNING(Service_SET, "(STUBBED) called");
449 737
@@ -453,18 +741,19 @@ void SET_SYS::GetErrorReportSharePermission(HLERequestContext& ctx) {
453} 741}
454 742
455void SET_SYS::GetAppletLaunchFlags(HLERequestContext& ctx) { 743void SET_SYS::GetAppletLaunchFlags(HLERequestContext& ctx) {
456 LOG_INFO(Service_SET, "called, applet_launch_flag={}", applet_launch_flag); 744 LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag);
457 745
458 IPC::ResponseBuilder rb{ctx, 3}; 746 IPC::ResponseBuilder rb{ctx, 3};
459 rb.Push(ResultSuccess); 747 rb.Push(ResultSuccess);
460 rb.Push(applet_launch_flag); 748 rb.Push(m_system_settings.applet_launch_flag);
461} 749}
462 750
463void SET_SYS::SetAppletLaunchFlags(HLERequestContext& ctx) { 751void SET_SYS::SetAppletLaunchFlags(HLERequestContext& ctx) {
464 IPC::RequestParser rp{ctx}; 752 IPC::RequestParser rp{ctx};
465 applet_launch_flag = rp.Pop<u32>(); 753 m_system_settings.applet_launch_flag = rp.Pop<u32>();
754 SetSaveNeeded();
466 755
467 LOG_INFO(Service_SET, "called, applet_launch_flag={}", applet_launch_flag); 756 LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag);
468 757
469 IPC::ResponseBuilder rb{ctx, 2}; 758 IPC::ResponseBuilder rb{ctx, 2};
470 rb.Push(ResultSuccess); 759 rb.Push(ResultSuccess);
@@ -489,6 +778,52 @@ void SET_SYS::GetKeyboardLayout(HLERequestContext& ctx) {
489 rb.Push(static_cast<u32>(selected_keyboard_layout)); 778 rb.Push(static_cast<u32>(selected_keyboard_layout));
490} 779}
491 780
781void SET_SYS::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
782 LOG_WARNING(Service_SET, "called.");
783
784 Service::Time::Clock::SteadyClockTimePoint time_point{};
785 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
786
787 IPC::ResponseBuilder rb{ctx, 4};
788 rb.Push(res);
789 rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point);
790}
791
792void SET_SYS::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
793 LOG_WARNING(Service_SET, "called.");
794
795 IPC::RequestParser rp{ctx};
796 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()};
797
798 auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
799
800 IPC::ResponseBuilder rb{ctx, 2};
801 rb.Push(res);
802}
803
804void SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx) {
805 LOG_WARNING(Service_SET, "called.");
806
807 Service::Time::Clock::SteadyClockTimePoint time_point{};
808 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
809
810 IPC::ResponseBuilder rb{ctx, 4};
811 rb.Push(res);
812 rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point);
813}
814
815void SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx) {
816 LOG_WARNING(Service_SET, "called.");
817
818 IPC::RequestParser rp{ctx};
819 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()};
820
821 auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
822
823 IPC::ResponseBuilder rb{ctx, 2};
824 rb.Push(res);
825}
826
492void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { 827void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
493 LOG_WARNING(Service_SET, "(STUBBED) called"); 828 LOG_WARNING(Service_SET, "(STUBBED) called");
494 829
@@ -508,7 +843,7 @@ void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
508 .extra = 0xFF000000, 843 .extra = 0xFF000000,
509 }; 844 };
510 845
511 IPC::ResponseBuilder rb{ctx, 7}; 846 IPC::ResponseBuilder rb{ctx, 2 + sizeof(HomeMenuScheme) / sizeof(u32)};
512 rb.Push(ResultSuccess); 847 rb.Push(ResultSuccess);
513 rb.PushRaw(default_color); 848 rb.PushRaw(default_color);
514} 849}
@@ -520,6 +855,7 @@ void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
520 rb.Push(ResultSuccess); 855 rb.Push(ResultSuccess);
521 rb.Push(0); 856 rb.Push(0);
522} 857}
858
523void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { 859void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
524 LOG_WARNING(Service_SET, "(STUBBED) called"); 860 LOG_WARNING(Service_SET, "(STUBBED) called");
525 861
@@ -528,7 +864,7 @@ void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
528 rb.Push<u8>(false); 864 rb.Push<u8>(false);
529} 865}
530 866
531SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { 867SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"}, m_system{system} {
532 // clang-format off 868 // clang-format off
533 static const FunctionInfo functions[] = { 869 static const FunctionInfo functions[] = {
534 {0, &SET_SYS::SetLanguageCode, "SetLanguageCode"}, 870 {0, &SET_SYS::SetLanguageCode, "SetLanguageCode"},
@@ -543,10 +879,10 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
543 {10, nullptr, "SetBacklightSettings"}, 879 {10, nullptr, "SetBacklightSettings"},
544 {11, nullptr, "SetBluetoothDevicesSettings"}, 880 {11, nullptr, "SetBluetoothDevicesSettings"},
545 {12, nullptr, "GetBluetoothDevicesSettings"}, 881 {12, nullptr, "GetBluetoothDevicesSettings"},
546 {13, nullptr, "GetExternalSteadyClockSourceId"}, 882 {13, &SET_SYS::GetExternalSteadyClockSourceId, "GetExternalSteadyClockSourceId"},
547 {14, nullptr, "SetExternalSteadyClockSourceId"}, 883 {14, &SET_SYS::SetExternalSteadyClockSourceId, "SetExternalSteadyClockSourceId"},
548 {15, nullptr, "GetUserSystemClockContext"}, 884 {15, &SET_SYS::GetUserSystemClockContext, "GetUserSystemClockContext"},
549 {16, nullptr, "SetUserSystemClockContext"}, 885 {16, &SET_SYS::SetUserSystemClockContext, "SetUserSystemClockContext"},
550 {17, &SET_SYS::GetAccountSettings, "GetAccountSettings"}, 886 {17, &SET_SYS::GetAccountSettings, "GetAccountSettings"},
551 {18, &SET_SYS::SetAccountSettings, "SetAccountSettings"}, 887 {18, &SET_SYS::SetAccountSettings, "SetAccountSettings"},
552 {19, nullptr, "GetAudioVolume"}, 888 {19, nullptr, "GetAudioVolume"},
@@ -581,15 +917,15 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
581 {50, nullptr, "SetDataDeletionSettings"}, 917 {50, nullptr, "SetDataDeletionSettings"},
582 {51, nullptr, "GetInitialSystemAppletProgramId"}, 918 {51, nullptr, "GetInitialSystemAppletProgramId"},
583 {52, nullptr, "GetOverlayDispProgramId"}, 919 {52, nullptr, "GetOverlayDispProgramId"},
584 {53, nullptr, "GetDeviceTimeZoneLocationName"}, 920 {53, &SET_SYS::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"},
585 {54, nullptr, "SetDeviceTimeZoneLocationName"}, 921 {54, &SET_SYS::SetDeviceTimeZoneLocationName, "SetDeviceTimeZoneLocationName"},
586 {55, nullptr, "GetWirelessCertificationFileSize"}, 922 {55, nullptr, "GetWirelessCertificationFileSize"},
587 {56, nullptr, "GetWirelessCertificationFile"}, 923 {56, nullptr, "GetWirelessCertificationFile"},
588 {57, &SET_SYS::SetRegionCode, "SetRegionCode"}, 924 {57, &SET_SYS::SetRegionCode, "SetRegionCode"},
589 {58, nullptr, "GetNetworkSystemClockContext"}, 925 {58, &SET_SYS::GetNetworkSystemClockContext, "GetNetworkSystemClockContext"},
590 {59, nullptr, "SetNetworkSystemClockContext"}, 926 {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
591 {60, nullptr, "IsUserSystemClockAutomaticCorrectionEnabled"}, 927 {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
592 {61, nullptr, "SetUserSystemClockAutomaticCorrectionEnabled"}, 928 {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
593 {62, nullptr, "GetDebugModeFlag"}, 929 {62, nullptr, "GetDebugModeFlag"},
594 {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, 930 {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
595 {64, nullptr, "SetPrimaryAlbumStorage"}, 931 {64, nullptr, "SetPrimaryAlbumStorage"},
@@ -633,8 +969,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
633 {102, nullptr, "SetExternalRtcResetFlag"}, 969 {102, nullptr, "SetExternalRtcResetFlag"},
634 {103, nullptr, "GetUsbFullKeyEnableFlag"}, 970 {103, nullptr, "GetUsbFullKeyEnableFlag"},
635 {104, nullptr, "SetUsbFullKeyEnableFlag"}, 971 {104, nullptr, "SetUsbFullKeyEnableFlag"},
636 {105, nullptr, "SetExternalSteadyClockInternalOffset"}, 972 {105, &SET_SYS::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"},
637 {106, nullptr, "GetExternalSteadyClockInternalOffset"}, 973 {106, &SET_SYS::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"},
638 {107, nullptr, "GetBacklightSettingsEx"}, 974 {107, nullptr, "GetBacklightSettingsEx"},
639 {108, nullptr, "SetBacklightSettingsEx"}, 975 {108, nullptr, "SetBacklightSettingsEx"},
640 {109, nullptr, "GetHeadphoneVolumeWarningCount"}, 976 {109, nullptr, "GetHeadphoneVolumeWarningCount"},
@@ -678,10 +1014,10 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
678 {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, 1014 {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
679 {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, 1015 {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
680 {149, nullptr, "GetRebootlessSystemUpdateVersion"}, 1016 {149, nullptr, "GetRebootlessSystemUpdateVersion"},
681 {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"}, 1017 {150, &SET_SYS::GetDeviceTimeZoneLocationUpdatedTime, "GetDeviceTimeZoneLocationUpdatedTime"},
682 {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"}, 1018 {151, &SET_SYS::SetDeviceTimeZoneLocationUpdatedTime, "SetDeviceTimeZoneLocationUpdatedTime"},
683 {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, 1019 {152, &SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
684 {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, 1020 {153, &SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime, "SetUserSystemClockAutomaticCorrectionUpdatedTime"},
685 {154, nullptr, "GetAccountOnlineStorageSettings"}, 1021 {154, nullptr, "GetAccountOnlineStorageSettings"},
686 {155, nullptr, "SetAccountOnlineStorageSettings"}, 1022 {155, nullptr, "SetAccountOnlineStorageSettings"},
687 {156, nullptr, "GetPctlReadyFlag"}, 1023 {156, nullptr, "GetPctlReadyFlag"},
@@ -743,8 +1079,184 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
743 // clang-format on 1079 // clang-format on
744 1080
745 RegisterHandlers(functions); 1081 RegisterHandlers(functions);
1082
1083 SetupSettings();
1084 m_save_thread =
1085 std::jthread([this](std::stop_token stop_token) { StoreSettingsThreadFunc(stop_token); });
1086}
1087
1088SET_SYS::~SET_SYS() {
1089 SetSaveNeeded();
1090 m_save_thread.request_stop();
1091}
1092
1093void SET_SYS::SetupSettings() {
1094 auto system_dir =
1095 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050";
1096 if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) {
1097 ASSERT(false);
1098 }
1099
1100 auto private_dir =
1101 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052";
1102 if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) {
1103 ASSERT(false);
1104 }
1105
1106 auto device_dir =
1107 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053";
1108 if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) {
1109 ASSERT(false);
1110 }
1111
1112 auto appln_dir =
1113 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054";
1114 if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) {
1115 ASSERT(false);
1116 }
1117}
1118
1119void SET_SYS::StoreSettings() {
1120 auto system_dir =
1121 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050";
1122 if (!StoreSettingsFile(system_dir, m_system_settings)) {
1123 LOG_ERROR(HW_GPU, "Failed to store System settings");
1124 }
1125
1126 auto private_dir =
1127 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052";
1128 if (!StoreSettingsFile(private_dir, m_private_settings)) {
1129 LOG_ERROR(HW_GPU, "Failed to store Private settings");
1130 }
1131
1132 auto device_dir =
1133 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053";
1134 if (!StoreSettingsFile(device_dir, m_device_settings)) {
1135 LOG_ERROR(HW_GPU, "Failed to store Device settings");
1136 }
1137
1138 auto appln_dir =
1139 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054";
1140 if (!StoreSettingsFile(appln_dir, m_appln_settings)) {
1141 LOG_ERROR(HW_GPU, "Failed to store ApplLn settings");
1142 }
1143}
1144
1145void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
1146 while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
1147 std::scoped_lock l{m_save_needed_mutex};
1148 if (!std::exchange(m_save_needed, false)) {
1149 continue;
1150 }
1151 StoreSettings();
1152 }
1153}
1154
1155void SET_SYS::SetSaveNeeded() {
1156 std::scoped_lock l{m_save_needed_mutex};
1157 m_save_needed = true;
1158}
1159
1160Result SET_SYS::GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category,
1161 const std::string& name) {
1162 auto settings{GetSettings()};
1163 R_UNLESS(settings.contains(category) && settings[category].contains(name), ResultUnknown);
1164
1165 out_value = settings[category][name];
1166 R_SUCCEED();
1167}
1168
1169Result SET_SYS::GetExternalSteadyClockSourceId(Common::UUID& out_id) {
1170 out_id = m_private_settings.external_clock_source_id;
1171 R_SUCCEED();
1172}
1173
1174Result SET_SYS::SetExternalSteadyClockSourceId(Common::UUID id) {
1175 m_private_settings.external_clock_source_id = id;
1176 SetSaveNeeded();
1177 R_SUCCEED();
1178}
1179
1180Result SET_SYS::GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context) {
1181 out_context = m_system_settings.user_system_clock_context;
1182 R_SUCCEED();
746} 1183}
747 1184
748SET_SYS::~SET_SYS() = default; 1185Result SET_SYS::SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context) {
1186 m_system_settings.user_system_clock_context = context;
1187 SetSaveNeeded();
1188 R_SUCCEED();
1189}
1190
1191Result SET_SYS::GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name) {
1192 out_name = m_system_settings.device_time_zone_location_name;
1193 R_SUCCEED();
1194}
1195
1196Result SET_SYS::SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name) {
1197 m_system_settings.device_time_zone_location_name = name;
1198 SetSaveNeeded();
1199 R_SUCCEED();
1200}
1201
1202Result SET_SYS::GetNetworkSystemClockContext(
1203 Service::Time::Clock::SystemClockContext& out_context) {
1204 out_context = m_system_settings.network_system_clock_context;
1205 R_SUCCEED();
1206}
1207
1208Result SET_SYS::SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context) {
1209 m_system_settings.network_system_clock_context = context;
1210 SetSaveNeeded();
1211 R_SUCCEED();
1212}
1213
1214Result SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) {
1215 out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled;
1216 R_SUCCEED();
1217}
1218
1219Result SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
1220 m_system_settings.user_system_clock_automatic_correction_enabled = enabled;
1221 SetSaveNeeded();
1222 R_SUCCEED();
1223}
1224
1225Result SET_SYS::SetExternalSteadyClockInternalOffset(s64 offset) {
1226 m_private_settings.external_steady_clock_internal_offset = offset;
1227 SetSaveNeeded();
1228 R_SUCCEED();
1229}
1230
1231Result SET_SYS::GetExternalSteadyClockInternalOffset(s64& out_offset) {
1232 out_offset = m_private_settings.external_steady_clock_internal_offset;
1233 R_SUCCEED();
1234}
1235
1236Result SET_SYS::GetDeviceTimeZoneLocationUpdatedTime(
1237 Service::Time::Clock::SteadyClockTimePoint& out_time_point) {
1238 out_time_point = m_system_settings.device_time_zone_location_updated_time;
1239 R_SUCCEED();
1240}
1241
1242Result SET_SYS::SetDeviceTimeZoneLocationUpdatedTime(
1243 Service::Time::Clock::SteadyClockTimePoint& time_point) {
1244 m_system_settings.device_time_zone_location_updated_time = time_point;
1245 SetSaveNeeded();
1246 R_SUCCEED();
1247}
1248
1249Result SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1250 Service::Time::Clock::SteadyClockTimePoint& out_time_point) {
1251 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
1252 R_SUCCEED();
1253}
1254
1255Result SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1256 Service::Time::Clock::SteadyClockTimePoint out_time_point) {
1257 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
1258 SetSaveNeeded();
1259 R_SUCCEED();
1260}
749 1261
750} // namespace Service::Set 1262} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index 5f770fd32..3785d93d8 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -3,17 +3,27 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <filesystem>
7#include <mutex>
8#include <string>
9#include <thread>
10
11#include "common/polyfill_thread.h"
6#include "common/uuid.h" 12#include "common/uuid.h"
7#include "core/hle/result.h" 13#include "core/hle/result.h"
8#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
15#include "core/hle/service/set/appln_settings.h"
16#include "core/hle/service/set/device_settings.h"
17#include "core/hle/service/set/private_settings.h"
18#include "core/hle/service/set/system_settings.h"
9#include "core/hle/service/time/clock_types.h" 19#include "core/hle/service/time/clock_types.h"
20#include "core/hle/service/time/time_zone_types.h"
10 21
11namespace Core { 22namespace Core {
12class System; 23class System;
13} 24}
14 25
15namespace Service::Set { 26namespace Service::Set {
16enum class LanguageCode : u64;
17enum class GetFirmwareVersionType { 27enum class GetFirmwareVersionType {
18 Version1, 28 Version1,
19 Version2, 29 Version2,
@@ -42,270 +52,38 @@ public:
42 explicit SET_SYS(Core::System& system_); 52 explicit SET_SYS(Core::System& system_);
43 ~SET_SYS() override; 53 ~SET_SYS() override;
44 54
45private: 55 Result GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category,
46 /// Indicates the current theme set by the system settings 56 const std::string& name);
47 enum class ColorSet : u32 { 57
48 BasicWhite = 0, 58 Result GetExternalSteadyClockSourceId(Common::UUID& out_id);
49 BasicBlack = 1, 59 Result SetExternalSteadyClockSourceId(Common::UUID id);
50 }; 60 Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context);
51 61 Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context);
52 /// Indicates the current console is a retail or kiosk unit 62 Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name);
53 enum class QuestFlag : u8 { 63 Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name);
54 Retail = 0, 64 Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context);
55 Kiosk = 1, 65 Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context);
56 }; 66 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled);
57 67 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled);
58 /// This is nn::settings::system::TvResolution 68 Result SetExternalSteadyClockInternalOffset(s64 offset);
59 enum class TvResolution : u32 { 69 Result GetExternalSteadyClockInternalOffset(s64& out_offset);
60 Auto, 70 Result GetDeviceTimeZoneLocationUpdatedTime(
61 Resolution1080p, 71 Service::Time::Clock::SteadyClockTimePoint& out_time_point);
62 Resolution720p, 72 Result SetDeviceTimeZoneLocationUpdatedTime(
63 Resolution480p, 73 Service::Time::Clock::SteadyClockTimePoint& time_point);
64 }; 74 Result GetUserSystemClockAutomaticCorrectionUpdatedTime(
65 75 Service::Time::Clock::SteadyClockTimePoint& out_time_point);
66 /// This is nn::settings::system::HdmiContentType 76 Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
67 enum class HdmiContentType : u32 { 77 Service::Time::Clock::SteadyClockTimePoint time_point);
68 None,
69 Graphics,
70 Cinema,
71 Photo,
72 Game,
73 };
74
75 /// This is nn::settings::system::RgbRange
76 enum class RgbRange : u32 {
77 Auto,
78 Full,
79 Limited,
80 };
81
82 /// This is nn::settings::system::CmuMode
83 enum class CmuMode : u32 {
84 None,
85 ColorInvert,
86 HighContrast,
87 GrayScale,
88 };
89
90 /// This is nn::settings::system::PrimaryAlbumStorage
91 enum class PrimaryAlbumStorage : u32 {
92 Nand,
93 SdCard,
94 };
95
96 /// This is nn::settings::system::NotificationVolume
97 enum class NotificationVolume : u32 {
98 Mute,
99 Low,
100 High,
101 };
102
103 /// This is nn::settings::system::ChineseTraditionalInputMethod
104 enum class ChineseTraditionalInputMethod : u32 {
105 Unknown0 = 0,
106 Unknown1 = 1,
107 Unknown2 = 2,
108 };
109
110 /// This is nn::settings::system::ErrorReportSharePermission
111 enum class ErrorReportSharePermission : u32 {
112 NotConfirmed,
113 Granted,
114 Denied,
115 };
116
117 /// This is nn::settings::system::FriendPresenceOverlayPermission
118 enum class FriendPresenceOverlayPermission : u8 {
119 NotConfirmed,
120 NoDisplay,
121 FavoriteFriends,
122 Friends,
123 };
124
125 /// This is nn::settings::system::HandheldSleepPlan
126 enum class HandheldSleepPlan : u32 {
127 Sleep1Min,
128 Sleep3Min,
129 Sleep5Min,
130 Sleep10Min,
131 Sleep30Min,
132 Never,
133 };
134
135 /// This is nn::settings::system::ConsoleSleepPlan
136 enum class ConsoleSleepPlan : u32 {
137 Sleep1Hour,
138 Sleep2Hour,
139 Sleep3Hour,
140 Sleep6Hour,
141 Sleep12Hour,
142 Never,
143 };
144
145 /// This is nn::settings::system::RegionCode
146 enum class RegionCode : u32 {
147 Japan,
148 Usa,
149 Europe,
150 Australia,
151 HongKongTaiwanKorea,
152 China,
153 };
154
155 /// This is nn::settings::system::EulaVersionClockType
156 enum class EulaVersionClockType : u32 {
157 NetworkSystemClock,
158 SteadyClock,
159 };
160
161 /// This is nn::settings::system::SleepFlag
162 struct SleepFlag {
163 union {
164 u32 raw{};
165
166 BitField<0, 1, u32> SleepsWhilePlayingMedia;
167 BitField<1, 1, u32> WakesAtPowerStateChange;
168 };
169 };
170 static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size");
171
172 /// This is nn::settings::system::TvFlag
173 struct TvFlag {
174 union {
175 u32 raw{};
176
177 BitField<0, 1, u32> Allows4k;
178 BitField<1, 1, u32> Allows3d;
179 BitField<2, 1, u32> AllowsCec;
180 BitField<3, 1, u32> PreventsScreenBurnIn;
181 };
182 };
183 static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size");
184
185 /// This is nn::settings::system::InitialLaunchFlag
186 struct InitialLaunchFlag {
187 union {
188 u32 raw{};
189
190 BitField<0, 1, u32> InitialLaunchCompletionFlag;
191 BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
192 BitField<16, 1, u32> InitialLaunchTimestampFlag;
193 };
194 };
195 static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
196
197 /// This is nn::settings::system::NotificationFlag
198 struct NotificationFlag {
199 union {
200 u32 raw{};
201
202 BitField<0, 1, u32> RingtoneFlag;
203 BitField<1, 1, u32> DownloadCompletionFlag;
204 BitField<8, 1, u32> EnablesNews;
205 BitField<9, 1, u32> IncomingLampFlag;
206 };
207 };
208 static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size");
209
210 /// This is nn::settings::system::AccountNotificationFlag
211 struct AccountNotificationFlag {
212 union {
213 u32 raw{};
214
215 BitField<0, 1, u32> FriendOnlineFlag;
216 BitField<1, 1, u32> FriendRequestFlag;
217 BitField<8, 1, u32> CoralInvitationFlag;
218 };
219 };
220 static_assert(sizeof(AccountNotificationFlag) == 4,
221 "AccountNotificationFlag is an invalid size");
222
223 /// This is nn::settings::system::TvSettings
224 struct TvSettings {
225 TvFlag flags;
226 TvResolution tv_resolution;
227 HdmiContentType hdmi_content_type;
228 RgbRange rgb_range;
229 CmuMode cmu_mode;
230 u32 tv_underscan;
231 f32 tv_gama;
232 f32 constrast_ratio;
233 };
234 static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
235
236 /// This is nn::settings::system::NotificationTime
237 struct NotificationTime {
238 u32 hour;
239 u32 minute;
240 };
241 static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size");
242
243 /// This is nn::settings::system::NotificationSettings
244 struct NotificationSettings {
245 NotificationFlag flags;
246 NotificationVolume volume;
247 NotificationTime start_time;
248 NotificationTime stop_time;
249 };
250 static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size");
251
252 /// This is nn::settings::system::AccountSettings
253 struct AccountSettings {
254 u32 flags;
255 };
256 static_assert(sizeof(AccountSettings) == 0x4, "AccountSettings is an invalid size");
257
258 /// This is nn::settings::system::AccountNotificationSettings
259 struct AccountNotificationSettings {
260 Common::UUID uid;
261 AccountNotificationFlag flags;
262 FriendPresenceOverlayPermission friend_presence_permission;
263 FriendPresenceOverlayPermission friend_invitation_permission;
264 INSERT_PADDING_BYTES(0x2);
265 };
266 static_assert(sizeof(AccountNotificationSettings) == 0x18,
267 "AccountNotificationSettings is an invalid size");
268
269 /// This is nn::settings::system::InitialLaunchSettings
270 struct SleepSettings {
271 SleepFlag flags;
272 HandheldSleepPlan handheld_sleep_plan;
273 ConsoleSleepPlan console_sleep_plan;
274 };
275 static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size");
276
277 /// This is nn::settings::system::InitialLaunchSettings
278 struct InitialLaunchSettings {
279 InitialLaunchFlag flags;
280 INSERT_PADDING_BYTES(0x4);
281 Time::Clock::SteadyClockTimePoint timestamp;
282 };
283 static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
284
285 /// This is nn::settings::system::InitialLaunchSettings
286 struct EulaVersion {
287 u32 version;
288 RegionCode region_code;
289 EulaVersionClockType clock_type;
290 INSERT_PADDING_BYTES(0x4);
291 s64 posix_time;
292 Time::Clock::SteadyClockTimePoint timestamp;
293 };
294 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
295
296 /// This is nn::settings::system::HomeMenuScheme
297 struct HomeMenuScheme {
298 u32 main;
299 u32 back;
300 u32 sub;
301 u32 bezel;
302 u32 extra;
303 };
304 static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
305 78
79private:
306 void SetLanguageCode(HLERequestContext& ctx); 80 void SetLanguageCode(HLERequestContext& ctx);
307 void GetFirmwareVersion(HLERequestContext& ctx); 81 void GetFirmwareVersion(HLERequestContext& ctx);
308 void GetFirmwareVersion2(HLERequestContext& ctx); 82 void GetFirmwareVersion2(HLERequestContext& ctx);
83 void GetExternalSteadyClockSourceId(HLERequestContext& ctx);
84 void SetExternalSteadyClockSourceId(HLERequestContext& ctx);
85 void GetUserSystemClockContext(HLERequestContext& ctx);
86 void SetUserSystemClockContext(HLERequestContext& ctx);
309 void GetAccountSettings(HLERequestContext& ctx); 87 void GetAccountSettings(HLERequestContext& ctx);
310 void SetAccountSettings(HLERequestContext& ctx); 88 void SetAccountSettings(HLERequestContext& ctx);
311 void GetEulaVersions(HLERequestContext& ctx); 89 void GetEulaVersions(HLERequestContext& ctx);
@@ -321,7 +99,13 @@ private:
321 void GetTvSettings(HLERequestContext& ctx); 99 void GetTvSettings(HLERequestContext& ctx);
322 void SetTvSettings(HLERequestContext& ctx); 100 void SetTvSettings(HLERequestContext& ctx);
323 void GetQuestFlag(HLERequestContext& ctx); 101 void GetQuestFlag(HLERequestContext& ctx);
102 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
103 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
324 void SetRegionCode(HLERequestContext& ctx); 104 void SetRegionCode(HLERequestContext& ctx);
105 void GetNetworkSystemClockContext(HLERequestContext& ctx);
106 void SetNetworkSystemClockContext(HLERequestContext& ctx);
107 void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
108 void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
325 void GetPrimaryAlbumStorage(HLERequestContext& ctx); 109 void GetPrimaryAlbumStorage(HLERequestContext& ctx);
326 void GetSleepSettings(HLERequestContext& ctx); 110 void GetSleepSettings(HLERequestContext& ctx);
327 void SetSleepSettings(HLERequestContext& ctx); 111 void SetSleepSettings(HLERequestContext& ctx);
@@ -333,59 +117,36 @@ private:
333 void GetMiiAuthorId(HLERequestContext& ctx); 117 void GetMiiAuthorId(HLERequestContext& ctx);
334 void GetAutoUpdateEnableFlag(HLERequestContext& ctx); 118 void GetAutoUpdateEnableFlag(HLERequestContext& ctx);
335 void GetBatteryPercentageFlag(HLERequestContext& ctx); 119 void GetBatteryPercentageFlag(HLERequestContext& ctx);
120 void SetExternalSteadyClockInternalOffset(HLERequestContext& ctx);
121 void GetExternalSteadyClockInternalOffset(HLERequestContext& ctx);
336 void GetErrorReportSharePermission(HLERequestContext& ctx); 122 void GetErrorReportSharePermission(HLERequestContext& ctx);
337 void GetAppletLaunchFlags(HLERequestContext& ctx); 123 void GetAppletLaunchFlags(HLERequestContext& ctx);
338 void SetAppletLaunchFlags(HLERequestContext& ctx); 124 void SetAppletLaunchFlags(HLERequestContext& ctx);
339 void GetKeyboardLayout(HLERequestContext& ctx); 125 void GetKeyboardLayout(HLERequestContext& ctx);
126 void GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx);
127 void SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx);
128 void GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
129 void SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
340 void GetChineseTraditionalInputMethod(HLERequestContext& ctx); 130 void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
341 void GetFieldTestingFlag(HLERequestContext& ctx);
342 void GetHomeMenuScheme(HLERequestContext& ctx); 131 void GetHomeMenuScheme(HLERequestContext& ctx);
343 void GetHomeMenuSchemeModel(HLERequestContext& ctx); 132 void GetHomeMenuSchemeModel(HLERequestContext& ctx);
133 void GetFieldTestingFlag(HLERequestContext& ctx);
344 134
345 AccountSettings account_settings{ 135 bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func);
346 .flags = {}, 136 bool StoreSettingsFile(std::filesystem::path& path, auto& settings);
347 }; 137 void SetupSettings();
348 138 void StoreSettings();
349 ColorSet color_set = ColorSet::BasicWhite; 139 void StoreSettingsThreadFunc(std::stop_token stop_token);
350 140 void SetSaveNeeded();
351 NotificationSettings notification_settings{ 141
352 .flags = {0x300}, 142 Core::System& m_system;
353 .volume = NotificationVolume::High, 143 SystemSettings m_system_settings{};
354 .start_time = {.hour = 9, .minute = 0}, 144 PrivateSettings m_private_settings{};
355 .stop_time = {.hour = 21, .minute = 0}, 145 DeviceSettings m_device_settings{};
356 }; 146 ApplnSettings m_appln_settings{};
357 147 std::jthread m_save_thread;
358 std::vector<AccountNotificationSettings> account_notifications{}; 148 std::mutex m_save_needed_mutex;
359 149 bool m_save_needed{false};
360 TvSettings tv_settings{
361 .flags = {0xc},
362 .tv_resolution = TvResolution::Auto,
363 .hdmi_content_type = HdmiContentType::Game,
364 .rgb_range = RgbRange::Auto,
365 .cmu_mode = CmuMode::None,
366 .tv_underscan = {},
367 .tv_gama = 1.0f,
368 .constrast_ratio = 0.5f,
369 };
370
371 InitialLaunchSettings launch_settings{
372 .flags = {0x10001},
373 .timestamp = {},
374 };
375
376 SleepSettings sleep_settings{
377 .flags = {0x3},
378 .handheld_sleep_plan = HandheldSleepPlan::Sleep10Min,
379 .console_sleep_plan = ConsoleSleepPlan::Sleep1Hour,
380 };
381
382 u32 applet_launch_flag{};
383
384 std::vector<EulaVersion> eula_versions{};
385
386 RegionCode region_code;
387
388 LanguageCode language_code_setting;
389}; 150};
390 151
391} // namespace Service::Set 152} // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/system_settings.cpp
new file mode 100644
index 000000000..2723417ad
--- /dev/null
+++ b/src/core/hle/service/set/system_settings.cpp
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/set/system_settings.h"
5
6namespace Service::Set {
7
8SystemSettings DefaultSystemSettings() {
9 SystemSettings settings{};
10
11 settings.version = 0x140000;
12 settings.flags = 7;
13
14 settings.color_set_id = ColorSet::BasicWhite;
15
16 settings.notification_settings = {
17 .flags{0x300},
18 .volume = NotificationVolume::High,
19 .start_time = {.hour = 9, .minute = 0},
20 .stop_time = {.hour = 21, .minute = 0},
21 };
22
23 settings.tv_settings = {
24 .flags = {0xC},
25 .tv_resolution = TvResolution::Auto,
26 .hdmi_content_type = HdmiContentType::Game,
27 .rgb_range = RgbRange::Auto,
28 .cmu_mode = CmuMode::None,
29 .tv_underscan = {},
30 .tv_gama = 1.0f,
31 .constrast_ratio = 0.5f,
32 };
33
34 settings.initial_launch_settings_packed = {
35 .flags = {0x10001},
36 .timestamp = {},
37 };
38
39 settings.sleep_settings = {
40 .flags = {0x3},
41 .handheld_sleep_plan = HandheldSleepPlan::Sleep10Min,
42 .console_sleep_plan = ConsoleSleepPlan::Sleep1Hour,
43 };
44
45 settings.device_time_zone_location_name = {"UTC"};
46 settings.user_system_clock_automatic_correction_enabled = false;
47
48 return settings;
49}
50
51} // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/system_settings.h
new file mode 100644
index 000000000..ded2906ad
--- /dev/null
+++ b/src/core/hle/service/set/system_settings.h
@@ -0,0 +1,699 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/set/private_settings.h"
12#include "core/hle/service/time/clock_types.h"
13
14namespace Service::Set {
15
16/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
17enum class LanguageCode : u64 {
18 JA = 0x000000000000616A,
19 EN_US = 0x00000053552D6E65,
20 FR = 0x0000000000007266,
21 DE = 0x0000000000006564,
22 IT = 0x0000000000007469,
23 ES = 0x0000000000007365,
24 ZH_CN = 0x0000004E432D687A,
25 KO = 0x0000000000006F6B,
26 NL = 0x0000000000006C6E,
27 PT = 0x0000000000007470,
28 RU = 0x0000000000007572,
29 ZH_TW = 0x00000057542D687A,
30 EN_GB = 0x00000042472D6E65,
31 FR_CA = 0x00000041432D7266,
32 ES_419 = 0x00003931342D7365,
33 ZH_HANS = 0x00736E61482D687A,
34 ZH_HANT = 0x00746E61482D687A,
35 PT_BR = 0x00000052422D7470,
36};
37
38/// This is nn::settings::system::ErrorReportSharePermission
39enum class ErrorReportSharePermission : u32 {
40 NotConfirmed,
41 Granted,
42 Denied,
43};
44
45/// This is nn::settings::system::ChineseTraditionalInputMethod
46enum class ChineseTraditionalInputMethod : u32 {
47 Unknown0 = 0,
48 Unknown1 = 1,
49 Unknown2 = 2,
50};
51
52/// This is nn::settings::system::HomeMenuScheme
53struct HomeMenuScheme {
54 u32 main;
55 u32 back;
56 u32 sub;
57 u32 bezel;
58 u32 extra;
59};
60static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
61
62/// Indicates the current theme set by the system settings
63enum class ColorSet : u32 {
64 BasicWhite = 0,
65 BasicBlack = 1,
66};
67
68/// Indicates the current console is a retail or kiosk unit
69enum class QuestFlag : u8 {
70 Retail = 0,
71 Kiosk = 1,
72};
73
74/// This is nn::settings::system::RegionCode
75enum class RegionCode : u32 {
76 Japan,
77 Usa,
78 Europe,
79 Australia,
80 HongKongTaiwanKorea,
81 China,
82};
83
84/// This is nn::settings::system::AccountSettings
85struct AccountSettings {
86 u32 flags;
87};
88static_assert(sizeof(AccountSettings) == 4, "AccountSettings is an invalid size");
89
90/// This is nn::settings::system::NotificationVolume
91enum class NotificationVolume : u32 {
92 Mute,
93 Low,
94 High,
95};
96
97/// This is nn::settings::system::NotificationFlag
98struct NotificationFlag {
99 union {
100 u32 raw{};
101
102 BitField<0, 1, u32> RingtoneFlag;
103 BitField<1, 1, u32> DownloadCompletionFlag;
104 BitField<8, 1, u32> EnablesNews;
105 BitField<9, 1, u32> IncomingLampFlag;
106 };
107};
108static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size");
109
110/// This is nn::settings::system::NotificationTime
111struct NotificationTime {
112 u32 hour;
113 u32 minute;
114};
115static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size");
116
117/// This is nn::settings::system::NotificationSettings
118struct NotificationSettings {
119 NotificationFlag flags;
120 NotificationVolume volume;
121 NotificationTime start_time;
122 NotificationTime stop_time;
123};
124static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size");
125
126/// This is nn::settings::system::AccountNotificationFlag
127struct AccountNotificationFlag {
128 union {
129 u32 raw{};
130
131 BitField<0, 1, u32> FriendOnlineFlag;
132 BitField<1, 1, u32> FriendRequestFlag;
133 BitField<8, 1, u32> CoralInvitationFlag;
134 };
135};
136static_assert(sizeof(AccountNotificationFlag) == 4, "AccountNotificationFlag is an invalid size");
137
138/// This is nn::settings::system::FriendPresenceOverlayPermission
139enum class FriendPresenceOverlayPermission : u8 {
140 NotConfirmed,
141 NoDisplay,
142 FavoriteFriends,
143 Friends,
144};
145
146/// This is nn::settings::system::AccountNotificationSettings
147struct AccountNotificationSettings {
148 Common::UUID uid;
149 AccountNotificationFlag flags;
150 FriendPresenceOverlayPermission friend_presence_permission;
151 FriendPresenceOverlayPermission friend_invitation_permission;
152 INSERT_PADDING_BYTES(0x2);
153};
154static_assert(sizeof(AccountNotificationSettings) == 0x18,
155 "AccountNotificationSettings is an invalid size");
156
157/// This is nn::settings::system::TvFlag
158struct TvFlag {
159 union {
160 u32 raw{};
161
162 BitField<0, 1, u32> Allows4k;
163 BitField<1, 1, u32> Allows3d;
164 BitField<2, 1, u32> AllowsCec;
165 BitField<3, 1, u32> PreventsScreenBurnIn;
166 };
167};
168static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size");
169
170/// This is nn::settings::system::TvResolution
171enum class TvResolution : u32 {
172 Auto,
173 Resolution1080p,
174 Resolution720p,
175 Resolution480p,
176};
177
178/// This is nn::settings::system::HdmiContentType
179enum class HdmiContentType : u32 {
180 None,
181 Graphics,
182 Cinema,
183 Photo,
184 Game,
185};
186
187/// This is nn::settings::system::RgbRange
188enum class RgbRange : u32 {
189 Auto,
190 Full,
191 Limited,
192};
193
194/// This is nn::settings::system::CmuMode
195enum class CmuMode : u32 {
196 None,
197 ColorInvert,
198 HighContrast,
199 GrayScale,
200};
201
202/// This is nn::settings::system::TvSettings
203struct TvSettings {
204 TvFlag flags;
205 TvResolution tv_resolution;
206 HdmiContentType hdmi_content_type;
207 RgbRange rgb_range;
208 CmuMode cmu_mode;
209 u32 tv_underscan;
210 f32 tv_gama;
211 f32 constrast_ratio;
212};
213static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
214
215/// This is nn::settings::system::PrimaryAlbumStorage
216enum class PrimaryAlbumStorage : u32 {
217 Nand,
218 SdCard,
219};
220
221/// This is nn::settings::system::HandheldSleepPlan
222enum class HandheldSleepPlan : u32 {
223 Sleep1Min,
224 Sleep3Min,
225 Sleep5Min,
226 Sleep10Min,
227 Sleep30Min,
228 Never,
229};
230
231/// This is nn::settings::system::ConsoleSleepPlan
232enum class ConsoleSleepPlan : u32 {
233 Sleep1Hour,
234 Sleep2Hour,
235 Sleep3Hour,
236 Sleep6Hour,
237 Sleep12Hour,
238 Never,
239};
240
241/// This is nn::settings::system::SleepFlag
242struct SleepFlag {
243 union {
244 u32 raw{};
245
246 BitField<0, 1, u32> SleepsWhilePlayingMedia;
247 BitField<1, 1, u32> WakesAtPowerStateChange;
248 };
249};
250static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size");
251
252/// This is nn::settings::system::SleepSettings
253struct SleepSettings {
254 SleepFlag flags;
255 HandheldSleepPlan handheld_sleep_plan;
256 ConsoleSleepPlan console_sleep_plan;
257};
258static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size");
259
260/// This is nn::settings::system::EulaVersionClockType
261enum class EulaVersionClockType : u32 {
262 NetworkSystemClock,
263 SteadyClock,
264};
265
266/// This is nn::settings::system::EulaVersion
267struct EulaVersion {
268 u32 version;
269 RegionCode region_code;
270 EulaVersionClockType clock_type;
271 INSERT_PADDING_BYTES(0x4);
272 s64 posix_time;
273 Time::Clock::SteadyClockTimePoint timestamp;
274};
275static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
276
277struct SystemSettings {
278 // 0/unwritten (1.0.0), 0x20000 (2.0.0), 0x30000 (3.0.0-3.0.1), 0x40001 (4.0.0-4.1.0), 0x50000
279 // (5.0.0-5.1.0), 0x60000 (6.0.0-6.2.0), 0x70000 (7.0.0), 0x80000 (8.0.0-8.1.1), 0x90000
280 // (9.0.0-10.0.4), 0x100100 (10.1.0+), 0x120000 (12.0.0-12.1.0), 0x130000 (13.0.0-13.2.1),
281 // 0x140000 (14.0.0+)
282 u32 version;
283 // 0/unwritten (1.0.0), 1 (6.0.0-8.1.0), 2 (8.1.1), 7 (9.0.0+).
284 // if (flags & 2), defaults are written for AnalogStickUserCalibration
285 u32 flags;
286
287 std::array<u8, 0x8> reserved_00008;
288
289 // nn::settings::LanguageCode
290 LanguageCode language_code;
291
292 std::array<u8, 0x38> reserved_00018;
293
294 // nn::settings::system::NetworkSettings
295 u32 network_setting_count;
296 bool wireless_lan_enable_flag;
297 std::array<u8, 0x3> pad_00055;
298
299 std::array<u8, 0x8> reserved_00058;
300
301 // nn::settings::system::NetworkSettings
302 std::array<std::array<u8, 0x400>, 32> network_settings_1B0;
303
304 // nn::settings::system::BluetoothDevicesSettings
305 std::array<u8, 0x4> bluetooth_device_settings_count;
306 bool bluetooth_enable_flag;
307 std::array<u8, 0x3> pad_08065;
308 bool bluetooth_afh_enable_flag;
309 std::array<u8, 0x3> pad_08069;
310 bool bluetooth_boost_enable_flag;
311 std::array<u8, 0x3> pad_0806D;
312 std::array<std::array<u8, 0x200>, 10> bluetooth_device_settings_first_10;
313
314 s32 ldn_channel;
315
316 std::array<u8, 0x3C> reserved_09474;
317
318 // nn::util::Uuid MiiAuthorId
319 std::array<u8, 0x10> mii_author_id;
320
321 std::array<u8, 0x30> reserved_094C0;
322
323 // nn::settings::system::NxControllerSettings
324 u32 nx_controller_settings_count;
325
326 std::array<u8, 0xC> reserved_094F4;
327
328 // nn::settings::system::NxControllerSettings,
329 // nn::settings::system::NxControllerLegacySettings on 13.0.0+
330 std::array<std::array<u8, 0x40>, 10> nx_controller_legacy_settings;
331
332 std::array<u8, 0x170> reserved_09780;
333
334 bool external_rtc_reset_flag;
335 std::array<u8, 0x3> pad_098F1;
336
337 std::array<u8, 0x3C> reserved_098F4;
338
339 s32 push_notification_activity_mode_on_sleep;
340
341 std::array<u8, 0x3C> reserved_09934;
342
343 // nn::settings::system::ErrorReportSharePermission
344 ErrorReportSharePermission error_report_share_permssion;
345
346 std::array<u8, 0x3C> reserved_09974;
347
348 // nn::settings::KeyboardLayout
349 std::array<u8, 0x4> keyboard_layout;
350
351 std::array<u8, 0x3C> reserved_099B4;
352
353 bool web_inspector_flag;
354 std::array<u8, 0x3> pad_099F1;
355
356 // nn::settings::system::AllowedSslHost
357 u32 allowed_ssl_host_count;
358
359 bool memory_usage_rate_flag;
360 std::array<u8, 0x3> pad_099F9;
361
362 std::array<u8, 0x34> reserved_099FC;
363
364 // nn::settings::system::HostFsMountPoint
365 std::array<u8, 0x100> host_fs_mount_point;
366
367 // nn::settings::system::AllowedSslHost
368 std::array<std::array<u8, 0x100>, 8> allowed_ssl_hosts;
369
370 std::array<u8, 0x6C0> reserved_0A330;
371
372 // nn::settings::system::BlePairingSettings
373 u32 ble_pairing_settings_count;
374 std::array<u8, 0xC> reserved_0A9F4;
375 std::array<std::array<u8, 0x80>, 10> ble_pairing_settings;
376
377 // nn::settings::system::AccountOnlineStorageSettings
378 u32 account_online_storage_settings_count;
379 std::array<u8, 0xC> reserved_0AF04;
380 std::array<std::array<u8, 0x40>, 8> account_online_storage_settings;
381
382 bool pctl_ready_flag;
383 std::array<u8, 0x3> pad_0B111;
384
385 std::array<u8, 0x3C> reserved_0B114;
386
387 // nn::settings::system::ThemeId
388 std::array<u8, 0x80> theme_id_type0;
389 std::array<u8, 0x80> theme_id_type1;
390
391 std::array<u8, 0x100> reserved_0B250;
392
393 // nn::settings::ChineseTraditionalInputMethod
394 ChineseTraditionalInputMethod chinese_traditional_input_method;
395
396 std::array<u8, 0x3C> reserved_0B354;
397
398 bool zoom_flag;
399 std::array<u8, 0x3> pad_0B391;
400
401 std::array<u8, 0x3C> reserved_0B394;
402
403 // nn::settings::system::ButtonConfigRegisteredSettings
404 u32 button_config_registered_settings_count;
405 std::array<u8, 0xC> reserved_0B3D4;
406
407 // nn::settings::system::ButtonConfigSettings
408 u32 button_config_settings_count;
409 std::array<u8, 0x4> reserved_0B3E4;
410 std::array<std::array<u8, 0x5A8>, 5> button_config_settings;
411 std::array<u8, 0x13B0> reserved_0D030;
412 u32 button_config_settings_embedded_count;
413 std::array<u8, 0x4> reserved_0E3E4;
414 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_embedded;
415 std::array<u8, 0x13B0> reserved_10030;
416 u32 button_config_settings_left_count;
417 std::array<u8, 0x4> reserved_113E4;
418 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_left;
419 std::array<u8, 0x13B0> reserved_13030;
420 u32 button_config_settings_right_count;
421 std::array<u8, 0x4> reserved_143E4;
422 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_right;
423 std::array<u8, 0x73B0> reserved_16030;
424 // nn::settings::system::ButtonConfigRegisteredSettings
425 std::array<u8, 0x5C8> button_config_registered_settings_embedded;
426 std::array<std::array<u8, 0x5C8>, 10> button_config_registered_settings;
427
428 std::array<u8, 0x7FF8> reserved_21378;
429
430 // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
431 std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias;
432 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
433 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias;
434 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
435 std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
436 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
437 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
438 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
439 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias;
440 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
441 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
442
443 std::array<u8, 0x70> reserved_29400;
444
445 bool lock_screen_flag;
446 std::array<u8, 0x3> pad_29471;
447
448 std::array<u8, 0x4> reserved_249274;
449
450 ColorSet color_set_id;
451
452 QuestFlag quest_flag;
453
454 // nn::settings::system::RegionCode
455 RegionCode region_code;
456
457 // Different to nn::settings::system::InitialLaunchSettings?
458 InitialLaunchSettingsPacked initial_launch_settings_packed;
459
460 bool battery_percentage_flag;
461 std::array<u8, 0x3> pad_294A1;
462
463 // BitFlagSet<32, nn::settings::system::AppletLaunchFlag>
464 u32 applet_launch_flag;
465
466 // nn::settings::system::ThemeSettings
467 std::array<u8, 0x8> theme_settings;
468 // nn::fssystem::ArchiveMacKey
469 std::array<u8, 0x10> theme_key;
470
471 bool field_testing_flag;
472 std::array<u8, 0x3> pad_294C1;
473
474 s32 panel_crc_mode;
475
476 std::array<u8, 0x28> reserved_294C8;
477
478 // nn::settings::system::BacklightSettings
479 std::array<u8, 0x2C> backlight_settings_mixed_up;
480
481 std::array<u8, 0x64> reserved_2951C;
482
483 // nn::time::SystemClockContext
484 Service::Time::Clock::SystemClockContext user_system_clock_context;
485 Service::Time::Clock::SystemClockContext network_system_clock_context;
486 bool user_system_clock_automatic_correction_enabled;
487 std::array<u8, 0x3> pad_295C1;
488 std::array<u8, 0x4> reserved_295C4;
489 // nn::time::SteadyClockTimePoint
490 Service::Time::Clock::SteadyClockTimePoint
491 user_system_clock_automatic_correction_updated_time_point;
492
493 std::array<u8, 0x10> reserved_295E0;
494
495 // nn::settings::system::AccountSettings
496 AccountSettings account_settings;
497
498 std::array<u8, 0xFC> reserved_295F4;
499
500 // nn::settings::system::AudioVolume
501 std::array<u8, 0x8> audio_volume_type0;
502 std::array<u8, 0x8> audio_volume_type1;
503 // nn::settings::system::AudioOutputMode
504 s32 audio_output_mode_type0;
505 s32 audio_output_mode_type1;
506 s32 audio_output_mode_type2;
507 bool force_mute_on_headphone_removed;
508 std::array<u8, 0x3> pad_2970D;
509 s32 headphone_volume_warning_count;
510 bool heaphone_volume_update_flag;
511 std::array<u8, 0x3> pad_29715;
512 // nn::settings::system::AudioVolume
513 std::array<u8, 0x8> audio_volume_type2;
514 // nn::settings::system::AudioOutputMode
515 s32 audio_output_mode_type3;
516 s32 audio_output_mode_type4;
517 bool hearing_protection_safeguard_flag;
518 std::array<u8, 0x3> pad_29729;
519 std::array<u8, 0x4> reserved_2972C;
520 s64 hearing_protection_safeguard_remaining_time;
521 std::array<u8, 0x38> reserved_29738;
522
523 bool console_information_upload_flag;
524 std::array<u8, 0x3> pad_29771;
525
526 std::array<u8, 0x3C> reserved_29774;
527
528 bool automatic_application_download_flag;
529 std::array<u8, 0x3> pad_297B1;
530
531 std::array<u8, 0x4> reserved_297B4;
532
533 // nn::settings::system::NotificationSettings
534 NotificationSettings notification_settings;
535
536 std::array<u8, 0x60> reserved_297D0;
537
538 // nn::settings::system::AccountNotificationSettings
539 u32 account_notification_settings_count;
540 std::array<u8, 0xC> reserved_29834;
541 std::array<AccountNotificationSettings, 8> account_notification_settings;
542
543 std::array<u8, 0x140> reserved_29900;
544
545 f32 vibration_master_volume;
546
547 bool usb_full_key_enable_flag;
548 std::array<u8, 0x3> pad_29A45;
549
550 // nn::settings::system::AnalogStickUserCalibration
551 std::array<u8, 0x10> analog_stick_user_calibration_left;
552 std::array<u8, 0x10> analog_stick_user_calibration_right;
553
554 // nn::settings::system::TouchScreenMode
555 s32 touch_screen_mode;
556
557 std::array<u8, 0x14> reserved_29A6C;
558
559 // nn::settings::system::TvSettings
560 TvSettings tv_settings;
561
562 // nn::settings::system::Edid
563 std::array<u8, 0x100> edid;
564
565 std::array<u8, 0x2E0> reserved_29BA0;
566
567 // nn::settings::system::DataDeletionSettings
568 std::array<u8, 0x8> data_deletion_settings;
569
570 std::array<u8, 0x38> reserved_29E88;
571
572 // nn::ncm::ProgramId
573 std::array<u8, 0x8> initial_system_applet_program_id;
574 std::array<u8, 0x8> overlay_disp_program_id;
575
576 std::array<u8, 0x4> reserved_29ED0;
577
578 bool requires_run_repair_time_reviser;
579
580 std::array<u8, 0x6B> reserved_29ED5;
581
582 // nn::time::LocationName
583 Service::Time::TimeZone::LocationName device_time_zone_location_name;
584 std::array<u8, 0x4> reserved_29F64;
585 // nn::time::SteadyClockTimePoint
586 Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time;
587
588 std::array<u8, 0xC0> reserved_29F80;
589
590 // nn::settings::system::PrimaryAlbumStorage
591 PrimaryAlbumStorage primary_album_storage;
592
593 std::array<u8, 0x3C> reserved_2A044;
594
595 bool usb_30_enable_flag;
596 std::array<u8, 0x3> pad_2A081;
597 bool usb_30_host_enable_flag;
598 std::array<u8, 0x3> pad_2A085;
599 bool usb_30_device_enable_flag;
600 std::array<u8, 0x3> pad_2A089;
601
602 std::array<u8, 0x34> reserved_2A08C;
603
604 bool nfc_enable_flag;
605 std::array<u8, 0x3> pad_2A0C1;
606
607 std::array<u8, 0x3C> reserved_2A0C4;
608
609 // nn::settings::system::SleepSettings
610 SleepSettings sleep_settings;
611
612 std::array<u8, 0x34> reserved_2A10C;
613
614 // nn::settings::system::EulaVersion
615 u32 eula_version_count;
616 std::array<u8, 0xC> reserved_2A144;
617 std::array<EulaVersion, 32> eula_versions;
618
619 std::array<u8, 0x200> reserved_2A750;
620
621 // nn::settings::system::DeviceNickName
622 std::array<u8, 0x80> device_nick_name;
623
624 std::array<u8, 0x80> reserved_2A9D0;
625
626 bool auto_update_enable_flag;
627 std::array<u8, 0x3> pad_2AA51;
628
629 std::array<u8, 0x4C> reserved_2AA54;
630
631 // nn::settings::system::BluetoothDevicesSettings
632 std::array<std::array<u8, 0x200>, 14> bluetooth_device_settings_last_14;
633
634 std::array<u8, 0x2000> reserved_2C6A0;
635
636 // nn::settings::system::NxControllerSettings
637 std::array<std::array<u8, 0x800>, 10> nx_controller_settings_data_from_offset_30;
638};
639
640static_assert(offsetof(SystemSettings, language_code) == 0x10);
641static_assert(offsetof(SystemSettings, network_setting_count) == 0x50);
642static_assert(offsetof(SystemSettings, network_settings_1B0) == 0x60);
643static_assert(offsetof(SystemSettings, bluetooth_device_settings_count) == 0x8060);
644static_assert(offsetof(SystemSettings, bluetooth_enable_flag) == 0x8064);
645static_assert(offsetof(SystemSettings, bluetooth_device_settings_first_10) == 0x8070);
646static_assert(offsetof(SystemSettings, ldn_channel) == 0x9470);
647static_assert(offsetof(SystemSettings, mii_author_id) == 0x94B0);
648static_assert(offsetof(SystemSettings, nx_controller_settings_count) == 0x94F0);
649static_assert(offsetof(SystemSettings, nx_controller_legacy_settings) == 0x9500);
650static_assert(offsetof(SystemSettings, external_rtc_reset_flag) == 0x98F0);
651static_assert(offsetof(SystemSettings, push_notification_activity_mode_on_sleep) == 0x9930);
652static_assert(offsetof(SystemSettings, allowed_ssl_host_count) == 0x99F4);
653static_assert(offsetof(SystemSettings, host_fs_mount_point) == 0x9A30);
654static_assert(offsetof(SystemSettings, allowed_ssl_hosts) == 0x9B30);
655static_assert(offsetof(SystemSettings, ble_pairing_settings_count) == 0xA9F0);
656static_assert(offsetof(SystemSettings, ble_pairing_settings) == 0xAA00);
657static_assert(offsetof(SystemSettings, account_online_storage_settings_count) == 0xAF00);
658static_assert(offsetof(SystemSettings, account_online_storage_settings) == 0xAF10);
659static_assert(offsetof(SystemSettings, pctl_ready_flag) == 0xB110);
660static_assert(offsetof(SystemSettings, theme_id_type0) == 0xB150);
661static_assert(offsetof(SystemSettings, chinese_traditional_input_method) == 0xB350);
662static_assert(offsetof(SystemSettings, button_config_registered_settings_count) == 0xB3D0);
663static_assert(offsetof(SystemSettings, button_config_settings_count) == 0xB3E0);
664static_assert(offsetof(SystemSettings, button_config_settings) == 0xB3E8);
665static_assert(offsetof(SystemSettings, button_config_registered_settings_embedded) == 0x1D3E0);
666static_assert(offsetof(SystemSettings, console_six_axis_sensor_acceleration_bias) == 0x29370);
667static_assert(offsetof(SystemSettings, lock_screen_flag) == 0x29470);
668static_assert(offsetof(SystemSettings, battery_percentage_flag) == 0x294A0);
669static_assert(offsetof(SystemSettings, field_testing_flag) == 0x294C0);
670static_assert(offsetof(SystemSettings, backlight_settings_mixed_up) == 0x294F0);
671static_assert(offsetof(SystemSettings, user_system_clock_context) == 0x29580);
672static_assert(offsetof(SystemSettings, network_system_clock_context) == 0x295A0);
673static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_enabled) == 0x295C0);
674static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_updated_time_point) ==
675 0x295C8);
676static_assert(offsetof(SystemSettings, account_settings) == 0x295F0);
677static_assert(offsetof(SystemSettings, audio_volume_type0) == 0x296F0);
678static_assert(offsetof(SystemSettings, hearing_protection_safeguard_remaining_time) == 0x29730);
679static_assert(offsetof(SystemSettings, automatic_application_download_flag) == 0x297B0);
680static_assert(offsetof(SystemSettings, notification_settings) == 0x297B8);
681static_assert(offsetof(SystemSettings, account_notification_settings) == 0x29840);
682static_assert(offsetof(SystemSettings, vibration_master_volume) == 0x29A40);
683static_assert(offsetof(SystemSettings, analog_stick_user_calibration_left) == 0x29A48);
684static_assert(offsetof(SystemSettings, touch_screen_mode) == 0x29A68);
685static_assert(offsetof(SystemSettings, edid) == 0x29AA0);
686static_assert(offsetof(SystemSettings, data_deletion_settings) == 0x29E80);
687static_assert(offsetof(SystemSettings, requires_run_repair_time_reviser) == 0x29ED4);
688static_assert(offsetof(SystemSettings, device_time_zone_location_name) == 0x29F40);
689static_assert(offsetof(SystemSettings, nfc_enable_flag) == 0x2A0C0);
690static_assert(offsetof(SystemSettings, eula_version_count) == 0x2A140);
691static_assert(offsetof(SystemSettings, device_nick_name) == 0x2A950);
692static_assert(offsetof(SystemSettings, bluetooth_device_settings_last_14) == 0x2AAA0);
693static_assert(offsetof(SystemSettings, nx_controller_settings_data_from_offset_30) == 0x2E6A0);
694
695static_assert(sizeof(SystemSettings) == 0x336A0, "SystemSettings has the wrong size!");
696
697SystemSettings DefaultSystemSettings();
698
699} // namespace Service::Set
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index 6c8427b0d..0fbb43057 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -240,7 +240,7 @@ private:
240 return ret; 240 return ret;
241 } 241 }
242 242
243 Result ReadImpl(std::vector<u8>* out_data, size_t size) { 243 Result ReadImpl(std::vector<u8>* out_data) {
244 ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); 244 ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
245 size_t actual_size{}; 245 size_t actual_size{};
246 Result res = backend->Read(&actual_size, *out_data); 246 Result res = backend->Read(&actual_size, *out_data);
@@ -326,8 +326,8 @@ private:
326 } 326 }
327 327
328 void Read(HLERequestContext& ctx) { 328 void Read(HLERequestContext& ctx) {
329 std::vector<u8> output_bytes; 329 std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
330 const Result res = ReadImpl(&output_bytes, ctx.GetWriteBufferSize()); 330 const Result res = ReadImpl(&output_bytes);
331 IPC::ResponseBuilder rb{ctx, 3}; 331 IPC::ResponseBuilder rb{ctx, 3};
332 rb.Push(res); 332 rb.Push(res);
333 if (res == ResultSuccess) { 333 if (res == ResultSuccess) {
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index d30f49877..71ce9be50 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -51,11 +51,24 @@ Display::~Display() {
51} 51}
52 52
53Layer& Display::GetLayer(std::size_t index) { 53Layer& Display::GetLayer(std::size_t index) {
54 return *layers.at(index); 54 size_t i = 0;
55 for (auto& layer : layers) {
56 if (!layer->IsOpen()) {
57 continue;
58 }
59
60 if (i == index) {
61 return *layer;
62 }
63
64 i++;
65 }
66
67 UNREACHABLE();
55} 68}
56 69
57const Layer& Display::GetLayer(std::size_t index) const { 70size_t Display::GetNumLayers() const {
58 return *layers.at(index); 71 return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); });
59} 72}
60 73
61Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) { 74Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) {
@@ -92,7 +105,11 @@ void Display::CreateLayer(u64 layer_id, u32 binder_id,
92 hos_binder_driver_server.RegisterProducer(std::move(producer)); 105 hos_binder_driver_server.RegisterProducer(std::move(producer));
93} 106}
94 107
95void Display::CloseLayer(u64 layer_id) { 108void Display::DestroyLayer(u64 layer_id) {
109 if (auto* layer = this->FindLayer(layer_id); layer != nullptr) {
110 layer->GetConsumer().Abandon();
111 }
112
96 std::erase_if(layers, 113 std::erase_if(layers,
97 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; }); 114 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
98} 115}
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 101cbce20..1d9360b96 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -66,18 +66,13 @@ public:
66 66
67 /// Whether or not this display has any layers added to it. 67 /// Whether or not this display has any layers added to it.
68 bool HasLayers() const { 68 bool HasLayers() const {
69 return !layers.empty(); 69 return GetNumLayers() > 0;
70 } 70 }
71 71
72 /// Gets a layer for this display based off an index. 72 /// Gets a layer for this display based off an index.
73 Layer& GetLayer(std::size_t index); 73 Layer& GetLayer(std::size_t index);
74 74
75 /// Gets a layer for this display based off an index. 75 std::size_t GetNumLayers() const;
76 const Layer& GetLayer(std::size_t index) const;
77
78 std::size_t GetNumLayers() const {
79 return layers.size();
80 }
81 76
82 /** 77 /**
83 * Gets the internal vsync event. 78 * Gets the internal vsync event.
@@ -100,11 +95,11 @@ public:
100 /// 95 ///
101 void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); 96 void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
102 97
103 /// Closes and removes a layer from this display with the given ID. 98 /// Removes a layer from this display with the given ID.
104 /// 99 ///
105 /// @param layer_id The ID assigned to the layer to close. 100 /// @param layer_id The ID assigned to the layer to destroy.
106 /// 101 ///
107 void CloseLayer(u64 layer_id); 102 void DestroyLayer(u64 layer_id);
108 103
109 /// Resets the display for a new connection. 104 /// Resets the display for a new connection.
110 void Reset() { 105 void Reset() {
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 9ae2e0e44..04e52a23b 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -8,8 +8,8 @@ namespace Service::VI {
8Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, 8Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
9 android::BufferQueueProducer& binder_, 9 android::BufferQueueProducer& binder_,
10 std::shared_ptr<android::BufferItemConsumer>&& consumer_) 10 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
11 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move( 11 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_},
12 consumer_)} {} 12 consumer{std::move(consumer_)}, open{false} {}
13 13
14Layer::~Layer() = default; 14Layer::~Layer() = default;
15 15
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index 8cf1b5275..295005e23 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -71,12 +71,25 @@ public:
71 return core; 71 return core;
72 } 72 }
73 73
74 bool IsOpen() const {
75 return open;
76 }
77
78 void Close() {
79 open = false;
80 }
81
82 void Open() {
83 open = true;
84 }
85
74private: 86private:
75 const u64 layer_id; 87 const u64 layer_id;
76 const u32 binder_id; 88 const u32 binder_id;
77 android::BufferQueueCore& core; 89 android::BufferQueueCore& core;
78 android::BufferQueueProducer& binder; 90 android::BufferQueueProducer& binder;
79 std::shared_ptr<android::BufferItemConsumer> consumer; 91 std::shared_ptr<android::BufferItemConsumer> consumer;
92 bool open;
80}; 93};
81 94
82} // namespace Service::VI 95} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index b1bfb9898..9ab8788e3 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -719,6 +719,8 @@ private:
719 return; 719 return;
720 } 720 }
721 721
722 nv_flinger.OpenLayer(layer_id);
723
722 android::OutputParcel parcel; 724 android::OutputParcel parcel;
723 parcel.WriteInterface(NativeWindow{*buffer_queue_id}); 725 parcel.WriteInterface(NativeWindow{*buffer_queue_id});
724 726
@@ -783,6 +785,7 @@ private:
783 const u64 layer_id = rp.Pop<u64>(); 785 const u64 layer_id = rp.Pop<u64>();
784 786
785 LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id); 787 LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
788 nv_flinger.DestroyLayer(layer_id);
786 789
787 IPC::ResponseBuilder rb{ctx, 2}; 790 IPC::ResponseBuilder rb{ctx, 2};
788 rb.Push(ResultSuccess); 791 rb.Push(ResultSuccess);
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index db30ba598..3fc4024dc 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -62,7 +62,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
62 } 62 }
63 63
64 const auto applet_resource = hid->GetResourceManager(); 64 const auto applet_resource = hid->GetResourceManager();
65 if (applet_resource == nullptr) { 65 if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
66 LOG_WARNING(CheatEngine, 66 LOG_WARNING(CheatEngine,
67 "Attempted to read input state, but applet resource is not initialized!"); 67 "Attempted to read input state, but applet resource is not initialized!");
68 return 0; 68 return 0;
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index 1a0491c2c..d9f99148b 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -214,6 +214,7 @@ void Config::ReadControlValues() {
214} 214}
215 215
216void Config::ReadMotionTouchValues() { 216void Config::ReadMotionTouchValues() {
217 Settings::values.touch_from_button_maps.clear();
217 int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps")); 218 int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps"));
218 219
219 if (num_touch_from_button_maps > 0) { 220 if (num_touch_from_button_maps > 0) {
diff --git a/src/tests/video_core/memory_tracker.cpp b/src/tests/video_core/memory_tracker.cpp
index 618793668..2dbff21af 100644
--- a/src/tests/video_core/memory_tracker.cpp
+++ b/src/tests/video_core/memory_tracker.cpp
@@ -23,13 +23,13 @@ constexpr VAddr c = 16 * HIGH_PAGE_SIZE;
23 23
24class RasterizerInterface { 24class RasterizerInterface {
25public: 25public:
26 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { 26 void UpdatePagesCachedCount(VAddr addr, u64 size, bool cache) {
27 const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS}; 27 const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS};
28 const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >> 28 const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >>
29 Core::Memory::YUZU_PAGEBITS}; 29 Core::Memory::YUZU_PAGEBITS};
30 for (u64 page = page_start; page < page_end; ++page) { 30 for (u64 page = page_start; page < page_end; ++page) {
31 int& value = page_table[page]; 31 int& value = page_table[page];
32 value += delta; 32 value += (cache ? 1 : -1);
33 if (value < 0) { 33 if (value < 0) {
34 throw std::logic_error{"negative page"}; 34 throw std::logic_error{"negative page"};
35 } 35 }
@@ -546,4 +546,4 @@ TEST_CASE("MemoryTracker: Cached write downloads") {
546 REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE)); 546 REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));
547 memory_track->MarkRegionAsCpuModified(c, WORD); 547 memory_track->MarkRegionAsCpuModified(c, WORD);
548 REQUIRE(rasterizer.Count() == 0); 548 REQUIRE(rasterizer.Count() == 0);
549} \ No newline at end of file 549}
diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h
index a336bde41..95b752055 100644
--- a/src/video_core/buffer_cache/word_manager.h
+++ b/src/video_core/buffer_cache/word_manager.h
@@ -473,7 +473,7 @@ private:
473 VAddr addr = cpu_addr + word_index * BYTES_PER_WORD; 473 VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
474 IteratePages(changed_bits, [&](size_t offset, size_t size) { 474 IteratePages(changed_bits, [&](size_t offset, size_t size) {
475 rasterizer->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE, 475 rasterizer->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE,
476 size * BYTES_PER_PAGE, add_to_rasterizer ? 1 : -1); 476 size * BYTES_PER_PAGE, add_to_rasterizer);
477 }); 477 });
478 } 478 }
479 479
diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp
index f200a650f..3c9477f6e 100644
--- a/src/video_core/rasterizer_accelerated.cpp
+++ b/src/video_core/rasterizer_accelerated.cpp
@@ -3,6 +3,7 @@
3 3
4#include <atomic> 4#include <atomic>
5 5
6#include "common/alignment.h"
6#include "common/assert.h" 7#include "common/assert.h"
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "common/div_ceil.h" 9#include "common/div_ceil.h"
@@ -11,61 +12,65 @@
11 12
12namespace VideoCore { 13namespace VideoCore {
13 14
15static constexpr u16 IdentityValue = 1;
16
14using namespace Core::Memory; 17using namespace Core::Memory;
15 18
16RasterizerAccelerated::RasterizerAccelerated(Memory& cpu_memory_) 19RasterizerAccelerated::RasterizerAccelerated(Memory& cpu_memory_) : map{}, cpu_memory{cpu_memory_} {
17 : cached_pages(std::make_unique<CachedPages>()), cpu_memory{cpu_memory_} {} 20 // We are tracking CPU memory, which cannot map more than 39 bits.
21 const VAddr start_address = 0;
22 const VAddr end_address = (1ULL << 39);
23 const IntervalType address_space_interval(start_address, end_address);
24 const auto value = std::make_pair(address_space_interval, IdentityValue);
25
26 map.add(value);
27}
18 28
19RasterizerAccelerated::~RasterizerAccelerated() = default; 29RasterizerAccelerated::~RasterizerAccelerated() = default;
20 30
21void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { 31void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, bool cache) {
22 u64 uncache_begin = 0; 32 std::scoped_lock lk{map_lock};
23 u64 cache_begin = 0;
24 u64 uncache_bytes = 0;
25 u64 cache_bytes = 0;
26
27 std::atomic_thread_fence(std::memory_order_acquire);
28 const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE);
29 for (u64 page = addr >> YUZU_PAGEBITS; page != page_end; ++page) {
30 std::atomic_uint16_t& count = cached_pages->at(page >> 2).Count(page);
31
32 if (delta > 0) {
33 ASSERT_MSG(count.load(std::memory_order::relaxed) < UINT16_MAX, "Count may overflow!");
34 } else if (delta < 0) {
35 ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!");
36 } else {
37 ASSERT_MSG(false, "Delta must be non-zero!");
38 }
39 33
40 // Adds or subtracts 1, as count is a unsigned 8-bit value 34 // Align sizes.
41 count.fetch_add(static_cast<u16>(delta), std::memory_order_release); 35 addr = Common::AlignDown(addr, YUZU_PAGESIZE);
42 36 size = Common::AlignUp(size, YUZU_PAGESIZE);
43 // Assume delta is either -1 or 1 37
44 if (count.load(std::memory_order::relaxed) == 0) { 38 // Declare the overall interval we are going to operate on.
45 if (uncache_bytes == 0) { 39 const VAddr start_address = addr;
46 uncache_begin = page; 40 const VAddr end_address = addr + size;
47 } 41 const IntervalType modification_range(start_address, end_address);
48 uncache_bytes += YUZU_PAGESIZE; 42
49 } else if (uncache_bytes > 0) { 43 // Find the boundaries of where to iterate.
50 cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, 44 const auto lower = map.lower_bound(modification_range);
51 false); 45 const auto upper = map.upper_bound(modification_range);
52 uncache_bytes = 0; 46
53 } 47 // Iterate over the contained intervals.
54 if (count.load(std::memory_order::relaxed) == 1 && delta > 0) { 48 for (auto it = lower; it != upper; it++) {
55 if (cache_bytes == 0) { 49 // Intersect interval range with modification range.
56 cache_begin = page; 50 const auto current_range = modification_range & it->first;
57 } 51
58 cache_bytes += YUZU_PAGESIZE; 52 // Calculate the address and size to operate over.
59 } else if (cache_bytes > 0) { 53 const auto current_addr = current_range.lower();
60 cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true); 54 const auto current_size = current_range.upper() - current_addr;
61 cache_bytes = 0; 55
56 // Get the current value of the range.
57 const auto value = it->second;
58
59 if (cache && value == IdentityValue) {
60 // If we are going to cache, and the value is not yet referenced, then cache this range.
61 cpu_memory.RasterizerMarkRegionCached(current_addr, current_size, true);
62 } else if (!cache && value == IdentityValue + 1) {
63 // If we are going to uncache, and this is the last reference, then uncache this range.
64 cpu_memory.RasterizerMarkRegionCached(current_addr, current_size, false);
62 } 65 }
63 } 66 }
64 if (uncache_bytes > 0) { 67
65 cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, false); 68 // Update the set.
66 } 69 const auto value = std::make_pair(modification_range, IdentityValue);
67 if (cache_bytes > 0) { 70 if (cache) {
68 cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true); 71 map.add(value);
72 } else {
73 map.subtract(value);
69 } 74 }
70} 75}
71 76
diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h
index e6c0ea87a..f1968f186 100644
--- a/src/video_core/rasterizer_accelerated.h
+++ b/src/video_core/rasterizer_accelerated.h
@@ -3,8 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <mutex>
7#include <atomic> 7#include <boost/icl/interval_map.hpp>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/rasterizer_interface.h" 10#include "video_core/rasterizer_interface.h"
@@ -21,28 +21,17 @@ public:
21 explicit RasterizerAccelerated(Core::Memory::Memory& cpu_memory_); 21 explicit RasterizerAccelerated(Core::Memory::Memory& cpu_memory_);
22 ~RasterizerAccelerated() override; 22 ~RasterizerAccelerated() override;
23 23
24 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; 24 void UpdatePagesCachedCount(VAddr addr, u64 size, bool cache) override;
25 25
26private: 26private:
27 class CacheEntry final { 27 using PageIndex = VAddr;
28 public: 28 using PageReferenceCount = u16;
29 CacheEntry() = default;
30 29
31 std::atomic_uint16_t& Count(std::size_t page) { 30 using IntervalMap = boost::icl::interval_map<PageIndex, PageReferenceCount>;
32 return values[page & 3]; 31 using IntervalType = IntervalMap::interval_type;
33 }
34 32
35 const std::atomic_uint16_t& Count(std::size_t page) const { 33 IntervalMap map;
36 return values[page & 3]; 34 std::mutex map_lock;
37 }
38
39 private:
40 std::array<std::atomic_uint16_t, 4> values{};
41 };
42 static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!");
43
44 using CachedPages = std::array<CacheEntry, 0x2000000>;
45 std::unique_ptr<CachedPages> cached_pages;
46 Core::Memory::Memory& cpu_memory; 35 Core::Memory::Memory& cpu_memory;
47}; 36};
48 37
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index af1469147..fd42d26b5 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -162,7 +162,7 @@ public:
162 } 162 }
163 163
164 /// Increase/decrease the number of object in pages touching the specified region 164 /// Increase/decrease the number of object in pages touching the specified region
165 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} 165 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, bool cache) {}
166 166
167 /// Initialize disk cached resources for the game being emulated 167 /// Initialize disk cached resources for the game being emulated
168 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 168 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index e6c70fb34..a71866b75 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -333,7 +333,7 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
333 glBindBuffersRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 333 glBindBuffersRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
334 static_cast<GLsizei>(bindings.buffers.size()), buffer_handles.data(), 334 static_cast<GLsizei>(bindings.buffers.size()), buffer_handles.data(),
335 reinterpret_cast<const GLintptr*>(bindings.offsets.data()), 335 reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
336 reinterpret_cast<const GLsizeiptr*>(bindings.strides.data())); 336 reinterpret_cast<const GLsizeiptr*>(bindings.sizes.data()));
337} 337}
338 338
339void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, 339void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 8e4c74b5c..a59e2d2d1 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -102,8 +102,8 @@ PresentManager::PresentManager(const vk::Instance& instance_,
102 memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_}, 102 memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_},
103 surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(), 103 surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(),
104 swapchain.GetImageViewFormat())}, 104 swapchain.GetImageViewFormat())},
105 use_present_thread{Settings::values.async_presentation.GetValue()}, 105 use_present_thread{Settings::values.async_presentation.GetValue()} {
106 image_count{swapchain.GetImageCount()} { 106 SetImageCount();
107 107
108 auto& dld = device.GetLogical(); 108 auto& dld = device.GetLogical();
109 cmdpool = dld.CreateCommandPool({ 109 cmdpool = dld.CreateCommandPool({
@@ -289,7 +289,14 @@ void PresentManager::PresentThread(std::stop_token token) {
289 289
290void PresentManager::RecreateSwapchain(Frame* frame) { 290void PresentManager::RecreateSwapchain(Frame* frame) {
291 swapchain.Create(*surface, frame->width, frame->height); 291 swapchain.Create(*surface, frame->width, frame->height);
292 image_count = swapchain.GetImageCount(); 292 SetImageCount();
293}
294
295void PresentManager::SetImageCount() {
296 // We cannot have more than 5 images in flight at any given time.
297 // FRAMES_IN_FLIGHT is 7, and the cache TICKS_TO_DESTROY is 6.
298 // Mali drivers will give us 6.
299 image_count = std::min<size_t>(swapchain.GetImageCount(), 5);
293} 300}
294 301
295void PresentManager::CopyToSwapchain(Frame* frame) { 302void PresentManager::CopyToSwapchain(Frame* frame) {
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h
index 337171a09..23ee61c8c 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.h
+++ b/src/video_core/renderer_vulkan/vk_present_manager.h
@@ -62,6 +62,8 @@ private:
62 62
63 void RecreateSwapchain(Frame* frame); 63 void RecreateSwapchain(Frame* frame);
64 64
65 void SetImageCount();
66
65private: 67private:
66 const vk::Instance& instance; 68 const vk::Instance& instance;
67 Core::Frontend::EmuWindow& render_window; 69 Core::Frontend::EmuWindow& render_window;
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index e81cd031b..a109f9cbe 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -132,7 +132,7 @@ void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t
132 132
133 storage.push_back(std::move(data)); 133 storage.push_back(std::move(data));
134 134
135 rasterizer.UpdatePagesCachedCount(addr, size, 1); 135 rasterizer.UpdatePagesCachedCount(addr, size, true);
136} 136}
137 137
138void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) { 138void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) {
@@ -209,7 +209,7 @@ void ShaderCache::UnmarkMemory(Entry* entry) {
209 209
210 const VAddr addr = entry->addr_start; 210 const VAddr addr = entry->addr_start;
211 const size_t size = entry->addr_end - addr; 211 const size_t size = entry->addr_end - addr;
212 rasterizer.UpdatePagesCachedCount(addr, size, -1); 212 rasterizer.UpdatePagesCachedCount(addr, size, false);
213} 213}
214 214
215void ShaderCache::RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders) { 215void ShaderCache::RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders) {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 0d5a1709f..d7941f6a4 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -2080,7 +2080,7 @@ void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
2080 ASSERT(False(image.flags & ImageFlagBits::Tracked)); 2080 ASSERT(False(image.flags & ImageFlagBits::Tracked));
2081 image.flags |= ImageFlagBits::Tracked; 2081 image.flags |= ImageFlagBits::Tracked;
2082 if (False(image.flags & ImageFlagBits::Sparse)) { 2082 if (False(image.flags & ImageFlagBits::Sparse)) {
2083 rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); 2083 rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, true);
2084 return; 2084 return;
2085 } 2085 }
2086 if (True(image.flags & ImageFlagBits::Registered)) { 2086 if (True(image.flags & ImageFlagBits::Registered)) {
@@ -2091,13 +2091,13 @@ void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
2091 const auto& map = slot_map_views[map_view_id]; 2091 const auto& map = slot_map_views[map_view_id];
2092 const VAddr cpu_addr = map.cpu_addr; 2092 const VAddr cpu_addr = map.cpu_addr;
2093 const std::size_t size = map.size; 2093 const std::size_t size = map.size;
2094 rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); 2094 rasterizer.UpdatePagesCachedCount(cpu_addr, size, true);
2095 } 2095 }
2096 return; 2096 return;
2097 } 2097 }
2098 ForEachSparseSegment(image, 2098 ForEachSparseSegment(image,
2099 [this]([[maybe_unused]] GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { 2099 [this]([[maybe_unused]] GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
2100 rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); 2100 rasterizer.UpdatePagesCachedCount(cpu_addr, size, true);
2101 }); 2101 });
2102} 2102}
2103 2103
@@ -2106,7 +2106,7 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
2106 ASSERT(True(image.flags & ImageFlagBits::Tracked)); 2106 ASSERT(True(image.flags & ImageFlagBits::Tracked));
2107 image.flags &= ~ImageFlagBits::Tracked; 2107 image.flags &= ~ImageFlagBits::Tracked;
2108 if (False(image.flags & ImageFlagBits::Sparse)) { 2108 if (False(image.flags & ImageFlagBits::Sparse)) {
2109 rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1); 2109 rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, false);
2110 return; 2110 return;
2111 } 2111 }
2112 ASSERT(True(image.flags & ImageFlagBits::Registered)); 2112 ASSERT(True(image.flags & ImageFlagBits::Registered));
@@ -2117,7 +2117,7 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
2117 const auto& map = slot_map_views[map_view_id]; 2117 const auto& map = slot_map_views[map_view_id];
2118 const VAddr cpu_addr = map.cpu_addr; 2118 const VAddr cpu_addr = map.cpu_addr;
2119 const std::size_t size = map.size; 2119 const std::size_t size = map.size;
2120 rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1); 2120 rasterizer.UpdatePagesCachedCount(cpu_addr, size, false);
2121 } 2121 }
2122} 2122}
2123 2123
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index dd43f0a0e..c8e871151 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -193,8 +193,8 @@ void ConfigureUi::RequestGameListUpdate() {
193void ConfigureUi::SetConfiguration() { 193void ConfigureUi::SetConfiguration() {
194 ui->theme_combobox->setCurrentIndex( 194 ui->theme_combobox->setCurrentIndex(
195 ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme))); 195 ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
196 ui->language_combobox->setCurrentIndex( 196 ui->language_combobox->setCurrentIndex(ui->language_combobox->findData(
197 ui->language_combobox->findData(QString::fromStdString(UISettings::values.language))); 197 QString::fromStdString(UISettings::values.language.GetValue())));
198 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 198 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
199 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); 199 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
200 ui->show_size->setChecked(UISettings::values.show_size.GetValue()); 200 ui->show_size->setChecked(UISettings::values.show_size.GetValue());
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index 636c5e640..417a43ec5 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -187,7 +187,6 @@ void QtConfig::ReadPathValues() {
187 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); 187 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
188 188
189 UISettings::values.roms_path = ReadStringSetting(std::string("romsPath")); 189 UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
190 UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath"));
191 UISettings::values.game_dir_deprecated = 190 UISettings::values.game_dir_deprecated =
192 ReadStringSetting(std::string("gameListRootDir"), std::string(".")); 191 ReadStringSetting(std::string("gameListRootDir"), std::string("."));
193 UISettings::values.game_dir_deprecated_deepscan = 192 UISettings::values.game_dir_deprecated_deepscan =
@@ -225,8 +224,6 @@ void QtConfig::ReadPathValues() {
225 UISettings::values.recent_files = 224 UISettings::values.recent_files =
226 QString::fromStdString(ReadStringSetting(std::string("recentFiles"))) 225 QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
227 .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive); 226 .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
228 UISettings::values.language =
229 ReadStringSetting(std::string("language"), std::make_optional(std::string("")));
230 227
231 EndGroup(); 228 EndGroup();
232} 229}
@@ -409,7 +406,6 @@ void QtConfig::SavePathValues() {
409 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); 406 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
410 407
411 WriteSetting(std::string("romsPath"), UISettings::values.roms_path); 408 WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
412 WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path);
413 BeginArray(std::string("gamedirs")); 409 BeginArray(std::string("gamedirs"));
414 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { 410 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
415 SetArrayIndex(i); 411 SetArrayIndex(i);
@@ -422,7 +418,6 @@ void QtConfig::SavePathValues() {
422 418
423 WriteSetting(std::string("recentFiles"), 419 WriteSetting(std::string("recentFiles"),
424 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); 420 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
425 WriteSetting(std::string("language"), UISettings::values.language);
426 421
427 EndGroup(); 422 EndGroup();
428} 423}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f31ed7ebb..059fcf041 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -5147,12 +5147,12 @@ void GMainWindow::UpdateUITheme() {
5147void GMainWindow::LoadTranslation() { 5147void GMainWindow::LoadTranslation() {
5148 bool loaded; 5148 bool loaded;
5149 5149
5150 if (UISettings::values.language.empty()) { 5150 if (UISettings::values.language.GetValue().empty()) {
5151 // If the selected language is empty, use system locale 5151 // If the selected language is empty, use system locale
5152 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); 5152 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
5153 } else { 5153 } else {
5154 // Otherwise load from the specified file 5154 // Otherwise load from the specified file
5155 loaded = translator.load(QString::fromStdString(UISettings::values.language), 5155 loaded = translator.load(QString::fromStdString(UISettings::values.language.GetValue()),
5156 QStringLiteral(":/languages/")); 5156 QStringLiteral(":/languages/"));
5157 } 5157 }
5158 5158
@@ -5164,7 +5164,7 @@ void GMainWindow::LoadTranslation() {
5164} 5164}
5165 5165
5166void GMainWindow::OnLanguageChanged(const QString& locale) { 5166void GMainWindow::OnLanguageChanged(const QString& locale) {
5167 if (UISettings::values.language != std::string("en")) { 5167 if (UISettings::values.language.GetValue() != std::string("en")) {
5168 qApp->removeTranslator(&translator); 5168 qApp->removeTranslator(&translator);
5169 } 5169 }
5170 5170
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 549a39e1b..f9906be33 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -154,12 +154,11 @@ struct Values {
154 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; 154 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
155 155
156 std::string roms_path; 156 std::string roms_path;
157 std::string symbols_path;
158 std::string game_dir_deprecated; 157 std::string game_dir_deprecated;
159 bool game_dir_deprecated_deepscan; 158 bool game_dir_deprecated_deepscan;
160 QVector<GameDir> game_dirs; 159 QVector<GameDir> game_dirs;
161 QStringList recent_files; 160 QStringList recent_files;
162 std::string language; 161 Setting<std::string> language{linkage, {}, "language", Category::Paths};
163 162
164 std::string theme; 163 std::string theme;
165 164