summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt224
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt39
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt416
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt93
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt76
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt83
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt31
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt134
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt31
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt240
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt300
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt68
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt148
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt79
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt58
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt260
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt301
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt85
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt798
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt183
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt112
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt60
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt80
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt87
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt47
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt274
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt50
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt124
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt102
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt227
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt193
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt46
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt71
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt106
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt71
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt67
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt456
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt141
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt33
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt1
-rw-r--r--src/android/app/src/main/jni/android_config.cpp141
-rw-r--r--src/android/app/src/main/jni/android_config.h7
-rw-r--r--src/android/app/src/main/jni/android_settings.h7
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.cpp43
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.h15
-rw-r--r--src/android/app/src/main/jni/native.cpp177
-rw-r--r--src/android/app/src/main/jni/native.h5
-rw-r--r--src/android/app/src/main/jni/native_config.cpp117
-rw-r--r--src/android/app/src/main/jni/native_input.cpp629
-rw-r--r--src/android/app/src/main/res/drawable/button_anim.xml142
-rw-r--r--src/android/app/src/main/res/drawable/ic_controller_disconnected.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_more_vert.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_new_label.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_overlay.xml21
-rw-r--r--src/android/app/src/main/res/drawable/ic_share.xml9
-rw-r--r--src/android/app/src/main/res/drawable/stick_one_direction_anim.xml118
-rw-r--r--src/android/app/src/main/res/drawable/stick_two_direction_anim.xml173
-rw-r--r--src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml63
-rw-r--r--src/android/app/src/main/res/layout/card_driver_option.xml9
-rw-r--r--src/android/app/src/main/res/layout/card_folder.xml3
-rw-r--r--src/android/app/src/main/res/layout/card_game.xml3
-rw-r--r--src/android/app/src/main/res/layout/card_simple_outlined.xml3
-rw-r--r--src/android/app/src/main/res/layout/dialog_input_profiles.xml6
-rw-r--r--src/android/app/src/main/res/layout/dialog_mapping.xml26
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_properties.xml3
-rw-r--r--src/android/app/src/main/res/layout/list_item_input_profile.xml74
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_input.xml63
-rw-r--r--src/android/app/src/main/res/menu/menu_in_game.xml7
-rw-r--r--src/android/app/src/main/res/menu/menu_input_options.xml34
-rw-r--r--src/android/app/src/main/res/navigation/settings_navigation.xml2
-rw-r--r--src/android/app/src/main/res/values-w600dp/dimens.xml2
-rw-r--r--src/android/app/src/main/res/values/arrays.xml11
-rw-r--r--src/android/app/src/main/res/values/dimens.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml102
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp4
-rw-r--r--src/audio_core/sink/sink_stream.cpp4
-rw-r--r--src/common/android/id_cache.cpp163
-rw-r--r--src/common/android/id_cache.h24
-rw-r--r--src/common/demangle.cpp4
-rw-r--r--src/common/host_memory.cpp4
-rw-r--r--src/common/page_table.cpp4
-rw-r--r--src/common/scope_exit.h66
-rw-r--r--src/common/settings.cpp3
-rw-r--r--src/common/settings.h32
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/common/settings_input.h4
-rw-r--r--src/core/CMakeLists.txt320
-rw-r--r--src/core/core.cpp21
-rw-r--r--src/core/cpu_manager.cpp4
-rw-r--r--src/core/device_memory_manager.inc4
-rw-r--r--src/core/file_sys/content_archive.cpp4
-rw-r--r--src/core/file_sys/content_archive.h1
-rw-r--r--src/core/file_sys/control_metadata.h4
-rw-r--r--src/core/file_sys/fs_directory.h4
-rw-r--r--src/core/file_sys/fs_path_utility.h12
-rw-r--r--src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp4
-rw-r--r--src/core/file_sys/program_metadata.cpp4
-rw-r--r--src/core/file_sys/system_archive/shared_font.cpp2
-rw-r--r--src/core/hle/kernel/k_client_session.cpp8
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp40
-rw-r--r--src/core/hle/kernel/k_process.cpp23
-rw-r--r--src/core/hle/kernel/k_server_session.cpp36
-rw-r--r--src/core/hle/kernel/k_thread.h4
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp4
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp4
-rw-r--r--src/core/hle/kernel/kernel.cpp69
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_event.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp4
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp5
-rw-r--r--src/core/hle/service/am/am.cpp16
-rw-r--r--src/core/hle/service/am/am.h6
-rw-r--r--src/core/hle/service/am/am_types.h91
-rw-r--r--src/core/hle/service/am/applet.h12
-rw-r--r--src/core/hle/service/am/applet_ae.cpp73
-rw-r--r--src/core/hle/service/am/applet_ae.h39
-rw-r--r--src/core/hle/service/am/applet_common_functions.cpp63
-rw-r--r--src/core/hle/service/am/applet_common_functions.h24
-rw-r--r--src/core/hle/service/am/applet_data_broker.cpp4
-rw-r--r--src/core/hle/service/am/applet_manager.cpp23
-rw-r--r--src/core/hle/service/am/applet_message_queue.cpp2
-rw-r--r--src/core/hle/service/am/applet_message_queue.h35
-rw-r--r--src/core/hle/service/am/applet_oe.cpp42
-rw-r--r--src/core/hle/service/am/applet_oe.h37
-rw-r--r--src/core/hle/service/am/application_creator.cpp25
-rw-r--r--src/core/hle/service/am/application_creator.h16
-rw-r--r--src/core/hle/service/am/application_functions.cpp594
-rw-r--r--src/core/hle/service/am/application_functions.h58
-rw-r--r--src/core/hle/service/am/application_proxy.cpp115
-rw-r--r--src/core/hle/service/am/application_proxy.h33
-rw-r--r--src/core/hle/service/am/audio_controller.cpp91
-rw-r--r--src/core/hle/service/am/audio_controller.h36
-rw-r--r--src/core/hle/service/am/common_state_getter.cpp314
-rw-r--r--src/core/hle/service/am/common_state_getter.h77
-rw-r--r--src/core/hle/service/am/debug_functions.cpp44
-rw-r--r--src/core/hle/service/am/display_controller.cpp135
-rw-r--r--src/core/hle/service/am/display_controller.h30
-rw-r--r--src/core/hle/service/am/display_layer_manager.cpp151
-rw-r--r--src/core/hle/service/am/display_layer_manager.h62
-rw-r--r--src/core/hle/service/am/frontend/applet_cabinet.cpp2
-rw-r--r--src/core/hle/service/am/frontend/applet_controller.cpp2
-rw-r--r--src/core/hle/service/am/frontend/applet_error.cpp2
-rw-r--r--src/core/hle/service/am/frontend/applet_general.cpp2
-rw-r--r--src/core/hle/service/am/frontend/applet_mii_edit.cpp2
-rw-r--r--src/core/hle/service/am/frontend/applet_profile_select.cpp2
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard.cpp14
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard.h2
-rw-r--r--src/core/hle/service/am/frontend/applet_web_browser.cpp4
-rw-r--r--src/core/hle/service/am/frontend/applets.cpp5
-rw-r--r--src/core/hle/service/am/global_state_controller.cpp34
-rw-r--r--src/core/hle/service/am/global_state_controller.h16
-rw-r--r--src/core/hle/service/am/home_menu_functions.cpp57
-rw-r--r--src/core/hle/service/am/home_menu_functions.h25
-rw-r--r--src/core/hle/service/am/idle.cpp25
-rw-r--r--src/core/hle/service/am/idle.h20
-rw-r--r--src/core/hle/service/am/library_applet_accessor.cpp202
-rw-r--r--src/core/hle/service/am/library_applet_accessor.h43
-rw-r--r--src/core/hle/service/am/library_applet_creator.cpp271
-rw-r--r--src/core/hle/service/am/library_applet_creator.h26
-rw-r--r--src/core/hle/service/am/library_applet_proxy.cpp143
-rw-r--r--src/core/hle/service/am/library_applet_proxy.h36
-rw-r--r--src/core/hle/service/am/library_applet_self_accessor.cpp338
-rw-r--r--src/core/hle/service/am/library_applet_self_accessor.h44
-rw-r--r--src/core/hle/service/am/library_applet_storage.cpp4
-rw-r--r--src/core/hle/service/am/lock_accessor.cpp71
-rw-r--r--src/core/hle/service/am/lock_accessor.h28
-rw-r--r--src/core/hle/service/am/managed_layer_holder.cpp59
-rw-r--r--src/core/hle/service/am/managed_layer_holder.h32
-rw-r--r--src/core/hle/service/am/omm.cpp48
-rw-r--r--src/core/hle/service/am/omm.h20
-rw-r--r--src/core/hle/service/am/process.cpp27
-rw-r--r--src/core/hle/service/am/process.h2
-rw-r--r--src/core/hle/service/am/process_winding_controller.cpp56
-rw-r--r--src/core/hle/service/am/process_winding_controller.h24
-rw-r--r--src/core/hle/service/am/self_controller.cpp456
-rw-r--r--src/core/hle/service/am/self_controller.h58
-rw-r--r--src/core/hle/service/am/service/all_system_applet_proxies_service.cpp79
-rw-r--r--src/core/hle/service/am/service/all_system_applet_proxies_service.h41
-rw-r--r--src/core/hle/service/am/service/applet_common_functions.cpp63
-rw-r--r--src/core/hle/service/am/service/applet_common_functions.h26
-rw-r--r--src/core/hle/service/am/service/application_accessor.cpp138
-rw-r--r--src/core/hle/service/am/service/application_accessor.h40
-rw-r--r--src/core/hle/service/am/service/application_creator.cpp35
-rw-r--r--src/core/hle/service/am/service/application_creator.h23
-rw-r--r--src/core/hle/service/am/service/application_functions.cpp485
-rw-r--r--src/core/hle/service/am/service/application_functions.h84
-rw-r--r--src/core/hle/service/am/service/application_proxy.cpp105
-rw-r--r--src/core/hle/service/am/service/application_proxy.h47
-rw-r--r--src/core/hle/service/am/service/application_proxy_service.cpp42
-rw-r--r--src/core/hle/service/am/service/application_proxy_service.h30
-rw-r--r--src/core/hle/service/am/service/audio_controller.cpp69
-rw-r--r--src/core/hle/service/am/service/audio_controller.h37
-rw-r--r--src/core/hle/service/am/service/common_state_getter.cpp278
-rw-r--r--src/core/hle/service/am/service/common_state_getter.h61
-rw-r--r--src/core/hle/service/am/service/cradle_firmware_updater.cpp52
-rw-r--r--src/core/hle/service/am/service/cradle_firmware_updater.h37
-rw-r--r--src/core/hle/service/am/service/debug_functions.cpp43
-rw-r--r--src/core/hle/service/am/service/debug_functions.h (renamed from src/core/hle/service/am/debug_functions.h)0
-rw-r--r--src/core/hle/service/am/service/display_controller.cpp105
-rw-r--r--src/core/hle/service/am/service/display_controller.h36
-rw-r--r--src/core/hle/service/am/service/global_state_controller.cpp61
-rw-r--r--src/core/hle/service/am/service/global_state_controller.h31
-rw-r--r--src/core/hle/service/am/service/home_menu_functions.cpp74
-rw-r--r--src/core/hle/service/am/service/home_menu_functions.h34
-rw-r--r--src/core/hle/service/am/service/library_applet_accessor.cpp157
-rw-r--r--src/core/hle/service/am/service/library_applet_accessor.h45
-rw-r--r--src/core/hle/service/am/service/library_applet_creator.cpp268
-rw-r--r--src/core/hle/service/am/service/library_applet_creator.h35
-rw-r--r--src/core/hle/service/am/service/library_applet_proxy.cpp132
-rw-r--r--src/core/hle/service/am/service/library_applet_proxy.h54
-rw-r--r--src/core/hle/service/am/service/library_applet_self_accessor.cpp325
-rw-r--r--src/core/hle/service/am/service/library_applet_self_accessor.h83
-rw-r--r--src/core/hle/service/am/service/lock_accessor.cpp75
-rw-r--r--src/core/hle/service/am/service/lock_accessor.h32
-rw-r--r--src/core/hle/service/am/service/process_winding_controller.cpp54
-rw-r--r--src/core/hle/service/am/service/process_winding_controller.h28
-rw-r--r--src/core/hle/service/am/service/self_controller.cpp394
-rw-r--r--src/core/hle/service/am/service/self_controller.h71
-rw-r--r--src/core/hle/service/am/service/storage.cpp48
-rw-r--r--src/core/hle/service/am/service/storage.h35
-rw-r--r--src/core/hle/service/am/service/storage_accessor.cpp68
-rw-r--r--src/core/hle/service/am/service/storage_accessor.h38
-rw-r--r--src/core/hle/service/am/service/system_applet_proxy.cpp131
-rw-r--r--src/core/hle/service/am/service/system_applet_proxy.h53
-rw-r--r--src/core/hle/service/am/service/window_controller.cpp86
-rw-r--r--src/core/hle/service/am/service/window_controller.h30
-rw-r--r--src/core/hle/service/am/spsm.cpp31
-rw-r--r--src/core/hle/service/am/spsm.h20
-rw-r--r--src/core/hle/service/am/storage.cpp59
-rw-r--r--src/core/hle/service/am/storage.h31
-rw-r--r--src/core/hle/service/am/storage_accessor.cpp90
-rw-r--r--src/core/hle/service/am/storage_accessor.h37
-rw-r--r--src/core/hle/service/am/system_applet_proxy.cpp136
-rw-r--r--src/core/hle/service/am/system_applet_proxy.h36
-rw-r--r--src/core/hle/service/am/system_buffer_manager.cpp69
-rw-r--r--src/core/hle/service/am/system_buffer_manager.h51
-rw-r--r--src/core/hle/service/am/window_controller.cpp86
-rw-r--r--src/core/hle/service/am/window_controller.h27
-rw-r--r--src/core/hle/service/audio/audctl.cpp201
-rw-r--r--src/core/hle/service/audio/audctl.h50
-rw-r--r--src/core/hle/service/audio/audio.cpp4
-rw-r--r--src/core/hle/service/audio/audio_controller.cpp174
-rw-r--r--src/core/hle/service/audio/audio_controller.h58
-rw-r--r--src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp2
-rw-r--r--src/core/hle/service/bcat/news/news_database_service.cpp24
-rw-r--r--src/core/hle/service/bcat/news/news_database_service.h8
-rw-r--r--src/core/hle/service/bcat/news/news_service.cpp17
-rw-r--r--src/core/hle/service/bcat/news/news_service.h4
-rw-r--r--src/core/hle/service/bcat/news/overwrite_event_holder.cpp2
-rw-r--r--src/core/hle/service/bcat/news/service_creator.cpp10
-rw-r--r--src/core/hle/service/btm/btm.cpp272
-rw-r--r--src/core/hle/service/btm/btm.h4
-rw-r--r--src/core/hle/service/btm/btm_debug.cpp33
-rw-r--r--src/core/hle/service/btm/btm_debug.h21
-rw-r--r--src/core/hle/service/btm/btm_system.cpp31
-rw-r--r--src/core/hle/service/btm/btm_system.h25
-rw-r--r--src/core/hle/service/btm/btm_system_core.cpp127
-rw-r--r--src/core/hle/service/btm/btm_system_core.h60
-rw-r--r--src/core/hle/service/btm/btm_user.cpp30
-rw-r--r--src/core/hle/service/btm/btm_user.h25
-rw-r--r--src/core/hle/service/btm/btm_user_core.cpp103
-rw-r--r--src/core/hle/service/btm/btm_user_core.h47
-rw-r--r--src/core/hle/service/caps/caps_a.cpp11
-rw-r--r--src/core/hle/service/caps/caps_a.h3
-rw-r--r--src/core/hle/service/erpt/erpt.cpp12
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.cpp9
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.h1
-rw-r--r--src/core/hle/service/glue/time/manager.cpp47
-rw-r--r--src/core/hle/service/glue/time/manager.h1
-rw-r--r--src/core/hle/service/glue/time/static.cpp58
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp36
-rw-r--r--src/core/hle/service/glue/time/worker.cpp2
-rw-r--r--src/core/hle/service/ldn/lan_discovery.cpp20
-rw-r--r--src/core/hle/service/ldn/lan_discovery.h5
-rw-r--r--src/core/hle/service/ldn/ldn.cpp802
-rw-r--r--src/core/hle/service/ldn/ldn.h6
-rw-r--r--src/core/hle/service/ldn/ldn_types.h68
-rw-r--r--src/core/hle/service/ldn/monitor_service.cpp43
-rw-r--r--src/core/hle/service/ldn/monitor_service.h28
-rw-r--r--src/core/hle/service/ldn/sf_monitor_service.cpp40
-rw-r--r--src/core/hle/service/ldn/sf_monitor_service.h26
-rw-r--r--src/core/hle/service/ldn/sf_service.cpp37
-rw-r--r--src/core/hle/service/ldn/sf_service.h21
-rw-r--r--src/core/hle/service/ldn/sf_service_monitor.cpp50
-rw-r--r--src/core/hle/service/ldn/sf_service_monitor.h26
-rw-r--r--src/core/hle/service/ldn/system_local_communication_service.cpp56
-rw-r--r--src/core/hle/service/ldn/system_local_communication_service.h25
-rw-r--r--src/core/hle/service/ldn/user_local_communication_service.cpp320
-rw-r--r--src/core/hle/service/ldn/user_local_communication_service.h103
-rw-r--r--src/core/hle/service/mii/mii.cpp9
-rw-r--r--src/core/hle/service/ns/account_proxy_interface.cpp21
-rw-r--r--src/core/hle/service/ns/account_proxy_interface.h16
-rw-r--r--src/core/hle/service/ns/application_manager_interface.cpp519
-rw-r--r--src/core/hle/service/ns/application_manager_interface.h62
-rw-r--r--src/core/hle/service/ns/application_version_interface.cpp33
-rw-r--r--src/core/hle/service/ns/application_version_interface.h16
-rw-r--r--src/core/hle/service/ns/content_management_interface.cpp72
-rw-r--r--src/core/hle/service/ns/content_management_interface.h25
-rw-r--r--src/core/hle/service/ns/develop_interface.cpp38
-rw-r--r--src/core/hle/service/ns/develop_interface.h16
-rw-r--r--src/core/hle/service/ns/document_interface.cpp38
-rw-r--r--src/core/hle/service/ns/document_interface.h22
-rw-r--r--src/core/hle/service/ns/download_task_interface.cpp39
-rw-r--r--src/core/hle/service/ns/download_task_interface.h20
-rw-r--r--src/core/hle/service/ns/dynamic_rights_interface.cpp62
-rw-r--r--src/core/hle/service/ns/dynamic_rights_interface.h22
-rw-r--r--src/core/hle/service/ns/ecommerce_interface.cpp27
-rw-r--r--src/core/hle/service/ns/ecommerce_interface.h16
-rw-r--r--src/core/hle/service/ns/factory_reset_interface.cpp27
-rw-r--r--src/core/hle/service/ns/factory_reset_interface.h16
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.cpp305
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.h58
-rw-r--r--src/core/hle/service/ns/ns.cpp903
-rw-r--r--src/core/hle/service/ns/ns.h133
-rw-r--r--src/core/hle/service/ns/ns_results.h (renamed from src/core/hle/service/ns/errors.h)0
-rw-r--r--src/core/hle/service/ns/ns_types.h111
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp67
-rw-r--r--src/core/hle/service/ns/pdm_qry.h32
-rw-r--r--src/core/hle/service/ns/platform_service_manager.cpp273
-rw-r--r--src/core/hle/service/ns/platform_service_manager.h79
-rw-r--r--src/core/hle/service/ns/query_service.cpp57
-rw-r--r--src/core/hle/service/ns/query_service.h36
-rw-r--r--src/core/hle/service/ns/read_only_application_control_data_interface.cpp122
-rw-r--r--src/core/hle/service/ns/read_only_application_control_data_interface.h30
-rw-r--r--src/core/hle/service/ns/read_only_application_record_interface.cpp38
-rw-r--r--src/core/hle/service/ns/read_only_application_record_interface.h22
-rw-r--r--src/core/hle/service/ns/service_getter_interface.cpp120
-rw-r--r--src/core/hle/service/ns/service_getter_interface.h47
-rw-r--r--src/core/hle/service/ns/system_update_control.cpp44
-rw-r--r--src/core/hle/service/ns/system_update_control.h16
-rw-r--r--src/core/hle/service/ns/system_update_interface.cpp61
-rw-r--r--src/core/hle/service/ns/system_update_interface.h38
-rw-r--r--src/core/hle/service/ns/vulnerability_manager_interface.cpp31
-rw-r--r--src/core/hle/service/ns/vulnerability_manager_interface.h21
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp7
-rw-r--r--src/core/hle/service/nvdrv/core/container.h1
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp17
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp4
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp3
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h9
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp10
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h4
-rw-r--r--src/core/hle/service/nvnflinger/binder.h24
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item_consumer.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item_consumer.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp76
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.h10
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp37
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.h7
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.h4
-rw-r--r--src/core/hle/service/nvnflinger/display.h53
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp345
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.h68
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.cpp66
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.h20
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver.cpp66
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver.h46
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp22
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver_server.h16
-rw-r--r--src/core/hle/service/nvnflinger/hwc_layer.h13
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp332
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h164
-rw-r--r--src/core/hle/service/nvnflinger/surface_flinger.cpp139
-rw-r--r--src/core/hle/service/nvnflinger/surface_flinger.h69
-rw-r--r--src/core/hle/service/omm/omm.cpp22
-rw-r--r--src/core/hle/service/omm/omm.h14
-rw-r--r--src/core/hle/service/omm/operation_mode_manager.cpp49
-rw-r--r--src/core/hle/service/omm/operation_mode_manager.h20
-rw-r--r--src/core/hle/service/omm/policy_manager_system.cpp26
-rw-r--r--src/core/hle/service/omm/policy_manager_system.h20
-rw-r--r--src/core/hle/service/omm/power_state_interface.cpp32
-rw-r--r--src/core/hle/service/omm/power_state_interface.h20
-rw-r--r--src/core/hle/service/psc/time/static.cpp33
-rw-r--r--src/core/hle/service/psc/time/steady_clock.cpp25
-rw-r--r--src/core/hle/service/psc/time/system_clock.cpp8
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.cpp32
-rw-r--r--src/core/hle/service/server_manager.cpp8
-rw-r--r--src/core/hle/service/service.cpp134
-rw-r--r--src/core/hle/service/service.h21
-rw-r--r--src/core/hle/service/services.cpp136
-rw-r--r--src/core/hle/service/services.h22
-rw-r--r--src/core/hle/service/set/key_code_map.h973
-rw-r--r--src/core/hle/service/set/private_settings.h72
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.h4
-rw-r--r--src/core/hle/service/set/settings_server.cpp258
-rw-r--r--src/core/hle/service/set/settings_server.h37
-rw-r--r--src/core/hle/service/set/settings_types.h29
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp1394
-rw-r--r--src/core/hle/service/set/system_settings_server.h208
-rw-r--r--src/core/hle/service/vi/application_display_service.cpp302
-rw-r--r--src/core/hle/service/vi/application_display_service.h81
-rw-r--r--src/core/hle/service/vi/application_root_service.cpp33
-rw-r--r--src/core/hle/service/vi/application_root_service.h33
-rw-r--r--src/core/hle/service/vi/conductor.cpp114
-rw-r--r--src/core/hle/service/vi/conductor.h57
-rw-r--r--src/core/hle/service/vi/container.cpp226
-rw-r--r--src/core/hle/service/vi/container.h89
-rw-r--r--src/core/hle/service/vi/display.h44
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp143
-rw-r--r--src/core/hle/service/vi/display/vi_display.h143
-rw-r--r--src/core/hle/service/vi/display_list.h83
-rw-r--r--src/core/hle/service/vi/layer.h81
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp16
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h105
-rw-r--r--src/core/hle/service/vi/layer_list.h71
-rw-r--r--src/core/hle/service/vi/manager_display_service.cpp140
-rw-r--r--src/core/hle/service/vi/manager_display_service.h37
-rw-r--r--src/core/hle/service/vi/manager_root_service.cpp38
-rw-r--r--src/core/hle/service/vi/manager_root_service.h32
-rw-r--r--src/core/hle/service/vi/service_creator.cpp38
-rw-r--r--src/core/hle/service/vi/service_creator.h28
-rw-r--r--src/core/hle/service/vi/shared_buffer_manager.cpp431
-rw-r--r--src/core/hle/service/vi/shared_buffer_manager.h92
-rw-r--r--src/core/hle/service/vi/system_display_service.cpp169
-rw-r--r--src/core/hle/service/vi/system_display_service.h47
-rw-r--r--src/core/hle/service/vi/system_root_service.cpp32
-rw-r--r--src/core/hle/service/vi/system_root_service.h32
-rw-r--r--src/core/hle/service/vi/vi.cpp974
-rw-r--r--src/core/hle/service/vi/vi.h42
-rw-r--r--src/core/hle/service/vi/vi_m.cpp34
-rw-r--r--src/core/hle/service/vi/vi_m.h32
-rw-r--r--src/core/hle/service/vi/vi_s.cpp30
-rw-r--r--src/core/hle/service/vi/vi_s.h32
-rw-r--r--src/core/hle/service/vi/vi_types.h92
-rw-r--r--src/core/hle/service/vi/vi_u.cpp30
-rw-r--r--src/core/hle/service/vi/vi_u.h32
-rw-r--r--src/core/hle/service/vi/vsync_manager.cpp26
-rw-r--r--src/core/hle/service/vi/vsync_manager.h29
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/memory.cpp8
-rw-r--r--src/core/memory/cheat_engine.cpp4
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp4
-rw-r--r--src/frontend_common/config.cpp19
-rw-r--r--src/frontend_common/config.h2
-rw-r--r--src/frontend_common/content_manager.h5
-rw-r--r--src/hid_core/frontend/emulated_controller.cpp66
-rw-r--r--src/hid_core/frontend/emulated_controller.h3
-rw-r--r--src/hid_core/resources/hid_firmware_settings.cpp37
-rw-r--r--src/hid_core/resources/npad/npad_vibration.cpp6
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_resource.cpp2
-rw-r--r--src/input_common/CMakeLists.txt10
-rw-r--r--src/input_common/drivers/android.cpp324
-rw-r--r--src/input_common/drivers/android.h124
-rw-r--r--src/input_common/helpers/joycon_driver.cpp4
-rw-r--r--src/input_common/main.cpp28
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp47
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h4
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h2
-rw-r--r--src/video_core/capture.h36
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/fence_manager.h4
-rw-r--r--src/video_core/framebuffer_config.h7
-rw-r--r--src/video_core/gpu.cpp15
-rw-r--r--src/video_core/gpu.h2
-rw-r--r--src/video_core/gpu_thread.cpp4
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp4
-rw-r--r--src/video_core/host_shaders/fidelityfx_fsr.frag21
-rw-r--r--src/video_core/host_shaders/fxaa.frag2
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr.frag19
-rw-r--r--src/video_core/host_shaders/opengl_present.frag2
-rw-r--r--src/video_core/host_shaders/present_bicubic.frag2
-rw-r--r--src/video_core/host_shaders/present_gaussian.frag14
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag2
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag2
-rw-r--r--src/video_core/macro/macro_hle.cpp8
-rw-r--r--src/video_core/present.h37
-rw-r--r--src/video_core/renderer_base.h3
-rw-r--r--src/video_core/renderer_null/renderer_null.cpp5
-rw-r--r--src/video_core/renderer_null/renderer_null.h2
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.h7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/present/layer.cpp35
-rw-r--r--src/video_core/renderer_opengl/present/layer.h8
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.cpp19
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp89
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h9
-rw-r--r--src/video_core/renderer_vulkan/present/layer.cpp23
-rw-r--r--src/video_core/renderer_vulkan/present/layer.h6
-rw-r--r--src/video_core/renderer_vulkan/present/util.cpp92
-rw-r--r--src/video_core/renderer_vulkan/present/util.h9
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.cpp29
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.h6
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp118
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp8
-rw-r--r--src/video_core/texture_cache/image_info.cpp1
-rw-r--r--src/video_core/texture_cache/image_info.h1
-rw-r--r--src/video_core/texture_cache/texture_cache.h23
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/configure_applets.cpp86
-rw-r--r--src/yuzu/configuration/configure_applets.h48
-rw-r--r--src/yuzu/configuration/configure_applets.ui65
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_dialog.h2
-rw-r--r--src/yuzu/configuration/configure_input.cpp3
-rw-r--r--src/yuzu/configuration/qt_config.cpp1
-rw-r--r--src/yuzu/configuration/shared_translation.cpp180
-rw-r--r--src/yuzu/hotkeys.cpp6
-rw-r--r--src/yuzu/main.cpp251
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/main.ui24
-rw-r--r--src/yuzu_cmd/sdl_config.cpp1
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
578 files changed, 23258 insertions, 14615 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 7890b30ca..b037fc055 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<application
android:name="org.yuzu.yuzu_emu.YuzuApplication"
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 6ebb46af7..02a20dacf 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
@@ -3,24 +3,21 @@
package org.yuzu.yuzu_emu
-import android.app.Dialog
import android.content.DialogInterface
import android.net.Uri
-import android.os.Bundle
import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.Surface
import android.view.View
import android.widget.TextView
import androidx.annotation.Keep
-import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
+import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.Log
-import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
import org.yuzu.yuzu_emu.model.InstallResult
import org.yuzu.yuzu_emu.model.Patch
import org.yuzu.yuzu_emu.model.GameVerificationResult
@@ -30,34 +27,6 @@ import org.yuzu.yuzu_emu.model.GameVerificationResult
* with the native side of the Yuzu code.
*/
object NativeLibrary {
- /**
- * Default controller id for each device
- */
- const val Player1Device = 0
- const val Player2Device = 1
- const val Player3Device = 2
- const val Player4Device = 3
- const val Player5Device = 4
- const val Player6Device = 5
- const val Player7Device = 6
- const val Player8Device = 7
- const val ConsoleDevice = 8
-
- /**
- * Controller type for each device
- */
- const val ProController = 3
- const val Handheld = 4
- const val JoyconDual = 5
- const val JoyconLeft = 6
- const val JoyconRight = 7
- const val GameCube = 8
- const val Pokeball = 9
- const val NES = 10
- const val SNES = 11
- const val N64 = 12
- const val SegaGenesis = 13
-
@JvmField
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
@@ -127,112 +96,6 @@ object NativeLibrary {
FileUtil.getFilename(Uri.parse(path))
}
- /**
- * Returns true if pro controller isn't available and handheld is
- */
- external fun isHandheldOnly(): Boolean
-
- /**
- * Changes controller type for a specific device.
- *
- * @param Device The input descriptor of the gamepad.
- * @param Type The NpadStyleIndex of the gamepad.
- */
- external fun setDeviceType(Device: Int, Type: Int): Boolean
-
- /**
- * Handles event when a gamepad is connected.
- *
- * @param Device The input descriptor of the gamepad.
- */
- external fun onGamePadConnectEvent(Device: Int): Boolean
-
- /**
- * Handles event when a gamepad is disconnected.
- *
- * @param Device The input descriptor of the gamepad.
- */
- external fun onGamePadDisconnectEvent(Device: Int): Boolean
-
- /**
- * Handles button press events for a gamepad.
- *
- * @param Device The input descriptor of the gamepad.
- * @param Button Key code identifying which button was pressed.
- * @param Action Mask identifying which action is happening (button pressed down, or button released).
- * @return If we handled the button press.
- */
- external fun onGamePadButtonEvent(Device: Int, Button: Int, Action: Int): Boolean
-
- /**
- * Handles joystick movement events.
- *
- * @param Device The device ID of the gamepad.
- * @param Axis The axis ID
- * @param x_axis The value of the x-axis represented by the given ID.
- * @param y_axis The value of the y-axis represented by the given ID.
- */
- external fun onGamePadJoystickEvent(
- Device: Int,
- Axis: Int,
- x_axis: Float,
- y_axis: Float
- ): Boolean
-
- /**
- * Handles motion events.
- *
- * @param delta_timestamp The finger id corresponding to this event
- * @param gyro_x,gyro_y,gyro_z The value of the accelerometer sensor.
- * @param accel_x,accel_y,accel_z The value of the y-axis
- */
- external fun onGamePadMotionEvent(
- Device: Int,
- delta_timestamp: Long,
- gyro_x: Float,
- gyro_y: Float,
- gyro_z: Float,
- accel_x: Float,
- accel_y: Float,
- accel_z: Float
- ): Boolean
-
- /**
- * Signals and load a nfc tag
- *
- * @param data Byte array containing all the data from a nfc tag
- */
- external fun onReadNfcTag(data: ByteArray?): Boolean
-
- /**
- * Removes current loaded nfc tag
- */
- external fun onRemoveNfcTag(): Boolean
-
- /**
- * Handles touch press events.
- *
- * @param finger_id The finger id corresponding to this event
- * @param x_axis The value of the x-axis.
- * @param y_axis The value of the y-axis.
- */
- external fun onTouchPressed(finger_id: Int, x_axis: Float, y_axis: Float)
-
- /**
- * Handles touch movement.
- *
- * @param x_axis The value of the instantaneous x-axis.
- * @param y_axis The value of the instantaneous y-axis.
- */
- external fun onTouchMoved(finger_id: Int, x_axis: Float, y_axis: Float)
-
- /**
- * Handles touch release events.
- *
- * @param finger_id The finger id corresponding to this event
- */
- external fun onTouchReleased(finger_id: Int)
-
external fun setAppDirectory(directory: String)
/**
@@ -318,46 +181,13 @@ object NativeLibrary {
ErrorUnknown
}
- private var coreErrorAlertResult = false
- private val coreErrorAlertLock = Object()
-
- class CoreErrorDialogFragment : DialogFragment() {
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val title = requireArguments().serializable<String>("title")
- val message = requireArguments().serializable<String>("message")
-
- return MaterialAlertDialogBuilder(requireActivity())
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(R.string.continue_button, null)
- .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
- coreErrorAlertResult = false
- synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
- }
- .create()
- }
-
- override fun onDismiss(dialog: DialogInterface) {
- coreErrorAlertResult = true
- synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
- }
-
- companion object {
- fun newInstance(title: String?, message: String?): CoreErrorDialogFragment {
- val frag = CoreErrorDialogFragment()
- val args = Bundle()
- args.putString("title", title)
- args.putString("message", message)
- frag.arguments = args
- return frag
- }
- }
- }
+ var coreErrorAlertResult = false
+ val coreErrorAlertLock = Object()
private fun onCoreErrorImpl(title: String, message: String) {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
- error("[NativeLibrary] EmulationActivity not present")
+ Log.error("[NativeLibrary] EmulationActivity not present")
return
}
@@ -373,7 +203,7 @@ object NativeLibrary {
fun onCoreError(error: CoreError?, details: String): Boolean {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
- error("[NativeLibrary] EmulationActivity not present")
+ Log.error("[NativeLibrary] EmulationActivity not present")
return false
}
@@ -404,7 +234,7 @@ object NativeLibrary {
}
// Show the AlertDialog on the main thread.
- emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) })
+ emulationActivity.runOnUiThread { onCoreErrorImpl(title, message) }
// Wait for the lock to notify that it is complete.
synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }
@@ -629,46 +459,4 @@ object NativeLibrary {
* Checks if all necessary keys are present for decryption
*/
external fun areKeysPresent(): Boolean
-
- /**
- * Button type for use in onTouchEvent
- */
- object ButtonType {
- const val BUTTON_A = 0
- const val BUTTON_B = 1
- const val BUTTON_X = 2
- const val BUTTON_Y = 3
- const val STICK_L = 4
- const val STICK_R = 5
- const val TRIGGER_L = 6
- const val TRIGGER_R = 7
- const val TRIGGER_ZL = 8
- const val TRIGGER_ZR = 9
- const val BUTTON_PLUS = 10
- const val BUTTON_MINUS = 11
- const val DPAD_LEFT = 12
- const val DPAD_UP = 13
- const val DPAD_RIGHT = 14
- const val DPAD_DOWN = 15
- const val BUTTON_SL = 16
- const val BUTTON_SR = 17
- const val BUTTON_HOME = 18
- const val BUTTON_CAPTURE = 19
- }
-
- /**
- * Stick type for use in onTouchEvent
- */
- object StickType {
- const val STICK_L = 0
- const val STICK_R = 1
- }
-
- /**
- * Button states
- */
- object ButtonState {
- const val RELEASED = 0
- const val PRESSED = 1
- }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 76778c10a..72943f33e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -7,6 +7,7 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
+import org.yuzu.yuzu_emu.features.input.NativeInput
import java.io.File
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
@@ -37,6 +38,7 @@ class YuzuApplication : Application() {
documentsTree = DocumentsTree()
DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters()
+ NativeInput.reloadInputDevices()
NativeLibrary.logDeviceInfo()
Log.logDeviceInfo()
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 7a8d03610..c962558a7 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
@@ -39,6 +39,7 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
@@ -47,7 +48,9 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil
+import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
+import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.ThemeHelper
import java.text.NumberFormat
import kotlin.math.roundToInt
@@ -63,8 +66,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var motionTimestamp: Long = 0
private var flipMotionOrientation: Boolean = false
- private var controllerIds = InputHandler.getGameControllerIds()
-
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
private val actionMute = "ACTION_EMULATOR_MUTE"
@@ -78,6 +79,33 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onCreate(savedInstanceState)
+ InputHandler.updateControllerData()
+ val players = NativeConfig.getInputSettings(true)
+ var hasConfiguredControllers = false
+ players.forEach {
+ if (it.hasMapping()) {
+ hasConfiguredControllers = true
+ }
+ }
+ if (!hasConfiguredControllers && InputHandler.androidControllers.isNotEmpty()) {
+ var params: ParamPackage? = null
+ for (controller in InputHandler.registeredControllers) {
+ if (controller.get("port", -1) == 0) {
+ params = controller
+ break
+ }
+ }
+
+ if (params != null) {
+ NativeInput.updateMappingsWithDefault(
+ 0,
+ params,
+ params.get("display", getString(R.string.unknown))
+ )
+ NativeConfig.saveGlobalConfig()
+ }
+ }
+
binding = ActivityEmulationBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -95,8 +123,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader = NfcReader(this)
nfcReader.initialize()
- InputHandler.initialize()
-
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
@@ -147,7 +173,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
- InputHandler.updateControllerIds()
+ InputHandler.updateControllerData()
buildPictureInPictureParams()
}
@@ -172,6 +198,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onNewIntent(intent)
setIntent(intent)
nfcReader.onNewIntent(intent)
+ InputHandler.updateControllerData()
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
@@ -244,8 +271,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
}
val deltaTimestamp = (event.timestamp - motionTimestamp) / 1000
motionTimestamp = event.timestamp
- NativeLibrary.onGamePadMotionEvent(
- NativeLibrary.Player1Device,
+ NativeInput.onDeviceMotionEvent(
+ NativeInput.Player1Device,
deltaTimestamp,
gyro[0],
gyro[1],
@@ -254,8 +281,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
accel[1],
accel[2]
)
- NativeLibrary.onGamePadMotionEvent(
- NativeLibrary.ConsoleDevice,
+ NativeInput.onDeviceMotionEvent(
+ NativeInput.ConsoleDevice,
deltaTimestamp,
gyro[0],
gyro[1],
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 f218c76ef..50663ad91 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
@@ -3,15 +3,15 @@
package org.yuzu.yuzu_emu.adapters
-import android.text.TextUtils
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver
import org.yuzu.yuzu_emu.model.DriverViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class DriverAdapter(private val driverViewModel: DriverViewModel) :
@@ -44,25 +44,15 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
}
// Delay marquee by 3s
- title.postDelayed(
- {
- title.isSelected = true
- title.ellipsize = TextUtils.TruncateAt.MARQUEE
- version.isSelected = true
- version.ellipsize = TextUtils.TruncateAt.MARQUEE
- description.isSelected = true
- description.ellipsize = TextUtils.TruncateAt.MARQUEE
- },
- 3000
- )
+ title.marquee()
+ version.marquee()
+ description.marquee()
title.text = model.title
version.text = model.version
description.text = model.description
- if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) {
- buttonDelete.visibility = View.VISIBLE
- } else {
- buttonDelete.visibility = View.GONE
- }
+ buttonDelete.setVisible(
+ model.title != binding.root.context.getString(R.string.system_gpu_driver)
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
index 3d8f0bda8..5cbd15d2a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.adapters
import android.net.Uri
-import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
@@ -12,6 +11,7 @@ import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
@@ -29,13 +29,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
override fun bind(model: GameDir) {
binding.apply {
path.text = Uri.parse(model.uriString).path
- path.postDelayed(
- {
- path.isSelected = true
- path.ellipsize = TextUtils.TruncateAt.MARQUEE
- },
- 3000
- )
+ path.marquee()
buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(model)
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 85c8249e6..b1f247ac3 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
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.adapters
import android.net.Uri
-import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.GameIconUtils
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) :
@@ -44,14 +44,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
- binding.textGameTitle.postDelayed(
- {
- binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE
- binding.textGameTitle.isSelected = true
- },
- 3000
- )
-
+ binding.textGameTitle.marquee()
binding.cardGame.setOnClickListener { onClick(model) }
binding.cardGame.setOnLongClickListener { onLongClick(model) }
}
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
index 0046d5314..7366e2c77 100644
--- 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
@@ -3,21 +3,18 @@
package org.yuzu.yuzu_emu.adapters
-import android.text.TextUtils
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.InstallableProperty
import org.yuzu.yuzu_emu.model.SubmenuProperty
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter(
@@ -76,23 +73,15 @@ class GamePropertiesAdapter(
)
)
- binding.details.postDelayed({
- binding.details.isSelected = true
- binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE
- }, 3000)
-
+ binding.details.marquee()
if (submenuProperty.details != null) {
- binding.details.visibility = View.VISIBLE
+ binding.details.setVisible(true)
binding.details.text = submenuProperty.details.invoke()
} else if (submenuProperty.detailsFlow != null) {
- binding.details.visibility = View.VISIBLE
- viewLifecycle.lifecycleScope.launch {
- viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- submenuProperty.detailsFlow.collect { binding.details.text = it }
- }
- }
+ binding.details.setVisible(true)
+ submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
} else {
- binding.details.visibility = View.GONE
+ binding.details.setVisible(false)
}
}
}
@@ -112,14 +101,10 @@ class GamePropertiesAdapter(
)
)
- if (installableProperty.install != null) {
- binding.buttonInstall.visibility = View.VISIBLE
- binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() }
- }
- if (installableProperty.export != null) {
- binding.buttonExport.visibility = View.VISIBLE
- binding.buttonExport.setOnClickListener { installableProperty.export.invoke() }
- }
+ binding.buttonInstall.setVisible(installableProperty.install != null)
+ binding.buttonInstall.setOnClickListener { installableProperty.install?.invoke() }
+ binding.buttonExport.setVisible(installableProperty.export != null)
+ binding.buttonExport.setOnClickListener { installableProperty.export?.invoke() }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index b512845d5..0bd196673 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -3,22 +3,19 @@
package org.yuzu.yuzu_emu.adapters
-import android.text.TextUtils
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter(
@@ -59,18 +56,8 @@ class HomeSettingAdapter(
binding.optionIcon.alpha = 0.5f
}
- viewLifecycle.lifecycleScope.launch {
- viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
- model.details.collect { updateOptionDetails(it) }
- }
- }
- binding.optionDetail.postDelayed(
- {
- binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
- binding.optionDetail.isSelected = true
- },
- 3000
- )
+ model.details.collect(viewLifecycle) { updateOptionDetails(it) }
+ binding.optionDetail.marquee()
binding.root.setOnClickListener { onClick(model) }
}
@@ -90,7 +77,7 @@ class HomeSettingAdapter(
private fun updateOptionDetails(detailString: String) {
if (detailString.isNotEmpty()) {
binding.optionDetail.text = detailString
- binding.optionDetail.visibility = View.VISIBLE
+ binding.optionDetail.setVisible(true)
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
index 4218c4e52..1ba75fa2f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
@@ -4,10 +4,10 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class InstallableAdapter(installables: List<Installable>) :
@@ -26,14 +26,10 @@ class InstallableAdapter(installables: List<Installable>) :
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId)
- if (model.install != null) {
- binding.buttonInstall.visibility = View.VISIBLE
- binding.buttonInstall.setOnClickListener { model.install.invoke() }
- }
- if (model.export != null) {
- binding.buttonExport.visibility = View.VISIBLE
- binding.buttonExport.setOnClickListener { model.export.invoke() }
- }
+ binding.buttonInstall.setVisible(model.install != null)
+ binding.buttonInstall.setOnClickListener { model.install?.invoke() }
+ binding.buttonExport.setVisible(model.export != null)
+ binding.buttonExport.setOnClickListener { model.export?.invoke() }
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index 38bb1f96f..1379968f9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -4,12 +4,12 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
@@ -25,7 +25,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<Lic
binding.apply {
textSettingName.text = root.context.getString(model.titleId)
textSettingDescription.text = root.context.getString(model.descriptionId)
- textSettingValue.visibility = View.GONE
+ textSettingValue.setVisible(false)
root.setOnClickListener { onClick(model) }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 02118e1a8..a5f610b31 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.adapters
import android.text.Html
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
@@ -17,6 +16,7 @@ import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
@@ -30,8 +30,8 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
AbstractViewHolder<SetupPage>(binding), SetupCallback {
override fun bind(model: SetupPage) {
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
- binding.buttonAction.visibility = View.INVISIBLE
- binding.textConfirmation.visibility = View.VISIBLE
+ binding.buttonAction.setVisible(visible = false, gone = false)
+ binding.textConfirmation.setVisible(true)
}
binding.icon.setImageDrawable(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt
new file mode 100644
index 000000000..15d776311
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt
@@ -0,0 +1,416 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input
+
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.features.input.model.InputType
+import org.yuzu.yuzu_emu.features.input.model.ButtonName
+import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
+import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ParamPackage
+import android.view.InputDevice
+
+object NativeInput {
+ /**
+ * Default controller id for each device
+ */
+ const val Player1Device = 0
+ const val Player2Device = 1
+ const val Player3Device = 2
+ const val Player4Device = 3
+ const val Player5Device = 4
+ const val Player6Device = 5
+ const val Player7Device = 6
+ const val Player8Device = 7
+ const val ConsoleDevice = 8
+
+ /**
+ * Button states
+ */
+ object ButtonState {
+ const val RELEASED = 0
+ const val PRESSED = 1
+ }
+
+ /**
+ * Returns true if pro controller isn't available and handheld is.
+ * Intended to check where the input overlay should direct its inputs.
+ */
+ external fun isHandheldOnly(): Boolean
+
+ /**
+ * Handles button press events for a gamepad.
+ * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
+ * @param port Port determined by controller connection order.
+ * @param buttonId The Android Keycode corresponding to this event.
+ * @param action Mask identifying which action is happening (button pressed down, or button released).
+ */
+ external fun onGamePadButtonEvent(
+ guid: String,
+ port: Int,
+ buttonId: Int,
+ action: Int
+ )
+
+ /**
+ * Handles axis movement events.
+ * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
+ * @param port Port determined by controller connection order.
+ * @param axis The axis ID.
+ * @param value Value along the given axis.
+ */
+ external fun onGamePadAxisEvent(guid: String, port: Int, axis: Int, value: Float)
+
+ /**
+ * Handles motion events.
+ * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
+ * @param port Port determined by controller connection order.
+ * @param deltaTimestamp The finger id corresponding to this event.
+ * @param xGyro The value of the x-axis for the gyroscope.
+ * @param yGyro The value of the y-axis for the gyroscope.
+ * @param zGyro The value of the z-axis for the gyroscope.
+ * @param xAccel The value of the x-axis for the accelerometer.
+ * @param yAccel The value of the y-axis for the accelerometer.
+ * @param zAccel The value of the z-axis for the accelerometer.
+ */
+ external fun onGamePadMotionEvent(
+ guid: String,
+ port: Int,
+ deltaTimestamp: Long,
+ xGyro: Float,
+ yGyro: Float,
+ zGyro: Float,
+ xAccel: Float,
+ yAccel: Float,
+ zAccel: Float
+ )
+
+ /**
+ * Signals and load a nfc tag
+ * @param data Byte array containing all the data from a nfc tag.
+ */
+ external fun onReadNfcTag(data: ByteArray?)
+
+ /**
+ * Removes current loaded nfc tag.
+ */
+ external fun onRemoveNfcTag()
+
+ /**
+ * Handles touch press events.
+ * @param fingerId The finger id corresponding to this event.
+ * @param xAxis The value of the x-axis on the touchscreen.
+ * @param yAxis The value of the y-axis on the touchscreen.
+ */
+ external fun onTouchPressed(fingerId: Int, xAxis: Float, yAxis: Float)
+
+ /**
+ * Handles touch movement.
+ * @param fingerId The finger id corresponding to this event.
+ * @param xAxis The value of the x-axis on the touchscreen.
+ * @param yAxis The value of the y-axis on the touchscreen.
+ */
+ external fun onTouchMoved(fingerId: Int, xAxis: Float, yAxis: Float)
+
+ /**
+ * Handles touch release events.
+ * @param fingerId The finger id corresponding to this event
+ */
+ external fun onTouchReleased(fingerId: Int)
+
+ /**
+ * Sends a button input to the global virtual controllers.
+ * @param port Port determined by controller connection order.
+ * @param button The [NativeButton] corresponding to this event.
+ * @param action Mask identifying which action is happening (button pressed down, or button released).
+ */
+ fun onOverlayButtonEvent(port: Int, button: NativeButton, action: Int) =
+ onOverlayButtonEventImpl(port, button.int, action)
+
+ private external fun onOverlayButtonEventImpl(port: Int, buttonId: Int, action: Int)
+
+ /**
+ * Sends a joystick input to the global virtual controllers.
+ * @param port Port determined by controller connection order.
+ * @param stick The [NativeAnalog] corresponding to this event.
+ * @param xAxis Value along the X axis.
+ * @param yAxis Value along the Y axis.
+ */
+ fun onOverlayJoystickEvent(port: Int, stick: NativeAnalog, xAxis: Float, yAxis: Float) =
+ onOverlayJoystickEventImpl(port, stick.int, xAxis, yAxis)
+
+ private external fun onOverlayJoystickEventImpl(
+ port: Int,
+ stickId: Int,
+ xAxis: Float,
+ yAxis: Float
+ )
+
+ /**
+ * Handles motion events for the global virtual controllers.
+ * @param port Port determined by controller connection order
+ * @param deltaTimestamp The finger id corresponding to this event.
+ * @param xGyro The value of the x-axis for the gyroscope.
+ * @param yGyro The value of the y-axis for the gyroscope.
+ * @param zGyro The value of the z-axis for the gyroscope.
+ * @param xAccel The value of the x-axis for the accelerometer.
+ * @param yAccel The value of the y-axis for the accelerometer.
+ * @param zAccel The value of the z-axis for the accelerometer.
+ */
+ external fun onDeviceMotionEvent(
+ port: Int,
+ deltaTimestamp: Long,
+ xGyro: Float,
+ yGyro: Float,
+ zGyro: Float,
+ xAccel: Float,
+ yAccel: Float,
+ zAccel: Float
+ )
+
+ /**
+ * Reloads all input devices from the currently loaded Settings::values.players into HID Core
+ */
+ external fun reloadInputDevices()
+
+ /**
+ * Registers a controller to be used with mapping
+ * @param device An [InputDevice] or the input overlay wrapped with [YuzuInputDevice]
+ */
+ external fun registerController(device: YuzuInputDevice)
+
+ /**
+ * Gets the names of input devices that have been registered with the input subsystem via [registerController]
+ */
+ external fun getInputDevices(): Array<String>
+
+ /**
+ * Reads all input profiles from disk. Must be called before creating a profile picker.
+ */
+ external fun loadInputProfiles()
+
+ /**
+ * Gets the names of each available input profile.
+ */
+ external fun getInputProfileNames(): Array<String>
+
+ /**
+ * Checks if the user-provided name for an input profile is valid.
+ * @param name User-provided name for an input profile.
+ * @return Whether [name] is valid or not.
+ */
+ external fun isProfileNameValid(name: String): Boolean
+
+ /**
+ * Creates a new input profile.
+ * @param name The new profile's name.
+ * @param playerIndex Index of the player that's currently being edited. Used to write the profile
+ * name to this player's config.
+ * @return Whether creating the profile was successful or not.
+ */
+ external fun createProfile(name: String, playerIndex: Int): Boolean
+
+ /**
+ * Deletes an input profile.
+ * @param name Name of the profile to delete.
+ * @param playerIndex Index of the player that's currently being edited. Used to remove the profile
+ * name from this player's config if they have it loaded.
+ * @return Whether deleting this profile was successful or not.
+ */
+ external fun deleteProfile(name: String, playerIndex: Int): Boolean
+
+ /**
+ * Loads an input profile.
+ * @param name Name of the input profile to load.
+ * @param playerIndex Index of the player that will have this profile loaded.
+ * @return Whether loading this profile was successful or not.
+ */
+ external fun loadProfile(name: String, playerIndex: Int): Boolean
+
+ /**
+ * Saves an input profile.
+ * @param name Name of the profile to save.
+ * @param playerIndex Index of the player that's currently being edited. Used to write the profile
+ * name to this player's config.
+ * @return Whether saving the profile was successful or not.
+ */
+ external fun saveProfile(name: String, playerIndex: Int): Boolean
+
+ /**
+ * Intended to be used immediately before a call to [NativeConfig.saveControlPlayerValues]
+ * Must be used while per-game config is loaded.
+ */
+ external fun loadPerGameConfiguration(
+ playerIndex: Int,
+ selectedIndex: Int,
+ selectedProfileName: String
+ )
+
+ /**
+ * Tells the input subsystem to start listening for inputs to map.
+ * @param type Type of input to map as shown by the int property in each [InputType].
+ */
+ external fun beginMapping(type: Int)
+
+ /**
+ * Gets an input's [ParamPackage] as a serialized string. Used for input verification before mapping.
+ * Must be run after [beginMapping] and before [stopMapping].
+ */
+ external fun getNextInput(): String
+
+ /**
+ * Tells the input subsystem to stop listening for inputs to map.
+ */
+ external fun stopMapping()
+
+ /**
+ * Updates a controller's mappings with auto-mapping params.
+ * @param playerIndex Index of the player to auto-map.
+ * @param deviceParams [ParamPackage] representing the device to auto-map as received
+ * from [getInputDevices].
+ * @param displayName Name of the device to auto-map as received from the "display" param in [deviceParams].
+ * Intended to be a way to provide a default name for a controller if the "display" param is empty.
+ */
+ fun updateMappingsWithDefault(
+ playerIndex: Int,
+ deviceParams: ParamPackage,
+ displayName: String
+ ) = updateMappingsWithDefaultImpl(playerIndex, deviceParams.serialize(), displayName)
+
+ private external fun updateMappingsWithDefaultImpl(
+ playerIndex: Int,
+ deviceParams: String,
+ displayName: String
+ )
+
+ /**
+ * Gets the params for a specific button.
+ * @param playerIndex Index of the player to get params from.
+ * @param button The [NativeButton] to get params for.
+ * @return A [ParamPackage] representing a player's specific button.
+ */
+ fun getButtonParam(playerIndex: Int, button: NativeButton): ParamPackage =
+ ParamPackage(getButtonParamImpl(playerIndex, button.int))
+
+ private external fun getButtonParamImpl(playerIndex: Int, buttonId: Int): String
+
+ /**
+ * Sets the params for a specific button.
+ * @param playerIndex Index of the player to set params for.
+ * @param button The [NativeButton] to set params for.
+ * @param param A [ParamPackage] to set.
+ */
+ fun setButtonParam(playerIndex: Int, button: NativeButton, param: ParamPackage) =
+ setButtonParamImpl(playerIndex, button.int, param.serialize())
+
+ private external fun setButtonParamImpl(playerIndex: Int, buttonId: Int, param: String)
+
+ /**
+ * Gets the params for a specific stick.
+ * @param playerIndex Index of the player to get params from.
+ * @param stick The [NativeAnalog] to get params for.
+ * @return A [ParamPackage] representing a player's specific stick.
+ */
+ fun getStickParam(playerIndex: Int, stick: NativeAnalog): ParamPackage =
+ ParamPackage(getStickParamImpl(playerIndex, stick.int))
+
+ private external fun getStickParamImpl(playerIndex: Int, stickId: Int): String
+
+ /**
+ * Sets the params for a specific stick.
+ * @param playerIndex Index of the player to set params for.
+ * @param stick The [NativeAnalog] to set params for.
+ * @param param A [ParamPackage] to set.
+ */
+ fun setStickParam(playerIndex: Int, stick: NativeAnalog, param: ParamPackage) =
+ setStickParamImpl(playerIndex, stick.int, param.serialize())
+
+ private external fun setStickParamImpl(playerIndex: Int, stickId: Int, param: String)
+
+ /**
+ * Gets the int representation of a [ButtonName]. Tells you what to show as the mapped input for
+ * a button/analog/other.
+ * @param param A [ParamPackage] that represents a specific button's params.
+ * @return The [ButtonName] for [param].
+ */
+ fun getButtonName(param: ParamPackage): ButtonName =
+ ButtonName.from(getButtonNameImpl(param.serialize()))
+
+ private external fun getButtonNameImpl(param: String): Int
+
+ /**
+ * Gets each supported [NpadStyleIndex] for a given player.
+ * @param playerIndex Index of the player to get supported indexes for.
+ * @return List of each supported [NpadStyleIndex].
+ */
+ fun getSupportedStyleTags(playerIndex: Int): List<NpadStyleIndex> =
+ getSupportedStyleTagsImpl(playerIndex).map { NpadStyleIndex.from(it) }
+
+ private external fun getSupportedStyleTagsImpl(playerIndex: Int): IntArray
+
+ /**
+ * Gets the [NpadStyleIndex] for a given player.
+ * @param playerIndex Index of the player to get an [NpadStyleIndex] from.
+ * @return The [NpadStyleIndex] for a given player.
+ */
+ fun getStyleIndex(playerIndex: Int): NpadStyleIndex =
+ NpadStyleIndex.from(getStyleIndexImpl(playerIndex))
+
+ private external fun getStyleIndexImpl(playerIndex: Int): Int
+
+ /**
+ * Sets the [NpadStyleIndex] for a given player.
+ * @param playerIndex Index of the player to change.
+ * @param style The new style to set.
+ */
+ fun setStyleIndex(playerIndex: Int, style: NpadStyleIndex) =
+ setStyleIndexImpl(playerIndex, style.int)
+
+ private external fun setStyleIndexImpl(playerIndex: Int, styleIndex: Int)
+
+ /**
+ * Checks if a device is a controller.
+ * @param params [ParamPackage] for an input device retrieved from [getInputDevices]
+ * @return Whether the device is a controller or not.
+ */
+ fun isController(params: ParamPackage): Boolean = isControllerImpl(params.serialize())
+
+ private external fun isControllerImpl(params: String): Boolean
+
+ /**
+ * Checks if a controller is connected
+ * @param playerIndex Index of the player to check.
+ * @return Whether the player is connected or not.
+ */
+ external fun getIsConnected(playerIndex: Int): Boolean
+
+ /**
+ * Connects/disconnects a controller and ensures that connection order stays in-tact.
+ * @param playerIndex Index of the player to connect/disconnect.
+ * @param connected Whether to connect or disconnect this controller.
+ */
+ fun connectControllers(playerIndex: Int, connected: Boolean = true) {
+ val connectedControllers = mutableListOf<Boolean>().apply {
+ if (connected) {
+ for (i in 0 until 8) {
+ add(i <= playerIndex)
+ }
+ } else {
+ for (i in 0 until 8) {
+ add(i < playerIndex)
+ }
+ }
+ }
+ connectControllersImpl(connectedControllers.toBooleanArray())
+ }
+
+ private external fun connectControllersImpl(connected: BooleanArray)
+
+ /**
+ * Resets all of the button and analog mappings for a player.
+ * @param playerIndex Index of the player that will have its mappings reset.
+ */
+ external fun resetControllerMappings(playerIndex: Int)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt
new file mode 100644
index 000000000..15cc38c7f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt
@@ -0,0 +1,93 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input
+
+import android.view.InputDevice
+import androidx.annotation.Keep
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.utils.InputHandler.getGUID
+
+@Keep
+interface YuzuInputDevice {
+ fun getName(): String
+
+ fun getGUID(): String
+
+ fun getPort(): Int
+
+ fun getSupportsVibration(): Boolean
+
+ fun vibrate(intensity: Float)
+
+ fun getAxes(): Array<Int> = arrayOf()
+ fun hasKeys(keys: IntArray): BooleanArray = BooleanArray(0)
+}
+
+class YuzuPhysicalDevice(
+ private val device: InputDevice,
+ private val port: Int,
+ useSystemVibrator: Boolean
+) : YuzuInputDevice {
+ private val vibrator = if (useSystemVibrator) {
+ YuzuVibrator.getSystemVibrator()
+ } else {
+ YuzuVibrator.getControllerVibrator(device)
+ }
+
+ override fun getName(): String {
+ return device.name
+ }
+
+ override fun getGUID(): String {
+ return device.getGUID()
+ }
+
+ override fun getPort(): Int {
+ return port
+ }
+
+ override fun getSupportsVibration(): Boolean {
+ return vibrator.supportsVibration()
+ }
+
+ override fun vibrate(intensity: Float) {
+ vibrator.vibrate(intensity)
+ }
+
+ override fun getAxes(): Array<Int> = device.motionRanges.map { it.axis }.toTypedArray()
+ override fun hasKeys(keys: IntArray): BooleanArray = device.hasKeys(*keys)
+}
+
+class YuzuInputOverlayDevice(
+ private val vibration: Boolean,
+ private val port: Int
+) : YuzuInputDevice {
+ private val vibrator = YuzuVibrator.getSystemVibrator()
+
+ override fun getName(): String {
+ return YuzuApplication.appContext.getString(R.string.input_overlay)
+ }
+
+ override fun getGUID(): String {
+ return "00000000000000000000000000000000"
+ }
+
+ override fun getPort(): Int {
+ return port
+ }
+
+ override fun getSupportsVibration(): Boolean {
+ if (vibration) {
+ return vibrator.supportsVibration()
+ }
+ return false
+ }
+
+ override fun vibrate(intensity: Float) {
+ if (vibration) {
+ vibrator.vibrate(intensity)
+ }
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt
new file mode 100644
index 000000000..aac49ecae
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt
@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input
+
+import android.content.Context
+import android.os.Build
+import android.os.CombinedVibration
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.os.VibratorManager
+import android.view.InputDevice
+import androidx.annotation.Keep
+import androidx.annotation.RequiresApi
+import org.yuzu.yuzu_emu.YuzuApplication
+
+@Keep
+@Suppress("DEPRECATION")
+interface YuzuVibrator {
+ fun supportsVibration(): Boolean
+
+ fun vibrate(intensity: Float)
+
+ companion object {
+ fun getControllerVibrator(device: InputDevice): YuzuVibrator =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ YuzuVibratorManager(device.vibratorManager)
+ } else {
+ YuzuVibratorManagerCompat(device.vibrator)
+ }
+
+ fun getSystemVibrator(): YuzuVibrator =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val vibratorManager = YuzuApplication.appContext
+ .getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
+ YuzuVibratorManager(vibratorManager)
+ } else {
+ val vibrator = YuzuApplication.appContext
+ .getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ YuzuVibratorManagerCompat(vibrator)
+ }
+
+ fun getVibrationEffect(intensity: Float): VibrationEffect? {
+ if (intensity > 0f) {
+ return VibrationEffect.createOneShot(
+ 50,
+ (255.0 * intensity).toInt().coerceIn(1, 255)
+ )
+ }
+ return null
+ }
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.S)
+class YuzuVibratorManager(private val vibratorManager: VibratorManager) : YuzuVibrator {
+ override fun supportsVibration(): Boolean {
+ return vibratorManager.vibratorIds.isNotEmpty()
+ }
+
+ override fun vibrate(intensity: Float) {
+ val vibration = YuzuVibrator.getVibrationEffect(intensity) ?: return
+ vibratorManager.vibrate(CombinedVibration.createParallel(vibration))
+ }
+}
+
+class YuzuVibratorManagerCompat(private val vibrator: Vibrator) : YuzuVibrator {
+ override fun supportsVibration(): Boolean {
+ return vibrator.hasVibrator()
+ }
+
+ override fun vibrate(intensity: Float) {
+ val vibration = YuzuVibrator.getVibrationEffect(intensity) ?: return
+ vibrator.vibrate(vibration)
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt
new file mode 100644
index 000000000..0a5fab2ae
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+enum class AnalogDirection(val int: Int, val param: String) {
+ Up(0, "up"),
+ Down(1, "down"),
+ Left(2, "left"),
+ Right(3, "right")
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt
new file mode 100644
index 000000000..b8846ecad
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+// Loosely matches the enum in common/input.h
+enum class ButtonName(val int: Int) {
+ Invalid(1),
+
+ // This will display the engine name instead of the button name
+ Engine(2),
+
+ // This will display the button by value instead of the button name
+ Value(3);
+
+ companion object {
+ fun from(int: Int): ButtonName = entries.firstOrNull { it.int == int } ?: Invalid
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt
new file mode 100644
index 000000000..f725231cb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+// Must match the corresponding enum in input_common/main.h
+enum class InputType(val int: Int) {
+ None(0),
+ Button(1),
+ Stick(2),
+ Motion(3),
+ Touch(4)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt
new file mode 100644
index 000000000..c3b7a785d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+// Must match enum in src/common/settings_input.h
+enum class NativeAnalog(val int: Int) {
+ LStick(0),
+ RStick(1);
+
+ companion object {
+ fun from(int: Int): NativeAnalog = entries.firstOrNull { it.int == int } ?: LStick
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt
new file mode 100644
index 000000000..c5ccd7115
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+// Must match enum in src/common/settings_input.h
+enum class NativeButton(val int: Int) {
+ A(0),
+ B(1),
+ X(2),
+ Y(3),
+ LStick(4),
+ RStick(5),
+ L(6),
+ R(7),
+ ZL(8),
+ ZR(9),
+ Plus(10),
+ Minus(11),
+
+ DLeft(12),
+ DUp(13),
+ DRight(14),
+ DDown(15),
+
+ SLLeft(16),
+ SRLeft(17),
+
+ Home(18),
+ Capture(19),
+
+ SLRight(20),
+ SRRight(21);
+
+ companion object {
+ fun from(int: Int): NativeButton = entries.firstOrNull { it.int == int } ?: A
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt
new file mode 100644
index 000000000..625f352b4
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+// Must match enum in src/common/settings_input.h
+enum class NativeTrigger(val int: Int) {
+ LTrigger(0),
+ RTrigger(1)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt
new file mode 100644
index 000000000..e2a3d7aff
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.R
+
+// Must match enum in src/core/hid/hid_types.h
+enum class NpadStyleIndex(val int: Int, @StringRes val nameId: Int = 0) {
+ None(0),
+ Fullkey(3, R.string.pro_controller),
+ Handheld(4, R.string.handheld),
+ HandheldNES(4),
+ JoyconDual(5, R.string.dual_joycons),
+ JoyconLeft(6, R.string.left_joycon),
+ JoyconRight(7, R.string.right_joycon),
+ GameCube(8, R.string.gamecube_controller),
+ Pokeball(9),
+ NES(10),
+ SNES(12),
+ N64(13),
+ SegaGenesis(14),
+ SystemExt(32),
+ System(33);
+
+ companion object {
+ fun from(int: Int): NpadStyleIndex = entries.firstOrNull { it.int == int } ?: None
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
new file mode 100644
index 000000000..d35de80c4
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.input.model
+
+import androidx.annotation.Keep
+
+@Keep
+data class PlayerInput(
+ var connected: Boolean,
+ var buttons: Array<String>,
+ var analogs: Array<String>,
+ var motions: Array<String>,
+
+ var vibrationEnabled: Boolean,
+ var vibrationStrength: Int,
+
+ var bodyColorLeft: Long,
+ var bodyColorRight: Long,
+ var buttonColorLeft: Long,
+ var buttonColorRight: Long,
+ var profileName: String,
+
+ var useSystemVibrator: Boolean
+) {
+ // It's recommended to use the generated equals() and hashCode() methods
+ // when using arrays in a data class
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as PlayerInput
+
+ if (connected != other.connected) return false
+ if (!buttons.contentEquals(other.buttons)) return false
+ if (!analogs.contentEquals(other.analogs)) return false
+ if (!motions.contentEquals(other.motions)) return false
+ if (vibrationEnabled != other.vibrationEnabled) return false
+ if (vibrationStrength != other.vibrationStrength) return false
+ if (bodyColorLeft != other.bodyColorLeft) return false
+ if (bodyColorRight != other.bodyColorRight) return false
+ if (buttonColorLeft != other.buttonColorLeft) return false
+ if (buttonColorRight != other.buttonColorRight) return false
+ if (profileName != other.profileName) return false
+ return useSystemVibrator == other.useSystemVibrator
+ }
+
+ override fun hashCode(): Int {
+ var result = connected.hashCode()
+ result = 31 * result + buttons.contentHashCode()
+ result = 31 * result + analogs.contentHashCode()
+ result = 31 * result + motions.contentHashCode()
+ result = 31 * result + vibrationEnabled.hashCode()
+ result = 31 * result + vibrationStrength
+ result = 31 * result + bodyColorLeft.hashCode()
+ result = 31 * result + bodyColorRight.hashCode()
+ result = 31 * result + buttonColorLeft.hashCode()
+ result = 31 * result + buttonColorRight.hashCode()
+ result = 31 * result + profileName.hashCode()
+ result = 31 * result + useSystemVibrator.hashCode()
+ return result
+ }
+
+ fun hasMapping(): Boolean {
+ var hasMapping = false
+ buttons.forEach {
+ if (it != "[empty]") {
+ hasMapping = true
+ }
+ }
+ analogs.forEach {
+ if (it != "[empty]") {
+ hasMapping = true
+ }
+ }
+ motions.forEach {
+ if (it != "[empty]") {
+ hasMapping = true
+ }
+ }
+ return hasMapping
+ }
+}
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 71be2d0b2..0165cb2d1 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
@@ -24,7 +24,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
THEME_MODE("theme_mode"),
OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity"),
- LOCK_DRAWER("lock_drawer");
+ LOCK_DRAWER("lock_drawer"),
+ VERTICAL_ALIGNMENT("vertical_alignment"),
+ FSR_SHARPENING_SLIDER("fsr_sharpening_slider");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
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 fee80bb21..4f6b93bd2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,17 +4,30 @@
package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
object Settings {
- enum class MenuTag(val titleId: Int) {
+ enum class MenuTag(val titleId: Int = 0) {
SECTION_ROOT(R.string.advanced_settings),
SECTION_SYSTEM(R.string.preferences_system),
SECTION_RENDERER(R.string.preferences_graphics),
SECTION_AUDIO(R.string.preferences_audio),
+ SECTION_INPUT(R.string.preferences_controls),
+ SECTION_INPUT_PLAYER_ONE,
+ SECTION_INPUT_PLAYER_TWO,
+ SECTION_INPUT_PLAYER_THREE,
+ SECTION_INPUT_PLAYER_FOUR,
+ SECTION_INPUT_PLAYER_FIVE,
+ SECTION_INPUT_PLAYER_SIX,
+ SECTION_INPUT_PLAYER_SEVEN,
+ SECTION_INPUT_PLAYER_EIGHT,
SECTION_THEME(R.string.preferences_theme),
SECTION_DEBUG(R.string.preferences_debug);
}
+ fun getPlayerString(player: Int): String =
+ YuzuApplication.appContext.getString(R.string.preferences_player, player)
+
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
@@ -93,4 +106,15 @@ object Settings {
entries.firstOrNull { it.int == int } ?: Unspecified
}
}
+
+ enum class EmulationVerticalAlignment(val int: Int) {
+ Top(1),
+ Center(0),
+ Bottom(2);
+
+ companion object {
+ fun from(int: Int): EmulationVerticalAlignment =
+ entries.firstOrNull { it.int == int } ?: Center
+ }
+ }
}
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 a0d8cfede..6f16cf5b1 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
@@ -6,7 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
- DRIVER_PATH("driver_path");
+ DRIVER_PATH("driver_path"),
+ DEVICE_NAME("device_name");
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt
new file mode 100644
index 000000000..a2996725e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
+import org.yuzu.yuzu_emu.features.input.model.InputType
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.utils.ParamPackage
+
+class AnalogInputSetting(
+ override val playerIndex: Int,
+ val nativeAnalog: NativeAnalog,
+ val analogDirection: AnalogDirection,
+ @StringRes titleId: Int = 0,
+ titleString: String = ""
+) : InputSetting(titleId, titleString) {
+ override val type = TYPE_INPUT
+ override val inputType = InputType.Stick
+
+ override fun getSelectedValue(): String {
+ val params = NativeInput.getStickParam(playerIndex, nativeAnalog)
+ val analog = analogToText(params, analogDirection.param)
+ return getDisplayString(params, analog)
+ }
+
+ override fun setSelectedValue(param: ParamPackage) =
+ NativeInput.setStickParam(playerIndex, nativeAnalog, param)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt
new file mode 100644
index 000000000..786d09a7a
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.utils.ParamPackage
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.InputType
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
+
+class ButtonInputSetting(
+ override val playerIndex: Int,
+ val nativeButton: NativeButton,
+ @StringRes titleId: Int = 0,
+ titleString: String = ""
+) : InputSetting(titleId, titleString) {
+ override val type = TYPE_INPUT
+ override val inputType = InputType.Button
+
+ override fun getSelectedValue(): String {
+ val params = NativeInput.getButtonParam(playerIndex, nativeButton)
+ val button = buttonToText(params)
+ return getDisplayString(params, button)
+ }
+
+ override fun setSelectedValue(param: ParamPackage) =
+ NativeInput.setButtonParam(playerIndex, nativeButton, param)
+}
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 1d81f5f2b..58febff1d 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
@@ -3,13 +3,16 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting
class DateTimeSetting(
private val longSetting: AbstractLongSetting,
- titleId: Int,
- descriptionId: Int
-) : SettingsItem(longSetting, titleId, descriptionId) {
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = ""
+) : SettingsItem(longSetting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_DATETIME_SETTING
fun getValue(needsGlobal: Boolean = false): Long = longSetting.getLong(needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
index d31ce1c31..8a6a51d5c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
@@ -3,8 +3,11 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.StringRes
+
class HeaderSetting(
- titleId: Int
-) : SettingsItem(emptySetting, titleId, 0) {
+ @StringRes titleId: Int = 0,
+ titleString: String = ""
+) : SettingsItem(emptySetting, titleId, titleString, 0, "") {
override val type = TYPE_HEADER
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt
new file mode 100644
index 000000000..c46de08c5
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.utils.NativeConfig
+
+class InputProfileSetting(private val playerIndex: Int) :
+ SettingsItem(emptySetting, R.string.profile, "", 0, "") {
+ override val type = TYPE_INPUT_PROFILE
+
+ fun getCurrentProfile(): String =
+ NativeConfig.getInputSettings(true)[playerIndex].profileName
+
+ fun getProfileNames(): Array<String> = NativeInput.getInputProfileNames()
+
+ fun isProfileNameValid(name: String): Boolean = NativeInput.isProfileNameValid(name)
+
+ fun createProfile(name: String): Boolean = NativeInput.createProfile(name, playerIndex)
+
+ fun deleteProfile(name: String): Boolean = NativeInput.deleteProfile(name, playerIndex)
+
+ fun loadProfile(name: String): Boolean {
+ val result = NativeInput.loadProfile(name, playerIndex)
+ NativeInput.reloadInputDevices()
+ return result
+ }
+
+ fun saveProfile(name: String): Boolean = NativeInput.saveProfile(name, playerIndex)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt
new file mode 100644
index 000000000..2d118bff3
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt
@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.ButtonName
+import org.yuzu.yuzu_emu.features.input.model.InputType
+import org.yuzu.yuzu_emu.utils.ParamPackage
+
+sealed class InputSetting(
+ @StringRes titleId: Int,
+ titleString: String
+) : SettingsItem(emptySetting, titleId, titleString, 0, "") {
+ override val type = TYPE_INPUT
+ abstract val inputType: InputType
+ abstract val playerIndex: Int
+
+ protected val context get() = YuzuApplication.appContext
+
+ abstract fun getSelectedValue(): String
+
+ abstract fun setSelectedValue(param: ParamPackage)
+
+ protected fun getDisplayString(params: ParamPackage, control: String): String {
+ val deviceName = params.get("display", "")
+ deviceName.ifEmpty {
+ return context.getString(R.string.not_set)
+ }
+ return "$deviceName: $control"
+ }
+
+ private fun getDirectionName(direction: String): String =
+ when (direction) {
+ "up" -> context.getString(R.string.up)
+ "down" -> context.getString(R.string.down)
+ "left" -> context.getString(R.string.left)
+ "right" -> context.getString(R.string.right)
+ else -> direction
+ }
+
+ protected fun buttonToText(param: ParamPackage): String {
+ if (!param.has("engine")) {
+ return context.getString(R.string.not_set)
+ }
+
+ val toggle = if (param.get("toggle", false)) "~" else ""
+ val inverted = if (param.get("inverted", false)) "!" else ""
+ val invert = if (param.get("invert", "+") == "-") "-" else ""
+ val turbo = if (param.get("turbo", false)) "$" else ""
+ val commonButtonName = NativeInput.getButtonName(param)
+
+ if (commonButtonName == ButtonName.Invalid) {
+ return context.getString(R.string.invalid)
+ }
+
+ if (commonButtonName == ButtonName.Engine) {
+ return param.get("engine", "")
+ }
+
+ if (commonButtonName == ButtonName.Value) {
+ if (param.has("hat")) {
+ val hat = getDirectionName(param.get("direction", ""))
+ return context.getString(R.string.qualified_hat, turbo, toggle, inverted, hat)
+ }
+ if (param.has("axis")) {
+ val axis = param.get("axis", "")
+ return context.getString(
+ R.string.qualified_button_stick_axis,
+ toggle,
+ inverted,
+ invert,
+ axis
+ )
+ }
+ if (param.has("button")) {
+ val button = param.get("button", "")
+ return context.getString(R.string.qualified_button, turbo, toggle, inverted, button)
+ }
+ }
+
+ return context.getString(R.string.unknown)
+ }
+
+ protected fun analogToText(param: ParamPackage, direction: String): String {
+ if (!param.has("engine")) {
+ return context.getString(R.string.not_set)
+ }
+
+ if (param.get("engine", "") == "analog_from_button") {
+ return buttonToText(ParamPackage(param.get(direction, "")))
+ }
+
+ if (!param.has("axis_x") || !param.has("axis_y")) {
+ return context.getString(R.string.unknown)
+ }
+
+ val xAxis = param.get("axis_x", "")
+ val yAxis = param.get("axis_y", "")
+ val xInvert = param.get("invert_x", "+") == "-"
+ val yInvert = param.get("invert_y", "+") == "-"
+
+ if (direction == "modifier") {
+ return context.getString(R.string.unused)
+ }
+
+ when (direction) {
+ "up" -> {
+ val yInvertString = if (yInvert) "+" else "-"
+ return context.getString(R.string.qualified_axis, yAxis, yInvertString)
+ }
+
+ "down" -> {
+ val yInvertString = if (yInvert) "-" else "+"
+ return context.getString(R.string.qualified_axis, yAxis, yInvertString)
+ }
+
+ "left" -> {
+ val xInvertString = if (xInvert) "+" else "-"
+ return context.getString(R.string.qualified_axis, xAxis, xInvertString)
+ }
+
+ "right" -> {
+ val xInvertString = if (xInvert) "-" else "+"
+ return context.getString(R.string.qualified_axis, xAxis, xInvertString)
+ }
+ }
+
+ return context.getString(R.string.unknown)
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt
new file mode 100644
index 000000000..e024c793a
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
+
+class IntSingleChoiceSetting(
+ private val intSetting: AbstractIntSetting,
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = "",
+ val choices: Array<String>,
+ val values: Array<Int>
+) : SettingsItem(intSetting, titleId, titleString, descriptionId, descriptionString) {
+ override val type = TYPE_INT_SINGLE_CHOICE
+
+ fun getValueAt(index: Int): Int =
+ if (values.indices.contains(index)) values[index] else -1
+
+ fun getChoiceAt(index: Int): String =
+ if (choices.indices.contains(index)) choices[index] else ""
+
+ fun getSelectedValue(needsGlobal: Boolean = false) = intSetting.getInt(needsGlobal)
+ fun setSelectedValue(value: Int) = intSetting.setInt(value)
+
+ val selectedValueIndex: Int
+ get() {
+ for (i in values.indices) {
+ if (values[i] == getSelectedValue()) {
+ return i
+ }
+ }
+ return -1
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt
new file mode 100644
index 000000000..a1db3cc87
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.InputType
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.utils.ParamPackage
+
+class ModifierInputSetting(
+ override val playerIndex: Int,
+ val nativeAnalog: NativeAnalog,
+ @StringRes titleId: Int = 0,
+ titleString: String = ""
+) : InputSetting(titleId, titleString) {
+ override val inputType = InputType.Button
+
+ override fun getSelectedValue(): String {
+ val analogParam = NativeInput.getStickParam(playerIndex, nativeAnalog)
+ val modifierParam = ParamPackage(analogParam.get("modifier", ""))
+ return buttonToText(modifierParam)
+ }
+
+ override fun setSelectedValue(param: ParamPackage) {
+ val newParam = NativeInput.getStickParam(playerIndex, nativeAnalog)
+ newParam.set("modifier", param.serialize())
+ NativeInput.setStickParam(playerIndex, nativeAnalog, newParam)
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
index 425160024..06f607424 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
@@ -4,13 +4,16 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
class RunnableSetting(
- titleId: Int,
- descriptionId: Int,
- val isRuntimeRunnable: Boolean,
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = "",
+ val isRunnable: Boolean,
@DrawableRes val iconId: Int = 0,
val runnable: () -> Unit
-) : SettingsItem(emptySetting, titleId, descriptionId) {
+) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_RUNNABLE
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 12f7aa1ab..5fdf98318 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
@@ -3,8 +3,12 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -12,6 +16,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.utils.NativeConfig
/**
@@ -23,13 +28,34 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
*/
abstract class SettingsItem(
val setting: AbstractSetting,
- val nameId: Int,
- val descriptionId: Int
+ @StringRes val titleId: Int,
+ val titleString: String,
+ @StringRes val descriptionId: Int,
+ val descriptionString: String
) {
abstract val type: Int
+ val title: String by lazy {
+ if (titleId != 0) {
+ return@lazy YuzuApplication.appContext.getString(titleId)
+ }
+ return@lazy titleString
+ }
+
+ val description: String by lazy {
+ if (descriptionId != 0) {
+ return@lazy YuzuApplication.appContext.getString(descriptionId)
+ }
+ return@lazy descriptionString
+ }
+
val isEditable: Boolean
get() {
+ // Can't change docked mode toggle when using handheld mode
+ if (setting.key == BooleanSetting.USE_DOCKED_MODE.key) {
+ return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld
+ }
+
// Can't edit settings that aren't saveable in per-game config even if they are switchable
if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
return false
@@ -50,6 +76,9 @@ abstract class SettingsItem(
get() = NativeLibrary.isRunning() && !setting.global &&
!NativeConfig.isPerGameConfigLoaded()
+ val clearable: Boolean
+ get() = !setting.global && NativeConfig.isPerGameConfigLoaded()
+
companion object {
const val TYPE_HEADER = 0
const val TYPE_SWITCH = 1
@@ -59,6 +88,10 @@ abstract class SettingsItem(
const val TYPE_STRING_SINGLE_CHOICE = 5
const val TYPE_DATETIME_SETTING = 6
const val TYPE_RUNNABLE = 7
+ const val TYPE_INPUT = 8
+ const val TYPE_INT_SINGLE_CHOICE = 9
+ const val TYPE_INPUT_PROFILE = 10
+ const val TYPE_STRING_INPUT = 11
const val FASTMEM_COMBINED = "fastmem_combined"
@@ -77,221 +110,246 @@ abstract class SettingsItem(
// List of all general
val settingsItems = HashMap<String, SettingsItem>().apply {
+ put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
- R.string.frame_limit_enable,
- R.string.frame_limit_enable_description
+ titleId = R.string.frame_limit_enable,
+ descriptionId = R.string.frame_limit_enable_description
)
)
put(
SliderSetting(
ShortSetting.RENDERER_SPEED_LIMIT,
- R.string.frame_limit_slider,
- R.string.frame_limit_slider_description,
- 1,
- 400,
- "%"
+ titleId = R.string.frame_limit_slider,
+ descriptionId = R.string.frame_limit_slider_description,
+ min = 1,
+ max = 400,
+ units = "%"
)
)
put(
SingleChoiceSetting(
IntSetting.CPU_BACKEND,
- R.string.cpu_backend,
- 0,
- R.array.cpuBackendArm64Names,
- R.array.cpuBackendArm64Values
+ titleId = R.string.cpu_backend,
+ choicesId = R.array.cpuBackendArm64Names,
+ valuesId = R.array.cpuBackendArm64Values
)
)
put(
SingleChoiceSetting(
IntSetting.CPU_ACCURACY,
- R.string.cpu_accuracy,
- 0,
- R.array.cpuAccuracyNames,
- R.array.cpuAccuracyValues
+ titleId = R.string.cpu_accuracy,
+ choicesId = R.array.cpuAccuracyNames,
+ valuesId = R.array.cpuAccuracyValues
)
)
put(
SwitchSetting(
BooleanSetting.PICTURE_IN_PICTURE,
- R.string.picture_in_picture,
- R.string.picture_in_picture_description
+ titleId = R.string.picture_in_picture,
+ descriptionId = R.string.picture_in_picture_description
)
)
+
+ val dockedModeSetting = object : AbstractBooleanSetting {
+ override val key = BooleanSetting.USE_DOCKED_MODE.key
+
+ override fun getBoolean(needsGlobal: Boolean): Boolean {
+ if (NativeInput.getStyleIndex(0) == NpadStyleIndex.Handheld) {
+ return false
+ }
+ return BooleanSetting.USE_DOCKED_MODE.getBoolean(needsGlobal)
+ }
+
+ override fun setBoolean(value: Boolean) =
+ BooleanSetting.USE_DOCKED_MODE.setBoolean(value)
+
+ override val defaultValue = BooleanSetting.USE_DOCKED_MODE.defaultValue
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ BooleanSetting.USE_DOCKED_MODE.getValueAsString(needsGlobal)
+
+ override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset()
+ }
put(
SwitchSetting(
- BooleanSetting.USE_DOCKED_MODE,
- R.string.use_docked_mode,
- R.string.use_docked_mode_description
+ dockedModeSetting,
+ titleId = R.string.use_docked_mode,
+ descriptionId = R.string.use_docked_mode_description
)
)
+
put(
SingleChoiceSetting(
IntSetting.REGION_INDEX,
- R.string.emulated_region,
- 0,
- R.array.regionNames,
- R.array.regionValues
+ titleId = R.string.emulated_region,
+ choicesId = R.array.regionNames,
+ valuesId = R.array.regionValues
)
)
put(
SingleChoiceSetting(
IntSetting.LANGUAGE_INDEX,
- R.string.emulated_language,
- 0,
- R.array.languageNames,
- R.array.languageValues
+ titleId = R.string.emulated_language,
+ choicesId = R.array.languageNames,
+ valuesId = R.array.languageValues
)
)
put(
SwitchSetting(
BooleanSetting.USE_CUSTOM_RTC,
- R.string.use_custom_rtc,
- R.string.use_custom_rtc_description
+ titleId = R.string.use_custom_rtc,
+ descriptionId = R.string.use_custom_rtc_description
)
)
- put(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0))
+ put(DateTimeSetting(LongSetting.CUSTOM_RTC, titleId = R.string.set_custom_rtc))
put(
SingleChoiceSetting(
IntSetting.RENDERER_ACCURACY,
- R.string.renderer_accuracy,
- 0,
- R.array.rendererAccuracyNames,
- R.array.rendererAccuracyValues
+ titleId = R.string.renderer_accuracy,
+ choicesId = R.array.rendererAccuracyNames,
+ valuesId = R.array.rendererAccuracyValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_RESOLUTION,
- R.string.renderer_resolution,
- 0,
- R.array.rendererResolutionNames,
- R.array.rendererResolutionValues
+ titleId = R.string.renderer_resolution,
+ choicesId = R.array.rendererResolutionNames,
+ valuesId = R.array.rendererResolutionValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_VSYNC,
- R.string.renderer_vsync,
- 0,
- R.array.rendererVSyncNames,
- R.array.rendererVSyncValues
+ titleId = R.string.renderer_vsync,
+ choicesId = R.array.rendererVSyncNames,
+ valuesId = R.array.rendererVSyncValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_SCALING_FILTER,
- R.string.renderer_scaling_filter,
- 0,
- R.array.rendererScalingFilterNames,
- R.array.rendererScalingFilterValues
+ titleId = R.string.renderer_scaling_filter,
+ choicesId = R.array.rendererScalingFilterNames,
+ valuesId = R.array.rendererScalingFilterValues
+ )
+ )
+ put(
+ SliderSetting(
+ IntSetting.FSR_SHARPENING_SLIDER,
+ titleId = R.string.fsr_sharpness,
+ descriptionId = R.string.fsr_sharpness_description,
+ units = "%"
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_ANTI_ALIASING,
- R.string.renderer_anti_aliasing,
- 0,
- R.array.rendererAntiAliasingNames,
- R.array.rendererAntiAliasingValues
+ titleId = R.string.renderer_anti_aliasing,
+ choicesId = R.array.rendererAntiAliasingNames,
+ valuesId = R.array.rendererAntiAliasingValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_SCREEN_LAYOUT,
- R.string.renderer_screen_layout,
- 0,
- R.array.rendererScreenLayoutNames,
- R.array.rendererScreenLayoutValues
+ titleId = R.string.renderer_screen_layout,
+ choicesId = R.array.rendererScreenLayoutNames,
+ valuesId = R.array.rendererScreenLayoutValues
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_ASPECT_RATIO,
- R.string.renderer_aspect_ratio,
- 0,
- R.array.rendererAspectRatioNames,
- R.array.rendererAspectRatioValues
+ titleId = R.string.renderer_aspect_ratio,
+ choicesId = R.array.rendererAspectRatioNames,
+ valuesId = R.array.rendererAspectRatioValues
+ )
+ )
+ put(
+ SingleChoiceSetting(
+ IntSetting.VERTICAL_ALIGNMENT,
+ titleId = R.string.vertical_alignment,
+ descriptionId = 0,
+ choicesId = R.array.verticalAlignmentEntries,
+ valuesId = R.array.verticalAlignmentValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
- R.string.use_disk_shader_cache,
- R.string.use_disk_shader_cache_description
+ titleId = R.string.use_disk_shader_cache,
+ descriptionId = R.string.use_disk_shader_cache_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
- R.string.renderer_force_max_clock,
- R.string.renderer_force_max_clock_description
+ titleId = R.string.renderer_force_max_clock,
+ descriptionId = R.string.renderer_force_max_clock_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
- R.string.renderer_asynchronous_shaders,
- R.string.renderer_asynchronous_shaders_description
+ titleId = R.string.renderer_asynchronous_shaders,
+ descriptionId = R.string.renderer_asynchronous_shaders_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_REACTIVE_FLUSHING,
- R.string.renderer_reactive_flushing,
- R.string.renderer_reactive_flushing_description
+ titleId = R.string.renderer_reactive_flushing,
+ descriptionId = R.string.renderer_reactive_flushing_description
)
)
put(
SingleChoiceSetting(
IntSetting.MAX_ANISOTROPY,
- R.string.anisotropic_filtering,
- R.string.anisotropic_filtering_description,
- R.array.anisoEntries,
- R.array.anisoValues
+ titleId = R.string.anisotropic_filtering,
+ descriptionId = R.string.anisotropic_filtering_description,
+ choicesId = R.array.anisoEntries,
+ valuesId = R.array.anisoValues
)
)
put(
SingleChoiceSetting(
IntSetting.AUDIO_OUTPUT_ENGINE,
- R.string.audio_output_engine,
- 0,
- R.array.outputEngineEntries,
- R.array.outputEngineValues
+ titleId = R.string.audio_output_engine,
+ choicesId = R.array.outputEngineEntries,
+ valuesId = R.array.outputEngineValues
)
)
put(
SliderSetting(
ByteSetting.AUDIO_VOLUME,
- R.string.audio_volume,
- R.string.audio_volume_description,
- 0,
- 100,
- "%"
+ titleId = R.string.audio_volume,
+ descriptionId = R.string.audio_volume_description,
+ units = "%"
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_BACKEND,
- R.string.renderer_api,
- 0,
- R.array.rendererApiNames,
- R.array.rendererApiValues
+ titleId = R.string.renderer_api,
+ choicesId = R.array.rendererApiNames,
+ valuesId = R.array.rendererApiValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_DEBUG,
- R.string.renderer_debug,
- R.string.renderer_debug_description
+ titleId = R.string.renderer_debug,
+ descriptionId = R.string.renderer_debug_description
)
)
put(
SwitchSetting(
BooleanSetting.CPU_DEBUG_MODE,
- R.string.cpu_debug_mode,
- R.string.cpu_debug_mode_description
+ titleId = R.string.cpu_debug_mode,
+ descriptionId = R.string.cpu_debug_mode_description
)
)
@@ -327,7 +385,7 @@ abstract class SettingsItem(
override fun reset() = setBoolean(defaultValue)
}
- put(SwitchSetting(fastmem, R.string.fastmem, 0))
+ put(SwitchSetting(fastmem, R.string.fastmem))
}
}
}
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 97a5a9e59..ea5e099ed 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
@@ -3,16 +3,20 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.ArrayRes
+import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class SingleChoiceSetting(
setting: AbstractSetting,
- titleId: Int,
- descriptionId: Int,
- val choicesId: Int,
- val valuesId: Int
-) : SettingsItem(setting, titleId, descriptionId) {
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = "",
+ @ArrayRes val choicesId: Int,
+ @ArrayRes val valuesId: Int
+) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_SINGLE_CHOICE
fun getSelectedValue(needsGlobal: Boolean = false) =
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 b9b709bf7..6a5cdf48b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
@@ -12,12 +13,14 @@ import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting,
- titleId: Int,
- descriptionId: Int,
- val min: Int,
- val max: Int,
- val units: String
-) : SettingsItem(setting, titleId, descriptionId) {
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = "",
+ val min: Int = 0,
+ val max: Int = 100,
+ val units: String = ""
+) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_SLIDER
fun getSelectedValue(needsGlobal: Boolean = false) =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
new file mode 100644
index 000000000..1eb999416
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
+
+class StringInputSetting(
+ setting: AbstractStringSetting,
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = ""
+) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
+ override val type = TYPE_STRING_INPUT
+
+ fun getSelectedValue(needsGlobal: Boolean = false) = setting.getValueAsString(needsGlobal)
+
+ fun setSelectedValue(selection: String) =
+ (setting as AbstractStringSetting).setString(selection)
+}
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 ba7920f50..5260ff4dc 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
@@ -3,15 +3,18 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
class StringSingleChoiceSetting(
private val stringSetting: AbstractStringSetting,
- titleId: Int,
- descriptionId: Int,
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = "",
val choices: Array<String>,
val values: Array<String>
-) : SettingsItem(stringSetting, titleId, descriptionId) {
+) : SettingsItem(stringSetting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_STRING_SINGLE_CHOICE
fun getValueAt(index: Int): String =
@@ -20,7 +23,7 @@ class StringSingleChoiceSetting(
fun getSelectedValue(needsGlobal: Boolean = false) = stringSetting.getString(needsGlobal)
fun setSelectedValue(value: String) = stringSetting.setString(value)
- val selectValueIndex: Int
+ val selectedValueIndex: Int
get() {
for (i in values.indices) {
if (values[i] == getSelectedValue()) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index 94953b18a..c722393dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -8,10 +8,12 @@ import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.Settings
class SubmenuSetting(
- @StringRes titleId: Int,
- @StringRes descriptionId: Int,
- @DrawableRes val iconId: Int,
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = "",
+ @DrawableRes val iconId: Int = 0,
val menuKey: Settings.MenuTag
-) : SettingsItem(emptySetting, titleId, descriptionId) {
+) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_SUBMENU
}
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 44d47dd69..4984bf52e 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
@@ -3,15 +3,18 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class SwitchSetting(
setting: AbstractSetting,
- titleId: Int,
- descriptionId: Int
-) : SettingsItem(setting, titleId, descriptionId) {
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = ""
+) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_SWITCH
fun getIsChecked(needsGlobal: Boolean = false): Boolean {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt
new file mode 100644
index 000000000..16a1d0504
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt
@@ -0,0 +1,300 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import android.app.Dialog
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.view.InputDevice
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.activityViewModels
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.DialogMappingBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
+import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting
+import org.yuzu.yuzu_emu.utils.InputHandler
+import org.yuzu.yuzu_emu.utils.ParamPackage
+
+class InputDialogFragment : DialogFragment() {
+ private var inputAccepted = false
+
+ private var position: Int = 0
+
+ private lateinit var inputSetting: InputSetting
+
+ private lateinit var binding: DialogMappingBinding
+
+ private val settingsViewModel: SettingsViewModel by activityViewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (settingsViewModel.clickedItem == null) dismiss()
+
+ position = requireArguments().getInt(POSITION)
+
+ InputHandler.updateControllerData()
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ inputSetting = settingsViewModel.clickedItem as InputSetting
+ binding = DialogMappingBinding.inflate(layoutInflater)
+
+ val builder = MaterialAlertDialogBuilder(requireContext())
+ .setPositiveButton(android.R.string.cancel) { _, _ ->
+ NativeInput.stopMapping()
+ dismiss()
+ }
+ .setView(binding.root)
+
+ val playButtonMapAnimation = { twoDirections: Boolean ->
+ val stickAnimation: AnimatedVectorDrawable
+ val buttonAnimation: AnimatedVectorDrawable
+ binding.imageStickAnimation.apply {
+ val anim = if (twoDirections) {
+ R.drawable.stick_two_direction_anim
+ } else {
+ R.drawable.stick_one_direction_anim
+ }
+ setBackgroundResource(anim)
+ stickAnimation = background as AnimatedVectorDrawable
+ }
+ binding.imageButtonAnimation.apply {
+ setBackgroundResource(R.drawable.button_anim)
+ buttonAnimation = background as AnimatedVectorDrawable
+ }
+ stickAnimation.registerAnimationCallback(object : Animatable2.AnimationCallback() {
+ override fun onAnimationEnd(drawable: Drawable?) {
+ buttonAnimation.start()
+ }
+ })
+ buttonAnimation.registerAnimationCallback(object : Animatable2.AnimationCallback() {
+ override fun onAnimationEnd(drawable: Drawable?) {
+ stickAnimation.start()
+ }
+ })
+ stickAnimation.start()
+ }
+
+ when (val setting = inputSetting) {
+ is AnalogInputSetting -> {
+ when (setting.nativeAnalog) {
+ NativeAnalog.LStick -> builder.setTitle(
+ getString(R.string.map_control, getString(R.string.left_stick))
+ )
+
+ NativeAnalog.RStick -> builder.setTitle(
+ getString(R.string.map_control, getString(R.string.right_stick))
+ )
+ }
+
+ builder.setMessage(R.string.stick_map_description)
+
+ playButtonMapAnimation.invoke(true)
+ }
+
+ is ModifierInputSetting -> {
+ builder.setTitle(getString(R.string.map_control, setting.title))
+ .setMessage(R.string.button_map_description)
+ playButtonMapAnimation.invoke(false)
+ }
+
+ is ButtonInputSetting -> {
+ if (setting.nativeButton == NativeButton.DUp ||
+ setting.nativeButton == NativeButton.DDown ||
+ setting.nativeButton == NativeButton.DLeft ||
+ setting.nativeButton == NativeButton.DRight
+ ) {
+ builder.setTitle(getString(R.string.map_dpad_direction, setting.title))
+ } else {
+ builder.setTitle(getString(R.string.map_control, setting.title))
+ }
+ builder.setMessage(R.string.button_map_description)
+ playButtonMapAnimation.invoke(false)
+ }
+ }
+
+ return builder.create()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ view.requestFocus()
+ view.setOnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() }
+ dialog?.setOnKeyListener { _, _, keyEvent -> onKeyEvent(keyEvent) }
+ binding.root.setOnGenericMotionListener { _, motionEvent -> onMotionEvent(motionEvent) }
+ NativeInput.beginMapping(inputSetting.inputType.int)
+ }
+
+ private fun onKeyEvent(event: KeyEvent): Boolean {
+ if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
+ event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
+ ) {
+ return false
+ }
+
+ val action = when (event.action) {
+ KeyEvent.ACTION_DOWN -> NativeInput.ButtonState.PRESSED
+ KeyEvent.ACTION_UP -> NativeInput.ButtonState.RELEASED
+ else -> return false
+ }
+ val controllerData =
+ InputHandler.androidControllers[event.device.controllerNumber] ?: return false
+ NativeInput.onGamePadButtonEvent(
+ controllerData.getGUID(),
+ controllerData.getPort(),
+ event.keyCode,
+ action
+ )
+ onInputReceived(event.device)
+ return true
+ }
+
+ private fun onMotionEvent(event: MotionEvent): Boolean {
+ if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
+ event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
+ ) {
+ return false
+ }
+
+ // Temp workaround for DPads that give both axis and button input. The input system can't
+ // take in a specific axis direction for a binding so you lose half of the directions for a DPad.
+
+ val controllerData =
+ InputHandler.androidControllers[event.device.controllerNumber] ?: return false
+ event.device.motionRanges.forEach {
+ NativeInput.onGamePadAxisEvent(
+ controllerData.getGUID(),
+ controllerData.getPort(),
+ it.axis,
+ event.getAxisValue(it.axis)
+ )
+ onInputReceived(event.device)
+ }
+ return true
+ }
+
+ private fun onInputReceived(device: InputDevice) {
+ val params = ParamPackage(NativeInput.getNextInput())
+ if (params.has("engine") && isInputAcceptable(params) && !inputAccepted) {
+ inputAccepted = true
+ setResult(params, device)
+ }
+ }
+
+ private fun setResult(params: ParamPackage, device: InputDevice) {
+ NativeInput.stopMapping()
+ params.set("display", "${device.name} ${params.get("port", 0)}")
+ when (val item = settingsViewModel.clickedItem as InputSetting) {
+ is ModifierInputSetting,
+ is ButtonInputSetting -> {
+ // Invert DPad up and left bindings by default
+ val tempSetting = inputSetting as? ButtonInputSetting
+ if (tempSetting != null) {
+ if (tempSetting.nativeButton == NativeButton.DUp ||
+ tempSetting.nativeButton == NativeButton.DLeft &&
+ params.has("axis")
+ ) {
+ params.set("invert", "-")
+ }
+ }
+
+ item.setSelectedValue(params)
+ settingsViewModel.setAdapterItemChanged(position)
+ }
+
+ is AnalogInputSetting -> {
+ var analogParam = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
+ analogParam = adjustAnalogParam(params, analogParam, item.analogDirection.param)
+
+ // Invert Y-Axis by default
+ analogParam.set("invert_y", "-")
+
+ item.setSelectedValue(analogParam)
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ }
+ }
+ dismiss()
+ }
+
+ private fun adjustAnalogParam(
+ inputParam: ParamPackage,
+ analogParam: ParamPackage,
+ buttonName: String
+ ): ParamPackage {
+ // The poller returned a complete axis, so set all the buttons
+ if (inputParam.has("axis_x") && inputParam.has("axis_y")) {
+ return inputParam
+ }
+
+ // Check if the current configuration has either no engine or an axis binding.
+ // Clears out the old binding and adds one with analog_from_button.
+ if (!analogParam.has("engine") || analogParam.has("axis_x") || analogParam.has("axis_y")) {
+ analogParam.clear()
+ analogParam.set("engine", "analog_from_button")
+ }
+ analogParam.set(buttonName, inputParam.serialize())
+ return analogParam
+ }
+
+ private fun isInputAcceptable(params: ParamPackage): Boolean {
+ if (InputHandler.registeredControllers.size == 1) {
+ return true
+ }
+
+ if (params.has("motion")) {
+ return true
+ }
+
+ val currentDevice = settingsViewModel.getCurrentDeviceParams(params)
+ if (currentDevice.get("engine", "any") == "any") {
+ return true
+ }
+
+ val guidMatch = params.get("guid", "") == currentDevice.get("guid", "") ||
+ params.get("guid", "") == currentDevice.get("guid2", "")
+ return params.get("engine", "") == currentDevice.get("engine", "") &&
+ guidMatch &&
+ params.get("port", 0) == currentDevice.get("port", 0)
+ }
+
+ companion object {
+ const val TAG = "InputDialogFragment"
+
+ const val POSITION = "Position"
+
+ fun newInstance(
+ inputMappingViewModel: SettingsViewModel,
+ setting: InputSetting,
+ position: Int
+ ): InputDialogFragment {
+ inputMappingViewModel.clickedItem = setting
+ val args = Bundle()
+ args.putInt(POSITION, position)
+ val fragment = InputDialogFragment()
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt
new file mode 100644
index 000000000..5656e9d8d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
+import org.yuzu.yuzu_emu.databinding.ListItemInputProfileBinding
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
+import org.yuzu.yuzu_emu.R
+
+class InputProfileAdapter(options: List<ProfileItem>) :
+ AbstractListAdapter<ProfileItem, AbstractViewHolder<ProfileItem>>(options) {
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): AbstractViewHolder<ProfileItem> {
+ ListItemInputProfileBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return InputProfileViewHolder(it) }
+ }
+
+ inner class InputProfileViewHolder(val binding: ListItemInputProfileBinding) :
+ AbstractViewHolder<ProfileItem>(binding) {
+ override fun bind(model: ProfileItem) {
+ when (model) {
+ is ExistingProfileItem -> {
+ binding.title.text = model.name
+ binding.buttonNew.visibility = View.GONE
+ binding.buttonDelete.visibility = View.VISIBLE
+ binding.buttonDelete.setOnClickListener { model.deleteProfile.invoke() }
+ binding.buttonSave.visibility = View.VISIBLE
+ binding.buttonSave.setOnClickListener { model.saveProfile.invoke() }
+ binding.buttonLoad.visibility = View.VISIBLE
+ binding.buttonLoad.setOnClickListener { model.loadProfile.invoke() }
+ }
+
+ is NewProfileItem -> {
+ binding.title.text = model.name
+ binding.buttonNew.visibility = View.VISIBLE
+ binding.buttonNew.setOnClickListener { model.createNewProfile.invoke() }
+ binding.buttonSave.visibility = View.GONE
+ binding.buttonDelete.visibility = View.GONE
+ binding.buttonLoad.visibility = View.GONE
+ }
+ }
+ }
+ }
+}
+
+sealed interface ProfileItem {
+ val name: String
+}
+
+data class NewProfileItem(
+ val createNewProfile: () -> Unit
+) : ProfileItem {
+ override val name: String = YuzuApplication.appContext.getString(R.string.create_new_profile)
+}
+
+data class ExistingProfileItem(
+ override val name: String,
+ val deleteProfile: () -> Unit,
+ val saveProfile: () -> Unit,
+ val loadProfile: () -> Unit
+) : ProfileItem
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
new file mode 100644
index 000000000..1bae593ae
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
@@ -0,0 +1,148 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.activityViewModels
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
+import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
+import org.yuzu.yuzu_emu.utils.collect
+
+class InputProfileDialogFragment : DialogFragment() {
+ private var position = 0
+
+ private val settingsViewModel: SettingsViewModel by activityViewModels()
+
+ private lateinit var binding: DialogInputProfilesBinding
+
+ private lateinit var setting: InputProfileSetting
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ position = requireArguments().getInt(POSITION)
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ binding = DialogInputProfilesBinding.inflate(layoutInflater)
+
+ setting = settingsViewModel.clickedItem as InputProfileSetting
+ val options = mutableListOf<ProfileItem>().apply {
+ add(
+ NewProfileItem(
+ createNewProfile = {
+ NewInputProfileDialogFragment.newInstance(
+ settingsViewModel,
+ setting,
+ position
+ ).show(parentFragmentManager, NewInputProfileDialogFragment.TAG)
+ dismiss()
+ }
+ )
+ )
+
+ val onActionDismiss = {
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ dismiss()
+ }
+ setting.getProfileNames().forEach {
+ add(
+ ExistingProfileItem(
+ it,
+ deleteProfile = {
+ settingsViewModel.setShouldShowDeleteProfileDialog(it)
+ },
+ saveProfile = {
+ if (!setting.saveProfile(it)) {
+ Toast.makeText(
+ requireContext(),
+ R.string.failed_to_save_profile,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ onActionDismiss.invoke()
+ },
+ loadProfile = {
+ if (!setting.loadProfile(it)) {
+ Toast.makeText(
+ requireContext(),
+ R.string.failed_to_load_profile,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ onActionDismiss.invoke()
+ }
+ )
+ )
+ }
+ }
+ binding.listProfiles.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ adapter = InputProfileAdapter(options)
+ }
+
+ return MaterialAlertDialogBuilder(requireContext())
+ .setView(binding.root)
+ .create()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
+ if (it.isNotEmpty()) {
+ MessageDialogFragment.newInstance(
+ activity = requireActivity(),
+ titleId = R.string.delete_input_profile,
+ descriptionId = R.string.delete_input_profile_description,
+ positiveAction = {
+ setting.deleteProfile(it)
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ },
+ negativeAction = {},
+ negativeButtonTitleId = android.R.string.cancel
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
+ settingsViewModel.setShouldShowDeleteProfileDialog("")
+ dismiss()
+ }
+ }
+ }
+
+ companion object {
+ const val TAG = "InputProfileDialogFragment"
+
+ const val POSITION = "Position"
+
+ fun newInstance(
+ settingsViewModel: SettingsViewModel,
+ profileSetting: InputProfileSetting,
+ position: Int
+ ): InputProfileDialogFragment {
+ settingsViewModel.clickedItem = profileSetting
+
+ val args = Bundle()
+ args.putInt(POSITION, position)
+ val fragment = InputProfileDialogFragment()
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt
new file mode 100644
index 000000000..6e52bea80
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import android.app.Dialog
+import android.os.Bundle
+import android.widget.Toast
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.activityViewModels
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
+import org.yuzu.yuzu_emu.R
+
+class NewInputProfileDialogFragment : DialogFragment() {
+ private var position = 0
+
+ private val settingsViewModel: SettingsViewModel by activityViewModels()
+
+ private lateinit var binding: DialogEditTextBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ position = requireArguments().getInt(POSITION)
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ binding = DialogEditTextBinding.inflate(layoutInflater)
+
+ val setting = settingsViewModel.clickedItem as InputProfileSetting
+ return MaterialAlertDialogBuilder(requireContext())
+ .setTitle(R.string.enter_profile_name)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ val profileName = binding.editText.text.toString()
+ if (!setting.isProfileNameValid(profileName)) {
+ Toast.makeText(
+ requireContext(),
+ R.string.invalid_profile_name,
+ Toast.LENGTH_SHORT
+ ).show()
+ return@setPositiveButton
+ }
+
+ if (!setting.createProfile(profileName)) {
+ Toast.makeText(
+ requireContext(),
+ R.string.profile_name_already_exists,
+ Toast.LENGTH_SHORT
+ ).show()
+ } else {
+ settingsViewModel.setAdapterItemChanged(position)
+ }
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .setView(binding.root)
+ .show()
+ }
+
+ companion object {
+ const val TAG = "NewInputProfileDialogFragment"
+
+ const val POSITION = "Position"
+
+ fun newInstance(
+ settingsViewModel: SettingsViewModel,
+ profileSetting: InputProfileSetting,
+ position: Int
+ ): NewInputProfileDialogFragment {
+ settingsViewModel.clickedItem = profileSetting
+
+ val args = Bundle()
+ args.putInt(POSITION, position)
+ val fragment = NewInputProfileDialogFragment()
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
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 6f072241a..455b3b5ff 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
@@ -13,21 +13,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
-import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.*
class SettingsActivity : AppCompatActivity() {
@@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() {
)
}
- lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldRecreate.collectLatest {
- if (it) {
- settingsViewModel.setShouldRecreate(false)
- recreate()
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldNavigateBack.collectLatest {
- if (it) {
- settingsViewModel.setShouldNavigateBack(false)
- navigateBack()
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
- if (it) {
- settingsViewModel.setShouldShowResetSettingsDialog(false)
- ResetSettingsDialogFragment().show(
- supportFragmentManager,
- ResetSettingsDialogFragment.TAG
- )
- }
- }
- }
+ settingsViewModel.shouldRecreate.collect(
+ this,
+ resetState = { settingsViewModel.setShouldRecreate(false) }
+ ) { if (it) recreate() }
+ settingsViewModel.shouldNavigateBack.collect(
+ this,
+ resetState = { settingsViewModel.setShouldNavigateBack(false) }
+ ) { if (it) navigateBack() }
+ settingsViewModel.shouldShowResetSettingsDialog.collect(
+ this,
+ resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
+ ) {
+ if (it) {
+ ResetSettingsDialogFragment().show(
+ supportFragmentManager,
+ ResetSettingsDialogFragment.TAG
+ )
}
}
@@ -137,6 +116,7 @@ class SettingsActivity : AppCompatActivity() {
super.onStop()
Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
if (isFinishing) {
+ NativeInput.reloadInputDevices()
NativeLibrary.applySettings()
if (args.game == null) {
NativeConfig.saveGlobalConfig()
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 be9b3031b..500ac6e66 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
@@ -8,12 +8,11 @@ import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.text.format.DateFormat
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
+import android.widget.PopupMenu
import androidx.fragment.app.Fragment
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
@@ -21,16 +20,18 @@ import androidx.recyclerview.widget.ListAdapter
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.SettingsNavigationDirections
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
+import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
-import org.yuzu.yuzu_emu.fragments.SettingsDialogFragment
-import org.yuzu.yuzu_emu.model.SettingsViewModel
+import org.yuzu.yuzu_emu.utils.ParamPackage
class SettingsAdapter(
private val fragment: Fragment,
@@ -41,19 +42,6 @@ class SettingsAdapter(
private val settingsViewModel: SettingsViewModel
get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java]
- init {
- fragment.viewLifecycleOwner.lifecycleScope.launch {
- fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
- settingsViewModel.adapterItemChanged.collect {
- if (it != -1) {
- notifyItemChanged(it)
- settingsViewModel.setAdapterItemChanged(-1)
- }
- }
- }
- }
- }
-
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
@@ -85,8 +73,23 @@ class SettingsAdapter(
RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
+ SettingsItem.TYPE_INPUT -> {
+ InputViewHolder(ListItemSettingInputBinding.inflate(inflater), this)
+ }
+
+ SettingsItem.TYPE_INT_SINGLE_CHOICE -> {
+ SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this)
+ }
+
+ SettingsItem.TYPE_INPUT_PROFILE -> {
+ InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
+ }
+
+ SettingsItem.TYPE_STRING_INPUT -> {
+ StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this)
+ }
+
else -> {
- // TODO: Create an error view since we can't return null now
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
}
@@ -126,6 +129,15 @@ class SettingsAdapter(
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
}
+ fun onIntSingleChoiceClick(item: IntSingleChoiceSetting, position: Int) {
+ SettingsDialogFragment.newInstance(
+ settingsViewModel,
+ item,
+ SettingsItem.TYPE_INT_SINGLE_CHOICE,
+ position
+ ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
+ }
+
fun onDateTimeClick(item: DateTimeSetting, position: Int) {
val storedTime = item.getValue() * 1000
@@ -185,6 +197,214 @@ class SettingsAdapter(
fragment.view?.findNavController()?.navigate(action)
}
+ fun onInputProfileClick(item: InputProfileSetting, position: Int) {
+ InputProfileDialogFragment.newInstance(
+ settingsViewModel,
+ item,
+ position
+ ).show(fragment.childFragmentManager, InputProfileDialogFragment.TAG)
+ }
+
+ fun onInputClick(item: InputSetting, position: Int) {
+ InputDialogFragment.newInstance(
+ settingsViewModel,
+ item,
+ position
+ ).show(fragment.childFragmentManager, InputDialogFragment.TAG)
+ }
+
+ fun onInputOptionsClick(anchor: View, item: InputSetting, position: Int) {
+ val popup = PopupMenu(context, anchor)
+ popup.menuInflater.inflate(R.menu.menu_input_options, popup.menu)
+
+ popup.menu.apply {
+ val invertAxis = findItem(R.id.invert_axis)
+ val invertButton = findItem(R.id.invert_button)
+ val toggleButton = findItem(R.id.toggle_button)
+ val turboButton = findItem(R.id.turbo_button)
+ val setThreshold = findItem(R.id.set_threshold)
+ val toggleAxis = findItem(R.id.toggle_axis)
+ when (item) {
+ is AnalogInputSetting -> {
+ val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
+
+ invertAxis.isVisible = true
+ invertAxis.isCheckable = true
+ invertAxis.isChecked = when (item.analogDirection) {
+ AnalogDirection.Left, AnalogDirection.Right -> {
+ params.get("invert_x", "+") == "-"
+ }
+
+ AnalogDirection.Up, AnalogDirection.Down -> {
+ params.get("invert_y", "+") == "-"
+ }
+ }
+ invertAxis.setOnMenuItemClickListener {
+ if (item.analogDirection == AnalogDirection.Left ||
+ item.analogDirection == AnalogDirection.Right
+ ) {
+ val invertValue = params.get("invert_x", "+") == "-"
+ val invertString = if (invertValue) "+" else "-"
+ params.set("invert_x", invertString)
+ } else if (
+ item.analogDirection == AnalogDirection.Up ||
+ item.analogDirection == AnalogDirection.Down
+ ) {
+ val invertValue = params.get("invert_y", "+") == "-"
+ val invertString = if (invertValue) "+" else "-"
+ params.set("invert_y", invertString)
+ }
+ true
+ }
+
+ popup.setOnDismissListener {
+ NativeInput.setStickParam(item.playerIndex, item.nativeAnalog, params)
+ settingsViewModel.setDatasetChanged(true)
+ }
+ }
+
+ is ButtonInputSetting -> {
+ val params = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
+ if (params.has("code") || params.has("button") || params.has("hat")) {
+ val buttonInvert = params.get("inverted", false)
+ invertButton.isVisible = true
+ invertButton.isCheckable = true
+ invertButton.isChecked = buttonInvert
+ invertButton.setOnMenuItemClickListener {
+ params.set("inverted", !buttonInvert)
+ true
+ }
+
+ val toggle = params.get("toggle", false)
+ toggleButton.isVisible = true
+ toggleButton.isCheckable = true
+ toggleButton.isChecked = toggle
+ toggleButton.setOnMenuItemClickListener {
+ params.set("toggle", !toggle)
+ true
+ }
+
+ val turbo = params.get("turbo", false)
+ turboButton.isVisible = true
+ turboButton.isCheckable = true
+ turboButton.isChecked = turbo
+ turboButton.setOnMenuItemClickListener {
+ params.set("turbo", !turbo)
+ true
+ }
+ } else if (params.has("axis")) {
+ val axisInvert = params.get("invert", "+") == "-"
+ invertAxis.isVisible = true
+ invertAxis.isCheckable = true
+ invertAxis.isChecked = axisInvert
+ invertAxis.setOnMenuItemClickListener {
+ params.set("invert", if (!axisInvert) "-" else "+")
+ true
+ }
+
+ val buttonInvert = params.get("inverted", false)
+ invertButton.isVisible = true
+ invertButton.isCheckable = true
+ invertButton.isChecked = buttonInvert
+ invertButton.setOnMenuItemClickListener {
+ params.set("inverted", !buttonInvert)
+ true
+ }
+
+ setThreshold.isVisible = true
+ val thresholdSetting = object : AbstractIntSetting {
+ override val key = ""
+
+ override fun getInt(needsGlobal: Boolean): Int =
+ (params.get("threshold", 0.5f) * 100).toInt()
+
+ override fun setInt(value: Int) {
+ params.set("threshold", value.toFloat() / 100)
+ NativeInput.setButtonParam(
+ item.playerIndex,
+ item.nativeButton,
+ params
+ )
+ }
+
+ override val defaultValue = 50
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ getInt(needsGlobal).toString()
+
+ override fun reset() = setInt(defaultValue)
+ }
+ setThreshold.setOnMenuItemClickListener {
+ onSliderClick(
+ SliderSetting(thresholdSetting, R.string.set_threshold),
+ position
+ )
+ true
+ }
+
+ val axisToggle = params.get("toggle", false)
+ toggleAxis.isVisible = true
+ toggleAxis.isCheckable = true
+ toggleAxis.isChecked = axisToggle
+ toggleAxis.setOnMenuItemClickListener {
+ params.set("toggle", !axisToggle)
+ true
+ }
+ }
+
+ popup.setOnDismissListener {
+ NativeInput.setButtonParam(item.playerIndex, item.nativeButton, params)
+ settingsViewModel.setAdapterItemChanged(position)
+ }
+ }
+
+ is ModifierInputSetting -> {
+ val stickParams = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
+ val modifierParams = ParamPackage(stickParams.get("modifier", ""))
+
+ val invert = modifierParams.get("inverted", false)
+ invertButton.isVisible = true
+ invertButton.isCheckable = true
+ invertButton.isChecked = invert
+ invertButton.setOnMenuItemClickListener {
+ modifierParams.set("inverted", !invert)
+ stickParams.set("modifier", modifierParams.serialize())
+ true
+ }
+
+ val toggle = modifierParams.get("toggle", false)
+ toggleButton.isVisible = true
+ toggleButton.isCheckable = true
+ toggleButton.isChecked = toggle
+ toggleButton.setOnMenuItemClickListener {
+ modifierParams.set("toggle", !toggle)
+ stickParams.set("modifier", modifierParams.serialize())
+ true
+ }
+
+ popup.setOnDismissListener {
+ NativeInput.setStickParam(
+ item.playerIndex,
+ item.nativeAnalog,
+ stickParams
+ )
+ settingsViewModel.setAdapterItemChanged(position)
+ }
+ }
+ }
+ }
+ popup.show()
+ }
+
+ fun onStringInputClick(item: StringInputSetting, position: Int) {
+ SettingsDialogFragment.newInstance(
+ settingsViewModel,
+ item,
+ SettingsItem.TYPE_STRING_INPUT,
+ position
+ ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
+ }
+
fun onLongClick(item: SettingsItem, position: Int): Boolean {
SettingsDialogFragment.newInstance(
settingsViewModel,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
new file mode 100644
index 000000000..7f562a1f4
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.activityViewModels
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.slider.Slider
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
+import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
+import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
+import org.yuzu.yuzu_emu.utils.ParamPackage
+import org.yuzu.yuzu_emu.utils.collect
+
+class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
+ private var type = 0
+ private var position = 0
+
+ private var defaultCancelListener =
+ DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
+
+ private val settingsViewModel: SettingsViewModel by activityViewModels()
+
+ private lateinit var sliderBinding: DialogSliderBinding
+ private lateinit var stringInputBinding: DialogEditTextBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ type = requireArguments().getInt(TYPE)
+ position = requireArguments().getInt(POSITION)
+
+ if (settingsViewModel.clickedItem == null) dismiss()
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return when (type) {
+ TYPE_RESET_SETTING -> {
+ MaterialAlertDialogBuilder(requireContext())
+ .setMessage(R.string.reset_setting_confirmation)
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
+ when (val item = settingsViewModel.clickedItem) {
+ is AnalogInputSetting -> {
+ val stickParam = NativeInput.getStickParam(
+ item.playerIndex,
+ item.nativeAnalog
+ )
+ if (stickParam.get("engine", "") == "analog_from_button") {
+ when (item.analogDirection) {
+ AnalogDirection.Up -> stickParam.erase("up")
+ AnalogDirection.Down -> stickParam.erase("down")
+ AnalogDirection.Left -> stickParam.erase("left")
+ AnalogDirection.Right -> stickParam.erase("right")
+ }
+ NativeInput.setStickParam(
+ item.playerIndex,
+ item.nativeAnalog,
+ stickParam
+ )
+ settingsViewModel.setAdapterItemChanged(position)
+ } else {
+ NativeInput.setStickParam(
+ item.playerIndex,
+ item.nativeAnalog,
+ ParamPackage()
+ )
+ settingsViewModel.setDatasetChanged(true)
+ }
+ }
+
+ is ButtonInputSetting -> {
+ NativeInput.setButtonParam(
+ item.playerIndex,
+ item.nativeButton,
+ ParamPackage()
+ )
+ settingsViewModel.setAdapterItemChanged(position)
+ }
+
+ else -> {
+ settingsViewModel.clickedItem!!.setting.reset()
+ settingsViewModel.setAdapterItemChanged(position)
+ }
+ }
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .create()
+ }
+
+ SettingsItem.TYPE_SINGLE_CHOICE -> {
+ val item = settingsViewModel.clickedItem as SingleChoiceSetting
+ val value = getSelectionForSingleChoiceValue(item)
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(item.title)
+ .setSingleChoiceItems(item.choicesId, value, this)
+ .create()
+ }
+
+ SettingsItem.TYPE_SLIDER -> {
+ sliderBinding = DialogSliderBinding.inflate(layoutInflater)
+ val item = settingsViewModel.clickedItem as SliderSetting
+
+ settingsViewModel.setSliderTextValue(item.getSelectedValue().toFloat(), item.units)
+ sliderBinding.slider.apply {
+ valueFrom = item.min.toFloat()
+ valueTo = item.max.toFloat()
+ value = settingsViewModel.sliderProgress.value.toFloat()
+ addOnChangeListener { _: Slider, value: Float, _: Boolean ->
+ settingsViewModel.setSliderTextValue(value, item.units)
+ }
+ }
+
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(item.title)
+ .setView(sliderBinding.root)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, defaultCancelListener)
+ .create()
+ }
+
+ SettingsItem.TYPE_STRING_INPUT -> {
+ stringInputBinding = DialogEditTextBinding.inflate(layoutInflater)
+ val item = settingsViewModel.clickedItem as StringInputSetting
+ stringInputBinding.editText.setText(item.getSelectedValue())
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(item.title)
+ .setView(stringInputBinding.root)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, defaultCancelListener)
+ .create()
+ }
+
+ SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
+ val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(item.title)
+ .setSingleChoiceItems(item.choices, item.selectedValueIndex, this)
+ .create()
+ }
+
+ SettingsItem.TYPE_INT_SINGLE_CHOICE -> {
+ val item = settingsViewModel.clickedItem as IntSingleChoiceSetting
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(item.title)
+ .setSingleChoiceItems(item.choices, item.selectedValueIndex, this)
+ .create()
+ }
+
+ else -> super.onCreateDialog(savedInstanceState)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return when (type) {
+ SettingsItem.TYPE_SLIDER -> sliderBinding.root
+ SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root
+ else -> super.onCreateView(inflater, container, savedInstanceState)
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ when (type) {
+ SettingsItem.TYPE_SLIDER -> {
+ settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
+ sliderBinding.textValue.text = it
+ }
+ settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
+ sliderBinding.slider.value = it.toFloat()
+ }
+ }
+ }
+ }
+
+ override fun onClick(dialog: DialogInterface, which: Int) {
+ when (settingsViewModel.clickedItem) {
+ is SingleChoiceSetting -> {
+ val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting
+ val value = getValueForSingleChoiceSelection(scSetting, which)
+ scSetting.setSelectedValue(value)
+ }
+
+ is StringSingleChoiceSetting -> {
+ val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting
+ val value = scSetting.getValueAt(which)
+ scSetting.setSelectedValue(value)
+ }
+
+ is IntSingleChoiceSetting -> {
+ val scSetting = settingsViewModel.clickedItem as IntSingleChoiceSetting
+ val value = scSetting.getValueAt(which)
+ scSetting.setSelectedValue(value)
+ }
+
+ is SliderSetting -> {
+ val sliderSetting = settingsViewModel.clickedItem as SliderSetting
+ sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
+ }
+
+ is StringInputSetting -> {
+ val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting
+ stringInputSetting.setSelectedValue(
+ (stringInputBinding.editText.text ?: "").toString()
+ )
+ }
+ }
+ closeDialog()
+ }
+
+ private fun closeDialog() {
+ settingsViewModel.setAdapterItemChanged(position)
+ settingsViewModel.clickedItem = null
+ settingsViewModel.setSliderProgress(-1f)
+ dismiss()
+ }
+
+ private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int {
+ val valuesId = item.valuesId
+ return if (valuesId > 0) {
+ val valuesArray = requireContext().resources.getIntArray(valuesId)
+ valuesArray[which]
+ } else {
+ which
+ }
+ }
+
+ private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
+ val value = item.getSelectedValue()
+ val valuesId = item.valuesId
+ if (valuesId > 0) {
+ val valuesArray = requireContext().resources.getIntArray(valuesId)
+ for (index in valuesArray.indices) {
+ val current = valuesArray[index]
+ if (current == value) {
+ return index
+ }
+ }
+ } else {
+ return value
+ }
+ return -1
+ }
+
+ companion object {
+ const val TAG = "SettingsDialogFragment"
+
+ const val TYPE_RESET_SETTING = -1
+
+ const val TITLE = "Title"
+ const val TYPE = "Type"
+ const val POSITION = "Position"
+
+ fun newInstance(
+ settingsViewModel: SettingsViewModel,
+ clickedItem: SettingsItem,
+ type: Int,
+ position: Int
+ ): SettingsDialogFragment {
+ when (type) {
+ SettingsItem.TYPE_HEADER,
+ SettingsItem.TYPE_SWITCH,
+ SettingsItem.TYPE_SUBMENU,
+ SettingsItem.TYPE_DATETIME_SETTING,
+ SettingsItem.TYPE_RUNNABLE ->
+ throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!")
+
+ SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress(
+ (clickedItem as SliderSetting).getSelectedValue().toFloat()
+ )
+ }
+ settingsViewModel.clickedItem = clickedItem
+
+ val args = Bundle()
+ args.putInt(TYPE, type)
+ args.putInt(POSITION, position)
+ val fragment = SettingsDialogFragment()
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
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 6f6e7be10..ec16f16c4 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
@@ -13,20 +13,17 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.model.SettingsViewModel
+import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter
@@ -45,6 +42,12 @@ class SettingsFragment : Fragment() {
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+
+ val playerIndex = getPlayerIndex()
+ if (playerIndex != -1) {
+ NativeInput.loadInputProfiles()
+ NativeInput.reloadInputDevices()
+ }
}
override fun onCreateView(
@@ -56,9 +59,9 @@ class SettingsFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
+ @SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
settingsAdapter = SettingsAdapter(this, requireContext())
presenter = SettingsFragmentPresenter(
settingsViewModel,
@@ -71,7 +74,17 @@ class SettingsFragment : Fragment() {
) {
args.game!!.title
} else {
- getString(args.menuTag.titleId)
+ when (args.menuTag) {
+ Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> Settings.getPlayerString(1)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> Settings.getPlayerString(2)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> Settings.getPlayerString(3)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> Settings.getPlayerString(4)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> Settings.getPlayerString(5)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> Settings.getPlayerString(6)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> Settings.getPlayerString(7)
+ Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> Settings.getPlayerString(8)
+ else -> getString(args.menuTag.titleId)
+ }
}
binding.listSettings.apply {
adapter = settingsAdapter
@@ -82,16 +95,37 @@ class SettingsFragment : Fragment() {
settingsViewModel.setShouldNavigateBack(true)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldReloadSettingsList.collectLatest {
- if (it) {
- settingsViewModel.setShouldReloadSettingsList(false)
- presenter.loadSettingsList()
- }
- }
- }
+ settingsViewModel.shouldReloadSettingsList.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
+ ) { if (it) presenter.loadSettingsList() }
+ settingsViewModel.adapterItemChanged.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setAdapterItemChanged(-1) }
+ ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
+ settingsViewModel.datasetChanged.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setDatasetChanged(false) }
+ ) { if (it) settingsAdapter?.notifyDataSetChanged() }
+ settingsViewModel.reloadListAndNotifyDataset.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
+ ) { if (it) presenter.loadSettingsList(true) }
+ settingsViewModel.shouldShowResetInputDialog.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
+ ) {
+ if (it) {
+ MessageDialogFragment.newInstance(
+ activity = requireActivity(),
+ titleId = R.string.reset_mapping,
+ descriptionId = R.string.reset_mapping_description,
+ positiveAction = {
+ NativeInput.resetControllerMappings(getPlayerIndex())
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ },
+ negativeAction = {}
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
}
}
@@ -115,6 +149,19 @@ class SettingsFragment : Fragment() {
setInsets()
}
+ private fun getPlayerIndex(): Int =
+ when (args.menuTag) {
+ Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0
+ Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> 1
+ Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> 2
+ Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> 3
+ Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> 4
+ Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> 5
+ Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> 6
+ Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> 7
+ else -> -1
+ }
+
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
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 2ad2f4966..6907bec02 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -3,11 +3,17 @@
package org.yuzu.yuzu_emu.features.settings.ui
+import android.annotation.SuppressLint
import android.os.Build
import android.widget.Toast
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
+import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -15,18 +21,22 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
-import org.yuzu.yuzu_emu.model.SettingsViewModel
+import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
class SettingsFragmentPresenter(
private val settingsViewModel: SettingsViewModel,
private val adapter: SettingsAdapter,
- private var menuTag: Settings.MenuTag
+ private var menuTag: MenuTag
) {
private var settingsList = ArrayList<SettingsItem>()
+ private val context get() = YuzuApplication.appContext
+
// Extension for altering settings list based on each setting's properties
fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!!
@@ -53,73 +63,90 @@ class SettingsFragmentPresenter(
add(item)
}
+ // Allows you to show/hide abstract settings based on the paired setting key
+ fun ArrayList<SettingsItem>.addAbstract(item: SettingsItem) {
+ val pairedSettingKey = item.setting.pairedSettingKey
+ if (pairedSettingKey.isNotEmpty()) {
+ val pairedSettingsItem =
+ this.firstOrNull { it.setting.key == pairedSettingKey } ?: return
+ val pairedSetting = pairedSettingsItem.setting as AbstractBooleanSetting
+ if (!pairedSetting.getBoolean(!NativeConfig.isPerGameConfigLoaded())) return
+ }
+ add(item)
+ }
+
fun onViewCreated() {
loadSettingsList()
}
- fun loadSettingsList() {
+ @SuppressLint("NotifyDataSetChanged")
+ fun loadSettingsList(notifyDataSetChanged: Boolean = false) {
val sl = ArrayList<SettingsItem>()
when (menuTag) {
- Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
- Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
- Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
- Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
- Settings.MenuTag.SECTION_THEME -> addThemeSettings(sl)
- Settings.MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
- else -> {
- val context = YuzuApplication.appContext
- Toast.makeText(
- context,
- context.getString(R.string.unimplemented_menu),
- Toast.LENGTH_SHORT
- ).show()
- return
- }
+ MenuTag.SECTION_ROOT -> addConfigSettings(sl)
+ MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
+ MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
+ MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
+ MenuTag.SECTION_INPUT -> addInputSettings(sl)
+ MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0)
+ MenuTag.SECTION_INPUT_PLAYER_TWO -> addInputPlayer(sl, 1)
+ MenuTag.SECTION_INPUT_PLAYER_THREE -> addInputPlayer(sl, 2)
+ MenuTag.SECTION_INPUT_PLAYER_FOUR -> addInputPlayer(sl, 3)
+ MenuTag.SECTION_INPUT_PLAYER_FIVE -> addInputPlayer(sl, 4)
+ MenuTag.SECTION_INPUT_PLAYER_SIX -> addInputPlayer(sl, 5)
+ MenuTag.SECTION_INPUT_PLAYER_SEVEN -> addInputPlayer(sl, 6)
+ MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
+ MenuTag.SECTION_THEME -> addThemeSettings(sl)
+ MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
}
settingsList = sl
- adapter.submitList(settingsList)
+ adapter.submitList(settingsList) {
+ if (notifyDataSetChanged) {
+ adapter.notifyDataSetChanged()
+ }
+ }
}
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(
SubmenuSetting(
- R.string.preferences_system,
- R.string.preferences_system_description,
- R.drawable.ic_system_settings,
- Settings.MenuTag.SECTION_SYSTEM
+ titleId = R.string.preferences_system,
+ descriptionId = R.string.preferences_system_description,
+ iconId = R.drawable.ic_system_settings,
+ menuKey = MenuTag.SECTION_SYSTEM
)
)
add(
SubmenuSetting(
- R.string.preferences_graphics,
- R.string.preferences_graphics_description,
- R.drawable.ic_graphics,
- Settings.MenuTag.SECTION_RENDERER
+ titleId = R.string.preferences_graphics,
+ descriptionId = R.string.preferences_graphics_description,
+ iconId = R.drawable.ic_graphics,
+ menuKey = MenuTag.SECTION_RENDERER
)
)
add(
SubmenuSetting(
- R.string.preferences_audio,
- R.string.preferences_audio_description,
- R.drawable.ic_audio,
- Settings.MenuTag.SECTION_AUDIO
+ titleId = R.string.preferences_audio,
+ descriptionId = R.string.preferences_audio_description,
+ iconId = R.drawable.ic_audio,
+ menuKey = MenuTag.SECTION_AUDIO
)
)
add(
SubmenuSetting(
- R.string.preferences_debug,
- R.string.preferences_debug_description,
- R.drawable.ic_code,
- Settings.MenuTag.SECTION_DEBUG
+ titleId = R.string.preferences_debug,
+ descriptionId = R.string.preferences_debug_description,
+ iconId = R.drawable.ic_code,
+ menuKey = MenuTag.SECTION_DEBUG
)
)
add(
RunnableSetting(
- R.string.reset_to_default,
- R.string.reset_to_default_description,
- false,
- R.drawable.ic_restore
+ titleId = R.string.reset_to_default,
+ descriptionId = R.string.reset_to_default_description,
+ isRunnable = !NativeLibrary.isRunning(),
+ iconId = R.drawable.ic_restore
) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
)
}
@@ -127,6 +154,7 @@ class SettingsFragmentPresenter(
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
+ add(StringSetting.DEVICE_NAME.key)
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key)
@@ -143,10 +171,12 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_RESOLUTION.key)
add(IntSetting.RENDERER_VSYNC.key)
add(IntSetting.RENDERER_SCALING_FILTER.key)
+ add(IntSetting.FSR_SHARPENING_SLIDER.key)
add(IntSetting.RENDERER_ANTI_ALIASING.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key)
+ add(IntSetting.VERTICAL_ALIGNMENT.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
@@ -162,6 +192,671 @@ class SettingsFragmentPresenter(
}
}
+ private fun addInputSettings(sl: ArrayList<SettingsItem>) {
+ settingsViewModel.currentDevice = 0
+
+ if (NativeConfig.isPerGameConfigLoaded()) {
+ NativeInput.loadInputProfiles()
+ val profiles = NativeInput.getInputProfileNames().toMutableList()
+ profiles.add(0, "")
+ val prettyProfiles = profiles.toTypedArray()
+ prettyProfiles[0] =
+ context.getString(R.string.use_global_input_configuration)
+ sl.apply {
+ for (i in 0 until 8) {
+ add(
+ IntSingleChoiceSetting(
+ getPerGameProfileSetting(profiles, i),
+ titleString = getPlayerProfileString(i + 1),
+ choices = prettyProfiles,
+ values = IntArray(profiles.size) { it }.toTypedArray()
+ )
+ )
+ }
+ }
+ return
+ }
+
+ val getConnectedIcon: (Int) -> Int = { playerIndex: Int ->
+ if (NativeInput.getIsConnected(playerIndex)) {
+ R.drawable.ic_controller
+ } else {
+ R.drawable.ic_controller_disconnected
+ }
+ }
+
+ val inputSettings = NativeConfig.getInputSettings(true)
+ sl.apply {
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(1),
+ descriptionString = inputSettings[0].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_ONE,
+ iconId = getConnectedIcon(0)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(2),
+ descriptionString = inputSettings[1].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_TWO,
+ iconId = getConnectedIcon(1)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(3),
+ descriptionString = inputSettings[2].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_THREE,
+ iconId = getConnectedIcon(2)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(4),
+ descriptionString = inputSettings[3].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_FOUR,
+ iconId = getConnectedIcon(3)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(5),
+ descriptionString = inputSettings[4].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_FIVE,
+ iconId = getConnectedIcon(4)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(6),
+ descriptionString = inputSettings[5].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_SIX,
+ iconId = getConnectedIcon(5)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(7),
+ descriptionString = inputSettings[6].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_SEVEN,
+ iconId = getConnectedIcon(6)
+ )
+ )
+ add(
+ SubmenuSetting(
+ titleString = Settings.getPlayerString(8),
+ descriptionString = inputSettings[7].profileName,
+ menuKey = MenuTag.SECTION_INPUT_PLAYER_EIGHT,
+ iconId = getConnectedIcon(7)
+ )
+ )
+ }
+ }
+
+ private fun getPlayerProfileString(player: Int): String =
+ context.getString(R.string.player_num_profile, player)
+
+ private fun getPerGameProfileSetting(
+ profiles: List<String>,
+ playerIndex: Int
+ ): AbstractIntSetting {
+ return object : AbstractIntSetting {
+ private val players
+ get() = NativeConfig.getInputSettings(false)
+
+ override val key = ""
+
+ override fun getInt(needsGlobal: Boolean): Int {
+ val currentProfile = players[playerIndex].profileName
+ profiles.forEachIndexed { i, profile ->
+ if (profile == currentProfile) {
+ return i
+ }
+ }
+ return 0
+ }
+
+ override fun setInt(value: Int) {
+ NativeInput.loadPerGameConfiguration(playerIndex, value, profiles[value])
+ NativeInput.connectControllers(playerIndex)
+ NativeConfig.saveControlPlayerValues()
+ }
+
+ override val defaultValue = 0
+
+ override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
+
+ override fun reset() = setInt(defaultValue)
+
+ override var global = true
+
+ override val isRuntimeModifiable = true
+
+ override val isSaveable = true
+ }
+ }
+
+ private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) {
+ sl.apply {
+ val connectedSetting = object : AbstractBooleanSetting {
+ override val key = "connected"
+
+ override fun getBoolean(needsGlobal: Boolean): Boolean =
+ NativeInput.getIsConnected(playerIndex)
+
+ override fun setBoolean(value: Boolean) =
+ NativeInput.connectControllers(playerIndex, value)
+
+ override val defaultValue = playerIndex == 0
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ getBoolean(needsGlobal).toString()
+
+ override fun reset() = setBoolean(defaultValue)
+ }
+ add(SwitchSetting(connectedSetting, R.string.connected))
+
+ val styleTags = NativeInput.getSupportedStyleTags(playerIndex)
+ val npadType = object : AbstractIntSetting {
+ override val key = "npad_type"
+ override fun getInt(needsGlobal: Boolean): Int {
+ val styleIndex = NativeInput.getStyleIndex(playerIndex)
+ return styleTags.indexOfFirst { it == styleIndex }
+ }
+
+ override fun setInt(value: Int) {
+ NativeInput.setStyleIndex(playerIndex, styleTags[value])
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ }
+
+ override val defaultValue = NpadStyleIndex.Fullkey.int
+ override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
+ override fun reset() = setInt(defaultValue)
+ override val pairedSettingKey: String = "connected"
+ }
+ addAbstract(
+ IntSingleChoiceSetting(
+ npadType,
+ titleId = R.string.controller_type,
+ choices = styleTags.map { context.getString(it.nameId) }
+ .toTypedArray(),
+ values = IntArray(styleTags.size) { it }.toTypedArray()
+ )
+ )
+
+ InputHandler.updateControllerData()
+
+ val autoMappingSetting = object : AbstractIntSetting {
+ override val key = "auto_mapping_device"
+
+ override fun getInt(needsGlobal: Boolean): Int = -1
+
+ override fun setInt(value: Int) {
+ val registeredController = InputHandler.registeredControllers[value + 1]
+ val displayName = registeredController.get(
+ "display",
+ context.getString(R.string.unknown)
+ )
+ NativeInput.updateMappingsWithDefault(
+ playerIndex,
+ registeredController,
+ displayName
+ )
+ Toast.makeText(
+ context,
+ context.getString(R.string.attempted_auto_map, displayName),
+ Toast.LENGTH_SHORT
+ ).show()
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ }
+
+ override val defaultValue = -1
+
+ override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
+
+ override fun reset() = setInt(defaultValue)
+
+ override val isRuntimeModifiable: Boolean = true
+ }
+
+ val unknownString = context.getString(R.string.unknown)
+ val prettyAutoMappingControllerList = InputHandler.registeredControllers.mapNotNull {
+ val port = it.get("port", -1)
+ return@mapNotNull if (port == 100 || port == -1) {
+ null
+ } else {
+ it.get("display", unknownString)
+ }
+ }.toTypedArray()
+ add(
+ IntSingleChoiceSetting(
+ autoMappingSetting,
+ titleId = R.string.auto_map,
+ descriptionId = R.string.auto_map_description,
+ choices = prettyAutoMappingControllerList,
+ values = IntArray(prettyAutoMappingControllerList.size) { it }.toTypedArray()
+ )
+ )
+
+ val mappingFilterSetting = object : AbstractIntSetting {
+ override val key = "mapping_filter"
+
+ override fun getInt(needsGlobal: Boolean): Int = settingsViewModel.currentDevice
+
+ override fun setInt(value: Int) {
+ settingsViewModel.currentDevice = value
+ }
+
+ override val defaultValue = 0
+
+ override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
+
+ override fun reset() = setInt(defaultValue)
+
+ override val isRuntimeModifiable: Boolean = true
+ }
+
+ val prettyControllerList = InputHandler.registeredControllers.mapNotNull {
+ return@mapNotNull if (it.get("port", 0) == 100) {
+ null
+ } else {
+ it.get("display", unknownString)
+ }
+ }.toTypedArray()
+ add(
+ IntSingleChoiceSetting(
+ mappingFilterSetting,
+ titleId = R.string.input_mapping_filter,
+ descriptionId = R.string.input_mapping_filter_description,
+ choices = prettyControllerList,
+ values = IntArray(prettyControllerList.size) { it }.toTypedArray()
+ )
+ )
+
+ add(InputProfileSetting(playerIndex))
+ add(
+ RunnableSetting(titleId = R.string.reset_to_default, isRunnable = true) {
+ settingsViewModel.setShouldShowResetInputDialog(true)
+ }
+ )
+
+ val styleIndex = NativeInput.getStyleIndex(playerIndex)
+
+ // Buttons
+ when (styleIndex) {
+ NpadStyleIndex.Fullkey,
+ NpadStyleIndex.Handheld,
+ NpadStyleIndex.JoyconDual -> {
+ add(HeaderSetting(R.string.buttons))
+ add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
+ add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
+ add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
+ add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
+ add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus))
+ add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus))
+ add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home))
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.Capture,
+ R.string.button_capture
+ )
+ )
+ }
+
+ NpadStyleIndex.JoyconLeft -> {
+ add(HeaderSetting(R.string.buttons))
+ add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus))
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.Capture,
+ R.string.button_capture
+ )
+ )
+ }
+
+ NpadStyleIndex.JoyconRight -> {
+ add(HeaderSetting(R.string.buttons))
+ add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
+ add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
+ add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
+ add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
+ add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus))
+ add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home))
+ }
+
+ NpadStyleIndex.GameCube -> {
+ add(HeaderSetting(R.string.buttons))
+ add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
+ add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
+ add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
+ add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
+ add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.start_pause))
+ }
+
+ else -> {
+ // No-op
+ }
+ }
+
+ when (styleIndex) {
+ NpadStyleIndex.Fullkey,
+ NpadStyleIndex.Handheld,
+ NpadStyleIndex.JoyconDual,
+ NpadStyleIndex.JoyconLeft -> {
+ add(HeaderSetting(R.string.dpad))
+ add(ButtonInputSetting(playerIndex, NativeButton.DUp, R.string.up))
+ add(ButtonInputSetting(playerIndex, NativeButton.DDown, R.string.down))
+ add(ButtonInputSetting(playerIndex, NativeButton.DLeft, R.string.left))
+ add(ButtonInputSetting(playerIndex, NativeButton.DRight, R.string.right))
+ }
+
+ else -> {
+ // No-op
+ }
+ }
+
+ // Left stick
+ when (styleIndex) {
+ NpadStyleIndex.Fullkey,
+ NpadStyleIndex.Handheld,
+ NpadStyleIndex.JoyconDual,
+ NpadStyleIndex.JoyconLeft -> {
+ add(HeaderSetting(R.string.left_stick))
+ addAll(getStickDirections(playerIndex, NativeAnalog.LStick))
+ add(ButtonInputSetting(playerIndex, NativeButton.LStick, R.string.pressed))
+ addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick))
+ }
+
+ NpadStyleIndex.GameCube -> {
+ add(HeaderSetting(R.string.control_stick))
+ addAll(getStickDirections(playerIndex, NativeAnalog.LStick))
+ addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick))
+ }
+
+ else -> {
+ // No-op
+ }
+ }
+
+ // Right stick
+ when (styleIndex) {
+ NpadStyleIndex.Fullkey,
+ NpadStyleIndex.Handheld,
+ NpadStyleIndex.JoyconDual,
+ NpadStyleIndex.JoyconRight -> {
+ add(HeaderSetting(R.string.right_stick))
+ addAll(getStickDirections(playerIndex, NativeAnalog.RStick))
+ add(ButtonInputSetting(playerIndex, NativeButton.RStick, R.string.pressed))
+ addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick))
+ }
+
+ NpadStyleIndex.GameCube -> {
+ add(HeaderSetting(R.string.c_stick))
+ addAll(getStickDirections(playerIndex, NativeAnalog.RStick))
+ addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick))
+ }
+
+ else -> {
+ // No-op
+ }
+ }
+
+ // L/R, ZL/ZR, and SL/SR
+ when (styleIndex) {
+ NpadStyleIndex.Fullkey,
+ NpadStyleIndex.Handheld -> {
+ add(HeaderSetting(R.string.triggers))
+ add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
+ add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
+ }
+
+ NpadStyleIndex.JoyconDual -> {
+ add(HeaderSetting(R.string.triggers))
+ add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
+ add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SLLeft,
+ R.string.button_sl_left
+ )
+ )
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SRLeft,
+ R.string.button_sr_left
+ )
+ )
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SLRight,
+ R.string.button_sl_right
+ )
+ )
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SRRight,
+ R.string.button_sr_right
+ )
+ )
+ }
+
+ NpadStyleIndex.JoyconLeft -> {
+ add(HeaderSetting(R.string.triggers))
+ add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SLLeft,
+ R.string.button_sl_left
+ )
+ )
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SRLeft,
+ R.string.button_sr_left
+ )
+ )
+ }
+
+ NpadStyleIndex.JoyconRight -> {
+ add(HeaderSetting(R.string.triggers))
+ add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SLRight,
+ R.string.button_sl_right
+ )
+ )
+ add(
+ ButtonInputSetting(
+ playerIndex,
+ NativeButton.SRRight,
+ R.string.button_sr_right
+ )
+ )
+ }
+
+ NpadStyleIndex.GameCube -> {
+ add(HeaderSetting(R.string.triggers))
+ add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_z))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_l))
+ add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_r))
+ }
+
+ else -> {
+ // No-op
+ }
+ }
+
+ add(HeaderSetting(R.string.vibration))
+ val vibrationEnabledSetting = object : AbstractBooleanSetting {
+ override val key = "vibration"
+
+ override fun getBoolean(needsGlobal: Boolean): Boolean =
+ NativeConfig.getInputSettings(true)[playerIndex].vibrationEnabled
+
+ override fun setBoolean(value: Boolean) {
+ val settings = NativeConfig.getInputSettings(true)
+ settings[playerIndex].vibrationEnabled = value
+ NativeConfig.setInputSettings(settings, true)
+ }
+
+ override val defaultValue = true
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ getBoolean(needsGlobal).toString()
+
+ override fun reset() = setBoolean(defaultValue)
+ }
+ add(SwitchSetting(vibrationEnabledSetting, R.string.vibration))
+
+ val useSystemVibratorSetting = object : AbstractBooleanSetting {
+ override val key = ""
+
+ override fun getBoolean(needsGlobal: Boolean): Boolean =
+ NativeConfig.getInputSettings(true)[playerIndex].useSystemVibrator
+
+ override fun setBoolean(value: Boolean) {
+ val settings = NativeConfig.getInputSettings(true)
+ settings[playerIndex].useSystemVibrator = value
+ NativeConfig.setInputSettings(settings, true)
+ }
+
+ override val defaultValue = playerIndex == 0
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ getBoolean(needsGlobal).toString()
+
+ override fun reset() = setBoolean(defaultValue)
+
+ override val pairedSettingKey: String = "vibration"
+ }
+ addAbstract(SwitchSetting(useSystemVibratorSetting, R.string.use_system_vibrator))
+
+ val vibrationStrengthSetting = object : AbstractIntSetting {
+ override val key = ""
+
+ override fun getInt(needsGlobal: Boolean): Int =
+ NativeConfig.getInputSettings(true)[playerIndex].vibrationStrength
+
+ override fun setInt(value: Int) {
+ val settings = NativeConfig.getInputSettings(true)
+ settings[playerIndex].vibrationStrength = value
+ NativeConfig.setInputSettings(settings, true)
+ }
+
+ override val defaultValue = 100
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ getInt(needsGlobal).toString()
+
+ override fun reset() = setInt(defaultValue)
+
+ override val pairedSettingKey: String = "vibration"
+ }
+ addAbstract(
+ SliderSetting(vibrationStrengthSetting, R.string.vibration_strength, units = "%")
+ )
+ }
+ }
+
+ // Convenience function for creating AbstractIntSettings for modifier range/stick range/stick deadzones
+ private fun getStickIntSettingFromParam(
+ playerIndex: Int,
+ paramName: String,
+ stick: NativeAnalog,
+ defaultValue: Int
+ ): AbstractIntSetting =
+ object : AbstractIntSetting {
+ val params get() = NativeInput.getStickParam(playerIndex, stick)
+
+ override val key = ""
+
+ override fun getInt(needsGlobal: Boolean): Int =
+ (params.get(paramName, 0.15f) * 100).toInt()
+
+ override fun setInt(value: Int) {
+ val tempParams = params
+ tempParams.set(paramName, value.toFloat() / 100)
+ NativeInput.setStickParam(playerIndex, stick, tempParams)
+ }
+
+ override val defaultValue = defaultValue
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ getInt(needsGlobal).toString()
+
+ override fun reset() = setInt(defaultValue)
+ }
+
+ private fun getExtraStickSettings(
+ playerIndex: Int,
+ nativeAnalog: NativeAnalog
+ ): List<SettingsItem> {
+ val stickIsController =
+ NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
+ val modifierRangeSetting =
+ getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
+ val stickRangeSetting =
+ getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
+ val stickDeadzoneSetting =
+ getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
+
+ val out = mutableListOf<SettingsItem>().apply {
+ if (stickIsController) {
+ add(SliderSetting(stickRangeSetting, titleId = R.string.range, min = 25, max = 150))
+ add(SliderSetting(stickDeadzoneSetting, R.string.deadzone))
+ } else {
+ add(ModifierInputSetting(playerIndex, NativeAnalog.LStick, R.string.modifier))
+ add(SliderSetting(modifierRangeSetting, R.string.modifier_range))
+ }
+ }
+ return out
+ }
+
+ private fun getStickDirections(player: Int, stick: NativeAnalog): List<AnalogInputSetting> =
+ listOf(
+ AnalogInputSetting(
+ player,
+ stick,
+ AnalogDirection.Up,
+ R.string.up
+ ),
+ AnalogInputSetting(
+ player,
+ stick,
+ AnalogDirection.Down,
+ R.string.down
+ ),
+ AnalogInputSetting(
+ player,
+ stick,
+ AnalogDirection.Left,
+ R.string.left
+ ),
+ AnalogInputSetting(
+ player,
+ stick,
+ AnalogDirection.Right,
+ R.string.right
+ )
+ )
+
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
val theme: AbstractIntSetting = object : AbstractIntSetting {
@@ -184,20 +879,18 @@ class SettingsFragmentPresenter(
add(
SingleChoiceSetting(
theme,
- R.string.change_app_theme,
- 0,
- R.array.themeEntriesA12,
- R.array.themeValuesA12
+ titleId = R.string.change_app_theme,
+ choicesId = R.array.themeEntriesA12,
+ valuesId = R.array.themeValuesA12
)
)
} else {
add(
SingleChoiceSetting(
theme,
- R.string.change_app_theme,
- 0,
- R.array.themeEntries,
- R.array.themeValues
+ titleId = R.string.change_app_theme,
+ choicesId = R.array.themeEntries,
+ valuesId = R.array.themeValues
)
)
}
@@ -226,10 +919,9 @@ class SettingsFragmentPresenter(
add(
SingleChoiceSetting(
themeMode,
- R.string.change_theme_mode,
- 0,
- R.array.themeModeEntries,
- R.array.themeModeValues
+ titleId = R.string.change_theme_mode,
+ choicesId = R.array.themeModeEntries,
+ valuesId = R.array.themeModeValues
)
)
@@ -260,8 +952,8 @@ class SettingsFragmentPresenter(
add(
SwitchSetting(
blackBackgrounds,
- R.string.use_black_backgrounds,
- R.string.use_black_backgrounds_description
+ titleId = R.string.use_black_backgrounds,
+ descriptionId = R.string.use_black_backgrounds_description
)
)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
new file mode 100644
index 000000000..ed60cf34f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
@@ -0,0 +1,183 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.updatePadding
+import androidx.core.widget.doOnTextChanged
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.divider.MaterialDividerItemDecoration
+import com.google.android.material.transition.MaterialSharedAxis
+import info.debatty.java.stringsimilarity.Cosine
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
+
+class SettingsSearchFragment : Fragment() {
+ private var _binding: FragmentSettingsSearchBinding? = null
+ private val binding get() = _binding!!
+
+ private var settingsAdapter: SettingsAdapter? = null
+
+ private val settingsViewModel: SettingsViewModel by activityViewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+ returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
+ reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
+ exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentSettingsSearchBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ if (savedInstanceState != null) {
+ binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
+ }
+
+ settingsAdapter = SettingsAdapter(this, requireContext())
+
+ val dividerDecoration = MaterialDividerItemDecoration(
+ requireContext(),
+ LinearLayoutManager.VERTICAL
+ )
+ dividerDecoration.isLastItemDecorated = false
+ binding.settingsList.apply {
+ adapter = settingsAdapter
+ layoutManager = LinearLayoutManager(requireContext())
+ addItemDecoration(dividerDecoration)
+ }
+
+ focusSearch()
+
+ binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) }
+ binding.searchBackground.setOnClickListener { focusSearch() }
+ binding.clearButton.setOnClickListener { binding.searchText.setText("") }
+ binding.searchText.doOnTextChanged { _, _, _, _ ->
+ search()
+ binding.settingsList.smoothScrollToPosition(0)
+ }
+ settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
+ if (it) {
+ settingsViewModel.setShouldReloadSettingsList(false)
+ search()
+ }
+ }
+
+ search()
+
+ setInsets()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putString(SEARCH_TEXT, binding.searchText.text.toString())
+ }
+
+ private fun search() {
+ val searchTerm = binding.searchText.text.toString().lowercase()
+ binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)
+ if (searchTerm.isEmpty()) {
+ binding.noResultsView.setVisible(visible = false, gone = false)
+ settingsAdapter?.submitList(emptyList())
+ return
+ }
+
+ val baseList = SettingsItem.settingsItems
+ val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1)
+ val sortedList: List<SettingsItem> = baseList.mapNotNull { item ->
+ val title = item.value.title.lowercase()
+ val similarity = similarityAlgorithm.similarity(searchTerm, title)
+ if (similarity > 0.08) {
+ Pair(similarity, item)
+ } else {
+ null
+ }
+ }.sortedByDescending { it.first }.mapNotNull {
+ val item = it.second.value
+ val pairedSettingKey = item.setting.pairedSettingKey
+ val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) {
+ val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false)
+ if (pairedSettingValue) it.second.value else null
+ } else {
+ it.second.value
+ }
+ optionalSetting
+ }
+ settingsAdapter?.submitList(sortedList)
+ binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)
+ }
+
+ private fun focusSearch() {
+ binding.searchText.requestFocus()
+ val imm = requireActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
+ imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
+ }
+
+ private fun setInsets() =
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
+ val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
+ val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
+ val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip)
+
+ val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+ val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
+
+ val leftInsets = barInsets.left + cutoutInsets.left
+ val rightInsets = barInsets.right + cutoutInsets.right
+
+ binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing)
+ binding.frameSearch.updatePadding(
+ left = leftInsets + sideMargin,
+ top = barInsets.top + topMargin,
+ right = rightInsets + sideMargin
+ )
+ binding.noResultsView.updatePadding(
+ left = leftInsets,
+ right = rightInsets,
+ bottom = barInsets.bottom
+ )
+
+ binding.settingsList.updateMargins(
+ left = leftInsets + sideMargin,
+ right = rightInsets + sideMargin
+ )
+ binding.divider.updateMargins(
+ left = leftInsets + sideMargin,
+ right = rightInsets + sideMargin
+ )
+
+ windowInsets
+ }
+
+ companion object {
+ const val SEARCH_TEXT = "SearchText"
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt
new file mode 100644
index 000000000..fbdca04e9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt
@@ -0,0 +1,112 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui
+
+import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.model.Game
+import org.yuzu.yuzu_emu.utils.InputHandler
+import org.yuzu.yuzu_emu.utils.ParamPackage
+
+class SettingsViewModel : ViewModel() {
+ var game: Game? = null
+
+ var clickedItem: SettingsItem? = null
+
+ var currentDevice = 0
+
+ val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
+ private val _shouldRecreate = MutableStateFlow(false)
+
+ val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack
+ private val _shouldNavigateBack = MutableStateFlow(false)
+
+ val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog
+ private val _shouldShowResetSettingsDialog = MutableStateFlow(false)
+
+ val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
+ private val _shouldReloadSettingsList = MutableStateFlow(false)
+
+ val sliderProgress: StateFlow<Int> get() = _sliderProgress
+ private val _sliderProgress = MutableStateFlow(-1)
+
+ val sliderTextValue: StateFlow<String> get() = _sliderTextValue
+ private val _sliderTextValue = MutableStateFlow("")
+
+ val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged
+ private val _adapterItemChanged = MutableStateFlow(-1)
+
+ private val _datasetChanged = MutableStateFlow(false)
+ val datasetChanged = _datasetChanged.asStateFlow()
+
+ private val _reloadListAndNotifyDataset = MutableStateFlow(false)
+ val reloadListAndNotifyDataset = _reloadListAndNotifyDataset.asStateFlow()
+
+ private val _shouldShowDeleteProfileDialog = MutableStateFlow("")
+ val shouldShowDeleteProfileDialog = _shouldShowDeleteProfileDialog.asStateFlow()
+
+ private val _shouldShowResetInputDialog = MutableStateFlow(false)
+ val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow()
+
+ fun setShouldRecreate(value: Boolean) {
+ _shouldRecreate.value = value
+ }
+
+ fun setShouldNavigateBack(value: Boolean) {
+ _shouldNavigateBack.value = value
+ }
+
+ fun setShouldShowResetSettingsDialog(value: Boolean) {
+ _shouldShowResetSettingsDialog.value = value
+ }
+
+ fun setShouldReloadSettingsList(value: Boolean) {
+ _shouldReloadSettingsList.value = value
+ }
+
+ fun setSliderTextValue(value: Float, units: String) {
+ _sliderProgress.value = value.toInt()
+ _sliderTextValue.value = String.format(
+ YuzuApplication.appContext.getString(R.string.value_with_units),
+ value.toInt().toString(),
+ units
+ )
+ }
+
+ fun setSliderProgress(value: Float) {
+ _sliderProgress.value = value.toInt()
+ }
+
+ fun setAdapterItemChanged(value: Int) {
+ _adapterItemChanged.value = value
+ }
+
+ fun setDatasetChanged(value: Boolean) {
+ _datasetChanged.value = value
+ }
+
+ fun setReloadListAndNotifyDataset(value: Boolean) {
+ _reloadListAndNotifyDataset.value = value
+ }
+
+ fun setShouldShowDeleteProfileDialog(profile: String) {
+ _shouldShowDeleteProfileDialog.value = profile
+ }
+
+ fun setShouldShowResetInputDialog(value: Boolean) {
+ _shouldShowResetInputDialog.value = value
+ }
+
+ fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage =
+ try {
+ InputHandler.registeredControllers[currentDevice]
+ } catch (e: IndexOutOfBoundsException) {
+ defaultParams
+ }
+}
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 5ad0899dd..0309fad59 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,7 +13,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@@ -21,28 +21,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) {
setting = item as DateTimeSetting
- binding.textSettingName.setText(item.nameId)
- if (item.descriptionId != 0) {
- binding.textSettingDescription.setText(item.descriptionId)
- binding.textSettingDescription.visibility = View.VISIBLE
- } else {
- binding.textSettingDescription.visibility = View.GONE
- }
-
- binding.textSettingValue.visibility = View.VISIBLE
+ binding.textSettingName.text = item.title
+ binding.textSettingDescription.setVisible(item.description.isNotEmpty())
+ binding.textSettingDescription.text = item.description
+ binding.textSettingValue.setVisible(true)
val epochTime = setting.getValue()
val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime)
- binding.buttonClear.visibility = if (setting.setting.global ||
- !NativeConfig.isPerGameConfigLoaded()
- ) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt
index f5bcf705c..0815c36e2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt
@@ -16,7 +16,7 @@ class HeaderViewHolder(val binding: ListItemSettingsHeaderBinding, adapter: Sett
}
override fun bind(item: SettingsItem) {
- binding.textHeaderName.setText(item.nameId)
+ binding.textHeaderName.text = item.title
}
override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
new file mode 100644
index 000000000..86af3a226
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui.viewholder
+
+import android.view.View
+import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+
+class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
+ SettingViewHolder(binding.root, adapter) {
+ private lateinit var setting: InputProfileSetting
+
+ override fun bind(item: SettingsItem) {
+ setting = item as InputProfileSetting
+ binding.textSettingName.text = setting.title
+ binding.textSettingValue.text =
+ setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) }
+
+ binding.textSettingDescription.setVisible(false)
+ binding.buttonClear.setVisible(false)
+ binding.icon.setVisible(false)
+ binding.buttonClear.setVisible(false)
+ }
+
+ override fun onClick(clicked: View) =
+ adapter.onInputProfileClick(setting, bindingAdapterPosition)
+
+ override fun onLongClick(clicked: View): Boolean = false
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
new file mode 100644
index 000000000..9d9047804
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui.viewholder
+
+import android.view.View
+import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+
+class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) :
+ SettingViewHolder(binding.root, adapter) {
+ private lateinit var setting: InputSetting
+
+ override fun bind(item: SettingsItem) {
+ setting = item as InputSetting
+ binding.textSettingName.text = setting.title
+ binding.textSettingValue.text = setting.getSelectedValue()
+
+ when (item) {
+ is AnalogInputSetting -> {
+ val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
+ binding.buttonOptions.setVisible(
+ param.get("engine", "") == "analog_from_button" ||
+ param.has("axis_x") || param.has("axis_y")
+ )
+ }
+
+ is ButtonInputSetting -> {
+ val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
+ binding.buttonOptions.setVisible(
+ param.has("code") || param.has("button") || param.has("hat") ||
+ param.has("axis")
+ )
+ }
+
+ is ModifierInputSetting -> {
+ val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
+ binding.buttonOptions.setVisible(params.has("modifier"))
+ }
+ }
+
+ binding.buttonOptions.setOnClickListener(null)
+ binding.buttonOptions.setOnClickListener {
+ adapter.onInputOptionsClick(binding.buttonOptions, setting, bindingAdapterPosition)
+ }
+ }
+
+ override fun onClick(clicked: View) =
+ adapter.onInputClick(setting, bindingAdapterPosition)
+
+ override fun onLongClick(clicked: View): Boolean =
+ adapter.onLongClick(setting, bindingAdapterPosition)
+}
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 507184238..fc2ffb515 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
@@ -5,11 +5,11 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import androidx.core.content.res.ResourcesCompat
-import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@@ -17,34 +17,28 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) {
setting = item as RunnableSetting
- if (item.iconId != 0) {
- binding.icon.visibility = View.VISIBLE
+ binding.icon.setVisible(setting.iconId != 0)
+ if (setting.iconId != 0) {
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
- item.iconId,
+ setting.iconId,
binding.icon.context.theme
)
)
- } else {
- binding.icon.visibility = View.GONE
}
- binding.textSettingName.setText(item.nameId)
- if (item.descriptionId != 0) {
- binding.textSettingDescription.setText(item.descriptionId)
- binding.textSettingDescription.visibility = View.VISIBLE
- } else {
- binding.textSettingDescription.visibility = View.GONE
- }
- binding.textSettingValue.visibility = View.GONE
- binding.buttonClear.visibility = View.GONE
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+ binding.textSettingDescription.text = item.description
+ binding.textSettingValue.setVisible(false)
+ binding.buttonClear.setVisible(false)
setStyle(setting.isEditable, binding)
}
override fun onClick(clicked: View) {
- if (!setting.isRuntimeRunnable && !NativeLibrary.isRunning()) {
+ if (setting.isRunnable) {
setting.runnable.invoke()
}
}
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 02dab3785..489f55455 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
@@ -5,11 +5,12 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@@ -17,40 +18,36 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
override fun bind(item: SettingsItem) {
setting = item
- binding.textSettingName.setText(item.nameId)
- if (item.descriptionId != 0) {
- binding.textSettingDescription.setText(item.descriptionId)
- binding.textSettingDescription.visibility = View.VISIBLE
- } else {
- binding.textSettingDescription.visibility = View.GONE
- }
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(item.description.isNotEmpty())
+ binding.textSettingDescription.text = item.description
- binding.textSettingValue.visibility = View.VISIBLE
- if (item is SingleChoiceSetting) {
- val resMgr = binding.textSettingValue.context.resources
- val values = resMgr.getIntArray(item.valuesId)
- for (i in values.indices) {
- if (values[i] == item.getSelectedValue()) {
- binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
- break
+ binding.textSettingValue.setVisible(true)
+ when (item) {
+ is SingleChoiceSetting -> {
+ val resMgr = binding.textSettingValue.context.resources
+ val values = resMgr.getIntArray(item.valuesId)
+ for (i in values.indices) {
+ if (values[i] == item.getSelectedValue()) {
+ binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
+ break
+ }
}
}
- } else if (item is StringSingleChoiceSetting) {
- for (i in item.values.indices) {
- if (item.values[i] == item.getSelectedValue()) {
- binding.textSettingValue.text = item.choices[i]
- break
- }
+
+ is StringSingleChoiceSetting -> {
+ binding.textSettingValue.text = item.getSelectedValue()
}
- }
- binding.buttonClear.visibility = if (setting.setting.global ||
- !NativeConfig.isPerGameConfigLoaded()
- ) {
- View.GONE
- } else {
- View.VISIBLE
+ is IntSingleChoiceSetting -> {
+ binding.textSettingValue.text = item.getChoiceAt(item.getSelectedValue())
+ }
+ }
+ if (binding.textSettingValue.text.isEmpty()) {
+ binding.textSettingValue.setVisible(false)
}
+
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
@@ -63,16 +60,25 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
return
}
- if (setting is SingleChoiceSetting) {
- adapter.onSingleChoiceClick(
- (setting as SingleChoiceSetting),
- bindingAdapterPosition
- )
- } else if (setting is StringSingleChoiceSetting) {
- adapter.onStringSingleChoiceClick(
- (setting as StringSingleChoiceSetting),
+ when (setting) {
+ is SingleChoiceSetting -> adapter.onSingleChoiceClick(
+ setting as SingleChoiceSetting,
bindingAdapterPosition
)
+
+ is StringSingleChoiceSetting -> {
+ adapter.onStringSingleChoiceClick(
+ setting as StringSingleChoiceSetting,
+ bindingAdapterPosition
+ )
+ }
+
+ is IntSingleChoiceSetting -> {
+ adapter.onIntSingleChoiceClick(
+ setting as IntSingleChoiceSetting,
+ bindingAdapterPosition
+ )
+ }
}
}
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 596c18012..90a7138cb 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,7 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@@ -17,27 +17,17 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
override fun bind(item: SettingsItem) {
setting = item as SliderSetting
- binding.textSettingName.setText(item.nameId)
- if (item.descriptionId != 0) {
- binding.textSettingDescription.setText(item.descriptionId)
- binding.textSettingDescription.visibility = View.VISIBLE
- } else {
- binding.textSettingDescription.visibility = View.GONE
- }
- binding.textSettingValue.visibility = View.VISIBLE
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(item.description.isNotEmpty())
+ binding.textSettingDescription.text = setting.description
+ binding.textSettingValue.setVisible(true)
binding.textSettingValue.text = String.format(
binding.textSettingValue.context.getString(R.string.value_with_units),
setting.getSelectedValue(),
setting.units
)
- binding.buttonClear.visibility = if (setting.setting.global ||
- !NativeConfig.isPerGameConfigLoaded()
- ) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt
new file mode 100644
index 000000000..a4fd36f62
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt
@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui.viewholder
+
+import android.view.View
+import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+
+class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
+ SettingViewHolder(binding.root, adapter) {
+ private lateinit var setting: StringInputSetting
+
+ override fun bind(item: SettingsItem) {
+ setting = item as StringInputSetting
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+ binding.textSettingDescription.text = setting.description
+ binding.textSettingValue.setVisible(true)
+ binding.textSettingValue.text = setting.getSelectedValue()
+
+ binding.buttonClear.setVisible(setting.clearable)
+ binding.buttonClear.setOnClickListener {
+ adapter.onClearClick(setting, bindingAdapterPosition)
+ }
+
+ setStyle(setting.isEditable, binding)
+ }
+
+ override fun onClick(clicked: View) {
+ if (setting.isEditable) {
+ adapter.onStringInputClick(setting, bindingAdapterPosition)
+ }
+ }
+
+ override fun onLongClick(clicked: View): Boolean {
+ if (setting.isEditable) {
+ return adapter.onLongClick(setting, bindingAdapterPosition)
+ }
+ return false
+ }
+}
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 20d35a17d..f7a9c08c3 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
@@ -9,39 +9,34 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
- private lateinit var item: SubmenuSetting
+ private lateinit var setting: SubmenuSetting
override fun bind(item: SettingsItem) {
- this.item = item as SubmenuSetting
- if (item.iconId != 0) {
- binding.icon.visibility = View.VISIBLE
+ setting = item as SubmenuSetting
+ binding.icon.setVisible(setting.iconId != 0)
+ if (setting.iconId != 0) {
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
- item.iconId,
+ setting.iconId,
binding.icon.context.theme
)
)
- } else {
- binding.icon.visibility = View.GONE
}
- binding.textSettingName.setText(item.nameId)
- if (item.descriptionId != 0) {
- binding.textSettingDescription.setText(item.descriptionId)
- binding.textSettingDescription.visibility = View.VISIBLE
- } else {
- binding.textSettingDescription.visibility = View.GONE
- }
- binding.textSettingValue.visibility = View.GONE
- binding.buttonClear.visibility = View.GONE
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+ binding.textSettingDescription.text = setting.description
+ binding.textSettingValue.setVisible(false)
+ binding.buttonClear.setVisible(false)
}
override fun onClick(clicked: View) {
- adapter.onSubmenuClick(item)
+ adapter.onSubmenuClick(setting)
}
override fun onLongClick(clicked: View): Boolean {
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 d26bf9374..e5763264a 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,7 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@@ -18,28 +18,17 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
override fun bind(item: SettingsItem) {
setting = item as SwitchSetting
- binding.textSettingName.setText(item.nameId)
- if (item.descriptionId != 0) {
- binding.textSettingDescription.setText(item.descriptionId)
- binding.textSettingDescription.visibility = View.VISIBLE
- } else {
- binding.textSettingDescription.text = ""
- binding.textSettingDescription.visibility = View.GONE
- }
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+ binding.textSettingDescription.text = setting.description
binding.switchWidget.setOnCheckedChangeListener(null)
binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
- adapter.onBooleanClick(item, binding.switchWidget.isChecked, bindingAdapterPosition)
+ adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
}
- binding.buttonClear.visibility = if (setting.setting.global ||
- !NativeConfig.isPerGameConfigLoaded()
- ) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
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
index f5647fa95..110aa2960 100644
--- 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
@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@@ -16,9 +15,6 @@ import androidx.core.view.updatePadding
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
@@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.File
class AddonsFragment : Fragment() {
@@ -60,8 +57,6 @@ class AddonsFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = false)
@@ -78,53 +73,41 @@ class AddonsFragment : Fragment() {
adapter = AddonAdapter(addonViewModel)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.addonList.collect {
- (binding.listAddons.adapter as AddonAdapter).submitList(it)
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.showModInstallPicker.collect {
- if (it) {
- installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
- addonViewModel.showModInstallPicker(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.showModNoticeDialog.collect {
- if (it) {
- MessageDialogFragment.newInstance(
- requireActivity(),
- titleId = R.string.addon_notice,
- descriptionId = R.string.addon_notice_description,
- positiveAction = { addonViewModel.showModInstallPicker(true) }
- ).show(parentFragmentManager, MessageDialogFragment.TAG)
- addonViewModel.showModNoticeDialog(false)
- }
- }
- }
+ addonViewModel.addonList.collect(viewLifecycleOwner) {
+ (binding.listAddons.adapter as AddonAdapter).submitList(it)
+ }
+ addonViewModel.showModInstallPicker.collect(
+ viewLifecycleOwner,
+ resetState = { addonViewModel.showModInstallPicker(false) }
+ ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
+ addonViewModel.showModNoticeDialog.collect(
+ viewLifecycleOwner,
+ resetState = { addonViewModel.showModNoticeDialog(false) }
+ ) {
+ if (it) {
+ MessageDialogFragment.newInstance(
+ requireActivity(),
+ titleId = R.string.addon_notice,
+ descriptionId = R.string.addon_notice_description,
+ dismissible = false,
+ positiveAction = { addonViewModel.showModInstallPicker(true) },
+ negativeAction = {},
+ negativeButtonTitleId = R.string.close
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.addonToDelete.collect {
- if (it != null) {
- MessageDialogFragment.newInstance(
- requireActivity(),
- titleId = R.string.confirm_uninstall,
- descriptionId = R.string.confirm_uninstall_description,
- positiveAction = { addonViewModel.onDeleteAddon(it) }
- ).show(parentFragmentManager, MessageDialogFragment.TAG)
- addonViewModel.setAddonToDelete(null)
- }
- }
- }
+ }
+ addonViewModel.addonToDelete.collect(
+ viewLifecycleOwner,
+ resetState = { addonViewModel.setAddonToDelete(null) }
+ ) {
+ if (it != null) {
+ MessageDialogFragment.newInstance(
+ requireActivity(),
+ titleId = R.string.confirm_uninstall,
+ descriptionId = R.string.confirm_uninstall_description,
+ positiveAction = { addonViewModel.onDeleteAddon(it) },
+ negativeAction = {}
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt
new file mode 100644
index 000000000..299f8da71
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.fragments
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.R
+
+class CoreErrorDialogFragment : DialogFragment() {
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
+ MaterialAlertDialogBuilder(requireActivity())
+ .setTitle(requireArguments().getString(TITLE))
+ .setMessage(requireArguments().getString(MESSAGE))
+ .setPositiveButton(R.string.continue_button, null)
+ .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
+ NativeLibrary.coreErrorAlertResult = false
+ synchronized(NativeLibrary.coreErrorAlertLock) {
+ NativeLibrary.coreErrorAlertLock.notify()
+ }
+ }
+ .create()
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ NativeLibrary.coreErrorAlertResult = true
+ synchronized(NativeLibrary.coreErrorAlertLock) { NativeLibrary.coreErrorAlertLock.notify() }
+ }
+
+ companion object {
+ const val TITLE = "Title"
+ const val MESSAGE = "Message"
+
+ fun newInstance(title: String, message: String): CoreErrorDialogFragment {
+ val frag = CoreErrorDialogFragment()
+ val args = Bundle()
+ args.putString(TITLE, title)
+ args.putString(MESSAGE, message)
+ frag.arguments = args
+ return frag
+ }
+ }
+}
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 41cff46c1..8b23a1021 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
@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
@@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.File
import java.io.IOException
@@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() {
}
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- driverViewModel.showClearButton.collect {
- binding.toolbarDrivers.menu
- .findItem(R.id.menu_driver_use_global).isVisible = it
- }
- }
- }
+ driverViewModel.showClearButton.collect(viewLifecycleOwner) {
+ binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
}
}
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 6a47b29f0..bad56e434 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
@@ -10,14 +10,11 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
+import org.yuzu.yuzu_emu.utils.collect
class DriversLoadingDialogFragment : DialogFragment() {
private val driverViewModel: DriverViewModel by activityViewModels()
@@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
- }
- }
- }
+ driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
}
companion object {
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 44af896da..bcc880e17 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
@@ -15,7 +15,9 @@ import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.os.SystemClock
+import android.util.Rational
import android.view.*
+import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
@@ -24,14 +26,12 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.window.layout.FoldingFeature
@@ -39,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -52,6 +49,7 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
+import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
@@ -59,6 +57,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.*
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.lang.NullPointerException
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@@ -86,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (context is EmulationActivity) {
emulationActivity = context
NativeLibrary.setEmulationActivity(context)
-
- lifecycleScope.launch(Dispatchers.Main) {
- lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- WindowInfoTracker.getOrCreate(context)
- .windowLayoutInfo(context)
- .collect { updateFoldableLayout(context, it) }
- }
- }
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@@ -164,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (requireActivity().isFinishing) {
@@ -273,6 +262,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
+ R.id.menu_controls -> {
+ val action = HomeNavigationDirections.actionGlobalSettingsActivity(
+ null,
+ Settings.MenuTag.SECTION_INPUT
+ )
+ binding.root.findNavController().navigate(action)
+ true
+ }
+
R.id.menu_overlay_controls -> {
showOverlayOptions()
true
@@ -337,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- WindowInfoTracker.getOrCreate(requireContext())
- .windowLayoutInfo(requireActivity())
- .collect {
- updateFoldableLayout(requireActivity() as EmulationActivity, it)
- }
- }
+ WindowInfoTracker.getOrCreate(requireContext())
+ .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
+ updateFoldableLayout(requireActivity() as EmulationActivity, it)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.shaderProgress.collectLatest {
- if (it > 0 && it != emulationViewModel.totalShaders.value) {
- binding.loadingProgressIndicator.isIndeterminate = false
-
- if (it < binding.loadingProgressIndicator.max) {
- binding.loadingProgressIndicator.progress = it
- }
- }
+ emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
+ if (it > 0 && it != emulationViewModel.totalShaders.value) {
+ binding.loadingProgressIndicator.isIndeterminate = false
- if (it == emulationViewModel.totalShaders.value) {
- binding.loadingText.setText(R.string.loading)
- binding.loadingProgressIndicator.isIndeterminate = true
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.totalShaders.collectLatest {
- binding.loadingProgressIndicator.max = it
- }
+ if (it < binding.loadingProgressIndicator.max) {
+ binding.loadingProgressIndicator.progress = it
}
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.shaderMessage.collectLatest {
- if (it.isNotEmpty()) {
- binding.loadingText.text = it
- }
- }
- }
+
+ if (it == emulationViewModel.totalShaders.value) {
+ binding.loadingText.setText(R.string.loading)
+ binding.loadingProgressIndicator.isIndeterminate = true
}
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- driverViewModel.isInteractionAllowed.collect {
- if (it) {
- startEmulation()
- }
- }
- }
+ }
+ emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
+ binding.loadingProgressIndicator.max = it
+ }
+ emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
+ if (it.isNotEmpty()) {
+ binding.loadingText.text = it
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.emulationStarted.collectLatest {
- if (it) {
- binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
- ViewUtils.showView(binding.surfaceInputOverlay)
- ViewUtils.hideView(binding.loadingIndicator)
-
- emulationState.updateSurface()
-
- // Setup overlays
- updateShowFpsOverlay()
- updateThermalOverlay()
- }
- }
- }
+ }
+
+ emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
+ if (it) {
+ binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
+ ViewUtils.showView(binding.surfaceInputOverlay)
+ ViewUtils.hideView(binding.loadingIndicator)
+
+ emulationState.updateSurface()
+
+ // Setup overlays
+ updateShowFpsOverlay()
+ updateThermalOverlay()
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.isEmulationStopping.collectLatest {
- if (it) {
- binding.loadingText.setText(R.string.shutting_down)
- ViewUtils.showView(binding.loadingIndicator)
- ViewUtils.hideView(binding.inputContainer)
- ViewUtils.hideView(binding.showFpsText)
- }
- }
- }
+ }
+ emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
+ if (it) {
+ binding.loadingText.setText(R.string.shutting_down)
+ ViewUtils.showView(binding.loadingIndicator)
+ ViewUtils.hideView(binding.inputContainer)
+ ViewUtils.hideView(binding.showFpsText)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.drawerOpen.collect {
- if (it) {
- binding.drawerLayout.open()
- binding.inGameMenu.requestFocus()
- } else {
- binding.drawerLayout.close()
- }
- }
- }
+ }
+ emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
+ if (it) {
+ binding.drawerLayout.open()
+ binding.inGameMenu.requestFocus()
+ } else {
+ binding.drawerLayout.close()
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.programChanged.collect {
- if (it != 0) {
- emulationViewModel.setEmulationStarted(false)
- binding.drawerLayout.close()
- binding.drawerLayout
- .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- ViewUtils.hideView(binding.surfaceInputOverlay)
- ViewUtils.showView(binding.loadingIndicator)
- }
- }
- }
+ }
+ emulationViewModel.programChanged.collect(viewLifecycleOwner) {
+ if (it != 0) {
+ emulationViewModel.setEmulationStarted(false)
+ binding.drawerLayout.close()
+ binding.drawerLayout
+ .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ ViewUtils.hideView(binding.surfaceInputOverlay)
+ ViewUtils.showView(binding.loadingIndicator)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.emulationStopped.collect {
- if (it && emulationViewModel.programChanged.value != -1) {
- if (perfStatsUpdater != null) {
- perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
- }
- emulationState.changeProgram(emulationViewModel.programChanged.value)
- emulationViewModel.setProgramChanged(-1)
- emulationViewModel.setEmulationStopped(false)
- }
- }
+ }
+ emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
+ if (it && emulationViewModel.programChanged.value != -1) {
+ if (perfStatsUpdater != null) {
+ perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
+ emulationState.changeProgram(emulationViewModel.programChanged.value)
+ emulationViewModel.setProgramChanged(-1)
+ emulationViewModel.setEmulationStopped(false)
}
}
+
+ driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
+ if (it) startEmulation()
+ }
}
private fun startEmulation(programIndex: Int = 0) {
@@ -487,14 +442,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.close()
}
if (showInputOverlay) {
- binding.surfaceInputOverlay.visibility = View.INVISIBLE
+ binding.surfaceInputOverlay.setVisible(visible = false, gone = false)
}
} else {
- if (showInputOverlay && emulationViewModel.emulationStarted.value) {
- binding.surfaceInputOverlay.visibility = View.VISIBLE
- } else {
- binding.surfaceInputOverlay.visibility = View.INVISIBLE
- }
+ binding.surfaceInputOverlay.setVisible(
+ showInputOverlay && emulationViewModel.emulationStarted.value
+ )
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
@@ -531,7 +484,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun updateShowFpsOverlay() {
- if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
+ val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
+ binding.showFpsText.setVisible(showOverlay)
+ if (showOverlay) {
val SYSTEM_FPS = 0
val FPS = 1
val FRAMETIME = 2
@@ -551,17 +506,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
perfStatsUpdateHandler.post(perfStatsUpdater!!)
- binding.showFpsText.visibility = View.VISIBLE
} else {
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
- binding.showFpsText.visibility = View.GONE
}
}
private fun updateThermalOverlay() {
- if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
+ val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
+ binding.showThermalsText.setVisible(showOverlay)
+ if (showOverlay) {
thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
@@ -583,12 +538,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
- binding.showThermalsText.visibility = View.VISIBLE
} else {
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
}
- binding.showThermalsText.visibility = View.GONE
}
}
@@ -617,7 +570,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun updateScreenLayout() {
- binding.surfaceEmulation.setAspectRatio(null)
+ val verticalAlignment =
+ EmulationVerticalAlignment.from(IntSetting.VERTICAL_ALIGNMENT.getInt())
+ val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.getInt()) {
+ 0 -> Rational(16, 9)
+ 1 -> Rational(4, 3)
+ 2 -> Rational(21, 9)
+ 3 -> Rational(16, 10)
+ else -> null // Best fit
+ }
+ when (verticalAlignment) {
+ EmulationVerticalAlignment.Top -> {
+ binding.surfaceEmulation.setAspectRatio(aspectRatio)
+ val params = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
+ binding.surfaceEmulation.layoutParams = params
+ }
+
+ EmulationVerticalAlignment.Center -> {
+ binding.surfaceEmulation.setAspectRatio(null)
+ binding.surfaceEmulation.updateLayoutParams {
+ width = ViewGroup.LayoutParams.MATCH_PARENT
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ }
+
+ EmulationVerticalAlignment.Bottom -> {
+ binding.surfaceEmulation.setAspectRatio(aspectRatio)
+ val params =
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ binding.surfaceEmulation.layoutParams = params
+ }
+ }
+ emulationState.updateSurface()
emulationActivity?.buildPictureInPictureParams()
updateOrientation()
}
@@ -818,12 +810,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
- binding.doneControlConfig.visibility = View.VISIBLE
+ binding.doneControlConfig.setVisible(true)
binding.surfaceInputOverlay.setIsInEditMode(true)
}
private fun stopConfiguringControls() {
- binding.doneControlConfig.visibility = View.GONE
+ binding.doneControlConfig.setVisible(false)
binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
index 5c558b1a5..3a6f7a38c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -13,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null
@@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() {
adapter = FolderAdapter(requireActivity(), gamesViewModel)
}
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.folders.collect {
- (binding.listFolders.adapter as FolderAdapter).submitList(it)
- }
- }
+ gamesViewModel.folders.collect(viewLifecycleOwner) {
+ (binding.listFolders.adapter as FolderAdapter).submitList(it)
}
val mainActivity = requireActivity() as MainActivity
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
index dbd56e84f..97a8954bb 100644
--- 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
@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.GameMetadata
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameInfoFragment : Fragment() {
@@ -85,7 +86,7 @@ class GameInfoFragment : Fragment() {
copyToClipboard(getString(R.string.developer), args.game.developer)
}
} else {
- developer.visibility = View.GONE
+ developer.setVisible(false)
}
version.setHint(R.string.version)
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
index d14b2c634..c06842c59 100644
--- 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
@@ -3,11 +3,9 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.os.Bundle
-import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -18,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
@@ -46,7 +42,9 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
+import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
import java.io.File
@@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -107,13 +103,7 @@ class GamePropertiesFragment : Fragment() {
GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
binding.title.text = args.game.title
- binding.title.postDelayed(
- {
- binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE
- binding.title.isSelected = true
- },
- 3000
- )
+ binding.title.marquee()
binding.buttonStart.setOnClickListener {
LaunchGameDialogFragment.newInstance(args.game)
@@ -122,28 +112,14 @@ class GamePropertiesFragment : Fragment() {
reloadList()
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- homeViewModel.openImportSaves.collect {
- if (it) {
- importSaves.launch(arrayOf("application/zip"))
- homeViewModel.setOpenImportSaves(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- homeViewModel.reloadPropertiesList.collect {
- if (it) {
- reloadList()
- homeViewModel.reloadPropertiesList(false)
- }
- }
- }
- }
- }
+ homeViewModel.openImportSaves.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.setOpenImportSaves(false) }
+ ) { if (it) importSaves.launch(arrayOf("application/zip")) }
+ homeViewModel.reloadPropertiesList.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.reloadPropertiesList(false) }
+ ) { if (it) reloadList() }
setInsets()
}
@@ -243,7 +219,9 @@ class GamePropertiesFragment : Fragment() {
requireActivity(),
titleId = R.string.delete_save_data,
descriptionId = R.string.delete_save_data_warning_description,
- positiveAction = {
+ positiveButtonTitleId = android.R.string.cancel,
+ negativeButtonTitleId = android.R.string.ok,
+ negativeAction = {
File(args.game.saveDir).deleteRecursively()
Toast.makeText(
YuzuApplication.appContext,
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 87e130d3e..14a2504b6 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
@@ -91,6 +91,20 @@ class HomeSettingsFragment : Fragment() {
)
add(
HomeSetting(
+ R.string.preferences_controls,
+ R.string.preferences_controls_description,
+ R.drawable.ic_controller,
+ {
+ val action = HomeNavigationDirections.actionGlobalSettingsActivity(
+ null,
+ Settings.MenuTag.SECTION_INPUT
+ )
+ binding.root.findNavController().navigate(action)
+ }
+ )
+ )
+ add(
+ HomeSetting(
R.string.gpu_driver_manager,
R.string.install_gpu_driver_description,
R.drawable.ic_build,
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 63112dc6f..d218da1c8 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
@@ -14,9 +14,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
@@ -75,14 +73,10 @@ class InstallableFragment : Fragment() {
binding.root.findNavController().popBackStack()
}
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.openImportSaves.collect {
- if (it) {
- importSaves.launch(arrayOf("application/zip"))
- homeViewModel.setOpenImportSaves(false)
- }
- }
+ homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
+ if (it) {
+ importSaves.launch(arrayOf("application/zip"))
+ homeViewModel.setOpenImportSaves(false)
}
}
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 22b084b9a..c370964e1 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
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
-import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@@ -16,18 +15,52 @@ import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.model.MessageDialogViewModel
+import org.yuzu.yuzu_emu.utils.Log
class MessageDialogFragment : DialogFragment() {
private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE_ID)
- val titleString = requireArguments().getString(TITLE_STRING)!!
+ val title = if (titleId != 0) {
+ getString(titleId)
+ } else {
+ requireArguments().getString(TITLE_STRING)!!
+ }
+
val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
- val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
+ val description = if (descriptionId != 0) {
+ getString(descriptionId)
+ } else {
+ requireArguments().getString(DESCRIPTION_STRING)!!
+ }
+
+ val positiveButtonId = requireArguments().getInt(POSITIVE_BUTTON_TITLE_ID)
+ val positiveButtonString = requireArguments().getString(POSITIVE_BUTTON_TITLE_STRING)!!
+ val positiveButton = if (positiveButtonId != 0) {
+ getString(positiveButtonId)
+ } else if (positiveButtonString.isNotEmpty()) {
+ positiveButtonString
+ } else if (messageDialogViewModel.positiveAction != null) {
+ getString(android.R.string.ok)
+ } else {
+ getString(R.string.close)
+ }
+
+ val negativeButtonId = requireArguments().getInt(NEGATIVE_BUTTON_TITLE_ID)
+ val negativeButtonString = requireArguments().getString(NEGATIVE_BUTTON_TITLE_STRING)!!
+ val negativeButton = if (negativeButtonId != 0) {
+ getString(negativeButtonId)
+ } else if (negativeButtonString.isNotEmpty()) {
+ negativeButtonString
+ } else {
+ getString(android.R.string.cancel)
+ }
+
val helpLinkId = requireArguments().getInt(HELP_LINK)
val dismissible = requireArguments().getBoolean(DISMISSIBLE)
- val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
+ val clearPositiveAction = requireArguments().getBoolean(CLEAR_ACTIONS)
+ val showNegativeButton = requireArguments().getBoolean(SHOW_NEGATIVE_BUTTON)
val builder = MaterialAlertDialogBuilder(requireContext())
@@ -35,21 +68,19 @@ class MessageDialogFragment : DialogFragment() {
messageDialogViewModel.positiveAction = null
}
- if (messageDialogViewModel.positiveAction == null) {
- builder.setPositiveButton(R.string.close, null)
- } else {
- builder.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
- messageDialogViewModel.positiveAction?.invoke()
- }.setNegativeButton(android.R.string.cancel, null)
+ builder.setPositiveButton(positiveButton) { _, _ ->
+ messageDialogViewModel.positiveAction?.invoke()
+ }
+ if (messageDialogViewModel.negativeAction != null || showNegativeButton) {
+ builder.setNegativeButton(negativeButton) { _, _ ->
+ messageDialogViewModel.negativeAction?.invoke()
+ }
}
- if (titleId != 0) builder.setTitle(titleId)
- if (titleString.isNotEmpty()) builder.setTitle(titleString)
-
- if (descriptionId != 0) {
- builder.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
+ if (title.isNotEmpty()) builder.setTitle(title)
+ if (description.isNotEmpty()) {
+ builder.setMessage(Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY))
}
- if (descriptionString.isNotEmpty()) builder.setMessage(descriptionString)
if (helpLinkId != 0) {
builder.setNeutralButton(R.string.learn_more) { _, _ ->
@@ -76,8 +107,41 @@ class MessageDialogFragment : DialogFragment() {
private const val DESCRIPTION_STRING = "DescriptionString"
private const val HELP_LINK = "Link"
private const val DISMISSIBLE = "Dismissible"
- private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
-
+ private const val CLEAR_ACTIONS = "ClearActions"
+ private const val POSITIVE_BUTTON_TITLE_ID = "PositiveButtonTitleId"
+ private const val POSITIVE_BUTTON_TITLE_STRING = "PositiveButtonTitleString"
+ private const val SHOW_NEGATIVE_BUTTON = "ShowNegativeButton"
+ private const val NEGATIVE_BUTTON_TITLE_ID = "NegativeButtonTitleId"
+ private const val NEGATIVE_BUTTON_TITLE_STRING = "NegativeButtonTitleString"
+
+ /**
+ * Creates a new [MessageDialogFragment] instance.
+ * @param activity Activity that will hold a [MessageDialogViewModel] instance if using
+ * [positiveAction] or [negativeAction].
+ * @param titleId String resource ID that will be used for the title. [titleString] used if 0.
+ * @param titleString String that will be used for the title. No title is set if empty.
+ * @param descriptionId String resource ID that will be used for the description.
+ * [descriptionString] used if 0.
+ * @param descriptionString String that will be used for the description.
+ * No description is set if empty.
+ * @param helpLinkId String resource ID that contains a help link. Will be added as a neutral
+ * button with the title R.string.help.
+ * @param dismissible Whether the dialog is dismissible or not. Typically used to ensure that
+ * the user clicks on one of the dialog buttons before closing.
+ * @param positiveButtonTitleId String resource ID that will be used for the positive button.
+ * [positiveButtonTitleString] used if 0.
+ * @param positiveButtonTitleString String that will be used for the positive button.
+ * android.R.string.close used if empty. android.R.string.ok will be used if [positiveAction]
+ * is not null.
+ * @param positiveAction Lambda to run when the positive button is clicked.
+ * @param showNegativeButton Normally the negative button isn't shown if there is no
+ * [negativeAction] set. This can override that behavior to always show a button.
+ * @param negativeButtonTitleId String resource ID that will be used for the negative button.
+ * [negativeButtonTitleString] used if 0.
+ * @param negativeButtonTitleString String that will be used for the negative button.
+ * android.R.string.cancel used if empty.
+ * @param negativeAction Lambda to run when the negative button is clicked
+ */
fun newInstance(
activity: FragmentActivity? = null,
titleId: Int = 0,
@@ -86,16 +150,27 @@ class MessageDialogFragment : DialogFragment() {
descriptionString: String = "",
helpLinkId: Int = 0,
dismissible: Boolean = true,
- positiveAction: (() -> Unit)? = null
+ positiveButtonTitleId: Int = 0,
+ positiveButtonTitleString: String = "",
+ positiveAction: (() -> Unit)? = null,
+ showNegativeButton: Boolean = false,
+ negativeButtonTitleId: Int = 0,
+ negativeButtonTitleString: String = "",
+ negativeAction: (() -> Unit)? = null
): MessageDialogFragment {
- var clearPositiveAction = false
+ var clearActions = false
if (activity != null) {
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
clear()
this.positiveAction = positiveAction
+ this.negativeAction = negativeAction
}
} else {
- clearPositiveAction = true
+ clearActions = true
+ }
+
+ if (activity == null && (positiveAction == null || negativeAction == null)) {
+ Log.warning("[$TAG] Tried to set action with no activity!")
}
val dialog = MessageDialogFragment()
@@ -106,7 +181,12 @@ class MessageDialogFragment : DialogFragment() {
putString(DESCRIPTION_STRING, descriptionString)
putInt(HELP_LINK, helpLinkId)
putBoolean(DISMISSIBLE, dismissible)
- putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
+ putBoolean(CLEAR_ACTIONS, clearActions)
+ putInt(POSITIVE_BUTTON_TITLE_ID, positiveButtonTitleId)
+ putString(POSITIVE_BUTTON_TITLE_STRING, positiveButtonTitleString)
+ putBoolean(SHOW_NEGATIVE_BUTTON, showNegativeButton)
+ putInt(NEGATIVE_BUTTON_TITLE_ID, negativeButtonTitleId)
+ putString(NEGATIVE_BUTTON_TITLE_STRING, negativeButtonTitleString)
}
dialog.arguments = bundle
return dialog
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index d201cb80c..ee3bb0386 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -13,15 +13,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
class ProgressDialogFragment : DialogFragment() {
private val taskViewModel: TaskViewModel by activityViewModels()
@@ -64,72 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.message.isSelected = true
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.isComplete.collect {
- if (it) {
- dismiss()
- when (val result = taskViewModel.result.value) {
- is String -> Toast.makeText(
- requireContext(),
- result,
- Toast.LENGTH_LONG
- ).show()
-
- is MessageDialogFragment -> result.show(
- requireActivity().supportFragmentManager,
- MessageDialogFragment.TAG
- )
-
- else -> {
- // Do nothing
- }
- }
- taskViewModel.clear()
- }
+ taskViewModel.isComplete.collect(viewLifecycleOwner) {
+ if (it) {
+ dismiss()
+ when (val result = taskViewModel.result.value) {
+ is String -> Toast.makeText(
+ requireContext(),
+ result,
+ Toast.LENGTH_LONG
+ ).show()
+
+ is MessageDialogFragment -> result.show(
+ requireActivity().supportFragmentManager,
+ MessageDialogFragment.TAG
+ )
+
+ else -> {
+ // Do nothing
}
}
+ taskViewModel.clear()
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.cancelled.collect {
- if (it) {
- dialog?.setTitle(R.string.cancelling)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.progress.collect {
- if (it != 0.0) {
- binding.progressBar.apply {
- isIndeterminate = false
- progress = (
- (it / taskViewModel.maxProgress.value) *
- PROGRESS_BAR_RESOLUTION
- ).toInt()
- min = 0
- max = PROGRESS_BAR_RESOLUTION
- }
- }
- }
- }
+ }
+ taskViewModel.cancelled.collect(viewLifecycleOwner) {
+ if (it) {
+ dialog?.setTitle(R.string.cancelling)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.message.collect {
- if (it.isEmpty()) {
- binding.message.visibility = View.GONE
- } else {
- binding.message.visibility = View.VISIBLE
- binding.message.text = it
- }
- }
+ }
+ taskViewModel.progress.collect(viewLifecycleOwner) {
+ if (it != 0.0) {
+ binding.progressBar.apply {
+ isIndeterminate = false
+ progress = (
+ (it / taskViewModel.maxProgress.value) *
+ PROGRESS_BAR_RESOLUTION
+ ).toInt()
+ min = 0
+ max = PROGRESS_BAR_RESOLUTION
}
}
}
+ taskViewModel.message.collect(viewLifecycleOwner) {
+ binding.message.setVisible(it.isNotEmpty())
+ binding.message.text = it
+ }
}
// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
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 20b10b1a0..662ae9760 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
@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
@@ -18,14 +17,9 @@ import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -35,6 +29,8 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -58,8 +54,6 @@ class SearchFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -81,42 +75,18 @@ class SearchFragment : Fragment() {
binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() }
binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
- if (text.toString().isNotEmpty()) {
- binding.clearButton.visibility = View.VISIBLE
- } else {
- binding.clearButton.visibility = View.INVISIBLE
- }
+ binding.clearButton.setVisible(text.toString().isNotEmpty())
filterAndSearch()
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.searchFocused.collect {
- if (it) {
- focusSearch()
- gamesViewModel.setSearchFocused(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.games.collectLatest { filterAndSearch() }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.searchedGames.collect {
- (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
- if (it.isEmpty()) {
- binding.noResultsView.visibility = View.VISIBLE
- } else {
- binding.noResultsView.visibility = View.GONE
- }
- }
- }
- }
+ gamesViewModel.searchFocused.collect(
+ viewLifecycleOwner,
+ resetState = { gamesViewModel.setSearchFocused(false) }
+ ) { if (it) focusSearch() }
+ gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
+ gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
+ (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
+ binding.noResultsView.setVisible(it.isNotEmpty())
}
binding.clearButton.setOnClickListener { binding.searchText.setText("") }
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
deleted file mode 100644
index 60e029f34..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.fragments
-
-import android.app.Dialog
-import android.content.DialogInterface
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.DialogFragment
-import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.slider.Slider
-import kotlinx.coroutines.launch
-import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
-import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
-import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
-import org.yuzu.yuzu_emu.model.SettingsViewModel
-
-class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
- private var type = 0
- private var position = 0
-
- private var defaultCancelListener =
- DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
-
- private val settingsViewModel: SettingsViewModel by activityViewModels()
-
- private lateinit var sliderBinding: DialogSliderBinding
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- type = requireArguments().getInt(TYPE)
- position = requireArguments().getInt(POSITION)
-
- if (settingsViewModel.clickedItem == null) dismiss()
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- return when (type) {
- TYPE_RESET_SETTING -> {
- MaterialAlertDialogBuilder(requireContext())
- .setMessage(R.string.reset_setting_confirmation)
- .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
- settingsViewModel.clickedItem!!.setting.reset()
- settingsViewModel.setAdapterItemChanged(position)
- }
- .setNegativeButton(android.R.string.cancel, null)
- .create()
- }
-
- SettingsItem.TYPE_SINGLE_CHOICE -> {
- val item = settingsViewModel.clickedItem as SingleChoiceSetting
- val value = getSelectionForSingleChoiceValue(item)
- MaterialAlertDialogBuilder(requireContext())
- .setTitle(item.nameId)
- .setSingleChoiceItems(item.choicesId, value, this)
- .create()
- }
-
- SettingsItem.TYPE_SLIDER -> {
- sliderBinding = DialogSliderBinding.inflate(layoutInflater)
- val item = settingsViewModel.clickedItem as SliderSetting
-
- settingsViewModel.setSliderTextValue(item.getSelectedValue().toFloat(), item.units)
- sliderBinding.slider.apply {
- valueFrom = item.min.toFloat()
- valueTo = item.max.toFloat()
- value = settingsViewModel.sliderProgress.value.toFloat()
- addOnChangeListener { _: Slider, value: Float, _: Boolean ->
- settingsViewModel.setSliderTextValue(value, item.units)
- }
- }
-
- MaterialAlertDialogBuilder(requireContext())
- .setTitle(item.nameId)
- .setView(sliderBinding.root)
- .setPositiveButton(android.R.string.ok, this)
- .setNegativeButton(android.R.string.cancel, defaultCancelListener)
- .create()
- }
-
- SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
- val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
- MaterialAlertDialogBuilder(requireContext())
- .setTitle(item.nameId)
- .setSingleChoiceItems(item.choices, item.selectValueIndex, this)
- .create()
- }
-
- else -> super.onCreateDialog(savedInstanceState)
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return when (type) {
- SettingsItem.TYPE_SLIDER -> sliderBinding.root
- else -> super.onCreateView(inflater, container, savedInstanceState)
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- when (type) {
- SettingsItem.TYPE_SLIDER -> {
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.sliderTextValue.collect {
- sliderBinding.textValue.text = it
- }
- }
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.sliderProgress.collect {
- sliderBinding.slider.value = it.toFloat()
- }
- }
- }
- }
- }
- }
-
- override fun onClick(dialog: DialogInterface, which: Int) {
- when (settingsViewModel.clickedItem) {
- is SingleChoiceSetting -> {
- val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting
- val value = getValueForSingleChoiceSelection(scSetting, which)
- scSetting.setSelectedValue(value)
- }
-
- is StringSingleChoiceSetting -> {
- val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting
- val value = scSetting.getValueAt(which)
- scSetting.setSelectedValue(value)
- }
-
- is SliderSetting -> {
- val sliderSetting = settingsViewModel.clickedItem as SliderSetting
- sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
- }
- }
- closeDialog()
- }
-
- private fun closeDialog() {
- settingsViewModel.setAdapterItemChanged(position)
- settingsViewModel.clickedItem = null
- settingsViewModel.setSliderProgress(-1f)
- dismiss()
- }
-
- private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int {
- val valuesId = item.valuesId
- return if (valuesId > 0) {
- val valuesArray = requireContext().resources.getIntArray(valuesId)
- valuesArray[which]
- } else {
- which
- }
- }
-
- private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
- val value = item.getSelectedValue()
- val valuesId = item.valuesId
- if (valuesId > 0) {
- val valuesArray = requireContext().resources.getIntArray(valuesId)
- for (index in valuesArray.indices) {
- val current = valuesArray[index]
- if (current == value) {
- return index
- }
- }
- } else {
- return value
- }
- return -1
- }
-
- companion object {
- const val TAG = "SettingsDialogFragment"
-
- const val TYPE_RESET_SETTING = -1
-
- const val TITLE = "Title"
- const val TYPE = "Type"
- const val POSITION = "Position"
-
- fun newInstance(
- settingsViewModel: SettingsViewModel,
- clickedItem: SettingsItem,
- type: Int,
- position: Int
- ): SettingsDialogFragment {
- when (type) {
- SettingsItem.TYPE_HEADER,
- SettingsItem.TYPE_SWITCH,
- SettingsItem.TYPE_SUBMENU,
- SettingsItem.TYPE_DATETIME_SETTING,
- SettingsItem.TYPE_RUNNABLE ->
- throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!")
-
- SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress(
- (clickedItem as SliderSetting).getSelectedValue().toFloat()
- )
- }
- settingsViewModel.clickedItem = clickedItem
-
- val args = Bundle()
- args.putInt(TYPE, type)
- args.putInt(POSITION, position)
- val fragment = SettingsDialogFragment()
- fragment.arguments = args
- return fragment
- }
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
deleted file mode 100644
index a135b80b4..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ /dev/null
@@ -1,193 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.fragments
-
-import android.content.Context
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.InputMethodManager
-import androidx.core.view.ViewCompat
-import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.updatePadding
-import androidx.core.widget.doOnTextChanged
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.divider.MaterialDividerItemDecoration
-import com.google.android.material.transition.MaterialSharedAxis
-import info.debatty.java.stringsimilarity.Cosine
-import kotlinx.coroutines.launch
-import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.model.SettingsViewModel
-import org.yuzu.yuzu_emu.utils.NativeConfig
-import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
-
-class SettingsSearchFragment : Fragment() {
- private var _binding: FragmentSettingsSearchBinding? = null
- private val binding get() = _binding!!
-
- private var settingsAdapter: SettingsAdapter? = null
-
- private val settingsViewModel: SettingsViewModel by activityViewModels()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
- returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
- reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
- exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentSettingsSearchBinding.inflate(layoutInflater)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- if (savedInstanceState != null) {
- binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
- }
-
- settingsAdapter = SettingsAdapter(this, requireContext())
-
- val dividerDecoration = MaterialDividerItemDecoration(
- requireContext(),
- LinearLayoutManager.VERTICAL
- )
- dividerDecoration.isLastItemDecorated = false
- binding.settingsList.apply {
- adapter = settingsAdapter
- layoutManager = LinearLayoutManager(requireContext())
- addItemDecoration(dividerDecoration)
- }
-
- focusSearch()
-
- binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) }
- binding.searchBackground.setOnClickListener { focusSearch() }
- binding.clearButton.setOnClickListener { binding.searchText.setText("") }
- binding.searchText.doOnTextChanged { _, _, _, _ ->
- search()
- binding.settingsList.smoothScrollToPosition(0)
- }
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldReloadSettingsList.collect {
- if (it) {
- settingsViewModel.setShouldReloadSettingsList(false)
- search()
- }
- }
- }
- }
-
- search()
-
- setInsets()
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putString(SEARCH_TEXT, binding.searchText.text.toString())
- }
-
- private fun search() {
- val searchTerm = binding.searchText.text.toString().lowercase()
- binding.clearButton.visibility =
- if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
- if (searchTerm.isEmpty()) {
- binding.noResultsView.visibility = View.VISIBLE
- settingsAdapter?.submitList(emptyList())
- return
- }
-
- val baseList = SettingsItem.settingsItems
- val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1)
- val sortedList: List<SettingsItem> = baseList.mapNotNull { item ->
- val title = getString(item.value.nameId).lowercase()
- val similarity = similarityAlgorithm.similarity(searchTerm, title)
- if (similarity > 0.08) {
- Pair(similarity, item)
- } else {
- null
- }
- }.sortedByDescending { it.first }.mapNotNull {
- val item = it.second.value
- val pairedSettingKey = item.setting.pairedSettingKey
- val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) {
- val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false)
- if (pairedSettingValue) it.second.value else null
- } else {
- it.second.value
- }
- optionalSetting
- }
- settingsAdapter?.submitList(sortedList)
- binding.noResultsView.visibility =
- if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
- }
-
- private fun focusSearch() {
- binding.searchText.requestFocus()
- val imm = requireActivity()
- .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
- imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
- }
-
- private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(
- binding.root
- ) { _: View, windowInsets: WindowInsetsCompat ->
- val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
- val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
- val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip)
-
- val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
- val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
-
- val leftInsets = barInsets.left + cutoutInsets.left
- val rightInsets = barInsets.right + cutoutInsets.right
-
- binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing)
- binding.frameSearch.updatePadding(
- left = leftInsets + sideMargin,
- top = barInsets.top + topMargin,
- right = rightInsets + sideMargin
- )
- binding.noResultsView.updatePadding(
- left = leftInsets,
- right = rightInsets,
- bottom = barInsets.bottom
- )
-
- binding.settingsList.updateMargins(
- left = leftInsets + sideMargin,
- right = rightInsets + sideMargin
- )
- binding.divider.updateMargins(
- left = leftInsets + sideMargin,
- right = rightInsets + sideMargin
- )
-
- windowInsets
- }
-
- companion object {
- const val SEARCH_TEXT = "SearchText"
- }
-}
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 ebf41a639..4f7548e98 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
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.fragments
import android.Manifest
-import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -23,9 +22,6 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
@@ -46,6 +42,8 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null
@@ -77,8 +75,6 @@ class SetupFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
@@ -210,28 +206,14 @@ class SetupFragment : Fragment() {
)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.shouldPageForward.collect {
- if (it) {
- pageForward()
- homeViewModel.setShouldPageForward(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.gamesDirSelected.collect {
- if (it) {
- gamesDirCallback.onStepCompleted()
- homeViewModel.setGamesDirSelected(false)
- }
- }
- }
- }
- }
+ homeViewModel.shouldPageForward.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.setShouldPageForward(false) }
+ ) { if (it) pageForward() }
+ homeViewModel.gamesDirSelected.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.setGamesDirSelected(false) }
+ ) { if (it) gamesDirCallback.onStepCompleted() }
binding.viewPager2.apply {
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
@@ -292,12 +274,8 @@ class SetupFragment : Fragment() {
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
- if (nextIsVisible) {
- binding.buttonNext.visibility = View.VISIBLE
- }
- if (backIsVisible) {
- binding.buttonBack.visibility = View.VISIBLE
- }
+ binding.buttonNext.setVisible(nextIsVisible)
+ binding.buttonBack.setVisible(backIsVisible)
} else {
hasBeenWarned = BooleanArray(pages.size)
}
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 641c5cb17..2db005e49 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
@@ -7,8 +7,10 @@ import androidx.lifecycle.ViewModel
class MessageDialogViewModel : ViewModel() {
var positiveAction: (() -> Unit)? = null
+ var negativeAction: (() -> Unit)? = null
fun clear() {
positiveAction = null
+ negativeAction = null
}
}
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
deleted file mode 100644
index 5cb6a5d57..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.model
-
-import androidx.lifecycle.ViewModel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-
-class SettingsViewModel : ViewModel() {
- var game: Game? = null
-
- var clickedItem: SettingsItem? = null
-
- val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
- private val _shouldRecreate = MutableStateFlow(false)
-
- val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack
- private val _shouldNavigateBack = MutableStateFlow(false)
-
- val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog
- private val _shouldShowResetSettingsDialog = MutableStateFlow(false)
-
- val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
- private val _shouldReloadSettingsList = MutableStateFlow(false)
-
- val sliderProgress: StateFlow<Int> get() = _sliderProgress
- private val _sliderProgress = MutableStateFlow(-1)
-
- val sliderTextValue: StateFlow<String> get() = _sliderTextValue
- private val _sliderTextValue = MutableStateFlow("")
-
- val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged
- private val _adapterItemChanged = MutableStateFlow(-1)
-
- fun setShouldRecreate(value: Boolean) {
- _shouldRecreate.value = value
- }
-
- fun setShouldNavigateBack(value: Boolean) {
- _shouldNavigateBack.value = value
- }
-
- fun setShouldShowResetSettingsDialog(value: Boolean) {
- _shouldShowResetSettingsDialog.value = value
- }
-
- fun setShouldReloadSettingsList(value: Boolean) {
- _shouldReloadSettingsList.value = value
- }
-
- fun setSliderTextValue(value: Float, units: String) {
- _sliderProgress.value = value.toInt()
- _sliderTextValue.value = String.format(
- YuzuApplication.appContext.getString(R.string.value_with_units),
- value.toInt().toString(),
- units
- )
- }
-
- fun setSliderProgress(value: Float) {
- _sliderProgress.value = value.toInt()
- }
-
- fun setAdapterItemChanged(value: Int) {
- _adapterItemChanged.value = value
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index c87486c90..737e03584 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -24,10 +24,11 @@ import androidx.core.content.ContextCompat
import androidx.window.layout.WindowMetricsCalculator
import kotlin.math.max
import kotlin.math.min
-import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
-import org.yuzu.yuzu_emu.NativeLibrary.StickType
+import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
+import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
@@ -99,20 +100,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
var shouldUpdateView = false
- val playerIndex =
- if (NativeLibrary.isHandheldOnly()) {
- NativeLibrary.ConsoleDevice
- } else {
- NativeLibrary.Player1Device
- }
+ val playerIndex = when (NativeInput.getStyleIndex(0)) {
+ NpadStyleIndex.Handheld -> 8
+ else -> 0
+ }
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
continue
}
- NativeLibrary.onGamePadButtonEvent(
+ NativeInput.onOverlayButtonEvent(
playerIndex,
- button.buttonId,
+ button.button,
button.status
)
playHaptics(event)
@@ -123,24 +122,24 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) {
continue
}
- NativeLibrary.onGamePadButtonEvent(
+ NativeInput.onOverlayButtonEvent(
playerIndex,
- dpad.upId,
+ dpad.up,
dpad.upStatus
)
- NativeLibrary.onGamePadButtonEvent(
+ NativeInput.onOverlayButtonEvent(
playerIndex,
- dpad.downId,
+ dpad.down,
dpad.downStatus
)
- NativeLibrary.onGamePadButtonEvent(
+ NativeInput.onOverlayButtonEvent(
playerIndex,
- dpad.leftId,
+ dpad.left,
dpad.leftStatus
)
- NativeLibrary.onGamePadButtonEvent(
+ NativeInput.onOverlayButtonEvent(
playerIndex,
- dpad.rightId,
+ dpad.right,
dpad.rightStatus
)
playHaptics(event)
@@ -151,16 +150,15 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
if (!joystick.updateStatus(event)) {
continue
}
- val axisID = joystick.joystickId
- NativeLibrary.onGamePadJoystickEvent(
+ NativeInput.onOverlayJoystickEvent(
playerIndex,
- axisID,
+ joystick.joystick,
joystick.xAxis,
joystick.realYAxis
)
- NativeLibrary.onGamePadButtonEvent(
+ NativeInput.onOverlayButtonEvent(
playerIndex,
- joystick.buttonId,
+ joystick.button,
joystick.buttonStatus
)
playHaptics(event)
@@ -187,7 +185,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
if (isActionDown && !isTouchInputConsumed(pointerId)) {
- NativeLibrary.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat())
+ NativeInput.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat())
}
if (isActionMove) {
@@ -196,12 +194,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
if (isTouchInputConsumed(fingerId)) {
continue
}
- NativeLibrary.onTouchMoved(fingerId, event.getX(i), event.getY(i))
+ NativeInput.onTouchMoved(fingerId, event.getX(i), event.getY(i))
}
}
if (isActionUp && !isTouchInputConsumed(pointerId)) {
- NativeLibrary.onTouchReleased(pointerId)
+ NativeInput.onTouchReleased(pointerId)
}
return true
@@ -359,7 +357,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_a,
R.drawable.facebutton_a_depressed,
- ButtonType.BUTTON_A,
+ NativeButton.A,
data,
position
)
@@ -373,7 +371,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_b,
R.drawable.facebutton_b_depressed,
- ButtonType.BUTTON_B,
+ NativeButton.B,
data,
position
)
@@ -387,7 +385,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_x,
R.drawable.facebutton_x_depressed,
- ButtonType.BUTTON_X,
+ NativeButton.X,
data,
position
)
@@ -401,7 +399,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_y,
R.drawable.facebutton_y_depressed,
- ButtonType.BUTTON_Y,
+ NativeButton.Y,
data,
position
)
@@ -415,7 +413,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_plus,
R.drawable.facebutton_plus_depressed,
- ButtonType.BUTTON_PLUS,
+ NativeButton.Plus,
data,
position
)
@@ -429,7 +427,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_minus,
R.drawable.facebutton_minus_depressed,
- ButtonType.BUTTON_MINUS,
+ NativeButton.Minus,
data,
position
)
@@ -443,7 +441,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_home,
R.drawable.facebutton_home_depressed,
- ButtonType.BUTTON_HOME,
+ NativeButton.Home,
data,
position
)
@@ -457,7 +455,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.facebutton_screenshot,
R.drawable.facebutton_screenshot_depressed,
- ButtonType.BUTTON_CAPTURE,
+ NativeButton.Capture,
data,
position
)
@@ -471,7 +469,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.l_shoulder,
R.drawable.l_shoulder_depressed,
- ButtonType.TRIGGER_L,
+ NativeButton.L,
data,
position
)
@@ -485,7 +483,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.r_shoulder,
R.drawable.r_shoulder_depressed,
- ButtonType.TRIGGER_R,
+ NativeButton.R,
data,
position
)
@@ -499,7 +497,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.zl_trigger,
R.drawable.zl_trigger_depressed,
- ButtonType.TRIGGER_ZL,
+ NativeButton.ZL,
data,
position
)
@@ -513,7 +511,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.zr_trigger,
R.drawable.zr_trigger_depressed,
- ButtonType.TRIGGER_ZR,
+ NativeButton.ZR,
data,
position
)
@@ -527,7 +525,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.button_l3,
R.drawable.button_l3_depressed,
- ButtonType.STICK_L,
+ NativeButton.LStick,
data,
position
)
@@ -541,7 +539,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize,
R.drawable.button_r3,
R.drawable.button_r3_depressed,
- ButtonType.STICK_R,
+ NativeButton.RStick,
data,
position
)
@@ -556,8 +554,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.joystick_range,
R.drawable.joystick,
R.drawable.joystick_depressed,
- StickType.STICK_L,
- ButtonType.STICK_L,
+ NativeAnalog.LStick,
+ NativeButton.LStick,
data,
position
)
@@ -572,8 +570,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.joystick_range,
R.drawable.joystick,
R.drawable.joystick_depressed,
- StickType.STICK_R,
- ButtonType.STICK_R,
+ NativeAnalog.RStick,
+ NativeButton.RStick,
data,
position
)
@@ -665,7 +663,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
val overlayControlData = NativeConfig.getOverlayControlData()
overlayControlData.forEach {
- it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
+ it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true
}
NativeConfig.setOverlayControlData(overlayControlData)
@@ -835,7 +833,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
windowSize: Pair<Point, Point>,
defaultResId: Int,
pressedResId: Int,
- buttonId: Int,
+ button: NativeButton,
overlayControlData: OverlayControlData,
position: Pair<Double, Double>
): InputOverlayDrawableButton {
@@ -869,7 +867,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
res,
defaultStateBitmap,
pressedStateBitmap,
- buttonId,
+ button,
overlayControlData
)
@@ -940,11 +938,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
res,
defaultStateBitmap,
pressedOneDirectionStateBitmap,
- pressedTwoDirectionsStateBitmap,
- ButtonType.DPAD_UP,
- ButtonType.DPAD_DOWN,
- ButtonType.DPAD_LEFT,
- ButtonType.DPAD_RIGHT
+ pressedTwoDirectionsStateBitmap
)
// Get the minimum and maximum coordinates of the screen where the button can be placed.
@@ -993,8 +987,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
resOuter: Int,
defaultResInner: Int,
pressedResInner: Int,
- joystick: Int,
- buttonId: Int,
+ joystick: NativeAnalog,
+ button: NativeButton,
overlayControlData: OverlayControlData,
position: Pair<Double, Double>
): InputOverlayDrawableJoystick {
@@ -1042,7 +1036,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
outerRect,
innerRect,
joystick,
- buttonId,
+ button,
overlayControlData.id
)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
index b14a4f96e..fee3d04ee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
@@ -9,7 +9,8 @@ import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
-import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
+import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
/**
@@ -19,13 +20,13 @@ import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
* @param res [Resources] instance.
* @param defaultStateBitmap [Bitmap] to use with the default state Drawable.
* @param pressedStateBitmap [Bitmap] to use with the pressed state Drawable.
- * @param buttonId Identifier for this type of button.
+ * @param button [NativeButton] for this type of button.
*/
class InputOverlayDrawableButton(
res: Resources,
defaultStateBitmap: Bitmap,
pressedStateBitmap: Bitmap,
- val buttonId: Int,
+ val button: NativeButton,
val overlayControlData: OverlayControlData
) {
// The ID value what motion event is tracking
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
index 8aef6f5a5..0cb6ff244 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
@@ -9,7 +9,8 @@ import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
-import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
+import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
/**
* Custom [BitmapDrawable] that is capable
@@ -19,20 +20,12 @@ import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
* @param defaultStateBitmap [Bitmap] of the default state.
* @param pressedOneDirectionStateBitmap [Bitmap] of the pressed state in one direction.
* @param pressedTwoDirectionsStateBitmap [Bitmap] of the pressed state in two direction.
- * @param buttonUp Identifier for the up button.
- * @param buttonDown Identifier for the down button.
- * @param buttonLeft Identifier for the left button.
- * @param buttonRight Identifier for the right button.
*/
class InputOverlayDrawableDpad(
res: Resources,
defaultStateBitmap: Bitmap,
pressedOneDirectionStateBitmap: Bitmap,
- pressedTwoDirectionsStateBitmap: Bitmap,
- buttonUp: Int,
- buttonDown: Int,
- buttonLeft: Int,
- buttonRight: Int
+ pressedTwoDirectionsStateBitmap: Bitmap
) {
/**
* Gets one of the InputOverlayDrawableDpad's button IDs.
@@ -40,10 +33,10 @@ class InputOverlayDrawableDpad(
* @return the requested InputOverlayDrawableDpad's button ID.
*/
// The ID identifying what type of button this Drawable represents.
- val upId: Int
- val downId: Int
- val leftId: Int
- val rightId: Int
+ val up = NativeButton.DUp
+ val down = NativeButton.DDown
+ val left = NativeButton.DLeft
+ val right = NativeButton.DRight
var trackId: Int
val width: Int
@@ -69,10 +62,6 @@ class InputOverlayDrawableDpad(
this.pressedTwoDirectionsStateBitmap = BitmapDrawable(res, pressedTwoDirectionsStateBitmap)
width = this.defaultStateBitmap.intrinsicWidth
height = this.defaultStateBitmap.intrinsicHeight
- upId = buttonUp
- downId = buttonDown
- leftId = buttonLeft
- rightId = buttonRight
trackId = -1
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index 113bf7c24..4b07107fc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -13,7 +13,9 @@ import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
-import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
+import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
+import org.yuzu.yuzu_emu.features.input.model.NativeButton
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
/**
@@ -26,8 +28,8 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
* @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick.
* @param rectOuter [Rect] which represents the outer joystick bounds.
* @param rectInner [Rect] which represents the inner joystick bounds.
- * @param joystickId The ID value what type of joystick this Drawable represents.
- * @param buttonId The ID value what type of button this Drawable represents.
+ * @param joystick The [NativeAnalog] this Drawable represents.
+ * @param button The [NativeButton] this Drawable represents.
*/
class InputOverlayDrawableJoystick(
res: Resources,
@@ -36,8 +38,8 @@ class InputOverlayDrawableJoystick(
bitmapInnerPressed: Bitmap,
rectOuter: Rect,
rectInner: Rect,
- val joystickId: Int,
- val buttonId: Int,
+ val joystick: NativeAnalog,
+ val button: NativeButton,
val prefId: String
) {
// The ID value what motion event is tracking
@@ -69,8 +71,7 @@ class InputOverlayDrawableJoystick(
// TODO: Add button support
val buttonStatus: Int
- get() =
- NativeLibrary.ButtonState.RELEASED
+ get() = ButtonState.RELEASED
var bounds: Rect
get() = outerBitmap.bounds
set(bounds) {
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 23ca49b53..fadb20e39 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
@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.ui
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -14,19 +13,16 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.color.MaterialColors
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class GamesFragment : Fragment() {
private var _binding: FragmentGamesBinding? = null
@@ -44,8 +40,6 @@ class GamesFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -88,49 +82,28 @@ class GamesFragment : Fragment() {
}
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.isReloading.collect {
- binding.swipeRefresh.isRefreshing = it
- if (gamesViewModel.games.value.isEmpty() && !it) {
- binding.noticeText.visibility = View.VISIBLE
- } else {
- binding.noticeText.visibility = View.INVISIBLE
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.games.collectLatest {
- (binding.gridGames.adapter as GameAdapter).submitList(it)
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.shouldSwapData.collect {
- if (it) {
- (binding.gridGames.adapter as GameAdapter).submitList(
- gamesViewModel.games.value
- )
- gamesViewModel.setShouldSwapData(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.shouldScrollToTop.collect {
- if (it) {
- scrollToTop()
- gamesViewModel.setShouldScrollToTop(false)
- }
- }
- }
+ gamesViewModel.isReloading.collect(viewLifecycleOwner) {
+ binding.swipeRefresh.isRefreshing = it
+ binding.noticeText.setVisible(
+ visible = gamesViewModel.games.value.isEmpty() && !it,
+ gone = false
+ )
+ }
+ gamesViewModel.games.collect(viewLifecycleOwner) {
+ (binding.gridGames.adapter as GameAdapter).submitList(it)
+ }
+ gamesViewModel.shouldSwapData.collect(
+ viewLifecycleOwner,
+ resetState = { gamesViewModel.setShouldSwapData(false) }
+ ) {
+ if (it) {
+ (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
}
}
+ gamesViewModel.shouldScrollToTop.collect(
+ viewLifecycleOwner,
+ resetState = { gamesViewModel.setShouldScrollToTop(false) }
+ ) { if (it) scrollToTop() }
setInsets()
}
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 4df4ac4c6..757463a0b 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
@@ -19,9 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
@@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationBarView
import java.io.File
import java.io.FilenameFilter
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.model.InstallResult
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.*
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.util.zip.ZipEntry
@@ -139,42 +136,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Prevents navigation from being drawn for a short time on recreation if set to hidden
if (!homeViewModel.navigationVisible.value.first) {
- binding.navigationView.visibility = View.INVISIBLE
- binding.statusBarShade.visibility = View.INVISIBLE
+ binding.navigationView.setVisible(visible = false, gone = false)
+ binding.statusBarShade.setVisible(visible = false, gone = false)
}
- lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.contentToInstall.collect {
- if (it != null) {
- installContent(it)
- homeViewModel.setContentToInstall(null)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.checkKeys.collect {
- if (it) {
- checkKeys()
- homeViewModel.setCheckKeys(false)
- }
- }
- }
+ homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
+ homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
+ homeViewModel.contentToInstall.collect(
+ this,
+ resetState = { homeViewModel.setContentToInstall(null) }
+ ) {
+ if (it != null) {
+ installContent(it)
}
}
+ homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
+ if (it) checkKeys()
+ }
setInsets()
}
@@ -214,18 +192,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun showNavigation(visible: Boolean, animated: Boolean) {
if (!animated) {
- if (visible) {
- binding.navigationView.visibility = View.VISIBLE
- } else {
- binding.navigationView.visibility = View.INVISIBLE
- }
+ binding.navigationView.setVisible(visible)
return
}
val smallLayout = resources.getBoolean(R.bool.small_layout)
binding.navigationView.animate().apply {
if (visible) {
- binding.navigationView.visibility = View.VISIBLE
+ binding.navigationView.setVisible(true)
duration = 300
interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
@@ -264,7 +238,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}.withEndAction {
if (!visible) {
- binding.navigationView.visibility = View.INVISIBLE
+ binding.navigationView.setVisible(visible = false, gone = false)
}
}.start()
}
@@ -272,7 +246,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun showStatusBarShade(visible: Boolean) {
binding.statusBarShade.animate().apply {
if (visible) {
- binding.statusBarShade.visibility = View.VISIBLE
+ binding.statusBarShade.setVisible(true)
binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
duration = 300
translationY(0f)
@@ -284,7 +258,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}.withEndAction {
if (!visible) {
- binding.statusBarShade.visibility = View.INVISIBLE
+ binding.statusBarShade.setVisible(visible = false, gone = false)
}
}.start()
}
@@ -524,7 +498,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
this@MainActivity,
titleId = R.string.content_install_notice,
descriptionId = R.string.content_install_notice_description,
- positiveAction = { homeViewModel.setContentToInstall(documents) }
+ positiveAction = { homeViewModel.setContentToInstall(documents) },
+ negativeAction = {}
)
}
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index e63382e1d..2c7356e6a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -6,439 +6,89 @@ package org.yuzu.yuzu_emu.utils
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
-import kotlin.math.sqrt
-import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.features.input.NativeInput
+import org.yuzu.yuzu_emu.features.input.YuzuInputOverlayDevice
+import org.yuzu.yuzu_emu.features.input.YuzuPhysicalDevice
object InputHandler {
- private var controllerIds = getGameControllerIds()
-
- fun initialize() {
- // Connect first controller
- NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device))
- }
-
- fun updateControllerIds() {
- controllerIds = getGameControllerIds()
- }
+ var androidControllers = mapOf<Int, YuzuPhysicalDevice>()
+ var registeredControllers = mutableListOf<ParamPackage>()
fun dispatchKeyEvent(event: KeyEvent): Boolean {
- val button: Int = when (event.device.vendorId) {
- 0x045E -> getInputXboxButtonKey(event.keyCode)
- 0x054C -> getInputDS5ButtonKey(event.keyCode)
- 0x057E -> getInputJoyconButtonKey(event.keyCode)
- 0x1532 -> getInputRazerButtonKey(event.keyCode)
- 0x3537 -> getInputRedmagicButtonKey(event.keyCode)
- 0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
- else -> getInputGenericButtonKey(event.keyCode)
- }
-
val action = when (event.action) {
- KeyEvent.ACTION_DOWN -> NativeLibrary.ButtonState.PRESSED
- KeyEvent.ACTION_UP -> NativeLibrary.ButtonState.RELEASED
+ KeyEvent.ACTION_DOWN -> NativeInput.ButtonState.PRESSED
+ KeyEvent.ACTION_UP -> NativeInput.ButtonState.RELEASED
else -> return false
}
- // Ignore invalid buttons
- if (button < 0) {
- return false
+ var controllerData = androidControllers[event.device.controllerNumber]
+ if (controllerData == null) {
+ updateControllerData()
+ controllerData = androidControllers[event.device.controllerNumber] ?: return false
}
- return NativeLibrary.onGamePadButtonEvent(
- getPlayerNumber(event.device.controllerNumber, event.deviceId),
- button,
+ NativeInput.onGamePadButtonEvent(
+ controllerData.getGUID(),
+ controllerData.getPort(),
+ event.keyCode,
action
)
+ return true
}
fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
- val device = event.device
- // Check every axis input available on the controller
- for (range in device.motionRanges) {
- val axis = range.axis
- when (device.vendorId) {
- 0x045E -> setGenericAxisInput(event, axis)
- 0x054C -> setGenericAxisInput(event, axis)
- 0x057E -> setJoyconAxisInput(event, axis)
- 0x1532 -> setRazerAxisInput(event, axis)
- else -> setGenericAxisInput(event, axis)
- }
+ val controllerData =
+ androidControllers[event.device.controllerNumber] ?: return false
+ event.device.motionRanges.forEach {
+ NativeInput.onGamePadAxisEvent(
+ controllerData.getGUID(),
+ controllerData.getPort(),
+ it.axis,
+ event.getAxisValue(it.axis)
+ )
}
-
return true
}
- private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int {
- var deviceIndex = index
- if (deviceId != -1) {
- deviceIndex = controllerIds[deviceId] ?: 0
- }
-
- // TODO: Joycons are handled as different controllers. Find a way to merge them.
- return when (deviceIndex) {
- 2 -> NativeLibrary.Player2Device
- 3 -> NativeLibrary.Player3Device
- 4 -> NativeLibrary.Player4Device
- 5 -> NativeLibrary.Player5Device
- 6 -> NativeLibrary.Player6Device
- 7 -> NativeLibrary.Player7Device
- 8 -> NativeLibrary.Player8Device
- else -> if (NativeLibrary.isHandheldOnly()) {
- NativeLibrary.ConsoleDevice
- } else {
- NativeLibrary.Player1Device
- }
- }
- }
-
- private fun setStickState(playerNumber: Int, index: Int, xAxis: Float, yAxis: Float) {
- // Calculate vector size
- val r2 = xAxis * xAxis + yAxis * yAxis
- var r = sqrt(r2.toDouble()).toFloat()
-
- // Adjust range of joystick
- val deadzone = 0.15f
- var x = xAxis
- var y = yAxis
-
- if (r > deadzone) {
- val deadzoneFactor = 1.0f / r * (r - deadzone) / (1.0f - deadzone)
- x *= deadzoneFactor
- y *= deadzoneFactor
- r *= deadzoneFactor
- } else {
- x = 0.0f
- y = 0.0f
- }
-
- // Normalize joystick
- if (r > 1.0f) {
- x /= r
- y /= r
- }
-
- NativeLibrary.onGamePadJoystickEvent(
- playerNumber,
- index,
- x,
- -y
- )
- }
-
- private fun getAxisToButton(axis: Float): Int {
- return if (axis > 0.5f) {
- NativeLibrary.ButtonState.PRESSED
- } else {
- NativeLibrary.ButtonState.RELEASED
- }
- }
-
- private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.DPAD_UP,
- getAxisToButton(-yAxis)
- )
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.DPAD_DOWN,
- getAxisToButton(yAxis)
- )
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.DPAD_LEFT,
- getAxisToButton(-xAxis)
- )
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.DPAD_RIGHT,
- getAxisToButton(xAxis)
- )
- }
-
- private fun getInputDS5ButtonKey(key: Int): Int {
- // The missing ds5 buttons are axis
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun getInputJoyconButtonKey(key: Int): Int {
- // Joycon support is half dead. A lot of buttons can't be mapped
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP
- KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
- KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
- KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
- KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun getInputXboxButtonKey(key: Int): Int {
- // The missing xbox buttons are axis
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun getInputRazerButtonKey(key: Int): Int {
- // The missing xbox buttons are axis
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun getInputRedmagicButtonKey(key: Int): Int {
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
- KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun getInputBackboneLabsButtonKey(key: Int): Int {
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
- KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun getInputGenericButtonKey(key: Int): Int {
- return when (key) {
- KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
- KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B
- KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
- KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
- KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP
- KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
- KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
- KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
- KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
- KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
- KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
- KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
- KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
- KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
- KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
- KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
- else -> -1
- }
- }
-
- private fun setGenericAxisInput(event: MotionEvent, axis: Int) {
- val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
-
- when (axis) {
- MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_L,
- event.getAxisValue(MotionEvent.AXIS_X),
- event.getAxisValue(MotionEvent.AXIS_Y)
- )
- MotionEvent.AXIS_RX, MotionEvent.AXIS_RY ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_R,
- event.getAxisValue(MotionEvent.AXIS_RX),
- event.getAxisValue(MotionEvent.AXIS_RY)
- )
- MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_R,
- event.getAxisValue(MotionEvent.AXIS_Z),
- event.getAxisValue(MotionEvent.AXIS_RZ)
- )
- MotionEvent.AXIS_LTRIGGER ->
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.TRIGGER_ZL,
- getAxisToButton(event.getAxisValue(MotionEvent.AXIS_LTRIGGER))
- )
- MotionEvent.AXIS_BRAKE ->
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.TRIGGER_ZL,
- getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE))
- )
- MotionEvent.AXIS_RTRIGGER ->
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.TRIGGER_ZR,
- getAxisToButton(event.getAxisValue(MotionEvent.AXIS_RTRIGGER))
- )
- MotionEvent.AXIS_GAS ->
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.TRIGGER_ZR,
- getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS))
- )
- MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y ->
- setAxisDpadState(
- playerNumber,
- event.getAxisValue(MotionEvent.AXIS_HAT_X),
- event.getAxisValue(MotionEvent.AXIS_HAT_Y)
- )
- }
- }
-
- private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
- // Joycon support is half dead. Right joystick doesn't work
- val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
-
- when (axis) {
- MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_L,
- event.getAxisValue(MotionEvent.AXIS_X),
- event.getAxisValue(MotionEvent.AXIS_Y)
- )
- MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_R,
- event.getAxisValue(MotionEvent.AXIS_Z),
- event.getAxisValue(MotionEvent.AXIS_RZ)
- )
- MotionEvent.AXIS_RX, MotionEvent.AXIS_RY ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_R,
- event.getAxisValue(MotionEvent.AXIS_RX),
- event.getAxisValue(MotionEvent.AXIS_RY)
- )
- }
- }
-
- private fun setRazerAxisInput(event: MotionEvent, axis: Int) {
- val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
-
- when (axis) {
- MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_L,
- event.getAxisValue(MotionEvent.AXIS_X),
- event.getAxisValue(MotionEvent.AXIS_Y)
- )
- MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
- setStickState(
- playerNumber,
- NativeLibrary.StickType.STICK_R,
- event.getAxisValue(MotionEvent.AXIS_Z),
- event.getAxisValue(MotionEvent.AXIS_RZ)
- )
- MotionEvent.AXIS_BRAKE ->
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.TRIGGER_ZL,
- getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE))
- )
- MotionEvent.AXIS_GAS ->
- NativeLibrary.onGamePadButtonEvent(
- playerNumber,
- NativeLibrary.ButtonType.TRIGGER_ZR,
- getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS))
- )
- MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y ->
- setAxisDpadState(
- playerNumber,
- event.getAxisValue(MotionEvent.AXIS_HAT_X),
- event.getAxisValue(MotionEvent.AXIS_HAT_Y)
- )
- }
- }
-
- fun getGameControllerIds(): Map<Int, Int> {
- val gameControllerDeviceIds = mutableMapOf<Int, Int>()
+ fun getDevices(): Map<Int, YuzuPhysicalDevice> {
+ val gameControllerDeviceIds = mutableMapOf<Int, YuzuPhysicalDevice>()
val deviceIds = InputDevice.getDeviceIds()
- var controllerSlot = 1
+ var port = 0
+ val inputSettings = NativeConfig.getInputSettings(true)
deviceIds.forEach { deviceId ->
InputDevice.getDevice(deviceId)?.apply {
- // Don't over-assign controllers
- if (controllerSlot >= 8) {
- return gameControllerDeviceIds
- }
-
// Verify that the device has gamepad buttons, control sticks, or both.
if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD ||
sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
) {
- // This device is a game controller. Store its device ID.
- if (deviceId and id and vendorId and productId != 0) {
- // Additionally filter out devices that have no ID
- gameControllerDeviceIds
- .takeIf { !it.contains(deviceId) }
- ?.put(deviceId, controllerSlot)
- controllerSlot++
+ if (!gameControllerDeviceIds.contains(controllerNumber)) {
+ gameControllerDeviceIds[controllerNumber] = YuzuPhysicalDevice(
+ this,
+ port,
+ inputSettings[port].useSystemVibrator
+ )
}
+ port++
}
}
}
return gameControllerDeviceIds
}
+
+ fun updateControllerData() {
+ androidControllers = getDevices()
+ androidControllers.forEach {
+ NativeInput.registerController(it.value)
+ }
+
+ // Register the input overlay on a dedicated port for all player 1 vibrations
+ NativeInput.registerController(YuzuInputOverlayDevice(androidControllers.isEmpty(), 100))
+ registeredControllers.clear()
+ NativeInput.getInputDevices().forEach {
+ registeredControllers.add(ParamPackage(it))
+ }
+ registeredControllers.sortBy { it.get("port", 0) }
+ }
+
+ fun InputDevice.getGUID(): String = String.format("%016x%016x", productId, vendorId)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
new file mode 100644
index 000000000..d5c19c681
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Collects this [Flow] with a given [LifecycleOwner].
+ * @param scope [LifecycleOwner] that this [Flow] will be collected with.
+ * @param repeatState When to repeat collection on this [Flow].
+ * @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after
+ * [stateCollector] has been run.
+ * @param stateCollector Lambda that receives new state.
+ */
+inline fun <reified T> Flow<T>.collect(
+ scope: LifecycleOwner,
+ repeatState: Lifecycle.State = Lifecycle.State.CREATED,
+ crossinline resetState: () -> Unit = {},
+ crossinline stateCollector: (state: T) -> Unit
+) {
+ scope.apply {
+ lifecycleScope.launch {
+ repeatOnLifecycle(repeatState) {
+ this@collect.collect {
+ stateCollector(it)
+ resetState()
+ }
+ }
+ }
+ }
+}
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 a4c14b3a7..7228f25d2 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
@@ -6,6 +6,8 @@ package org.yuzu.yuzu_emu.utils
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
+import org.yuzu.yuzu_emu.features.input.model.PlayerInput
+
object NativeConfig {
/**
* Loads global config.
@@ -168,4 +170,17 @@ object NativeConfig {
*/
@Synchronized
external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
+
+ @Synchronized
+ external fun getInputSettings(global: Boolean): Array<PlayerInput>
+
+ @Synchronized
+ external fun setInputSettings(value: Array<PlayerInput>, global: Boolean)
+
+ /**
+ * Saves control values for a specific player
+ * Must be used when per game config is loaded
+ */
+ @Synchronized
+ external fun saveControlPlayerValues()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
index 68ed66565..331b7ddca 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
@@ -14,7 +14,7 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import java.io.IOException
-import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.features.input.NativeInput
class NfcReader(private val activity: Activity) {
private var nfcAdapter: NfcAdapter? = null
@@ -76,12 +76,12 @@ class NfcReader(private val activity: Activity) {
amiibo.connect()
val tagData = ntag215ReadAll(amiibo) ?: return
- NativeLibrary.onReadNfcTag(tagData)
+ NativeInput.onReadNfcTag(tagData)
nfcAdapter?.ignore(
tag,
1000,
- { NativeLibrary.onRemoveNfcTag() },
+ { NativeInput.onRemoveNfcTag() },
Handler(Looper.getMainLooper())
)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt
new file mode 100644
index 000000000..83fc7da3c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt
@@ -0,0 +1,141 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+// Kotlin version of src/common/param_package.h
+class ParamPackage(serialized: String = "") {
+ private val KEY_VALUE_SEPARATOR = ":"
+ private val PARAM_SEPARATOR = ","
+
+ private val ESCAPE_CHARACTER = "$"
+ private val KEY_VALUE_SEPARATOR_ESCAPE = "$0"
+ private val PARAM_SEPARATOR_ESCAPE = "$1"
+ private val ESCAPE_CHARACTER_ESCAPE = "$2"
+
+ private val EMPTY_PLACEHOLDER = "[empty]"
+
+ val data = mutableMapOf<String, String>()
+
+ init {
+ val pairs = serialized.split(PARAM_SEPARATOR)
+ for (pair in pairs) {
+ val keyValue = pair.split(KEY_VALUE_SEPARATOR).toMutableList()
+ if (keyValue.size != 2) {
+ Log.error("[ParamPackage] Invalid key pair $keyValue")
+ continue
+ }
+
+ keyValue.forEachIndexed { i: Int, _: String ->
+ keyValue[i] = keyValue[i].replace(KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR)
+ keyValue[i] = keyValue[i].replace(PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR)
+ keyValue[i] = keyValue[i].replace(ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER)
+ }
+
+ set(keyValue[0], keyValue[1])
+ }
+ }
+
+ constructor(params: List<Pair<String, String>>) : this() {
+ params.forEach {
+ data[it.first] = it.second
+ }
+ }
+
+ fun serialize(): String {
+ if (data.isEmpty()) {
+ return EMPTY_PLACEHOLDER
+ }
+
+ val result = StringBuilder()
+ data.forEach {
+ val keyValue = mutableListOf(it.key, it.value)
+ keyValue.forEachIndexed { i, _ ->
+ keyValue[i] = keyValue[i].replace(ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE)
+ keyValue[i] = keyValue[i].replace(PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE)
+ keyValue[i] = keyValue[i].replace(KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE)
+ }
+ result.append("${keyValue[0]}$KEY_VALUE_SEPARATOR${keyValue[1]}$PARAM_SEPARATOR")
+ }
+ return result.removeSuffix(PARAM_SEPARATOR).toString()
+ }
+
+ fun get(key: String, defaultValue: String): String =
+ if (has(key)) {
+ data[key]!!
+ } else {
+ Log.debug("[ParamPackage] key $key not found")
+ defaultValue
+ }
+
+ fun get(key: String, defaultValue: Int): Int =
+ if (has(key)) {
+ try {
+ data[key]!!.toInt()
+ } catch (e: NumberFormatException) {
+ Log.debug("[ParamPackage] failed to convert ${data[key]!!} to int")
+ defaultValue
+ }
+ } else {
+ Log.debug("[ParamPackage] key $key not found")
+ defaultValue
+ }
+
+ private fun Int.toBoolean(): Boolean =
+ if (this == 1) {
+ true
+ } else if (this == 0) {
+ false
+ } else {
+ throw Exception("Tried to convert a value to a boolean that was not 0 or 1!")
+ }
+
+ fun get(key: String, defaultValue: Boolean): Boolean =
+ if (has(key)) {
+ try {
+ get(key, if (defaultValue) 1 else 0).toBoolean()
+ } catch (e: Exception) {
+ Log.debug("[ParamPackage] failed to convert ${data[key]!!} to boolean")
+ defaultValue
+ }
+ } else {
+ Log.debug("[ParamPackage] key $key not found")
+ defaultValue
+ }
+
+ fun get(key: String, defaultValue: Float): Float =
+ if (has(key)) {
+ try {
+ data[key]!!.toFloat()
+ } catch (e: NumberFormatException) {
+ Log.debug("[ParamPackage] failed to convert ${data[key]!!} to float")
+ defaultValue
+ }
+ } else {
+ Log.debug("[ParamPackage] key $key not found")
+ defaultValue
+ }
+
+ fun set(key: String, value: String) {
+ data[key] = value
+ }
+
+ fun set(key: String, value: Int) {
+ data[key] = value.toString()
+ }
+
+ fun Boolean.toInt(): Int = if (this) 1 else 0
+ fun set(key: String, value: Boolean) {
+ data[key] = value.toInt().toString()
+ }
+
+ fun set(key: String, value: Float) {
+ data[key] = value.toString()
+ }
+
+ fun has(key: String): Boolean = data.containsKey(key)
+
+ fun erase(key: String) = data.remove(key)
+
+ fun clear() = data.clear()
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
index ffbfa9337..244091aec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -3,8 +3,10 @@
package org.yuzu.yuzu_emu.utils
+import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
object ViewUtils {
fun showView(view: View, length: Long = 300) {
@@ -57,4 +59,35 @@ object ViewUtils {
}
this.layoutParams = layoutParams
}
+
+ /**
+ * Shows or hides a view.
+ * @param visible Whether a view will be made View.VISIBLE or View.INVISIBLE/GONE.
+ * @param gone Optional parameter for hiding a view. Uses View.GONE if true and View.INVISIBLE otherwise.
+ */
+ fun View.setVisible(visible: Boolean, gone: Boolean = true) {
+ visibility = if (visible) {
+ View.VISIBLE
+ } else {
+ if (gone) {
+ View.GONE
+ } else {
+ View.INVISIBLE
+ }
+ }
+ }
+
+ /**
+ * Starts a marquee on some text.
+ * @param delay Optional parameter for changing the start delay. 3 seconds of delay by default.
+ */
+ fun TextView.marquee(delay: Long = 3000) {
+ ellipsize = null
+ marqueeRepeatLimit = -1
+ isSingleLine = true
+ postDelayed({
+ ellipsize = TextUtils.TruncateAt.MARQUEE
+ isSelected = true
+ }, delay)
+ }
}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 20b319c12..ec8ae5c57 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -12,6 +12,7 @@ add_library(yuzu-android SHARED
native_log.cpp
android_config.cpp
android_config.h
+ native_input.cpp
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index e147560c3..a79a64afb 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <common/logging/log.h>
+#include <input_common/main.h>
#include "android_config.h"
#include "android_settings.h"
#include "common/settings_setting.h"
@@ -32,6 +34,7 @@ void AndroidConfig::ReadAndroidValues() {
ReadOverlayValues();
}
ReadDriverValues();
+ ReadAndroidControlValues();
}
void AndroidConfig::ReadAndroidUIValues() {
@@ -107,6 +110,76 @@ void AndroidConfig::ReadOverlayValues() {
EndGroup();
}
+void AndroidConfig::ReadAndroidPlayerValues(std::size_t player_index) {
+ std::string player_prefix;
+ if (type != ConfigType::InputProfile) {
+ player_prefix.append("player_").append(ToString(player_index)).append("_");
+ }
+
+ auto& player = Settings::values.players.GetValue()[player_index];
+ if (IsCustomConfig()) {
+ const auto profile_name =
+ ReadStringSetting(std::string(player_prefix).append("profile_name"));
+ if (profile_name.empty()) {
+ // Use the global input config
+ player = Settings::values.players.GetValue(true)[player_index];
+ player.profile_name = "";
+ return;
+ }
+ }
+
+ // Android doesn't have default options for controllers. We have the input overlay for that.
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ const std::string default_param;
+ auto& player_buttons = player.buttons[i];
+
+ player_buttons = ReadStringSetting(
+ std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
+ if (player_buttons.empty()) {
+ player_buttons = default_param;
+ }
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ const std::string default_param;
+ auto& player_analogs = player.analogs[i];
+
+ player_analogs = ReadStringSetting(
+ std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
+ if (player_analogs.empty()) {
+ player_analogs = default_param;
+ }
+ }
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param;
+ auto& player_motions = player.motions[i];
+
+ player_motions = ReadStringSetting(
+ std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
+ if (player_motions.empty()) {
+ player_motions = default_param;
+ }
+ }
+ player.use_system_vibrator = ReadBooleanSetting(
+ std::string(player_prefix).append("use_system_vibrator"), player_index == 0);
+}
+
+void AndroidConfig::ReadAndroidControlValues() {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
+
+ Settings::values.players.SetGlobal(!IsCustomConfig());
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
+ ReadAndroidPlayerValues(p);
+ }
+ if (IsCustomConfig()) {
+ EndGroup();
+ return;
+ }
+ // ReadDebugControlValues();
+ // ReadHidbusValues();
+
+ EndGroup();
+}
+
void AndroidConfig::SaveAndroidValues() {
if (global) {
SaveAndroidUIValues();
@@ -114,6 +187,7 @@ void AndroidConfig::SaveAndroidValues() {
SaveOverlayValues();
}
SaveDriverValues();
+ SaveAndroidControlValues();
WriteToIni();
}
@@ -187,6 +261,52 @@ void AndroidConfig::SaveOverlayValues() {
EndGroup();
}
+void AndroidConfig::SaveAndroidPlayerValues(std::size_t player_index) {
+ std::string player_prefix;
+ if (type != ConfigType::InputProfile) {
+ player_prefix = std::string("player_").append(ToString(player_index)).append("_");
+ }
+
+ const auto& player = Settings::values.players.GetValue()[player_index];
+ if (IsCustomConfig() && player.profile_name.empty()) {
+ // No custom profile selected
+ return;
+ }
+
+ const std::string default_param;
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
+ player.buttons[i], std::make_optional(default_param));
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
+ player.analogs[i], std::make_optional(default_param));
+ }
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
+ player.motions[i], std::make_optional(default_param));
+ }
+ WriteBooleanSetting(std::string(player_prefix).append("use_system_vibrator"),
+ player.use_system_vibrator, std::make_optional(player_index == 0));
+}
+
+void AndroidConfig::SaveAndroidControlValues() {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
+
+ Settings::values.players.SetGlobal(!IsCustomConfig());
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
+ SaveAndroidPlayerValues(p);
+ }
+ if (IsCustomConfig()) {
+ EndGroup();
+ return;
+ }
+ // SaveDebugControlValues();
+ // SaveHidbusValues();
+
+ EndGroup();
+}
+
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {
@@ -194,3 +314,24 @@ std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::
}
return AndroidSettings::values.linkage.by_category[category];
}
+
+void AndroidConfig::ReadAndroidControlPlayerValues(std::size_t player_index) {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
+
+ ReadPlayerValues(player_index);
+ ReadAndroidPlayerValues(player_index);
+
+ EndGroup();
+}
+
+void AndroidConfig::SaveAndroidControlPlayerValues(std::size_t player_index) {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
+
+ LOG_DEBUG(Config, "Saving players control configuration values");
+ SavePlayerValues(player_index);
+ SaveAndroidPlayerValues(player_index);
+
+ EndGroup();
+
+ WriteToIni();
+}
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index 693e1e3f0..28ef5d0a8 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -13,7 +13,12 @@ public:
void ReloadAllValues() override;
void SaveAllValues() override;
+ void ReadAndroidControlPlayerValues(std::size_t player_index);
+ void SaveAndroidControlPlayerValues(std::size_t player_index);
+
protected:
+ void ReadAndroidPlayerValues(std::size_t player_index);
+ void ReadAndroidControlValues();
void ReadAndroidValues();
void ReadAndroidUIValues();
void ReadDriverValues();
@@ -27,6 +32,8 @@ protected:
void ReadUILayoutValues() override {}
void ReadMultiplayerValues() override {}
+ void SaveAndroidPlayerValues(std::size_t player_index);
+ void SaveAndroidControlValues();
void SaveAndroidValues();
void SaveAndroidUIValues();
void SaveDriverValues();
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 4a3bc8e53..00baf86a9 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -38,6 +38,13 @@ struct Values {
Settings::Specialization::Default,
true,
true};
+ Settings::Setting<s32> vertical_alignment{linkage,
+ 0,
+ "vertical_alignment",
+ Settings::Category::Android,
+ Settings::Specialization::Default,
+ true,
+ true};
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
Settings::Category::GpuDriver};
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp
index c927cddda..06db55369 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.cpp
+++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp
@@ -5,6 +5,7 @@
#include "common/android/id_cache.h"
#include "common/logging/log.h"
+#include "input_common/drivers/android.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h"
#include "input_common/drivers/virtual_gamepad.h"
@@ -24,39 +25,18 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
- m_input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
+ EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(touch_x,
+ touch_y, id);
}
void EmuWindow_Android::OnTouchMoved(int id, float x, float y) {
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
- m_input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
+ EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(touch_x,
+ touch_y, id);
}
void EmuWindow_Android::OnTouchReleased(int id) {
- m_input_subsystem->GetTouchScreen()->TouchReleased(id);
-}
-
-void EmuWindow_Android::OnGamepadButtonEvent(int player_index, int button_id, bool pressed) {
- m_input_subsystem->GetVirtualGamepad()->SetButtonState(player_index, button_id, pressed);
-}
-
-void EmuWindow_Android::OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y) {
- m_input_subsystem->GetVirtualGamepad()->SetStickPosition(player_index, stick_id, x, y);
-}
-
-void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x,
- float gyro_y, float gyro_z, float accel_x,
- float accel_y, float accel_z) {
- m_input_subsystem->GetVirtualGamepad()->SetMotionState(
- player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
-}
-
-void EmuWindow_Android::OnReadNfcTag(std::span<u8> data) {
- m_input_subsystem->GetVirtualAmiibo()->LoadAmiibo(data);
-}
-
-void EmuWindow_Android::OnRemoveNfcTag() {
- m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
+ EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id);
}
void EmuWindow_Android::OnFrameDisplayed() {
@@ -67,10 +47,9 @@ void EmuWindow_Android::OnFrameDisplayed() {
}
}
-EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
- ANativeWindow* surface,
+EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library)
- : m_input_subsystem{input_subsystem}, m_driver_library{driver_library} {
+ : m_driver_library{driver_library} {
LOG_INFO(Frontend, "initializing");
if (!surface) {
@@ -80,10 +59,4 @@ EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsyste
OnSurfaceChanged(surface);
window_info.type = Core::Frontend::WindowSystemType::Android;
-
- m_input_subsystem->Initialize();
-}
-
-EmuWindow_Android::~EmuWindow_Android() {
- m_input_subsystem->Shutdown();
}
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h
index a34a0e479..d7b5fc6da 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.h
+++ b/src/android/app/src/main/jni/emu_window/emu_window.h
@@ -30,22 +30,17 @@ private:
class EmuWindow_Android final : public Core::Frontend::EmuWindow {
public:
- EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, ANativeWindow* surface,
+ EmuWindow_Android(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library);
- ~EmuWindow_Android();
+ ~EmuWindow_Android() = default;
void OnSurfaceChanged(ANativeWindow* surface);
+ void OnFrameDisplayed() override;
+
void OnTouchPressed(int id, float x, float y);
void OnTouchMoved(int id, float x, float y);
void OnTouchReleased(int id);
- void OnGamepadButtonEvent(int player_index, int button_id, bool pressed);
- void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y);
- void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y,
- float gyro_z, float accel_x, float accel_y, float accel_z);
- void OnReadNfcTag(std::span<u8> data);
- void OnRemoveNfcTag();
- void OnFrameDisplayed() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
@@ -55,8 +50,6 @@ public:
};
private:
- InputCommon::InputSubsystem* m_input_subsystem{};
-
float m_window_width{};
float m_window_height{};
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 4acc60956..4ea82e217 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -49,9 +49,7 @@
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
-#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
@@ -90,6 +88,10 @@ FileSys::ManualContentProvider* EmulationSession::GetContentProvider() {
return m_manual_provider.get();
}
+InputCommon::InputSubsystem& EmulationSession::GetInputSubsystem() {
+ return m_input_subsystem;
+}
+
const EmuWindow_Android& EmulationSession::Window() const {
return *m_window;
}
@@ -200,6 +202,8 @@ void EmulationSession::InitializeSystem(bool reload) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
+
+ m_input_subsystem.Initialize();
}
// Initialize filesystem.
@@ -224,8 +228,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
std::scoped_lock lock(m_mutex);
// Create the render window.
- m_window =
- std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
+ m_window = std::make_unique<EmuWindow_Android>(m_native_window, m_vulkan_library);
// Initialize system.
jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>();
@@ -357,60 +360,6 @@ void EmulationSession::RunEmulation() {
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
}
-bool EmulationSession::IsHandheldOnly() {
- jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
-
- if (npad_style_set.fullkey == 1) {
- return false;
- }
-
- if (npad_style_set.handheld == 0) {
- return false;
- }
-
- return !Settings::IsDockedMode();
-}
-
-void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
- jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
- controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
-}
-
-void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
- jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
-
- // Ensure that player1 is configured correctly and handheld disconnected
- if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
- jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
-
- if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
- handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
- controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
- handheld->Disconnect();
- }
- }
-
- // Ensure that handheld is configured correctly and player 1 disconnected
- if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
- jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
-
- if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
- player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
- controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
- player1->Disconnect();
- }
- }
-
- if (!controller->IsConnected()) {
- controller->Connect();
- }
-}
-
-void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
- jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
- controller->Disconnect();
-}
-
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
return m_software_keyboard;
}
@@ -455,7 +404,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
const size_t program_index,
const bool frontend_initiated) {
MicroProfileOnThreadCreate("EmuThread");
- SCOPE_EXIT({ MicroProfileShutdown(); });
+ SCOPE_EXIT {
+ MicroProfileShutdown();
+ };
LOG_INFO(Frontend, "starting");
@@ -464,7 +415,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
return Core::SystemResultStatus::ErrorLoader;
}
- SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
+ SCOPE_EXIT {
+ EmulationSession::GetInstance().ShutdownEmulation();
+ };
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
frontend_initiated);
@@ -576,14 +529,14 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
nullptr, nullptr, file_redirect_dir_, nullptr);
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
InputCommon::InputSubsystem input_subsystem;
- auto m_window = std::make_unique<EmuWindow_Android>(
- &input_subsystem, ANativeWindow_fromSurface(env, j_surf), driver_library);
+ auto window =
+ std::make_unique<EmuWindow_Android>(ANativeWindow_fromSurface(env, j_surf), driver_library);
Vulkan::vk::InstanceDispatch dld;
Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance(
*driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android);
- auto surface = Vulkan::CreateSurface(vk_instance, m_window->GetWindowInfo());
+ auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo());
auto device = Vulkan::CreateDevice(vk_instance, dld, *surface);
@@ -624,103 +577,6 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass claz
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
- return EmulationSession::GetInstance().IsHandheldOnly();
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
- jint j_device, jint j_type) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
- }
- return static_cast<jboolean>(true);
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
- jint j_device) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
- }
- return static_cast<jboolean>(true);
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
- jint j_device) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
- }
- return static_cast<jboolean>(true);
-}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
- jint j_device, jint j_button,
- jint action) {
- if (EmulationSession::GetInstance().IsRunning()) {
- // Ensure gamepad is connected
- EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
- EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button,
- action != 0);
- }
- return static_cast<jboolean>(true);
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
- jint j_device, jint stick_id,
- jfloat x, jfloat y) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnGamepadJoystickEvent(j_device, stick_id, x, y);
- }
- return static_cast<jboolean>(true);
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
- JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
- jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
- j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
- }
- return static_cast<jboolean>(true);
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
- jbyteArray j_data) {
- jboolean isCopy{false};
- std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
- static_cast<size_t>(env->GetArrayLength(j_data)));
-
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnReadNfcTag(data);
- }
- return static_cast<jboolean>(true);
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnRemoveNfcTag();
- }
- return static_cast<jboolean>(true);
-}
-
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
- jfloat x, jfloat y) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
- }
-}
-
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
- jfloat x, jfloat y) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
- }
-}
-
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
- if (EmulationSession::GetInstance().IsRunning()) {
- EmulationSession::GetInstance().Window().OnTouchReleased(id);
- }
-}
-
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
jboolean reload) {
// Initialize the emulated system.
@@ -761,6 +617,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
EmulationSession::GetInstance().System().ApplySettings();
+ EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj) {
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index 47936e305..6a4551ada 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -23,6 +23,7 @@ public:
const Core::System& System() const;
Core::System& System();
FileSys::ManualContentProvider* GetContentProvider();
+ InputCommon::InputSubsystem& GetInputSubsystem();
const EmuWindow_Android& Window() const;
EmuWindow_Android& Window();
@@ -50,10 +51,6 @@ public:
const std::size_t program_index,
const bool frontend_initiated);
- bool IsHandheldOnly();
- void SetDeviceType([[maybe_unused]] int index, int type);
- void OnGamepadConnectEvent([[maybe_unused]] int index);
- void OnGamepadDisconnectEvent([[maybe_unused]] int index);
Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
static void OnEmulationStarted();
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 8ae10fbc7..0b26280c6 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -3,7 +3,6 @@
#include <string>
-#include <common/fs/fs_util.h>
#include <jni.h>
#include "android_config.h"
@@ -425,4 +424,120 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
}
}
+jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInputSettings(JNIEnv* env, jobject obj,
+ jboolean j_global) {
+ Settings::values.players.SetGlobal(static_cast<bool>(j_global));
+ auto& players = Settings::values.players.GetValue();
+ jobjectArray j_input_settings =
+ env->NewObjectArray(players.size(), Common::Android::GetPlayerInputClass(), nullptr);
+ for (size_t i = 0; i < players.size(); ++i) {
+ auto j_connected = static_cast<jboolean>(players[i].connected);
+
+ jobjectArray j_buttons = env->NewObjectArray(
+ players[i].buttons.size(), Common::Android::GetStringClass(), env->NewStringUTF(""));
+ for (size_t j = 0; j < players[i].buttons.size(); ++j) {
+ env->SetObjectArrayElement(j_buttons, j,
+ Common::Android::ToJString(env, players[i].buttons[j]));
+ }
+ jobjectArray j_analogs = env->NewObjectArray(
+ players[i].analogs.size(), Common::Android::GetStringClass(), env->NewStringUTF(""));
+ for (size_t j = 0; j < players[i].analogs.size(); ++j) {
+ env->SetObjectArrayElement(j_analogs, j,
+ Common::Android::ToJString(env, players[i].analogs[j]));
+ }
+ jobjectArray j_motions = env->NewObjectArray(
+ players[i].motions.size(), Common::Android::GetStringClass(), env->NewStringUTF(""));
+ for (size_t j = 0; j < players[i].motions.size(); ++j) {
+ env->SetObjectArrayElement(j_motions, j,
+ Common::Android::ToJString(env, players[i].motions[j]));
+ }
+
+ auto j_vibration_enabled = static_cast<jboolean>(players[i].vibration_enabled);
+ auto j_vibration_strength = static_cast<jint>(players[i].vibration_strength);
+
+ auto j_body_color_left = static_cast<jlong>(players[i].body_color_left);
+ auto j_body_color_right = static_cast<jlong>(players[i].body_color_right);
+ auto j_button_color_left = static_cast<jlong>(players[i].button_color_left);
+ auto j_button_color_right = static_cast<jlong>(players[i].button_color_right);
+
+ auto j_profile_name = Common::Android::ToJString(env, players[i].profile_name);
+
+ auto j_use_system_vibrator = players[i].use_system_vibrator;
+
+ jobject playerInput = env->NewObject(
+ Common::Android::GetPlayerInputClass(), Common::Android::GetPlayerInputConstructor(),
+ j_connected, j_buttons, j_analogs, j_motions, j_vibration_enabled, j_vibration_strength,
+ j_body_color_left, j_body_color_right, j_button_color_left, j_button_color_right,
+ j_profile_name, j_use_system_vibrator);
+ env->SetObjectArrayElement(j_input_settings, i, playerInput);
+ }
+ return j_input_settings;
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInputSettings(JNIEnv* env, jobject obj,
+ jobjectArray j_value,
+ jboolean j_global) {
+ auto& players = Settings::values.players.GetValue(static_cast<bool>(j_global));
+ int playersSize = env->GetArrayLength(j_value);
+ for (int i = 0; i < playersSize; ++i) {
+ jobject jplayer = env->GetObjectArrayElement(j_value, i);
+
+ players[i].connected = static_cast<bool>(
+ env->GetBooleanField(jplayer, Common::Android::GetPlayerInputConnectedField()));
+
+ auto j_buttons_array = static_cast<jobjectArray>(
+ env->GetObjectField(jplayer, Common::Android::GetPlayerInputButtonsField()));
+ int buttons_size = env->GetArrayLength(j_buttons_array);
+ for (int j = 0; j < buttons_size; ++j) {
+ auto button = static_cast<jstring>(env->GetObjectArrayElement(j_buttons_array, j));
+ players[i].buttons[j] = Common::Android::GetJString(env, button);
+ }
+ auto j_analogs_array = static_cast<jobjectArray>(
+ env->GetObjectField(jplayer, Common::Android::GetPlayerInputAnalogsField()));
+ int analogs_size = env->GetArrayLength(j_analogs_array);
+ for (int j = 0; j < analogs_size; ++j) {
+ auto analog = static_cast<jstring>(env->GetObjectArrayElement(j_analogs_array, j));
+ players[i].analogs[j] = Common::Android::GetJString(env, analog);
+ }
+ auto j_motions_array = static_cast<jobjectArray>(
+ env->GetObjectField(jplayer, Common::Android::GetPlayerInputMotionsField()));
+ int motions_size = env->GetArrayLength(j_motions_array);
+ for (int j = 0; j < motions_size; ++j) {
+ auto motion = static_cast<jstring>(env->GetObjectArrayElement(j_motions_array, j));
+ players[i].motions[j] = Common::Android::GetJString(env, motion);
+ }
+
+ players[i].vibration_enabled = static_cast<bool>(
+ env->GetBooleanField(jplayer, Common::Android::GetPlayerInputVibrationEnabledField()));
+ players[i].vibration_strength = static_cast<int>(
+ env->GetIntField(jplayer, Common::Android::GetPlayerInputVibrationStrengthField()));
+
+ players[i].body_color_left = static_cast<u32>(
+ env->GetLongField(jplayer, Common::Android::GetPlayerInputBodyColorLeftField()));
+ players[i].body_color_right = static_cast<u32>(
+ env->GetLongField(jplayer, Common::Android::GetPlayerInputBodyColorRightField()));
+ players[i].button_color_left = static_cast<u32>(
+ env->GetLongField(jplayer, Common::Android::GetPlayerInputButtonColorLeftField()));
+ players[i].button_color_right = static_cast<u32>(
+ env->GetLongField(jplayer, Common::Android::GetPlayerInputButtonColorRightField()));
+
+ auto profileName = static_cast<jstring>(
+ env->GetObjectField(jplayer, Common::Android::GetPlayerInputProfileNameField()));
+ players[i].profile_name = Common::Android::GetJString(env, profileName);
+
+ players[i].use_system_vibrator =
+ env->GetBooleanField(jplayer, Common::Android::GetPlayerInputUseSystemVibratorField());
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveControlPlayerValues(JNIEnv* env, jobject obj) {
+ Settings::values.players.SetGlobal(false);
+
+ // Clear all controls from the config in case the user reverted back to globals
+ per_game_config->ClearControlPlayerValues();
+ for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
+ per_game_config->SaveAndroidControlPlayerValues(index);
+ }
+}
+
} // extern "C"
diff --git a/src/android/app/src/main/jni/native_input.cpp b/src/android/app/src/main/jni/native_input.cpp
new file mode 100644
index 000000000..37a65f2b8
--- /dev/null
+++ b/src/android/app/src/main/jni/native_input.cpp
@@ -0,0 +1,629 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <common/fs/fs.h>
+#include <common/fs/path_util.h>
+#include <common/settings.h>
+#include <hid_core/hid_types.h>
+#include <jni.h>
+
+#include "android_config.h"
+#include "common/android/android_common.h"
+#include "common/android/id_cache.h"
+#include "hid_core/frontend/emulated_controller.h"
+#include "hid_core/hid_core.h"
+#include "input_common/drivers/android.h"
+#include "input_common/drivers/touch_screen.h"
+#include "input_common/drivers/virtual_amiibo.h"
+#include "input_common/drivers/virtual_gamepad.h"
+#include "native.h"
+
+std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
+
+bool IsHandheldOnly() {
+ const auto npad_style_set =
+ EmulationSession::GetInstance().System().HIDCore().GetSupportedStyleTag();
+
+ if (npad_style_set.fullkey == 1) {
+ return false;
+ }
+
+ if (npad_style_set.handheld == 0) {
+ return false;
+ }
+
+ return !Settings::IsDockedMode();
+}
+
+std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
+ return filename.replace_extension();
+}
+
+bool IsProfileNameValid(std::string_view profile_name) {
+ return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
+}
+
+bool ProfileExistsInFilesystem(std::string_view profile_name) {
+ return Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input" /
+ fmt::format("{}.ini", profile_name));
+}
+
+bool ProfileExistsInMap(const std::string& profile_name) {
+ return map_profiles.find(profile_name) != map_profiles.end();
+}
+
+bool SaveProfile(const std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ Settings::values.players.GetValue()[player_index].profile_name = profile_name;
+ map_profiles[profile_name]->SaveAndroidControlPlayerValues(player_index);
+ return true;
+}
+
+bool LoadProfile(std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ map_profiles.erase(profile_name);
+ return false;
+ }
+
+ LOG_INFO(Config, "Loading input profile `{}`", profile_name);
+
+ Settings::values.players.GetValue()[player_index].profile_name = profile_name;
+ map_profiles[profile_name]->ReadAndroidControlPlayerValues(player_index);
+ return true;
+}
+
+void ApplyControllerConfig(size_t player_index,
+ const std::function<void(Core::HID::EmulatedController*)>& apply) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ if (player_index == 0) {
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ handheld->EnableConfiguration();
+ player_one->EnableConfiguration();
+ apply(handheld);
+ apply(player_one);
+ handheld->DisableConfiguration();
+ player_one->DisableConfiguration();
+ handheld->SaveCurrentConfig();
+ player_one->SaveCurrentConfig();
+ } else {
+ auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
+ controller->EnableConfiguration();
+ apply(controller);
+ controller->DisableConfiguration();
+ controller->SaveCurrentConfig();
+ }
+}
+
+void ConnectController(size_t player_index, bool connected) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ if (player_index == 0) {
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ handheld->EnableConfiguration();
+ player_one->EnableConfiguration();
+ if (player_one->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
+ if (connected) {
+ handheld->Connect();
+ } else {
+ handheld->Disconnect();
+ }
+ player_one->Disconnect();
+ } else {
+ if (connected) {
+ player_one->Connect();
+ } else {
+ player_one->Disconnect();
+ }
+ handheld->Disconnect();
+ }
+ handheld->DisableConfiguration();
+ player_one->DisableConfiguration();
+ handheld->SaveCurrentConfig();
+ player_one->SaveCurrentConfig();
+ } else {
+ auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
+ controller->EnableConfiguration();
+ if (connected) {
+ controller->Connect();
+ } else {
+ controller->Disconnect();
+ }
+ controller->DisableConfiguration();
+ controller->SaveCurrentConfig();
+ }
+}
+
+extern "C" {
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isHandheldOnly(JNIEnv* env,
+ jobject j_obj) {
+ return IsHandheldOnly();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadButtonEvent(
+ JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_button_id, jint j_action) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetButtonState(
+ Common::Android::GetJString(env, j_guid), j_port, j_button_id, j_action != 0);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadAxisEvent(
+ JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_stick_id, jfloat j_value) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetAxisPosition(
+ Common::Android::GetJString(env, j_guid), j_port, j_stick_id, j_value);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadMotionEvent(
+ JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jlong j_delta_timestamp,
+ jfloat j_x_gyro, jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel,
+ jfloat j_z_accel) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetMotionState(
+ Common::Android::GetJString(env, j_guid), j_port, j_delta_timestamp, j_x_gyro, j_y_gyro,
+ j_z_gyro, j_x_accel, j_y_accel, j_z_accel);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onReadNfcTag(JNIEnv* env, jobject j_obj,
+ jbyteArray j_data) {
+ jboolean isCopy{false};
+ std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
+ static_cast<size_t>(env->GetArrayLength(j_data)));
+
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->LoadAmiibo(data);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onRemoveNfcTag(JNIEnv* env, jobject j_obj) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->CloseAmiibo();
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* env, jobject j_obj,
+ jint j_id, jfloat j_x_axis,
+ jfloat j_y_axis) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().Window().OnTouchPressed(j_id, j_x_axis, j_y_axis);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env, jobject j_obj,
+ jint j_id, jfloat j_x_axis,
+ jfloat j_y_axis) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().Window().OnTouchMoved(j_id, j_x_axis, j_y_axis);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
+ jint j_id) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().Window().OnTouchReleased(j_id);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayButtonEventImpl(
+ JNIEnv* env, jobject j_obj, jint j_port, jint j_button_id, jint j_action) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetButtonState(
+ j_port, j_button_id, j_action == 1);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayJoystickEventImpl(
+ JNIEnv* env, jobject j_obj, jint j_port, jint j_stick_id, jfloat j_x_axis, jfloat j_y_axis) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetStickPosition(
+ j_port, j_stick_id, j_x_axis, j_y_axis);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onDeviceMotionEvent(
+ JNIEnv* env, jobject j_obj, jint j_port, jlong j_delta_timestamp, jfloat j_x_gyro,
+ jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel, jfloat j_z_accel) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetMotionState(
+ j_port, j_delta_timestamp, j_x_gyro, j_y_gyro, j_z_gyro, j_x_accel, j_y_accel,
+ j_z_accel);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_reloadInputDevices(JNIEnv* env,
+ jobject j_obj) {
+ EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_registerController(JNIEnv* env,
+ jobject j_obj,
+ jobject j_device) {
+ EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->RegisterController(j_device);
+}
+
+jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputDevices(JNIEnv* env,
+ jobject j_obj) {
+ auto devices = EmulationSession::GetInstance().GetInputSubsystem().GetInputDevices();
+ jobjectArray jdevices = env->NewObjectArray(devices.size(), Common::Android::GetStringClass(),
+ Common::Android::ToJString(env, ""));
+ for (size_t i = 0; i < devices.size(); ++i) {
+ env->SetObjectArrayElement(jdevices, i,
+ Common::Android::ToJString(env, devices[i].Serialize()));
+ }
+ return jdevices;
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadInputProfiles(JNIEnv* env,
+ jobject j_obj) {
+ map_profiles.clear();
+ const auto input_profile_loc =
+ Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input";
+
+ if (Common::FS::IsDir(input_profile_loc)) {
+ Common::FS::IterateDirEntries(
+ input_profile_loc,
+ [&](const std::filesystem::path& full_path) {
+ const auto filename = full_path.filename();
+ const auto name_without_ext =
+ Common::FS::PathToUTF8String(GetNameWithoutExtension(filename));
+
+ if (filename.extension() == ".ini" && IsProfileNameValid(name_without_ext)) {
+ map_profiles.insert_or_assign(
+ name_without_ext, std::make_unique<AndroidConfig>(
+ name_without_ext, Config::ConfigType::InputProfile));
+ }
+
+ return true;
+ },
+ Common::FS::DirEntryFilter::File);
+ }
+}
+
+jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputProfileNames(
+ JNIEnv* env, jobject j_obj) {
+ std::vector<std::string> profile_names;
+ profile_names.reserve(map_profiles.size());
+
+ auto it = map_profiles.cbegin();
+ while (it != map_profiles.cend()) {
+ const auto& [profile_name, config] = *it;
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ it = map_profiles.erase(it);
+ continue;
+ }
+
+ profile_names.push_back(profile_name);
+ ++it;
+ }
+
+ std::stable_sort(profile_names.begin(), profile_names.end());
+
+ jobjectArray j_profile_names =
+ env->NewObjectArray(profile_names.size(), Common::Android::GetStringClass(),
+ Common::Android::ToJString(env, ""));
+ for (size_t i = 0; i < profile_names.size(); ++i) {
+ env->SetObjectArrayElement(j_profile_names, i,
+ Common::Android::ToJString(env, profile_names[i]));
+ }
+
+ return j_profile_names;
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isProfileNameValid(JNIEnv* env,
+ jobject j_obj,
+ jstring j_name) {
+ return Common::Android::GetJString(env, j_name).find_first_of("<>:;\"/\\|,.!?*") ==
+ std::string::npos;
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_createProfile(JNIEnv* env,
+ jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ if (ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ map_profiles.insert_or_assign(
+ profile_name,
+ std::make_unique<AndroidConfig>(profile_name, Config::ConfigType::InputProfile));
+
+ return SaveProfile(profile_name, j_player_index);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_deleteProfile(JNIEnv* env,
+ jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name) ||
+ Common::FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) {
+ map_profiles.erase(profile_name);
+ }
+
+ Settings::values.players.GetValue()[j_player_index].profile_name = "";
+ return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadProfile(JNIEnv* env, jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ return LoadProfile(profile_name, j_player_index);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_saveProfile(JNIEnv* env, jobject j_obj,
+ jstring j_name,
+ jint j_player_index) {
+ auto profile_name = Common::Android::GetJString(env, j_name);
+ return SaveProfile(profile_name, j_player_index);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadPerGameConfiguration(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jint j_selected_index,
+ jstring j_selected_profile_name) {
+ static constexpr size_t HANDHELD_INDEX = 8;
+
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ Settings::values.players.SetGlobal(false);
+
+ auto profile_name = Common::Android::GetJString(env, j_selected_profile_name);
+ auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(j_player_index);
+
+ if (j_selected_index == 0) {
+ Settings::values.players.GetValue()[j_player_index].profile_name = "";
+ if (j_player_index == 0) {
+ Settings::values.players.GetValue()[HANDHELD_INDEX] = {};
+ }
+ Settings::values.players.SetGlobal(true);
+ emulated_controller->ReloadFromSettings();
+ return;
+ }
+ if (profile_name.empty()) {
+ return;
+ }
+ auto& player = Settings::values.players.GetValue()[j_player_index];
+ auto& global_player = Settings::values.players.GetValue(true)[j_player_index];
+ player.profile_name = profile_name;
+ global_player.profile_name = profile_name;
+ // Read from the profile into the custom player settings
+ LoadProfile(profile_name, j_player_index);
+ // Make sure the controller is connected
+ player.connected = true;
+
+ emulated_controller->ReloadFromSettings();
+
+ if (j_player_index > 0) {
+ return;
+ }
+ // Handle Handheld cases
+ auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX];
+ auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ if (player.controller_type == Settings::ControllerType::Handheld) {
+ handheld_player = player;
+ } else {
+ handheld_player = {};
+ }
+ handheld_controller->ReloadFromSettings();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_beginMapping(JNIEnv* env, jobject j_obj,
+ jint jtype) {
+ EmulationSession::GetInstance().GetInputSubsystem().BeginMapping(
+ static_cast<InputCommon::Polling::InputType>(jtype));
+}
+
+jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getNextInput(JNIEnv* env,
+ jobject j_obj) {
+ return Common::Android::ToJString(
+ env, EmulationSession::GetInstance().GetInputSubsystem().GetNextInput().Serialize());
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_stopMapping(JNIEnv* env, jobject j_obj) {
+ EmulationSession::GetInstance().GetInputSubsystem().StopMapping();
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_updateMappingsWithDefaultImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jstring j_device_params,
+ jstring j_display_name) {
+ auto& input_subsystem = EmulationSession::GetInstance().GetInputSubsystem();
+
+ // Clear all previous mappings
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(button_id, {});
+ });
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(analog_id, {});
+ });
+ }
+
+ // Apply new mappings
+ auto device = Common::ParamPackage(Common::Android::GetJString(env, j_device_params));
+ auto button_mappings = input_subsystem.GetButtonMappingForDevice(device);
+ auto analog_mappings = input_subsystem.GetAnalogMappingForDevice(device);
+ auto display_name = Common::Android::GetJString(env, j_display_name);
+ for (const auto& button_mapping : button_mappings) {
+ const std::size_t index = button_mapping.first;
+ auto named_mapping = button_mapping.second;
+ named_mapping.Set("display", display_name);
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(index, named_mapping);
+ });
+ }
+ for (const auto& analog_mapping : analog_mappings) {
+ const std::size_t index = analog_mapping.first;
+ auto named_mapping = analog_mapping.second;
+ named_mapping.Set("display", display_name);
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(index, named_mapping);
+ });
+ }
+}
+
+jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonParamImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index,
+ jint j_button) {
+ return Common::Android::ToJString(env, EmulationSession::GetInstance()
+ .System()
+ .HIDCore()
+ .GetEmulatedControllerByIndex(j_player_index)
+ ->GetButtonParam(j_button)
+ .Serialize());
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setButtonParamImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jint j_button_id, jstring j_param) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(j_button_id,
+ Common::ParamPackage(Common::Android::GetJString(env, j_param)));
+ });
+}
+
+jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStickParamImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index,
+ jint j_stick) {
+ return Common::Android::ToJString(env, EmulationSession::GetInstance()
+ .System()
+ .HIDCore()
+ .GetEmulatedControllerByIndex(j_player_index)
+ ->GetStickParam(j_stick)
+ .Serialize());
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStickParamImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index, jint j_stick_id, jstring j_param) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(j_stick_id,
+ Common::ParamPackage(Common::Android::GetJString(env, j_param)));
+ });
+}
+
+jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv* env,
+ jobject j_obj,
+ jstring j_param) {
+ return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().GetButtonName(
+ Common::ParamPackage(Common::Android::GetJString(env, j_param))));
+}
+
+jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
+ JNIEnv* env, jobject j_obj, jint j_player_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ const auto npad_style_set = hid_core.GetSupportedStyleTag();
+ std::vector<s32> supported_indexes;
+ if (npad_style_set.fullkey == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
+ }
+
+ if (npad_style_set.joycon_dual == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
+ }
+
+ if (npad_style_set.joycon_left == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
+ }
+
+ if (npad_style_set.joycon_right == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
+ }
+
+ if (j_player_index == 0 && npad_style_set.handheld == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
+ }
+
+ if (npad_style_set.gamecube == 1) {
+ supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
+ }
+
+ jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
+ env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
+ supported_indexes.data());
+ return j_supported_indexes;
+}
+
+jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStyleIndexImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index) {
+ return static_cast<s32>(EmulationSession::GetInstance()
+ .System()
+ .HIDCore()
+ .GetEmulatedControllerByIndex(j_player_index)
+ ->GetNpadStyleIndex(true));
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStyleIndexImpl(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index,
+ jint j_style_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ auto type = static_cast<Core::HID::NpadStyleIndex>(j_style_index);
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetNpadStyleIndex(type);
+ });
+ if (j_player_index == 0) {
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ ConnectController(j_player_index,
+ player_one->IsConnected(true) || handheld->IsConnected(true));
+ }
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isControllerImpl(JNIEnv* env,
+ jobject j_obj,
+ jstring jparams) {
+ return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().IsController(
+ Common::ParamPackage(Common::Android::GetJString(env, jparams))));
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getIsConnected(JNIEnv* env,
+ jobject j_obj,
+ jint j_player_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ auto* controller = hid_core.GetEmulatedControllerByIndex(static_cast<size_t>(j_player_index));
+ if (j_player_index == 0 &&
+ controller->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
+ return hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld)->IsConnected(true);
+ }
+ return controller->IsConnected(true);
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_connectControllersImpl(
+ JNIEnv* env, jobject j_obj, jbooleanArray j_connected) {
+ jboolean isCopy = false;
+ auto j_connected_array_size = env->GetArrayLength(j_connected);
+ jboolean* j_connected_array = env->GetBooleanArrayElements(j_connected, &isCopy);
+ for (int i = 0; i < j_connected_array_size; ++i) {
+ ConnectController(i, j_connected_array[i]);
+ }
+}
+
+void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_resetControllerMappings(
+ JNIEnv* env, jobject j_obj, jint j_player_index) {
+ // Clear all previous mappings
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetButtonParam(button_id, {});
+ });
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
+ controller->SetStickParam(analog_id, {});
+ });
+ }
+}
+
+} // extern "C"
diff --git a/src/android/app/src/main/res/drawable/button_anim.xml b/src/android/app/src/main/res/drawable/button_anim.xml
new file mode 100644
index 000000000..ccdc5ca6a
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/button_anim.xml
@@ -0,0 +1,142 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="1000dp"
+ android:height="1000dp"
+ android:viewportWidth="1000"
+ android:viewportHeight="1000">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:pivotX="100"
+ android:pivotY="100"
+ android:scaleX="4.5"
+ android:scaleY="4.5"
+ android:translateX="400"
+ android:translateY="400">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?attr/colorSecondaryContainer"
+ android:fillType="nonZero"
+ android:pathData=" M198.56 100 C198.56,154.43 154.43,198.56 100,198.56 C45.57,198.56 1.44,154.43 1.44,100 C1.44,45.57 45.57,1.44 100,1.44 C154.43,1.44 198.56,45.57 198.56,100c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0.8"
+ android:fillColor="?attr/colorOnSecondaryContainer"
+ android:fillType="nonZero"
+ android:pathData=" M50.14 151.21 C50.53,150.18 89.6,49.87 90.1,48.63 C90.1,48.63 90.67,47.2 90.67,47.2 C90.67,47.2 101.67,47.2 101.67,47.2 C101.67,47.2 112.67,47.2 112.67,47.2 C112.67,47.2 133.47,99.12 133.47,99.12 C144.91,127.68 154.32,151.17 154.38,151.33 C154.47,151.56 152.2,151.6 143.14,151.55 C143.14,151.55 131.79,151.48 131.79,151.48 C131.79,151.48 127.22,139.57 127.22,139.57 C127.22,139.57 122.65,127.66 122.65,127.66 C122.65,127.66 101.68,127.73 101.68,127.73 C101.68,127.73 80.71,127.8 80.71,127.8 C80.71,127.8 76.38,139.71 76.38,139.71 C76.38,139.71 72.06,151.62 72.06,151.62 C72.06,151.62 61.02,151.62 61.02,151.62 C50.61,151.62 50,151.55 50.14,151.22 C50.14,151.22 50.14,151.21 50.14,151.21c M115.86 110.06 C115.8,109.91 112.55,101.13 108.62,90.56 C104.7,80 101.42,71.43 101.34,71.53 C101.22,71.66 92.84,94.61 87.25,110.06 C87.17,110.29 90.13,110.34 101.56,110.34 C113,110.34 115.95,110.28 115.86,110.06c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="4.5"
+ android:valueTo="3.75"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="4.5"
+ android:valueTo="3.75"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="234"
+ android:propertyName="scaleX"
+ android:startOffset="100"
+ android:valueFrom="3.75"
+ android:valueTo="3.75"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="234"
+ android:propertyName="scaleY"
+ android:startOffset="100"
+ android:valueFrom="3.75"
+ android:valueTo="3.75"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleX"
+ android:startOffset="334"
+ android:valueFrom="3.75"
+ android:valueTo="4.75"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="scaleY"
+ android:startOffset="334"
+ android:valueFrom="3.75"
+ android:valueTo="4.75"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="501"
+ android:valueFrom="4.75"
+ android:valueTo="4.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="501"
+ android:valueFrom="4.75"
+ android:valueTo="4.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1034"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml b/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml
new file mode 100644
index 000000000..8e3c66f74
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M700,480q-25,0 -42.5,-17.5T640,420q0,-25 17.5,-42.5T700,360q25,0 42.5,17.5T760,420q0,25 -17.5,42.5T700,480ZM366,480ZM280,600v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM160,720q-33,0 -56.5,-23.5T80,640v-320q0,-34 24,-57.5t58,-23.5h77l81,81L160,320v320h366L55,169l57,-57 736,736 -57,57 -185,-185L160,720ZM880,640q0,26 -14,46t-37,29l-29,-29v-366L434,320l-80,-80h446q33,0 56.5,23.5T880,320v320ZM617,503Z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_more_vert.xml b/src/android/app/src/main/res/drawable/ic_more_vert.xml
new file mode 100644
index 000000000..9f62ac595
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_more_vert.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="24dp">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_new_label.xml b/src/android/app/src/main/res/drawable/ic_new_label.xml
new file mode 100644
index 000000000..fac562c26
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_new_label.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M21,12l-4.37,6.16C16.26,18.68 15.65,19 15,19h-3l0,-6H9v-3H3V7c0,-1.1 0.9,-2 2,-2h10c0.65,0 1.26,0.31 1.63,0.84L21,12zM10,15H7v-3H5v3H2v2h3v3h2v-3h3V15z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_overlay.xml b/src/android/app/src/main/res/drawable/ic_overlay.xml
new file mode 100644
index 000000000..c7986c5a2
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_overlay.xml
@@ -0,0 +1,21 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M21,5H3C1.9,5 1,5.9 1,7v10c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7C23,5.9 22.1,5 21,5zM18,17H6V7h12V17z" />
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M15,11.25h1.5v1.5h-1.5z" />
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M12.5,11.25h1.5v1.5h-1.5z" />
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M10,11.25h1.5v1.5h-1.5z" />
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M7.5,11.25h1.5v1.5h-1.5z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_share.xml b/src/android/app/src/main/res/drawable/ic_share.xml
new file mode 100644
index 000000000..3fc2f3c99
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_share.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" />
+</vector>
diff --git a/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml b/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml
new file mode 100644
index 000000000..a1da1316f
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml
@@ -0,0 +1,118 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="1000dp"
+ android:height="1000dp"
+ android:viewportWidth="1000"
+ android:viewportHeight="1000">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotX="100"
+ android:pivotY="100"
+ android:scaleX="5"
+ android:scaleY="5"
+ android:translateX="400"
+ android:translateY="400">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M100 199.39 C59.8,199.39 23.56,175.17 8.18,138.04 C-7.2,100.9 1.3,58.15 29.73,29.72 C58.15,1.3 100.9,-7.21 138.04,8.18 C175.18,23.56 199.39,59.8 199.39,100 C199.33,154.87 154.87,199.33 100,199.39c "
+ android:strokeWidth="1"
+ android:strokeAlpha="0.6"
+ android:strokeColor="?attr/colorOutline"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:scaleX="5"
+ android:scaleY="5"
+ android:translateX="500"
+ android:translateY="500">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-100"
+ android:translateY="-100">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?attr/colorSecondaryContainer"
+ android:fillType="nonZero"
+ android:pathData=" M100.45 28.02 C140.63,28.02 173.2,60.59 173.2,100.77 C173.2,140.95 140.63,173.52 100.45,173.52 C60.27,173.52 27.7,140.95 27.7,100.77 C27.7,60.59 60.27,28.02 100.45,28.02c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0.8"
+ android:fillColor="?attr/colorOnSecondaryContainer"
+ android:fillType="nonZero"
+ android:pathData=" M100.45 50.26 C128.62,50.26 151.46,73.1 151.46,101.28 C151.46,129.45 128.62,152.29 100.45,152.29 C72.27,152.29 49.43,129.45 49.43,101.28 C49.43,73.1 72.27,50.26 100.45,50.26c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="267"
+ android:pathData="M 500,500C 500,500 364,500 364,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="234"
+ android:pathData="M 364,500C 364,500 364,500 364,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="267">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="133"
+ android:pathData="M 364,500C 364,500 525,500 525,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="501">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:pathData="M 525,500C 525,500 500,500 500,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="634">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="968"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml b/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml
new file mode 100644
index 000000000..bc71adcbd
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml
@@ -0,0 +1,173 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="1000dp"
+ android:height="1000dp"
+ android:viewportWidth="1000"
+ android:viewportHeight="1000">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotX="100"
+ android:pivotY="100"
+ android:scaleX="5"
+ android:scaleY="5"
+ android:translateX="400"
+ android:translateY="400">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M100 199.39 C59.8,199.39 23.56,175.17 8.18,138.04 C-7.2,100.9 1.3,58.15 29.73,29.72 C58.15,1.3 100.9,-7.21 138.04,8.18 C175.18,23.56 199.39,59.8 199.39,100 C199.33,154.87 154.87,199.33 100,199.39c "
+ android:strokeWidth="1"
+ android:strokeAlpha="0.6"
+ android:strokeColor="?attr/colorOutline"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:scaleX="5"
+ android:scaleY="5"
+ android:translateX="500"
+ android:translateY="500">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-100"
+ android:translateY="-100">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="?attr/colorSecondaryContainer"
+ android:fillType="nonZero"
+ android:pathData=" M100.45 28.02 C140.63,28.02 173.2,60.59 173.2,100.77 C173.2,140.95 140.63,173.52 100.45,173.52 C60.27,173.52 27.7,140.95 27.7,100.77 C27.7,60.59 60.27,28.02 100.45,28.02c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0.8"
+ android:fillColor="?attr/colorOnSecondaryContainer"
+ android:fillType="nonZero"
+ android:pathData=" M100.45 50.26 C128.62,50.26 151.46,73.1 151.46,101.28 C151.46,129.45 128.62,152.29 100.45,152.29 C72.27,152.29 49.43,129.45 49.43,101.28 C49.43,73.1 72.27,50.26 100.45,50.26c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="267"
+ android:pathData="M 500,500C 500,500 364,500 364,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="234"
+ android:pathData="M 364,500C 364,500 364,500 364,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="267">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="133"
+ android:pathData="M 364,500C 364,500 525,500 525,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="501">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:pathData="M 525,500C 525,500 500,500 500,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="634">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="400"
+ android:pathData="M 500,500C 500,500 500,500 500,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="734">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="267"
+ android:pathData="M 500,500C 500,500 500,364 500,364"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="1134">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="234"
+ android:pathData="M 500,364C 500,364 500,364 500,364"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="1401">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="133"
+ android:pathData="M 500,364C 500,364 500,535 500,535"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="1635">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:pathData="M 500,535C 500,535 500,500 500,500"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="1768">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="2269"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml b/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml
new file mode 100644
index 000000000..583620dc6
--- /dev/null
+++ b/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/setting_body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:minHeight="72dp"
+ android:padding="16dp"
+ android:nextFocusLeft="@id/button_options">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_setting_name"
+ style="@style/TextAppearance.Material3.HeadlineMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:textSize="17sp"
+ app:lineHeight="22dp"
+ tools:text="Setting Name" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_setting_value"
+ style="@style/TextAppearance.Material3.LabelMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/spacing_small"
+ android:textAlignment="viewStart"
+ android:textStyle="bold"
+ android:textSize="13sp"
+ tools:text="1x" />
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/button_options"
+ style="?attr/materialIconButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:nextFocusRight="@id/setting_body"
+ app:icon="@drawable/ic_more_vert"
+ app:iconSize="24dp"
+ app:iconTint="?attr/colorOnSurface" />
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml
index bda524f0f..09e26990b 100644
--- a/src/android/app/src/main/res/layout/card_driver_option.xml
+++ b/src/android/app/src/main/res/layout/card_driver_option.xml
@@ -39,10 +39,7 @@
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="none"
- android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
- android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/select_gpu_driver_default" />
@@ -52,10 +49,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
- android:ellipsize="none"
- android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
- android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
@@ -65,10 +59,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
- android:ellipsize="none"
- android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
- android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml
index ed4a7ca8f..e3a5f1a86 100644
--- a/src/android/app/src/main/res/layout/card_folder.xml
+++ b/src/android/app/src/main/res/layout/card_folder.xml
@@ -21,10 +21,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:ellipsize="none"
- android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
- android:singleLine="true"
android:textAlignment="viewStart"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button_layout"
diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml
index 6340171ec..411b50315 100644
--- a/src/android/app/src/main/res/layout/card_game.xml
+++ b/src/android/app/src/main/res/layout/card_game.xml
@@ -40,10 +40,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:ellipsize="none"
- android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
- android:singleLine="true"
android:textAlignment="center"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
diff --git a/src/android/app/src/main/res/layout/card_simple_outlined.xml b/src/android/app/src/main/res/layout/card_simple_outlined.xml
index b73930e7e..e29df6a2d 100644
--- a/src/android/app/src/main/res/layout/card_simple_outlined.xml
+++ b/src/android/app/src/main/res/layout/card_simple_outlined.xml
@@ -59,9 +59,6 @@
android:textAlignment="viewStart"
android:textSize="14sp"
android:textStyle="bold"
- android:singleLine="true"
- android:marqueeRepeatLimit="marquee_forever"
- android:ellipsize="none"
android:requiresFadingEdge="horizontal"
android:layout_marginTop="6dp"
android:visibility="gone"
diff --git a/src/android/app/src/main/res/layout/dialog_input_profiles.xml b/src/android/app/src/main/res/layout/dialog_input_profiles.xml
new file mode 100644
index 000000000..6ad76fe41
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_input_profiles.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_profiles"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fadeScrollbars="false" />
diff --git a/src/android/app/src/main/res/layout/dialog_mapping.xml b/src/android/app/src/main/res/layout/dialog_mapping.xml
new file mode 100644
index 000000000..06190b8d2
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_mapping.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:focusedByDefault="true"
+ android:orientation="horizontal"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/image_stick_animation"
+ android:layout_width="@dimen/mapping_anim_size"
+ android:layout_height="@dimen/mapping_anim_size"
+ tools:src="@drawable/stick_two_direction_anim" />
+
+ <ImageView
+ android:id="@+id/image_button_animation"
+ android:layout_width="@dimen/mapping_anim_size"
+ android:layout_height="@dimen/mapping_anim_size"
+ android:layout_marginStart="48dp"
+ tools:src="@drawable/button_anim" />
+
+</LinearLayout>
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
index 436ebd79d..5e3f3cf28 100644
--- a/src/android/app/src/main/res/layout/fragment_game_properties.xml
+++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml
@@ -76,10 +76,7 @@
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:layout_marginHorizontal="16dp"
- android:ellipsize="none"
- android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
- android:singleLine="true"
android:textAlignment="center"
tools:text="deko_basic" />
diff --git a/src/android/app/src/main/res/layout/list_item_input_profile.xml b/src/android/app/src/main/res/layout/list_item_input_profile.xml
new file mode 100644
index 000000000..a08dccf0c
--- /dev/null
+++ b/src/android/app/src/main/res/layout/list_item_input_profile.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:paddingHorizontal="20dp"
+ android:paddingVertical="16dp">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.Material3.HeadlineMedium"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:textAlignment="viewStart"
+ android:gravity="start|center_vertical"
+ android:textSize="17sp"
+ android:layout_marginEnd="16dp"
+ app:layout_constraintBottom_toBottomOf="@+id/button_layout"
+ app:layout_constraintEnd_toStartOf="@+id/button_layout"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:lineHeight="28dp"
+ tools:text="My profile" />
+
+ <LinearLayout
+ android:id="@+id/button_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <Button
+ android:id="@+id/button_new"
+ style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/create_new_profile"
+ android:tooltipText="@string/create_new_profile"
+ app:icon="@drawable/ic_new_label" />
+
+ <Button
+ android:id="@+id/button_delete"
+ style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/delete"
+ android:tooltipText="@string/delete"
+ app:icon="@drawable/ic_delete" />
+
+ <Button
+ android:id="@+id/button_save"
+ style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/save"
+ android:tooltipText="@string/save"
+ app:icon="@drawable/ic_save" />
+
+ <Button
+ android:id="@+id/button_load"
+ style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/load"
+ android:tooltipText="@string/load"
+ app:icon="@drawable/ic_import" />
+
+ </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_setting_input.xml b/src/android/app/src/main/res/layout/list_item_setting_input.xml
new file mode 100644
index 000000000..d67cbe245
--- /dev/null
+++ b/src/android/app/src/main/res/layout/list_item_setting_input.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/setting_body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:minHeight="72dp"
+ android:padding="16dp"
+ android:nextFocusRight="@id/button_options">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_setting_name"
+ style="@style/TextAppearance.Material3.HeadlineMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:textSize="17sp"
+ app:lineHeight="22dp"
+ tools:text="Setting Name" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_setting_value"
+ style="@style/TextAppearance.Material3.LabelMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/spacing_small"
+ android:textAlignment="viewStart"
+ android:textStyle="bold"
+ android:textSize="13sp"
+ tools:text="1x" />
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/button_options"
+ style="?attr/materialIconButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:nextFocusLeft="@id/setting_body"
+ app:icon="@drawable/ic_more_vert"
+ app:iconSize="24dp"
+ app:iconTint="?attr/colorOnSurface" />
+
+ </LinearLayout>
+
+</RelativeLayout>
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 eecb0563b..867197ebc 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
@@ -17,8 +17,13 @@
android:title="@string/per_game_settings" />
<item
- android:id="@+id/menu_overlay_controls"
+ android:id="@+id/menu_controls"
android:icon="@drawable/ic_controller"
+ android:title="@string/preferences_controls" />
+
+ <item
+ android:id="@+id/menu_overlay_controls"
+ android:icon="@drawable/ic_overlay"
android:title="@string/emulation_input_overlay" />
<item
diff --git a/src/android/app/src/main/res/menu/menu_input_options.xml b/src/android/app/src/main/res/menu/menu_input_options.xml
new file mode 100644
index 000000000..81ea5043f
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_input_options.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/invert_axis"
+ android:title="@string/invert_axis"
+ android:visible="false" />
+
+ <item
+ android:id="@+id/invert_button"
+ android:title="@string/invert_button"
+ android:visible="false" />
+
+ <item
+ android:id="@+id/toggle_button"
+ android:title="@string/toggle_button"
+ android:visible="false" />
+
+ <item
+ android:id="@+id/turbo_button"
+ android:title="@string/turbo_button"
+ android:visible="false" />
+
+ <item
+ android:id="@+id/set_threshold"
+ android:title="@string/set_threshold"
+ android:visible="false" />
+
+ <item
+ android:id="@+id/toggle_axis"
+ android:title="@string/toggle_axis"
+ android:visible="false" />
+
+</menu>
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml
index 1d87d36b3..e4c66e7d5 100644
--- a/src/android/app/src/main/res/navigation/settings_navigation.xml
+++ b/src/android/app/src/main/res/navigation/settings_navigation.xml
@@ -26,7 +26,7 @@
<fragment
android:id="@+id/settingsSearchFragment"
- android:name="org.yuzu.yuzu_emu.fragments.SettingsSearchFragment"
+ android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSearchFragment"
android:label="SettingsSearchFragment" />
</navigation>
diff --git a/src/android/app/src/main/res/values-w600dp/dimens.xml b/src/android/app/src/main/res/values-w600dp/dimens.xml
index 128319e27..0e2d40876 100644
--- a/src/android/app/src/main/res/values-w600dp/dimens.xml
+++ b/src/android/app/src/main/res/values-w600dp/dimens.xml
@@ -2,4 +2,6 @@
<resources>
<dimen name="spacing_navigation">0dp</dimen>
<dimen name="spacing_navigation_rail">80dp</dimen>
+
+ <dimen name="mapping_anim_size">100dp</dimen>
</resources>
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 4701913eb..1bd6455b4 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -292,4 +292,15 @@
<item>5</item>
</integer-array>
+ <string-array name="verticalAlignmentEntries">
+ <item>@string/top</item>
+ <item>@string/center</item>
+ <item>@string/bottom</item>
+ </string-array>
+ <integer-array name="verticalAlignmentValues">
+ <item>1</item>
+ <item>0</item>
+ <item>2</item>
+ </integer-array>
+
</resources>
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml
index 992b5ae44..bf733637f 100644
--- a/src/android/app/src/main/res/values/dimens.xml
+++ b/src/android/app/src/main/res/values/dimens.xml
@@ -18,4 +18,6 @@
<dimen name="dialog_margin">20dp</dimen>
<dimen name="elevated_app_bar">3dp</dimen>
+
+ <dimen name="mapping_anim_size">75dp</dimen>
</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 489e00107..f7f19cdad 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -209,6 +209,7 @@
<string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings -->
+ <string name="device_name">Device name</string>
<string name="use_docked_mode">Docked Mode</string>
<string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string>
<string name="emulated_region">Emulated region</string>
@@ -226,6 +227,8 @@
<string name="renderer_screen_layout">Orientation</string>
<string name="renderer_aspect_ratio">Aspect ratio</string>
<string name="renderer_scaling_filter">Window adapting filter</string>
+ <string name="fsr_sharpness">FSR sharpness</string>
+ <string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string>
<string name="renderer_anti_aliasing">Anti-aliasing method</string>
<string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string>
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
@@ -253,6 +256,92 @@
<string name="audio_volume">Volume</string>
<string name="audio_volume_description">Specifies the volume of audio output.</string>
+ <!-- Input strings -->
+ <string name="buttons">Buttons</string>
+ <string name="button_a">A</string>
+ <string name="button_b">B</string>
+ <string name="button_x">X</string>
+ <string name="button_y">Y</string>
+ <string name="button_plus">Plus</string>
+ <string name="button_minus">Minus</string>
+ <string name="button_home">Home</string>
+ <string name="button_capture">Capture</string>
+ <string name="start_pause">Start/Pause</string>
+ <string name="dpad">D-Pad</string>
+ <string name="up">Up</string>
+ <string name="down">Down</string>
+ <string name="left">Left</string>
+ <string name="right">Right</string>
+ <string name="left_stick">Left stick</string>
+ <string name="control_stick">Control stick</string>
+ <string name="right_stick">Right stick</string>
+ <string name="c_stick">C-Stick</string>
+ <string name="pressed">Pressed</string>
+ <string name="range">Range</string>
+ <string name="deadzone">Deadzone</string>
+ <string name="modifier">Modifier</string>
+ <string name="modifier_range">Modifier range</string>
+ <string name="triggers">Triggers</string>
+ <string name="button_l">L</string>
+ <string name="button_r">R</string>
+ <string name="button_zl">ZL</string>
+ <string name="button_zr">ZR</string>
+ <string name="button_sl_left">Left SL</string>
+ <string name="button_sr_left">Left SR</string>
+ <string name="button_sl_right">Right SL</string>
+ <string name="button_sr_right">Right SR</string>
+ <string name="button_z">Z</string>
+ <string name="invalid">Invalid</string>
+ <string name="not_set">Not set</string>
+ <string name="unknown">Unknown</string>
+ <string name="qualified_hat">%1$s%2$s%3$sHat %4$s</string>
+ <string name="qualified_button_stick_axis">%1$s%2$s%3$sAxis %4$s</string>
+ <string name="qualified_button">%1$s%2$s%3$sButton %4$s</string>
+ <string name="qualified_axis">Axis %1$s%2$s</string>
+ <string name="unused">Unused</string>
+ <string name="input_prompt">Move or press an input</string>
+ <string name="unsupported_input">Unsupported input type</string>
+ <string name="input_mapping_filter">Input mapping filter</string>
+ <string name="input_mapping_filter_description">Select a device to filter mapping inputs</string>
+ <string name="auto_map">Auto-map a controller</string>
+ <string name="auto_map_description">Select a device to attempt auto-mapping</string>
+ <string name="attempted_auto_map">Attempted auto-map with %1$s</string>
+ <string name="controller_type">Controller type</string>
+ <string name="pro_controller">Pro Controller</string>
+ <string name="handheld">Handheld</string>
+ <string name="dual_joycons">Dual Joycons</string>
+ <string name="left_joycon">Left Joycon</string>
+ <string name="right_joycon">Right Joycon</string>
+ <string name="gamecube_controller">GameCube Controller</string>
+ <string name="invert_axis">Invert axis</string>
+ <string name="invert_button">Invert button</string>
+ <string name="toggle_button">Toggle button</string>
+ <string name="turbo_button">Turbo button</string>
+ <string name="set_threshold">Set threshold</string>
+ <string name="toggle_axis">Toggle axis</string>
+ <string name="connected">Connected</string>
+ <string name="use_system_vibrator">Use system vibrator</string>
+ <string name="input_overlay">Input overlay</string>
+ <string name="vibration">Vibration</string>
+ <string name="vibration_strength">Vibration strength</string>
+ <string name="profile">Profile</string>
+ <string name="create_new_profile">Create new profile</string>
+ <string name="enter_profile_name">Enter profile name</string>
+ <string name="profile_name_already_exists">Profile name already exists</string>
+ <string name="invalid_profile_name">Invalid profile name</string>
+ <string name="use_global_input_configuration">Use global input configuration</string>
+ <string name="player_num_profile">Player %d profile</string>
+ <string name="delete_input_profile">Delete input profile</string>
+ <string name="delete_input_profile_description">Are you sure that you want to delete this profile? This is not recoverable.</string>
+ <string name="stick_map_description">Move a stick left and then up or press a button</string>
+ <string name="button_map_description">Press a button or move a trigger/stick</string>
+ <string name="map_dpad_direction">Map to D-Pad %1$s</string>
+ <string name="map_control">Map to %1$s</string>
+ <string name="failed_to_load_profile">Failed to load profile</string>
+ <string name="failed_to_save_profile">Failed to save profile</string>
+ <string name="reset_mapping">Reset mappings</string>
+ <string name="reset_mapping_description">Are you sure that you want to reset all mappings for this controller to default? This cannot be undone.</string>
+
<!-- Miscellaneous -->
<string name="slider_default">Default</string>
<string name="ini_saved">Saved settings</string>
@@ -290,6 +379,10 @@
<string name="more_options">More options</string>
<string name="use_global_setting">Use global setting</string>
<string name="operation_completed_successfully">The operation completed successfully</string>
+ <string name="retry">Retry</string>
+ <string name="confirm">Confirm</string>
+ <string name="load">Load</string>
+ <string name="save">Save</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string>
@@ -311,6 +404,9 @@
<string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
<string name="preferences_audio">Audio</string>
<string name="preferences_audio_description">Output engine, volume</string>
+ <string name="preferences_controls">Controls</string>
+ <string name="preferences_controls_description">Map controller input</string>
+ <string name="preferences_player">Player %d</string>
<string name="preferences_theme">Theme and color</string>
<string name="preferences_debug">Debug</string>
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
@@ -558,6 +654,12 @@
<string name="mute">Mute</string>
<string name="unmute">Unmute</string>
+ <!-- Emulation vertical alignment -->
+ <string name="vertical_alignment">Vertical alignment</string>
+ <string name="top">Top</string>
+ <string name="center">Center</string>
+ <string name="bottom">Bottom</string>
+
<!-- Licenses screen strings -->
<string name="licenses">Licenses</string>
<string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index d97ca2a40..49efae8e3 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -357,7 +357,9 @@ bool IsCubebSuitable() {
return false;
}
- SCOPE_EXIT({ cubeb_destroy(ctx); });
+ SCOPE_EXIT {
+ cubeb_destroy(ctx);
+ };
#ifdef _WIN32
if (SUCCEEDED(com_init_result)) {
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index c047b0668..0a98eb31e 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -20,10 +20,10 @@
namespace AudioCore::Sink {
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
queue.enqueue(buffer);
++queued_buffers;
- });
+ };
if (type == StreamType::In) {
return;
diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp
index f39262db9..1145cbdf2 100644
--- a/src/common/android/id_cache.cpp
+++ b/src/common/android/id_cache.cpp
@@ -65,6 +65,30 @@ static jclass s_boolean_class;
static jmethodID s_boolean_constructor;
static jfieldID s_boolean_value_field;
+static jclass s_player_input_class;
+static jmethodID s_player_input_constructor;
+static jfieldID s_player_input_connected_field;
+static jfieldID s_player_input_buttons_field;
+static jfieldID s_player_input_analogs_field;
+static jfieldID s_player_input_motions_field;
+static jfieldID s_player_input_vibration_enabled_field;
+static jfieldID s_player_input_vibration_strength_field;
+static jfieldID s_player_input_body_color_left_field;
+static jfieldID s_player_input_body_color_right_field;
+static jfieldID s_player_input_button_color_left_field;
+static jfieldID s_player_input_button_color_right_field;
+static jfieldID s_player_input_profile_name_field;
+static jfieldID s_player_input_use_system_vibrator_field;
+
+static jclass s_yuzu_input_device_interface;
+static jmethodID s_yuzu_input_device_get_name;
+static jmethodID s_yuzu_input_device_get_guid;
+static jmethodID s_yuzu_input_device_get_port;
+static jmethodID s_yuzu_input_device_get_supports_vibration;
+static jmethodID s_yuzu_input_device_vibrate;
+static jmethodID s_yuzu_input_device_get_axes;
+static jmethodID s_yuzu_input_device_has_keys;
+
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace Common::Android {
@@ -276,6 +300,94 @@ jfieldID GetBooleanValueField() {
return s_boolean_value_field;
}
+jclass GetPlayerInputClass() {
+ return s_player_input_class;
+}
+
+jmethodID GetPlayerInputConstructor() {
+ return s_player_input_constructor;
+}
+
+jfieldID GetPlayerInputConnectedField() {
+ return s_player_input_connected_field;
+}
+
+jfieldID GetPlayerInputButtonsField() {
+ return s_player_input_buttons_field;
+}
+
+jfieldID GetPlayerInputAnalogsField() {
+ return s_player_input_analogs_field;
+}
+
+jfieldID GetPlayerInputMotionsField() {
+ return s_player_input_motions_field;
+}
+
+jfieldID GetPlayerInputVibrationEnabledField() {
+ return s_player_input_vibration_enabled_field;
+}
+
+jfieldID GetPlayerInputVibrationStrengthField() {
+ return s_player_input_vibration_strength_field;
+}
+
+jfieldID GetPlayerInputBodyColorLeftField() {
+ return s_player_input_body_color_left_field;
+}
+
+jfieldID GetPlayerInputBodyColorRightField() {
+ return s_player_input_body_color_right_field;
+}
+
+jfieldID GetPlayerInputButtonColorLeftField() {
+ return s_player_input_button_color_left_field;
+}
+
+jfieldID GetPlayerInputButtonColorRightField() {
+ return s_player_input_button_color_right_field;
+}
+
+jfieldID GetPlayerInputProfileNameField() {
+ return s_player_input_profile_name_field;
+}
+
+jfieldID GetPlayerInputUseSystemVibratorField() {
+ return s_player_input_use_system_vibrator_field;
+}
+
+jclass GetYuzuInputDeviceInterface() {
+ return s_yuzu_input_device_interface;
+}
+
+jmethodID GetYuzuDeviceGetName() {
+ return s_yuzu_input_device_get_name;
+}
+
+jmethodID GetYuzuDeviceGetGUID() {
+ return s_yuzu_input_device_get_guid;
+}
+
+jmethodID GetYuzuDeviceGetPort() {
+ return s_yuzu_input_device_get_port;
+}
+
+jmethodID GetYuzuDeviceGetSupportsVibration() {
+ return s_yuzu_input_device_get_supports_vibration;
+}
+
+jmethodID GetYuzuDeviceVibrate() {
+ return s_yuzu_input_device_vibrate;
+}
+
+jmethodID GetYuzuDeviceGetAxes() {
+ return s_yuzu_input_device_get_axes;
+}
+
+jmethodID GetYuzuDeviceHasKeys() {
+ return s_yuzu_input_device_has_keys;
+}
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -387,6 +499,55 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
env->DeleteLocalRef(boolean_class);
+ const jclass player_input_class =
+ env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
+ s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
+ s_player_input_constructor = env->GetMethodID(
+ player_input_class, "<init>",
+ "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
+ s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
+ s_player_input_buttons_field =
+ env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
+ s_player_input_analogs_field =
+ env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
+ s_player_input_motions_field =
+ env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
+ s_player_input_vibration_enabled_field =
+ env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
+ s_player_input_vibration_strength_field =
+ env->GetFieldID(player_input_class, "vibrationStrength", "I");
+ s_player_input_body_color_left_field =
+ env->GetFieldID(player_input_class, "bodyColorLeft", "J");
+ s_player_input_body_color_right_field =
+ env->GetFieldID(player_input_class, "bodyColorRight", "J");
+ s_player_input_button_color_left_field =
+ env->GetFieldID(player_input_class, "buttonColorLeft", "J");
+ s_player_input_button_color_right_field =
+ env->GetFieldID(player_input_class, "buttonColorRight", "J");
+ s_player_input_profile_name_field =
+ env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
+ s_player_input_use_system_vibrator_field =
+ env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
+ env->DeleteLocalRef(player_input_class);
+
+ const jclass yuzu_input_device_interface =
+ env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
+ s_yuzu_input_device_interface =
+ reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
+ s_yuzu_input_device_get_name =
+ env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
+ s_yuzu_input_device_get_guid =
+ env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
+ s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", "()I");
+ s_yuzu_input_device_get_supports_vibration =
+ env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
+ s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", "(F)V");
+ s_yuzu_input_device_get_axes =
+ env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
+ s_yuzu_input_device_has_keys =
+ env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
+ env->DeleteLocalRef(yuzu_input_device_interface);
+
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
@@ -416,6 +577,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
env->DeleteGlobalRef(s_double_class);
env->DeleteGlobalRef(s_integer_class);
env->DeleteGlobalRef(s_boolean_class);
+ env->DeleteGlobalRef(s_player_input_class);
+ env->DeleteGlobalRef(s_yuzu_input_device_interface);
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h
index 47802f96c..cd2844dcc 100644
--- a/src/common/android/id_cache.h
+++ b/src/common/android/id_cache.h
@@ -85,4 +85,28 @@ jclass GetBooleanClass();
jmethodID GetBooleanConstructor();
jfieldID GetBooleanValueField();
+jclass GetPlayerInputClass();
+jmethodID GetPlayerInputConstructor();
+jfieldID GetPlayerInputConnectedField();
+jfieldID GetPlayerInputButtonsField();
+jfieldID GetPlayerInputAnalogsField();
+jfieldID GetPlayerInputMotionsField();
+jfieldID GetPlayerInputVibrationEnabledField();
+jfieldID GetPlayerInputVibrationStrengthField();
+jfieldID GetPlayerInputBodyColorLeftField();
+jfieldID GetPlayerInputBodyColorRightField();
+jfieldID GetPlayerInputButtonColorLeftField();
+jfieldID GetPlayerInputButtonColorRightField();
+jfieldID GetPlayerInputProfileNameField();
+jfieldID GetPlayerInputUseSystemVibratorField();
+
+jclass GetYuzuInputDeviceInterface();
+jmethodID GetYuzuDeviceGetName();
+jmethodID GetYuzuDeviceGetGUID();
+jmethodID GetYuzuDeviceGetPort();
+jmethodID GetYuzuDeviceGetSupportsVibration();
+jmethodID GetYuzuDeviceVibrate();
+jmethodID GetYuzuDeviceGetAxes();
+jmethodID GetYuzuDeviceHasKeys();
+
} // namespace Common::Android
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp
index 6e117cb41..b2c9d126a 100644
--- a/src/common/demangle.cpp
+++ b/src/common/demangle.cpp
@@ -20,7 +20,9 @@ std::string DemangleSymbol(const std::string& mangled) {
}
char* demangled = nullptr;
- SCOPE_EXIT({ std::free(demangled); });
+ SCOPE_EXIT {
+ std::free(demangled);
+ };
if (is_itanium(mangled)) {
demangled = llvm::itaniumDemangle(mangled.c_str());
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 860c39e6a..e0b5a6a67 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -430,11 +430,11 @@ public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_} {
bool good = false;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (!good) {
Release();
}
- });
+ };
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != 0x1000) {
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 85dc18c11..3205eb7da 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -24,10 +24,10 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
out_entry->block_size = page_size;
// Regardless of whether the page was mapped, advance on exit.
- SCOPE_EXIT({
+ SCOPE_EXIT {
context->next_page += 1;
context->next_offset += page_size;
- });
+ };
// Validate that we can read the actual entry.
const auto page = context->next_page;
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index e9c789c88..f3e88cde9 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -7,29 +7,61 @@
#include "common/common_funcs.h"
namespace detail {
-template <typename Func>
-struct ScopeExitHelper {
- explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {}
- ~ScopeExitHelper() {
+template <class F>
+class ScopeGuard {
+ YUZU_NON_COPYABLE(ScopeGuard);
+
+private:
+ F f;
+ bool active;
+
+public:
+ constexpr ScopeGuard(F f_) : f(std::move(f_)), active(true) {}
+ constexpr ~ScopeGuard() {
if (active) {
- func();
+ f();
}
}
-
- void Cancel() {
+ constexpr void Cancel() {
active = false;
}
- Func func;
- bool active{true};
+ constexpr ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
+ rhs.Cancel();
+ }
+
+ ScopeGuard& operator=(ScopeGuard&& rhs) = delete;
};
-template <typename Func>
-ScopeExitHelper<Func> ScopeExit(Func&& func) {
- return ScopeExitHelper<Func>(std::forward<Func>(func));
+template <class F>
+constexpr ScopeGuard<F> MakeScopeGuard(F f) {
+ return ScopeGuard<F>(std::move(f));
}
+
+enum class ScopeGuardOnExit {};
+
+template <typename F>
+constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
+ return ScopeGuard<F>(std::forward<F>(f));
+}
+
} // namespace detail
+#define CONCATENATE_IMPL(s1, s2) s1##s2
+#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
+
+#ifdef __COUNTER__
+#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
+#else
+#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
+#endif
+
+/**
+ * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
+ * used when the caller might want to cancel the ScopeExit.
+ */
+#define SCOPE_GUARD detail::ScopeGuardOnExit() + [&]()
+
/**
* This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
* for doing ad-hoc clean-up tasks in a function with multiple returns.
@@ -38,7 +70,7 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
* \code
* const int saved_val = g_foo;
* g_foo = 55;
- * SCOPE_EXIT({ g_foo = saved_val; });
+ * SCOPE_EXIT{ g_foo = saved_val; };
*
* if (Bar()) {
* return 0;
@@ -47,10 +79,4 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
* }
* \endcode
*/
-#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
-
-/**
- * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
- * used when the caller might want to cancel the ScopeExit.
- */
-#define SCOPE_GUARD(body) detail::ScopeExit([&]() body)
+#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 07709d4e5..80d388fe8 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -30,6 +30,7 @@ namespace Settings {
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
+SETTING(AppletMode, false);
SETTING(AudioEngine, false);
SETTING(bool, false);
SETTING(int, false);
@@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
return "Debugging";
case Category::GpuDriver:
return "GpuDriver";
+ case Category::LibraryApplet:
+ return "LibraryApplet";
case Category::Miscellaneous:
return "Miscellaneous";
case Category::Network:
diff --git a/src/common/settings.h b/src/common/settings.h
index f1b1add56..aa054dc24 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -133,6 +133,38 @@ struct TouchFromButtonMap {
struct Values {
Linkage linkage{};
+ // Applet
+ Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> player_select_applet_mode{
+ linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet};
+ Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> photo_viewer_applet_mode{
+ linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
+ Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
+ Category::LibraryApplet};
+ Setting<AppletMode> wifi_web_auth_applet_mode{
+ linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet};
+ Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
+ Category::LibraryApplet};
+
// Audio
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
Category::Audio, Specialization::RuntimeList};
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 987489e8a..2df3f0809 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -44,6 +44,7 @@ enum class Category : u32 {
Services,
Paths,
Linux,
+ LibraryApplet,
MaxEnum,
};
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 617036588..f42367e67 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked);
+ENUM(AppletMode, HLE, LLE);
+
template <typename Type>
inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations();
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 53a95ef8f..a99bb0892 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -395,6 +395,10 @@ struct PlayerInput {
u32 button_color_left;
u32 button_color_right;
std::string profile_name;
+
+ // This is meant to tell the Android frontend whether to use a device's built-in vibration
+ // motor or a controller's vibrations.
+ bool use_system_vibrator;
};
struct TouchscreenInput {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 2d5490968..f67a12f8f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(core STATIC
- arm/arm_interface.h
arm/arm_interface.cpp
+ arm/arm_interface.h
arm/debug.cpp
arm/debug.h
arm/exclusive_monitor.cpp
@@ -37,10 +37,10 @@ add_library(core STATIC
debugger/gdbstub.h
debugger/gdbstub_arch.cpp
debugger/gdbstub_arch.h
- device_memory_manager.h
- device_memory_manager.inc
device_memory.cpp
device_memory.h
+ device_memory_manager.h
+ device_memory_manager.inc
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp
@@ -390,6 +390,20 @@ add_library(core STATIC
hle/service/acc/errors.h
hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h
+ hle/service/am/am.cpp
+ hle/service/am/am.h
+ hle/service/am/am_results.h
+ hle/service/am/am_types.h
+ hle/service/am/applet.cpp
+ hle/service/am/applet.h
+ hle/service/am/applet_data_broker.cpp
+ hle/service/am/applet_data_broker.h
+ hle/service/am/applet_manager.cpp
+ hle/service/am/applet_manager.h
+ hle/service/am/applet_message_queue.cpp
+ hle/service/am/applet_message_queue.h
+ hle/service/am/display_layer_manager.cpp
+ hle/service/am/display_layer_manager.h
hle/service/am/frontend/applet_cabinet.cpp
hle/service/am/frontend/applet_cabinet.h
hle/service/am/frontend/applet_controller.cpp
@@ -411,80 +425,62 @@ add_library(core STATIC
hle/service/am/frontend/applet_web_browser_types.h
hle/service/am/frontend/applets.cpp
hle/service/am/frontend/applets.h
- hle/service/am/am.cpp
- hle/service/am/am.h
- hle/service/am/am_results.h
- hle/service/am/am_types.h
- hle/service/am/applet.cpp
- hle/service/am/applet.h
- hle/service/am/applet_ae.cpp
- hle/service/am/applet_ae.h
- hle/service/am/applet_manager.cpp
- hle/service/am/applet_data_broker.cpp
- hle/service/am/applet_data_broker.h
- hle/service/am/applet_manager.h
- hle/service/am/applet_oe.cpp
- hle/service/am/applet_oe.h
- hle/service/am/applet_common_functions.cpp
- hle/service/am/applet_common_functions.h
- hle/service/am/applet_message_queue.cpp
- hle/service/am/applet_message_queue.h
- hle/service/am/application_creator.cpp
- hle/service/am/application_creator.h
- hle/service/am/application_functions.cpp
- hle/service/am/application_functions.h
- hle/service/am/application_proxy.cpp
- hle/service/am/application_proxy.h
- hle/service/am/audio_controller.cpp
- hle/service/am/audio_controller.h
- hle/service/am/common_state_getter.cpp
- hle/service/am/common_state_getter.h
- hle/service/am/debug_functions.cpp
- hle/service/am/debug_functions.h
- hle/service/am/display_controller.cpp
- hle/service/am/display_controller.h
- hle/service/am/global_state_controller.cpp
- hle/service/am/global_state_controller.h
hle/service/am/hid_registration.cpp
hle/service/am/hid_registration.h
- hle/service/am/home_menu_functions.cpp
- hle/service/am/home_menu_functions.h
- hle/service/am/idle.cpp
- hle/service/am/idle.h
- hle/service/am/library_applet_accessor.cpp
- hle/service/am/library_applet_accessor.h
- hle/service/am/library_applet_creator.cpp
- hle/service/am/library_applet_creator.h
- hle/service/am/library_applet_proxy.cpp
- hle/service/am/library_applet_proxy.h
- hle/service/am/library_applet_self_accessor.cpp
- hle/service/am/library_applet_self_accessor.h
hle/service/am/library_applet_storage.cpp
hle/service/am/library_applet_storage.h
- hle/service/am/lock_accessor.cpp
- hle/service/am/lock_accessor.h
- hle/service/am/managed_layer_holder.cpp
- hle/service/am/managed_layer_holder.h
- hle/service/am/omm.cpp
- hle/service/am/omm.h
- hle/service/am/process_winding_controller.cpp
- hle/service/am/process_winding_controller.h
hle/service/am/process.cpp
hle/service/am/process.h
- hle/service/am/self_controller.cpp
- hle/service/am/self_controller.h
- hle/service/am/system_applet_proxy.cpp
- hle/service/am/system_applet_proxy.h
- hle/service/am/system_buffer_manager.cpp
- hle/service/am/system_buffer_manager.h
- hle/service/am/spsm.cpp
- hle/service/am/spsm.h
- hle/service/am/storage_accessor.cpp
- hle/service/am/storage_accessor.h
- hle/service/am/storage.cpp
- hle/service/am/storage.h
- hle/service/am/window_controller.cpp
- hle/service/am/window_controller.h
+ hle/service/am/service/all_system_applet_proxies_service.cpp
+ hle/service/am/service/all_system_applet_proxies_service.h
+ hle/service/am/service/applet_common_functions.cpp
+ hle/service/am/service/applet_common_functions.h
+ hle/service/am/service/application_accessor.cpp
+ hle/service/am/service/application_accessor.h
+ hle/service/am/service/application_creator.cpp
+ hle/service/am/service/application_creator.h
+ hle/service/am/service/application_functions.cpp
+ hle/service/am/service/application_functions.h
+ hle/service/am/service/application_proxy.cpp
+ hle/service/am/service/application_proxy.h
+ hle/service/am/service/application_proxy_service.cpp
+ hle/service/am/service/application_proxy_service.h
+ hle/service/am/service/audio_controller.cpp
+ hle/service/am/service/audio_controller.h
+ hle/service/am/service/common_state_getter.cpp
+ hle/service/am/service/common_state_getter.h
+ hle/service/am/service/cradle_firmware_updater.cpp
+ hle/service/am/service/cradle_firmware_updater.h
+ hle/service/am/service/debug_functions.cpp
+ hle/service/am/service/debug_functions.h
+ hle/service/am/service/display_controller.cpp
+ hle/service/am/service/display_controller.h
+ hle/service/am/service/global_state_controller.cpp
+ hle/service/am/service/global_state_controller.h
+ hle/service/am/service/home_menu_functions.cpp
+ hle/service/am/service/home_menu_functions.h
+ hle/service/am/service/library_applet_accessor.cpp
+ hle/service/am/service/library_applet_accessor.h
+ hle/service/am/service/library_applet_creator.cpp
+ hle/service/am/service/library_applet_creator.h
+ hle/service/am/service/library_applet_proxy.cpp
+ hle/service/am/service/library_applet_proxy.h
+ hle/service/am/service/library_applet_self_accessor.cpp
+ hle/service/am/service/library_applet_self_accessor.h
+ hle/service/am/service/lock_accessor.cpp
+ hle/service/am/service/lock_accessor.h
+ hle/service/am/service/process_winding_controller.cpp
+ hle/service/am/service/process_winding_controller.h
+ hle/service/am/service/self_controller.cpp
+ hle/service/am/service/self_controller.h
+ hle/service/am/service/storage.cpp
+ hle/service/am/service/storage.h
+ hle/service/am/service/storage_accessor.cpp
+ hle/service/am/service/storage_accessor.h
+ hle/service/am/service/system_applet_proxy.cpp
+ hle/service/am/service/system_applet_proxy.h
+ hle/service/am/service/window_controller.cpp
+ hle/service/am/service/window_controller.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
@@ -493,12 +489,12 @@ add_library(core STATIC
hle/service/apm/apm_controller.h
hle/service/apm/apm_interface.cpp
hle/service/apm/apm_interface.h
- hle/service/audio/audctl.cpp
- hle/service/audio/audctl.h
hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h
hle/service/audio/audio.cpp
hle/service/audio/audio.h
+ hle/service/audio/audio_controller.cpp
+ hle/service/audio/audio_controller.h
hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_a.cpp
@@ -512,18 +508,6 @@ add_library(core STATIC
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
- hle/service/bcat/news/newly_arrived_event_holder.cpp
- hle/service/bcat/news/newly_arrived_event_holder.h
- hle/service/bcat/news/news_data_service.cpp
- hle/service/bcat/news/news_data_service.h
- hle/service/bcat/news/news_database_service.cpp
- hle/service/bcat/news/news_database_service.h
- hle/service/bcat/news/news_service.cpp
- hle/service/bcat/news/news_service.h
- hle/service/bcat/news/overwrite_event_holder.cpp
- hle/service/bcat/news/overwrite_event_holder.h
- hle/service/bcat/news/service_creator.cpp
- hle/service/bcat/news/service_creator.h
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/bcat_result.h
@@ -539,6 +523,18 @@ add_library(core STATIC
hle/service/bcat/delivery_cache_progress_service.h
hle/service/bcat/delivery_cache_storage_service.cpp
hle/service/bcat/delivery_cache_storage_service.h
+ hle/service/bcat/news/newly_arrived_event_holder.cpp
+ hle/service/bcat/news/newly_arrived_event_holder.h
+ hle/service/bcat/news/news_data_service.cpp
+ hle/service/bcat/news/news_data_service.h
+ hle/service/bcat/news/news_database_service.cpp
+ hle/service/bcat/news/news_database_service.h
+ hle/service/bcat/news/news_service.cpp
+ hle/service/bcat/news/news_service.h
+ hle/service/bcat/news/overwrite_event_holder.cpp
+ hle/service/bcat/news/overwrite_event_holder.h
+ hle/service/bcat/news/service_creator.cpp
+ hle/service/bcat/news/service_creator.h
hle/service/bcat/service_creator.cpp
hle/service/bcat/service_creator.h
hle/service/bpc/bpc.cpp
@@ -547,6 +543,16 @@ add_library(core STATIC
hle/service/btdrv/btdrv.h
hle/service/btm/btm.cpp
hle/service/btm/btm.h
+ hle/service/btm/btm_debug.cpp
+ hle/service/btm/btm_debug.h
+ hle/service/btm/btm_system.cpp
+ hle/service/btm/btm_system.h
+ hle/service/btm/btm_system_core.cpp
+ hle/service/btm/btm_system_core.h
+ hle/service/btm/btm_user.cpp
+ hle/service/btm/btm_user.h
+ hle/service/btm/btm_user_core.cpp
+ hle/service/btm/btm_user_core.h
hle/service/caps/caps.cpp
hle/service/caps/caps.h
hle/service/caps/caps_a.cpp
@@ -602,8 +608,6 @@ add_library(core STATIC
hle/service/filesystem/romfs_controller.h
hle/service/filesystem/save_data_controller.cpp
hle/service/filesystem/save_data_controller.h
- hle/service/fgm/fgm.cpp
- hle/service/fgm/fgm.h
hle/service/friend/friend.cpp
hle/service/friend/friend.h
hle/service/friend/friend_interface.cpp
@@ -668,6 +672,18 @@ add_library(core STATIC
hle/service/ldn/ldn.h
hle/service/ldn/ldn_results.h
hle/service/ldn/ldn_types.h
+ hle/service/ldn/monitor_service.cpp
+ hle/service/ldn/monitor_service.h
+ hle/service/ldn/sf_monitor_service.cpp
+ hle/service/ldn/sf_monitor_service.h
+ hle/service/ldn/sf_service.cpp
+ hle/service/ldn/sf_service.h
+ hle/service/ldn/sf_service_monitor.cpp
+ hle/service/ldn/sf_service_monitor.h
+ hle/service/ldn/system_local_communication_service.cpp
+ hle/service/ldn/system_local_communication_service.h
+ hle/service/ldn/user_local_communication_service.cpp
+ hle/service/ldn/user_local_communication_service.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
@@ -729,15 +745,48 @@ add_library(core STATIC
hle/service/nim/nim.h
hle/service/npns/npns.cpp
hle/service/npns/npns.h
- hle/service/ns/errors.h
- hle/service/ns/iplatform_service_manager.cpp
- hle/service/ns/iplatform_service_manager.h
+ hle/service/ns/account_proxy_interface.cpp
+ hle/service/ns/account_proxy_interface.h
+ hle/service/ns/application_manager_interface.cpp
+ hle/service/ns/application_manager_interface.h
+ hle/service/ns/application_version_interface.cpp
+ hle/service/ns/application_version_interface.h
+ hle/service/ns/content_management_interface.cpp
+ hle/service/ns/content_management_interface.h
+ hle/service/ns/develop_interface.cpp
+ hle/service/ns/develop_interface.h
+ hle/service/ns/document_interface.cpp
+ hle/service/ns/document_interface.h
+ hle/service/ns/download_task_interface.cpp
+ hle/service/ns/download_task_interface.h
+ hle/service/ns/dynamic_rights_interface.cpp
+ hle/service/ns/dynamic_rights_interface.h
+ hle/service/ns/ecommerce_interface.cpp
+ hle/service/ns/ecommerce_interface.h
+ hle/service/ns/factory_reset_interface.cpp
+ hle/service/ns/factory_reset_interface.h
hle/service/ns/language.cpp
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
- hle/service/ns/pdm_qry.cpp
- hle/service/ns/pdm_qry.h
+ hle/service/ns/ns_results.h
+ hle/service/ns/ns_types.h
+ hle/service/ns/platform_service_manager.cpp
+ hle/service/ns/platform_service_manager.h
+ hle/service/ns/query_service.cpp
+ hle/service/ns/query_service.h
+ hle/service/ns/read_only_application_control_data_interface.cpp
+ hle/service/ns/read_only_application_control_data_interface.h
+ hle/service/ns/read_only_application_record_interface.cpp
+ hle/service/ns/read_only_application_record_interface.h
+ hle/service/ns/service_getter_interface.cpp
+ hle/service/ns/service_getter_interface.h
+ hle/service/ns/system_update_control.cpp
+ hle/service/ns/system_update_control.h
+ hle/service/ns/system_update_interface.cpp
+ hle/service/ns/system_update_interface.h
+ hle/service/ns/vulnerability_manager_interface.cpp
+ hle/service/ns/vulnerability_manager_interface.h
hle/service/nvdrv/core/container.cpp
hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp
@@ -790,14 +839,14 @@ add_library(core STATIC
hle/service/nvnflinger/consumer_base.cpp
hle/service/nvnflinger/consumer_base.h
hle/service/nvnflinger/consumer_listener.h
- hle/service/nvnflinger/fb_share_buffer_manager.cpp
- hle/service/nvnflinger/fb_share_buffer_manager.h
hle/service/nvnflinger/graphic_buffer_producer.cpp
hle/service/nvnflinger/graphic_buffer_producer.h
- hle/service/nvnflinger/hos_binder_driver_server.cpp
- hle/service/nvnflinger/hos_binder_driver_server.h
hle/service/nvnflinger/hardware_composer.cpp
hle/service/nvnflinger/hardware_composer.h
+ hle/service/nvnflinger/hos_binder_driver.cpp
+ hle/service/nvnflinger/hos_binder_driver.h
+ hle/service/nvnflinger/hos_binder_driver_server.cpp
+ hle/service/nvnflinger/hos_binder_driver_server.h
hle/service/nvnflinger/hwc_layer.h
hle/service/nvnflinger/nvnflinger.cpp
hle/service/nvnflinger/nvnflinger.h
@@ -805,19 +854,29 @@ add_library(core STATIC
hle/service/nvnflinger/pixel_format.h
hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h
+ hle/service/nvnflinger/surface_flinger.cpp
+ hle/service/nvnflinger/surface_flinger.h
hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
+ hle/service/omm/omm.cpp
+ hle/service/omm/omm.h
+ hle/service/omm/operation_mode_manager.cpp
+ hle/service/omm/operation_mode_manager.h
+ hle/service/omm/policy_manager_system.cpp
+ hle/service/omm/policy_manager_system.h
+ hle/service/omm/power_state_interface.cpp
+ hle/service/omm/power_state_interface.h
hle/service/os/event.cpp
hle/service/os/event.h
+ hle/service/os/multi_wait.cpp
+ hle/service/os/multi_wait.h
hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h
- hle/service/os/multi_wait.cpp
- hle/service/os/multi_wait.h
hle/service/os/mutex.cpp
hle/service/os/mutex.h
hle/service/pcie/pcie.cpp
@@ -855,15 +914,17 @@ add_library(core STATIC
hle/service/psc/time/common.cpp
hle/service/psc/time/common.h
hle/service/psc/time/errors.h
- hle/service/psc/time/shared_memory.cpp
- hle/service/psc/time/shared_memory.h
- hle/service/psc/time/static.cpp
- hle/service/psc/time/static.h
hle/service/psc/time/manager.h
+ hle/service/psc/time/power_state_request_manager.cpp
+ hle/service/psc/time/power_state_request_manager.h
hle/service/psc/time/power_state_service.cpp
hle/service/psc/time/power_state_service.h
hle/service/psc/time/service_manager.cpp
hle/service/psc/time/service_manager.h
+ hle/service/psc/time/shared_memory.cpp
+ hle/service/psc/time/shared_memory.h
+ hle/service/psc/time/static.cpp
+ hle/service/psc/time/static.h
hle/service/psc/time/steady_clock.cpp
hle/service/psc/time/steady_clock.h
hle/service/psc/time/system_clock.cpp
@@ -872,8 +933,6 @@ add_library(core STATIC
hle/service/psc/time/time_zone.h
hle/service/psc/time/time_zone_service.cpp
hle/service/psc/time/time_zone_service.h
- hle/service/psc/time/power_state_request_manager.cpp
- hle/service/psc/time/power_state_request_manager.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
hle/service/ptm/ptm.cpp
@@ -890,18 +949,21 @@ add_library(core STATIC
hle/service/server_manager.h
hle/service/service.cpp
hle/service/service.h
+ hle/service/services.cpp
+ hle/service/services.h
+ hle/service/set/factory_settings_server.cpp
+ hle/service/set/factory_settings_server.h
+ hle/service/set/firmware_debug_settings_server.cpp
+ hle/service/set/firmware_debug_settings_server.h
+ hle/service/set/key_code_map.h
hle/service/set/setting_formats/appln_settings.cpp
hle/service/set/setting_formats/appln_settings.h
hle/service/set/setting_formats/device_settings.cpp
hle/service/set/setting_formats/device_settings.h
- hle/service/set/setting_formats/system_settings.cpp
- hle/service/set/setting_formats/system_settings.h
hle/service/set/setting_formats/private_settings.cpp
hle/service/set/setting_formats/private_settings.h
- hle/service/set/factory_settings_server.cpp
- hle/service/set/factory_settings_server.h
- hle/service/set/firmware_debug_settings_server.cpp
- hle/service/set/firmware_debug_settings_server.h
+ hle/service/set/setting_formats/system_settings.cpp
+ hle/service/set/setting_formats/system_settings.h
hle/service/set/settings.cpp
hle/service/set/settings.h
hle/service/set/settings_server.cpp
@@ -936,18 +998,36 @@ add_library(core STATIC
hle/service/ssl/ssl_backend.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
- hle/service/vi/display/vi_display.cpp
- hle/service/vi/display/vi_display.h
- hle/service/vi/layer/vi_layer.cpp
- hle/service/vi/layer/vi_layer.h
+ hle/service/vi/application_display_service.cpp
+ hle/service/vi/application_display_service.h
+ hle/service/vi/application_root_service.cpp
+ hle/service/vi/application_root_service.h
+ hle/service/vi/conductor.cpp
+ hle/service/vi/conductor.h
+ hle/service/vi/container.cpp
+ hle/service/vi/container.h
+ hle/service/vi/display.h
+ hle/service/vi/display_list.h
+ hle/service/vi/layer.h
+ hle/service/vi/layer_list.h
+ hle/service/vi/manager_display_service.cpp
+ hle/service/vi/manager_display_service.h
+ hle/service/vi/manager_root_service.cpp
+ hle/service/vi/manager_root_service.h
+ hle/service/vi/service_creator.cpp
+ hle/service/vi/service_creator.h
+ hle/service/vi/shared_buffer_manager.cpp
+ hle/service/vi/shared_buffer_manager.h
+ hle/service/vi/system_display_service.cpp
+ hle/service/vi/system_display_service.h
+ hle/service/vi/system_root_service.cpp
+ hle/service/vi/system_root_service.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
- hle/service/vi/vi_m.cpp
- hle/service/vi/vi_m.h
- hle/service/vi/vi_s.cpp
- hle/service/vi/vi_s.h
- hle/service/vi/vi_u.cpp
- hle/service/vi/vi_u.h
+ hle/service/vi/vi_results.h
+ hle/service/vi/vi_types.h
+ hle/service/vi/vsync_manager.cpp
+ hle/service/vi/vsync_manager.h
internal_network/network.cpp
internal_network/network.h
internal_network/network_interface.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 435ef6793..9e8936728 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -47,6 +47,7 @@
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/services.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/internal_network/network.h"
@@ -242,7 +243,7 @@ struct System::Impl {
void Run() {
std::unique_lock<std::mutex> lk(suspend_guard);
- kernel.SuspendApplication(false);
+ kernel.SuspendEmulation(false);
core_timing.SyncPause(false);
is_paused.store(false, std::memory_order_relaxed);
}
@@ -251,7 +252,7 @@ struct System::Impl {
std::unique_lock<std::mutex> lk(suspend_guard);
core_timing.SyncPause(true);
- kernel.SuspendApplication(true);
+ kernel.SuspendEmulation(true);
is_paused.store(true, std::memory_order_relaxed);
}
@@ -261,7 +262,7 @@ struct System::Impl {
std::unique_lock<std::mutex> StallApplication() {
std::unique_lock<std::mutex> lk(suspend_guard);
- kernel.SuspendApplication(true);
+ kernel.SuspendEmulation(true);
core_timing.SyncPause(true);
return lk;
}
@@ -269,7 +270,7 @@ struct System::Impl {
void UnstallApplication() {
if (!IsPaused()) {
core_timing.SyncPause(false);
- kernel.SuspendApplication(false);
+ kernel.SuspendEmulation(false);
}
}
@@ -310,7 +311,8 @@ struct System::Impl {
audio_core = std::make_unique<AudioCore::AudioCore>(system);
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
- services = std::make_unique<Service::Services>(service_manager, system);
+ services =
+ std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
is_powered_on = true;
exit_locked = false;
@@ -458,11 +460,10 @@ struct System::Impl {
gpu_core->NotifyShutdown();
}
+ stop_event.request_stop();
+ core_timing.SyncPause(false);
Network::CancelPendingSocketOperations();
- kernel.SuspendApplication(true);
- if (services) {
- services->KillNVNFlinger();
- }
+ kernel.SuspendEmulation(true);
kernel.CloseServices();
kernel.ShutdownCores();
applet_manager.Reset();
@@ -480,6 +481,7 @@ struct System::Impl {
cpu_manager.Shutdown();
debugger.reset();
kernel.Shutdown();
+ stop_event = {};
Network::RestartSocketOperations();
if (auto room_member = room_network.GetRoomMember().lock()) {
@@ -615,6 +617,7 @@ struct System::Impl {
ExecuteProgramCallback execute_program_callback;
ExitCallback exit_callback;
+ std::stop_source stop_event;
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7a5c22f78..9b1c77387 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -199,10 +199,10 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
data.host_context = Common::Fiber::ThreadToFiber();
// Cleanup
- SCOPE_EXIT({
+ SCOPE_EXIT {
data.host_context->Exit();
MicroProfileOnThreadExit();
- });
+ };
// Running
if (!gpu_barrier->Sync(token)) {
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index 6dfee806c..37c1e69c3 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -391,12 +391,12 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr =
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
- SCOPE_EXIT({
+ SCOPE_EXIT{
page_index += next_pages;
page_offset = 0;
increment(copy_amount);
remaining_size -= copy_amount;
- });
+ };
auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr == 0) {
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 285fe4db6..665252358 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const {
return reader->GetSdkAddonVersion();
}
+u8 NCA::GetKeyGeneration() const {
+ return reader->GetKeyGeneration();
+}
+
bool NCA::IsUpdate() const {
return is_update;
}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f68464eb0..8560617f5 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -77,6 +77,7 @@ public:
u64 GetTitleId() const;
RightsId GetRightsId() const;
u32 GetSDKVersion() const;
+ u8 GetKeyGeneration() const;
bool IsUpdate() const;
VirtualFile GetRomFS() const;
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 555b9d8f7..667efbbab 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -64,8 +64,8 @@ struct RawNACP {
u64_le cache_storage_size;
u64_le cache_storage_journal_size;
u64_le cache_storage_data_and_journal_max_size;
- u64_le cache_storage_max_index;
- INSERT_PADDING_BYTES(0xE70);
+ u16_le cache_storage_max_index;
+ INSERT_PADDING_BYTES(0xE76);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h
index 25c9cb18a..3f90abb8f 100644
--- a/src/core/file_sys/fs_directory.h
+++ b/src/core/file_sys/fs_directory.h
@@ -3,6 +3,10 @@
#pragma once
+#include <string_view>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
namespace FileSys {
constexpr inline size_t EntryNameLengthMax = 0x300;
diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h
index e9011d065..5643141f9 100644
--- a/src/core/file_sys/fs_path_utility.h
+++ b/src/core/file_sys/fs_path_utility.h
@@ -447,7 +447,7 @@ public:
char* replacement_path = nullptr;
size_t replacement_path_size = 0;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (replacement_path != nullptr) {
if (std::is_constant_evaluated()) {
delete[] replacement_path;
@@ -455,7 +455,7 @@ public:
Deallocate(replacement_path, replacement_path_size);
}
}
- });
+ };
// Perform path replacement, if necessary
if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
@@ -1102,8 +1102,8 @@ public:
R_SUCCEED();
}
- static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
- const PathFlags& flags) {
+ static constexpr Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
+ const PathFlags& flags) {
// Use StringTraits names for remainder of scope
using namespace StringTraits;
@@ -1199,7 +1199,7 @@ public:
const size_t replaced_src_len = path_len - (src - path);
char* replaced_src = nullptr;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (replaced_src != nullptr) {
if (std::is_constant_evaluated()) {
delete[] replaced_src;
@@ -1207,7 +1207,7 @@ public:
Deallocate(replaced_src, replaced_src_len);
}
}
- });
+ };
if (std::is_constant_evaluated()) {
replaced_src = new char[replaced_src_len];
diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
index caea0b8f8..a68fd973c 100644
--- a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
+++ b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
@@ -36,7 +36,9 @@ Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 lay
// Get the base storage size.
m_base_storage_size = base_storages[2]->GetSize();
{
- auto size_guard = SCOPE_GUARD({ m_base_storage_size = 0; });
+ auto size_guard = SCOPE_GUARD {
+ m_base_storage_size = 0;
+ };
R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
<< m_log_size_ratio << m_log_size_ratio,
ResultHierarchicalSha256BaseStorageTooLarge);
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index ae4e441c9..289969cc4 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -98,7 +98,9 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
const u64 original_program_id = aci_header.title_id;
- SCOPE_EXIT({ aci_header.title_id = original_program_id; });
+ SCOPE_EXIT {
+ aci_header.title_id = original_program_id;
+ };
return this->Load(file);
}
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp
index deb52069d..9ea16aa59 100644
--- a/src/core/file_sys/system_archive/shared_font.cpp
+++ b/src/core/file_sys/system_archive/shared_font.cpp
@@ -9,7 +9,7 @@
#include "core/file_sys/system_archive/data/font_standard.h"
#include "core/file_sys/system_archive/shared_font.h"
#include "core/file_sys/vfs/vfs_vector.h"
-#include "core/hle/service/ns/iplatform_service_manager.h"
+#include "core/hle/service/ns/platform_service_manager.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 472e8571c..3e01e3b67 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -24,7 +24,9 @@ Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
- SCOPE_EXIT({ request->Close(); });
+ SCOPE_EXIT {
+ request->Close();
+ };
// Initialize the request.
request->Initialize(nullptr, address, size);
@@ -37,7 +39,9 @@ Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
- SCOPE_EXIT({ request->Close(); });
+ SCOPE_EXIT {
+ request->Close();
+ };
// Initialize the request.
request->Initialize(event, address, size);
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 1dd86fb3c..19cdf4f3a 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -1305,11 +1305,11 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
// Ensure that we maintain the instruction cache.
bool reprotected_pages = false;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (reprotected_pages && any_code_pages) {
InvalidateInstructionCache(m_kernel, this, dst_address, size);
}
- });
+ };
// Unmap.
{
@@ -1397,7 +1397,9 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
// Close the opened pages when we're done with them.
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
// automatically.
- SCOPE_EXIT({ pg.Close(); });
+ SCOPE_EXIT {
+ pg.Close();
+ };
// Clear all the newly allocated pages.
for (const auto& it : pg) {
@@ -1603,7 +1605,9 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
// Ensure that the page group is closed when we're done working with it.
- SCOPE_EXIT({ pg.Close(); });
+ SCOPE_EXIT {
+ pg.Close();
+ };
// Clear all pages.
for (const auto& it : pg) {
@@ -2191,7 +2195,9 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
// Close the opened pages when we're done with them.
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
// automatically.
- SCOPE_EXIT({ pg.Close(); });
+ SCOPE_EXIT {
+ pg.Close();
+ };
// Clear all the newly allocated pages.
for (const auto& it : pg) {
@@ -2592,7 +2598,9 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
// Temporarily unlock ourselves, so that other operations can occur while we flush the
// region.
m_general_lock.Unlock();
- SCOPE_EXIT({ m_general_lock.Lock(); });
+ SCOPE_EXIT {
+ m_general_lock.Lock();
+ };
// Flush the region.
R_ASSERT(FlushDataCache(dst_address, size));
@@ -3311,10 +3319,10 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre
// Ensure we unmap the io memory when we're done with it.
const KPageProperties unmap_properties =
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
- SCOPE_EXIT({
+ SCOPE_EXIT {
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
unmap_properties, OperationType::Unmap, true));
- });
+ };
// Read the memory.
const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
@@ -3347,10 +3355,10 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd
// Ensure we unmap the io memory when we're done with it.
const KPageProperties unmap_properties =
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
- SCOPE_EXIT({
+ SCOPE_EXIT {
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
unmap_properties, OperationType::Unmap, true));
- });
+ };
// Write the memory.
const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
@@ -4491,14 +4499,14 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
// free on scope exit.
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (start_partial_page != 0) {
m_kernel.MemoryManager().Close(start_partial_page, 1);
}
if (end_partial_page != 0) {
m_kernel.MemoryManager().Close(end_partial_page, 1);
}
- });
+ };
ON_RESULT_FAILURE {
if (cur_mapped_addr != dst_addr) {
@@ -5166,10 +5174,10 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
// If we fail in the next bit (or retry), we need to cleanup the pages.
- auto pg_guard = SCOPE_GUARD({
+ auto pg_guard = SCOPE_GUARD {
pg.OpenFirst();
pg.Close();
- });
+ };
// Map the memory.
{
@@ -5694,7 +5702,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
// Ensure that any pages we track are closed on exit.
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
- SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
+ SCOPE_EXIT {
+ pages_to_close.CloseAndReset();
+ };
// Make a page group representing the region to unmap.
this->MakePageGroup(pages_to_close, virt_addr, num_pages);
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 0b08e877e..cb9a11a63 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -4,8 +4,9 @@
#include <random>
#include "common/scope_exit.h"
#include "common/settings.h"
+#include "core/arm/dynarmic/arm_dynarmic.h"
+#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core.h"
-#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
@@ -76,7 +77,9 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process,
}
// Terminate and close the thread.
- SCOPE_EXIT({ cur_child->Close(); });
+ SCOPE_EXIT {
+ cur_child->Close();
+ };
if (const Result terminate_result = cur_child->Terminate();
ResultTerminationRequested == terminate_result) {
@@ -465,11 +468,11 @@ void KProcess::DoWorkerTaskImpl() {
Result KProcess::StartTermination() {
// Finalize the handle table when we're done, if the process isn't immortal.
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (!m_is_immortal) {
this->FinalizeHandleTable();
}
- });
+ };
// Terminate child threads other than the current one.
R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel)));
@@ -963,7 +966,9 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
// Create a new thread for the process.
KThread* main_thread = KThread::Create(m_kernel);
R_UNLESS(main_thread != nullptr, ResultOutOfResource);
- SCOPE_EXIT({ main_thread->Close(); });
+ SCOPE_EXIT {
+ main_thread->Close();
+ };
// Initialize the thread.
R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0,
@@ -1154,7 +1159,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
// Ensure we maintain a clean state on exit.
- SCOPE_EXIT({ res_limit->Close(); });
+ SCOPE_EXIT {
+ res_limit->Close();
+ };
// Declare flags and code address.
Svc::CreateProcessFlag flag{};
@@ -1258,6 +1265,10 @@ void KProcess::InitializeInterfaces() {
#ifdef HAS_NCE
if (this->IsApplication() && Settings::IsNceEnabled()) {
+ // Register the scoped JIT handler before creating any NCE instances
+ // so that its signal handler will appear first in the signal chain.
+ Core::ScopedJitExecution::RegisterHandler();
+
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
}
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index adaabdd6d..40c3323ef 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -651,11 +651,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any special data.
if (src_header.GetHasSpecialHeader()) {
// After we process, make sure we track whether the receive list is broken.
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (offset > dst_recv_list_idx) {
recv_list_broken = true;
}
- });
+ };
// Process special data.
R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
@@ -665,11 +665,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any pointer buffers.
for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
// After we process, make sure we track whether the receive list is broken.
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (offset > dst_recv_list_idx) {
recv_list_broken = true;
}
- });
+ };
R_TRY(ProcessReceiveMessagePointerDescriptors(
offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
@@ -680,11 +680,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any map alias buffers.
for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
// After we process, make sure we track whether the receive list is broken.
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (offset > dst_recv_list_idx) {
recv_list_broken = true;
}
- });
+ };
// We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
const KMemoryPermission perm = (i >= src_header.GetSendCount())
@@ -702,11 +702,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any raw data.
if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
// After we process, make sure we track whether the receive list is broken.
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (offset + raw_count > dst_recv_list_idx) {
recv_list_broken = true;
}
- });
+ };
// Get the offset and size.
const size_t offset_words = offset * sizeof(u32);
@@ -1124,7 +1124,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
client_thread->Open();
}
- SCOPE_EXIT({ client_thread->Close(); });
+ SCOPE_EXIT {
+ client_thread->Close();
+ };
// Set the request as our current.
m_current_request = request;
@@ -1174,7 +1176,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
// Reply to the client.
{
// After we reply, close our reference to the request.
- SCOPE_EXIT({ request->Close(); });
+ SCOPE_EXIT {
+ request->Close();
+ };
// Get the event to check whether the request is async.
if (KEvent* event = request->GetEvent(); event != nullptr) {
@@ -1236,7 +1240,9 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff
}
// Close reference to the request once we're done processing it.
- SCOPE_EXIT({ request->Close(); });
+ SCOPE_EXIT {
+ request->Close();
+ };
// Extract relevant information from the request.
const uint64_t client_message = request->GetAddress();
@@ -1394,7 +1400,9 @@ void KServerSession::CleanupRequests() {
}
// Close a reference to the request once it's cleaned up.
- SCOPE_EXIT({ request->Close(); });
+ SCOPE_EXIT {
+ request->Close();
+ };
// Extract relevant information from the request.
const uint64_t client_message = request->GetAddress();
@@ -1491,7 +1499,9 @@ void KServerSession::OnClientClosed() {
ASSERT(thread != nullptr);
// Ensure that we close the request when done.
- SCOPE_EXIT({ request->Close(); });
+ SCOPE_EXIT {
+ request->Close();
+ };
// If we're terminating, close a reference to the thread and event.
if (terminate) {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f13e232b2..e928cfebc 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -66,6 +66,7 @@ enum class SuspendType : u32 {
Debug = 2,
Backtrace = 3,
Init = 4,
+ System = 5,
Count,
};
@@ -84,8 +85,9 @@ enum class ThreadState : u16 {
DebugSuspended = (1 << (2 + SuspendShift)),
BacktraceSuspended = (1 << (3 + SuspendShift)),
InitSuspended = (1 << (4 + SuspendShift)),
+ SystemSuspended = (1 << (5 + SuspendShift)),
- SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
+ SuspendFlagMask = ((1 << 6) - 1) << SuspendShift,
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
index a632d1634..1952c0083 100644
--- a/src/core/hle/kernel/k_thread_local_page.cpp
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -21,7 +21,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
// Allocate a new page.
KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
- auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
+ auto page_buf_guard = SCOPE_GUARD {
+ KPageBuffer::Free(kernel, page_buf);
+ };
// Map the address in.
const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index cbb1b02bb..09295e8ad 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -24,7 +24,9 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
// Construct the page group, guarding to make sure our state is valid on exit.
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
- auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); });
+ auto pg_guard = SCOPE_GUARD {
+ m_page_group.reset();
+ };
// Lock the memory.
R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 34b25be66..9e5eaeec4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -109,7 +109,9 @@ struct KernelCore::Impl {
void Shutdown() {
is_shutting_down.store(true, std::memory_order_relaxed);
- SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
+ SCOPE_EXIT {
+ is_shutting_down.store(false, std::memory_order_relaxed);
+ };
CloseServices();
@@ -1080,7 +1082,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
// Ensure that we don't hold onto any extra references.
- SCOPE_EXIT({ process->Close(); });
+ SCOPE_EXIT {
+ process->Close();
+ };
// Register the new process.
KProcess::Register(*this, process);
@@ -1108,7 +1112,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
// Ensure that we don't hold onto any extra references.
- SCOPE_EXIT({ process->Close(); });
+ SCOPE_EXIT {
+ process->Close();
+ };
// Register the new process.
KProcess::Register(*this, process);
@@ -1204,39 +1210,48 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
return *impl->hidbus_shared_mem;
}
-void KernelCore::SuspendApplication(bool suspended) {
+void KernelCore::SuspendEmulation(bool suspended) {
const bool should_suspend{exception_exited || suspended};
- const auto activity =
- should_suspend ? Svc::ProcessActivity::Paused : Svc::ProcessActivity::Runnable;
+ auto processes = GetProcessList();
- // Get the application process.
- KScopedAutoObject<KProcess> process = ApplicationProcess();
- if (process.IsNull()) {
- return;
+ for (auto& process : processes) {
+ KScopedLightLock ll{process->GetListLock()};
+
+ for (auto& thread : process->GetThreadList()) {
+ if (should_suspend) {
+ thread.RequestSuspend(SuspendType::System);
+ } else {
+ thread.Resume(SuspendType::System);
+ }
+ }
}
- // Set the new activity.
- process->SetActivity(activity);
+ if (!should_suspend) {
+ return;
+ }
// Wait for process execution to stop.
- bool must_wait{should_suspend};
-
- // KernelCore::SuspendApplication must be called from locked context,
- // or we could race another call to SetActivity, interfering with waiting.
- while (must_wait) {
+ // KernelCore::SuspendEmulation must be called from locked context,
+ // or we could race another call, interfering with waiting.
+ const auto TryWait = [&]() {
KScopedSchedulerLock sl{*this};
- // Assume that all threads have finished running.
- must_wait = false;
-
- for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
- if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
- process.GetPointerUnsafe()) {
- // A thread has not finished running yet.
- // Continue waiting.
- must_wait = true;
+ for (auto& process : processes) {
+ for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
+ if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
+ process.GetPointerUnsafe()) {
+ // A thread has not finished running yet.
+ // Continue waiting.
+ return false;
+ }
}
}
+
+ return true;
+ };
+
+ while (!TryWait()) {
+ // ...
}
}
@@ -1260,7 +1275,7 @@ bool KernelCore::IsShuttingDown() const {
void KernelCore::ExceptionalExitApplication() {
exception_exited = true;
- SuspendApplication(true);
+ SuspendEmulation(true);
}
void KernelCore::EnterSVCProfile() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8ea5bed1c..57182c0c8 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -258,8 +258,8 @@ public:
/// Gets the shared memory object for HIDBus services.
const Kernel::KSharedMemory& GetHidBusSharedMem() const;
- /// Suspend/unsuspend application process.
- void SuspendApplication(bool suspend);
+ /// Suspend/unsuspend emulated processes.
+ void SuspendEmulation(bool suspend);
/// Exceptional exit application process.
void ExceptionalExitApplication();
diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp
index bae4cb0cd..7be2802f0 100644
--- a/src/core/hle/kernel/svc/svc_code_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -45,7 +45,9 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t
KCodeMemory* code_mem = KCodeMemory::Create(kernel);
R_UNLESS(code_mem != nullptr, ResultOutOfResource);
- SCOPE_EXIT({ code_mem->Close(); });
+ SCOPE_EXIT {
+ code_mem->Close();
+ };
// Verify that the region is in range.
R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size),
diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp
index 42add9473..ac828320f 100644
--- a/src/core/hle/kernel/svc/svc_device_address_space.cpp
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -28,7 +28,9 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_
// Create the device address space.
KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
R_UNLESS(das != nullptr, ResultOutOfResource);
- SCOPE_EXIT({ das->Close(); });
+ SCOPE_EXIT {
+ das->Close();
+ };
// Initialize the device address space.
R_TRY(das->Initialize(das_address, das_size));
diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp
index 901202e6a..8e4beb396 100644
--- a/src/core/hle/kernel/svc/svc_event.cpp
+++ b/src/core/hle/kernel/svc/svc_event.cpp
@@ -72,10 +72,10 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
event_reservation.Commit();
// Ensure that we clean up the event (and its only references are handle table) on function end.
- SCOPE_EXIT({
+ SCOPE_EXIT {
event->GetReadableEvent().Close();
event->Close();
- });
+ };
// Register the event.
KEvent::Register(kernel, event);
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 85cc4f561..b619bd70a 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -129,11 +129,11 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
}
// Ensure handles are closed when we're done.
- SCOPE_EXIT({
+ SCOPE_EXIT {
for (auto i = 0; i < num_handles; ++i) {
objs[i]->Close();
}
- });
+ };
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
num_handles, reply_target, timeout_ns));
@@ -208,10 +208,10 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha
event_reservation.Commit();
// At end of scope, kill the standing references to the sub events.
- SCOPE_EXIT({
+ SCOPE_EXIT {
event->GetReadableEvent().Close();
event->Close();
- });
+ };
// Register the event.
KEvent::Register(system.Kernel(), event);
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
index 737749f7d..9a22dadaf 100644
--- a/src/core/hle/kernel/svc/svc_port.cpp
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -68,10 +68,10 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
port->Initialize(max_sessions, is_light, name);
// Ensure that we clean up the port (and its only references are handle table) on function end.
- SCOPE_EXIT({
+ SCOPE_EXIT {
port->GetServerPort().Close();
port->GetClientPort().Close();
- });
+ };
// Register the port.
KPort::Register(kernel, port);
@@ -150,10 +150,10 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t
KPort::Register(system.Kernel(), port);
// Ensure that our only reference to the port is in the handle table when we're done.
- SCOPE_EXIT({
+ SCOPE_EXIT {
port->GetClientPort().Close();
port->GetServerPort().Close();
- });
+ };
// Register the handle in the table.
R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp
index c8e820b6a..6f3972482 100644
--- a/src/core/hle/kernel/svc/svc_resource_limit.cpp
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -18,7 +18,9 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
// Ensure we don't leak a reference to the limit.
- SCOPE_EXIT({ resource_limit->Close(); });
+ SCOPE_EXIT {
+ resource_limit->Close();
+ };
// Initialize the resource limit.
resource_limit->Initialize();
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
index 2f5905f32..b034d21d1 100644
--- a/src/core/hle/kernel/svc/svc_session.cpp
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -69,10 +69,10 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
// Ensure that we clean up the session (and its only references are handle table) on function
// end.
- SCOPE_EXIT({
+ SCOPE_EXIT {
session->GetClientSession().Close();
session->GetServerSession().Close();
- });
+ };
// Register the session.
T::Register(system.Kernel(), session);
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
index 6c79cfd8d..fb03908d7 100644
--- a/src/core/hle/kernel/svc/svc_synchronization.cpp
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -78,11 +78,11 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
}
// Ensure handles are closed when we're done.
- SCOPE_EXIT({
+ SCOPE_EXIT {
for (auto i = 0; i < num_handles; ++i) {
objs[i]->Close();
}
- });
+ };
// Convert the timeout from nanoseconds to ticks.
s64 timeout;
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index 7681afa33..7517bb9d3 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -51,7 +51,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
// Create the thread.
KThread* thread = KThread::Create(kernel);
R_UNLESS(thread != nullptr, ResultOutOfResource)
- SCOPE_EXIT({ thread->Close(); });
+ SCOPE_EXIT {
+ thread->Close();
+ };
// Initialize the thread.
{
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
index 671bca23f..2ea0d4421 100644
--- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -52,7 +52,9 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
R_UNLESS(trmem != nullptr, ResultOutOfResource);
// Ensure the only reference is in the handle table when we're done.
- SCOPE_EXIT({ trmem->Close(); });
+ SCOPE_EXIT {
+ trmem->Close();
+ };
// Ensure that the region is in range.
R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 29a10ad13..ee9795532 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -329,9 +329,8 @@ bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase&
/// Returns if the system is allowing user registrations or not
bool ProfileManager::CanSystemRegisterUser() const {
- return false; // TODO(ogniK): Games shouldn't have
- // access to user registration, when we
- // emulate qlaunch. Update this to dynamically change.
+ // TODO: Both games and applets can register users. Determine when this condition is not meet.
+ return true;
}
bool ProfileManager::RemoveUser(UUID uuid) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 8f90eba34..8c4e14f08 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,25 +2,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applet_ae.h"
-#include "core/hle/service/am/applet_oe.h"
-#include "core/hle/service/am/idle.h"
-#include "core/hle/service/am/omm.h"
-#include "core/hle/service/am/spsm.h"
+#include "core/hle/service/am/service/all_system_applet_proxies_service.h"
+#include "core/hle/service/am/service/application_proxy_service.h"
#include "core/hle/service/server_manager.h"
namespace Service::AM {
-void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
+void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("appletAE",
- std::make_shared<AppletAE>(nvnflinger, system));
+ std::make_shared<IAllSystemAppletProxiesService>(system));
server_manager->RegisterNamedService("appletOE",
- std::make_shared<AppletOE>(nvnflinger, system));
- server_manager->RegisterNamedService("idle:sys", std::make_shared<IdleSys>(system));
- server_manager->RegisterNamedService("omm", std::make_shared<OMM>(system));
- server_manager->RegisterNamedService("spsm", std::make_shared<SPSM>(system));
+ std::make_shared<IApplicationProxyService>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 4a2d797bd..1afe253ae 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -7,12 +7,8 @@ namespace Core {
class System;
}
-namespace Service::Nvnflinger {
-class Nvnflinger;
-}
-
namespace Service::AM {
-void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system);
+void LoopProcess(Core::System& system);
} // namespace Service::AM
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h
index a2b852b12..46afb3996 100644
--- a/src/core/hle/service/am/am_types.h
+++ b/src/core/hle/service/am/am_types.h
@@ -18,7 +18,7 @@ enum class AppletType {
SystemApplet,
};
-enum class GameplayRecordingState : u32 {
+enum class GamePlayRecordingState : u32 {
Disabled,
Enabled,
};
@@ -67,10 +67,9 @@ enum class ScreenshotPermission : u32 {
};
struct FocusHandlingMode {
- bool unknown0;
- bool unknown1;
- bool unknown2;
- bool unknown3;
+ bool notify;
+ bool background;
+ bool suspend;
};
enum class IdleTimeDetectionExtension : u32 {
@@ -128,14 +127,53 @@ enum class AppletProgramId : u64 {
MaxProgramId = 0x0100000000001FFFull,
};
+// This is nn::am::AppletMessage
+enum class AppletMessage : u32 {
+ None = 0,
+ ChangeIntoForeground = 1,
+ ChangeIntoBackground = 2,
+ Exit = 4,
+ ApplicationExited = 6,
+ FocusStateChanged = 15,
+ Resume = 16,
+ DetectShortPressingHomeButton = 20,
+ DetectLongPressingHomeButton = 21,
+ DetectShortPressingPowerButton = 22,
+ DetectMiddlePressingPowerButton = 23,
+ DetectLongPressingPowerButton = 24,
+ RequestToPrepareSleep = 25,
+ FinishedSleepSequence = 26,
+ SleepRequiredByHighTemperature = 27,
+ SleepRequiredByLowBattery = 28,
+ AutoPowerDown = 29,
+ OperationModeChanged = 30,
+ PerformanceModeChanged = 31,
+ DetectReceivingCecSystemStandby = 32,
+ SdCardRemoved = 33,
+ LaunchApplicationRequested = 50,
+ RequestToDisplay = 51,
+ ShowApplicationLogo = 55,
+ HideApplicationLogo = 56,
+ ForceHideApplicationLogo = 57,
+ FloatingApplicationDetected = 60,
+ DetectShortPressingCaptureButton = 90,
+ AlbumScreenShotTaken = 92,
+ AlbumRecordingSaved = 93,
+};
+
enum class LibraryAppletMode : u32 {
AllForeground = 0,
- Background = 1,
- NoUI = 2,
- BackgroundIndirectDisplay = 3,
+ PartialForeground = 1,
+ NoUi = 2,
+ PartialForegroundIndirectDisplay = 3,
AllForegroundInitiallyHidden = 4,
};
+enum class LaunchParameterKind : u32 {
+ UserChannel = 1,
+ AccountPreselectedUser = 2,
+};
+
enum class CommonArgumentVersion : u32 {
Version0,
Version1,
@@ -152,6 +190,22 @@ enum class ThemeColor : u32 {
BasicBlack = 3,
};
+enum class InputDetectionPolicy : u32 {
+ Unknown0 = 0,
+ Unknown1 = 1,
+};
+
+enum class WindowOriginMode : u32 {
+ LowerLeft = 0,
+ UpperLeft = 1,
+};
+
+enum class ProgramSpecifyKind : u32 {
+ ExecuteProgram = 0,
+ JumpToSubApplicationProgramForDevelopment = 1,
+ RestartProgram = 2,
+};
+
struct CommonArguments {
CommonArgumentVersion arguments_version;
CommonArgumentSize size;
@@ -169,6 +223,27 @@ struct AppletIdentityInfo {
};
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
+struct AppletAttribute {
+ u8 flag;
+ INSERT_PADDING_BYTES_NOINIT(0x7F);
+};
+static_assert(sizeof(AppletAttribute) == 0x80, "AppletAttribute has incorrect size.");
+
+// This is nn::oe::DisplayVersion
+struct DisplayVersion {
+ std::array<char, 0x10> string;
+};
+static_assert(sizeof(DisplayVersion) == 0x10, "DisplayVersion has incorrect size.");
+
+// This is nn::pdm::ApplicationPlayStatistics
+struct ApplicationPlayStatistics {
+ u64 application_id;
+ u64 play_time_ns;
+ u64 launch_count;
+};
+static_assert(sizeof(ApplicationPlayStatistics) == 0x18,
+ "ApplicationPlayStatistics has incorrect size.");
+
using AppletResourceUserId = u64;
using ProgramId = u64;
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
index b29ecdfed..ad602153e 100644
--- a/src/core/hle/service/am/applet.h
+++ b/src/core/hle/service/am/applet.h
@@ -3,7 +3,6 @@
#pragma once
-#include <list>
#include <mutex>
#include "common/math_util.h"
@@ -15,11 +14,9 @@
#include "core/hle/service/am/am_types.h"
#include "core/hle/service/am/applet_message_queue.h"
+#include "core/hle/service/am/display_layer_manager.h"
#include "core/hle/service/am/hid_registration.h"
-#include "core/hle/service/am/managed_layer_holder.h"
#include "core/hle/service/am/process.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/am/system_buffer_manager.h"
namespace Service::AM {
@@ -56,8 +53,7 @@ struct Applet {
HidRegistration hid_registration;
// vi state
- SystemBufferManager system_buffer_manager{};
- ManagedLayerHolder managed_layer_holder{};
+ DisplayLayerManager display_layer_manager{};
// Applet common functions
Result terminate_result{};
@@ -76,8 +72,8 @@ struct Applet {
u32 application_core_usage_mode{};
// Application functions
- bool gameplay_recording_supported{};
- GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled};
+ bool game_play_recording_supported{};
+ GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled};
bool jit_service_launched{};
bool is_running{};
bool application_crash_report_enabled{};
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
deleted file mode 100644
index 1b715dea6..000000000
--- a/src/core/hle/service/am/applet_ae.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet_ae.h"
-#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/library_applet_proxy.h"
-#include "core/hle/service/am/system_applet_proxy.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
- : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
- {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
- {201, &AppletAE::OpenLibraryAppletProxy, "OpenLibraryAppletProxy"},
- {300, nullptr, "OpenOverlayAppletProxy"},
- {350, nullptr, "OpenSystemApplicationProxy"},
- {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
- {410, nullptr, "GetSystemAppletControllerForDebug"},
- {1000, nullptr, "GetDebugFunctions"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-AppletAE::~AppletAE() = default;
-
-void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- if (const auto applet = GetAppletFromContext(ctx)) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, applet, system);
- } else {
- UNIMPLEMENTED();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- }
-}
-
-void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- if (const auto applet = GetAppletFromContext(ctx)) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, applet, system);
- } else {
- UNIMPLEMENTED();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- }
-}
-
-void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- return OpenLibraryAppletProxy(ctx);
-}
-
-std::shared_ptr<Applet> AppletAE::GetAppletFromContext(HLERequestContext& ctx) {
- const auto aruid = ctx.GetPID();
- return system.GetAppletManager().GetByAppletResourceUserId(aruid);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
deleted file mode 100644
index 3d7961fa1..000000000
--- a/src/core/hle/service/am/applet_ae.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-
-#include "core/hle/service/service.h"
-
-namespace Service {
-namespace FileSystem {
-class FileSystemController;
-}
-
-namespace Nvnflinger {
-class Nvnflinger;
-}
-
-namespace AM {
-
-struct Applet;
-
-class AppletAE final : public ServiceFramework<AppletAE> {
-public:
- explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
- ~AppletAE() override;
-
-private:
- void OpenSystemAppletProxy(HLERequestContext& ctx);
- void OpenLibraryAppletProxy(HLERequestContext& ctx);
- void OpenLibraryAppletProxyOld(HLERequestContext& ctx);
-
- std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nvnflinger;
-};
-
-} // namespace AM
-} // namespace Service
diff --git a/src/core/hle/service/am/applet_common_functions.cpp b/src/core/hle/service/am/applet_common_functions.cpp
deleted file mode 100644
index 130614ae5..000000000
--- a/src/core/hle/service/am/applet_common_functions.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet.h"
-#include "core/hle/service/am/applet_common_functions.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
- std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "SetTerminateResult"},
- {10, nullptr, "ReadThemeStorage"},
- {11, nullptr, "WriteThemeStorage"},
- {20, nullptr, "PushToAppletBoundChannel"},
- {21, nullptr, "TryPopFromAppletBoundChannel"},
- {40, nullptr, "GetDisplayLogicalResolution"},
- {42, nullptr, "SetDisplayMagnification"},
- {50, nullptr, "SetHomeButtonDoubleClickEnabled"},
- {51, nullptr, "GetHomeButtonDoubleClickEnabled"},
- {52, nullptr, "IsHomeButtonShortPressedBlocked"},
- {60, nullptr, "IsVrModeCurtainRequired"},
- {61, nullptr, "IsSleepRequiredByHighTemperature"},
- {62, nullptr, "IsSleepRequiredByLowBattery"},
- {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"},
- {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
- {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
- {90, nullptr, "OpenNamedChannelAsParent"},
- {91, nullptr, "OpenNamedChannelAsChild"},
- {100, nullptr, "SetApplicationCoreUsageMode"},
- {300, &IAppletCommonFunctions::GetCurrentApplicationId, "GetCurrentApplicationId"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IAppletCommonFunctions::~IAppletCommonFunctions() = default;
-
-void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
-
- std::scoped_lock lk{applet->lock};
- applet->cpu_boost_request_priority = rp.Pop<s32>();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IAppletCommonFunctions::GetCurrentApplicationId(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(system.GetApplicationProcessProgramID() & ~0xFFFULL);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_common_functions.h b/src/core/hle/service/am/applet_common_functions.h
deleted file mode 100644
index b86adf5cb..000000000
--- a/src/core/hle/service/am/applet_common_functions.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
-public:
- explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~IAppletCommonFunctions() override;
-
-private:
- void SetCpuBoostRequestPriority(HLERequestContext& ctx);
- void GetCurrentApplicationId(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp
index 4d58c4db5..9057244a9 100644
--- a/src/core/hle/service/am/applet_data_broker.cpp
+++ b/src/core/hle/service/am/applet_data_broker.cpp
@@ -24,11 +24,11 @@ void AppletStorageChannel::Push(std::shared_ptr<IStorage> storage) {
Result AppletStorageChannel::Pop(std::shared_ptr<IStorage>* out_storage) {
std::scoped_lock lk{m_lock};
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (m_data.empty()) {
m_event.Clear();
}
- });
+ };
R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel);
diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp
index 52200d5b2..2e109181d 100644
--- a/src/core/hle/service/am/applet_manager.cpp
+++ b/src/core/hle/service/am/applet_manager.cpp
@@ -12,6 +12,7 @@
#include "core/hle/service/am/frontend/applet_controller.h"
#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
+#include "core/hle/service/am/service/storage.h"
#include "hid_core/hid_types.h"
namespace Service::AM {
@@ -34,6 +35,21 @@ AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system,
return applet->caller_applet_broker->GetInData();
}
+void PushInShowQlaunch(Core::System& system, AppletStorageChannel& channel) {
+ const CommonArguments arguments{
+ .arguments_version = CommonArgumentVersion::Version3,
+ .size = CommonArgumentSize::Version3,
+ .library_version = 0,
+ .theme_color = ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ std::vector<u8> argument_data(sizeof(arguments));
+ std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
+ channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
+}
+
void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) {
const CommonArguments arguments{
.arguments_version = CommonArgumentVersion::Version3,
@@ -283,6 +299,9 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
// Starting from frontend, some applets require input data.
switch (applet->applet_id) {
+ case AppletId::QLaunch:
+ PushInShowQlaunch(m_system, InitializeFakeCallerApplet(m_system, applet));
+ break;
case AppletId::Cabinet:
PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet));
break;
@@ -303,8 +322,8 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
}
// Applet was started by frontend, so it is foreground.
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
+ applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);
+ applet->message_queue.PushMessage(AppletMessage::FocusStateChanged);
applet->focus_state = FocusState::InFocus;
this->InsertApplet(std::move(applet));
diff --git a/src/core/hle/service/am/applet_message_queue.cpp b/src/core/hle/service/am/applet_message_queue.cpp
index 5ed996b70..83c3c5a55 100644
--- a/src/core/hle/service/am/applet_message_queue.cpp
+++ b/src/core/hle/service/am/applet_message_queue.cpp
@@ -33,7 +33,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
on_new_message->Signal();
}
-AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
+AppletMessage AppletMessageQueue::PopMessage() {
std::scoped_lock lk{lock};
if (messages.empty()) {
on_new_message->Clear();
diff --git a/src/core/hle/service/am/applet_message_queue.h b/src/core/hle/service/am/applet_message_queue.h
index 5cb236d47..429b77d37 100644
--- a/src/core/hle/service/am/applet_message_queue.h
+++ b/src/core/hle/service/am/applet_message_queue.h
@@ -5,6 +5,7 @@
#include <queue>
+#include "core/hle/service/am/am_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -16,40 +17,6 @@ namespace Service::AM {
class AppletMessageQueue {
public:
- // This is nn::am::AppletMessage
- enum class AppletMessage : u32 {
- None = 0,
- ChangeIntoForeground = 1,
- ChangeIntoBackground = 2,
- Exit = 4,
- ApplicationExited = 6,
- FocusStateChanged = 15,
- Resume = 16,
- DetectShortPressingHomeButton = 20,
- DetectLongPressingHomeButton = 21,
- DetectShortPressingPowerButton = 22,
- DetectMiddlePressingPowerButton = 23,
- DetectLongPressingPowerButton = 24,
- RequestToPrepareSleep = 25,
- FinishedSleepSequence = 26,
- SleepRequiredByHighTemperature = 27,
- SleepRequiredByLowBattery = 28,
- AutoPowerDown = 29,
- OperationModeChanged = 30,
- PerformanceModeChanged = 31,
- DetectReceivingCecSystemStandby = 32,
- SdCardRemoved = 33,
- LaunchApplicationRequested = 50,
- RequestToDisplay = 51,
- ShowApplicationLogo = 55,
- HideApplicationLogo = 56,
- ForceHideApplicationLogo = 57,
- FloatingApplicationDetected = 60,
- DetectShortPressingCaptureButton = 90,
- AlbumScreenShotTaken = 92,
- AlbumRecordingSaved = 93,
- };
-
explicit AppletMessageQueue(Core::System& system);
~AppletMessageQueue();
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
deleted file mode 100644
index 56bafd162..000000000
--- a/src/core/hle/service/am/applet_oe.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/applet_oe.h"
-#include "core/hle/service/am/application_proxy.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
- : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_} {
- static const FunctionInfo functions[] = {
- {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
- };
- RegisterHandlers(functions);
-}
-
-AppletOE::~AppletOE() = default;
-
-void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- if (const auto applet = GetAppletFromContext(ctx)) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationProxy>(nvnflinger, applet, system);
- } else {
- UNIMPLEMENTED();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- }
-}
-
-std::shared_ptr<Applet> AppletOE::GetAppletFromContext(HLERequestContext& ctx) {
- const auto aruid = ctx.GetPID();
- return system.GetAppletManager().GetByAppletResourceUserId(aruid);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
deleted file mode 100644
index f2ba1c924..000000000
--- a/src/core/hle/service/am/applet_oe.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-
-#include "core/hle/service/service.h"
-
-namespace Service {
-namespace FileSystem {
-class FileSystemController;
-}
-
-namespace Nvnflinger {
-class Nvnflinger;
-}
-
-namespace AM {
-
-struct Applet;
-
-class AppletOE final : public ServiceFramework<AppletOE> {
-public:
- explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
- ~AppletOE() override;
-
-private:
- void OpenApplicationProxy(HLERequestContext& ctx);
-
- std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nvnflinger;
-};
-
-} // namespace AM
-} // namespace Service
diff --git a/src/core/hle/service/am/application_creator.cpp b/src/core/hle/service/am/application_creator.cpp
deleted file mode 100644
index 79ea045a3..000000000
--- a/src/core/hle/service/am/application_creator.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/application_creator.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IApplicationCreator::IApplicationCreator(Core::System& system_)
- : ServiceFramework{system_, "IApplicationCreator"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "CreateApplication"},
- {1, nullptr, "PopLaunchRequestedApplication"},
- {10, nullptr, "CreateSystemApplication"},
- {100, nullptr, "PopFloatingApplicationForDevelopment"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IApplicationCreator::~IApplicationCreator() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_creator.h b/src/core/hle/service/am/application_creator.h
deleted file mode 100644
index 375a3c476..000000000
--- a/src/core/hle/service/am/application_creator.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
-public:
- explicit IApplicationCreator(Core::System& system_);
- ~IApplicationCreator() override;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_functions.cpp b/src/core/hle/service/am/application_functions.cpp
deleted file mode 100644
index 51c5be2d1..000000000
--- a/src/core/hle/service/am/application_functions.cpp
+++ /dev/null
@@ -1,594 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/settings.h"
-#include "common/uuid.h"
-#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/patch_manager.h"
-#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/savedata_factory.h"
-#include "core/hle/service/acc/profile_manager.h"
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/applet.h"
-#include "core/hle/service/am/application_functions.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/filesystem/save_data_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ns/ns.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace Service::AM {
-
-enum class LaunchParameterKind : u32 {
- UserChannel = 1,
- AccountPreselectedUser = 2,
-};
-
-IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
- {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
- {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
- {12, nullptr, "CreateApplicationAndRequestToStart"},
- {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
- {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
- {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
- {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
- {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
- {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
- {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
- {24, nullptr, "GetLaunchStorageInfoForDebug"},
- {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
- {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
- {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
- {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"},
- {29, nullptr, "GetCacheStorageMax"},
- {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
- {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
- {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
- {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
- {34, nullptr, "SelectApplicationLicense"},
- {35, nullptr, "GetDeviceSaveDataSizeMax"},
- {36, nullptr, "GetLimitedApplicationLicense"},
- {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
- {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
- {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
- {60, nullptr, "SetMediaPlaybackStateForApplication"},
- {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
- {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
- {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
- {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
- {70, nullptr, "RequestToShutdown"},
- {71, nullptr, "RequestToReboot"},
- {72, nullptr, "RequestToSleep"},
- {80, nullptr, "ExitAndRequestToShowThanksMessage"},
- {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
- {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
- {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"},
- {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
- {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
- {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
- {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
- {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
- {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
- {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
- {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
- {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
- {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
- {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
- {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
- {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
- {151, nullptr, "TryPopFromNotificationStorageChannel"},
- {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
- {170, nullptr, "SetHdcpAuthenticationActivated"},
- {180, nullptr, "GetLaunchRequiredVersion"},
- {181, nullptr, "UpgradeLaunchRequiredVersion"},
- {190, nullptr, "SendServerMaintenanceOverlayNotification"},
- {200, nullptr, "GetLastApplicationExitReason"},
- {500, nullptr, "StartContinuousRecordingFlushForDebug"},
- {1000, nullptr, "CreateMovieMaker"},
- {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IApplicationFunctions::~IApplicationFunctions() = default;
-
-void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->application_crash_report_enabled = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto is_visible = rp.Pop<bool>();
-
- LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->home_button_long_pressed_blocked = true;
- applet->home_button_short_pressed_blocked = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->home_button_long_pressed_blocked = false;
- applet->home_button_short_pressed_blocked = false;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->home_button_long_pressed_blocked = true;
- applet->home_button_short_pressed_blocked = true;
- applet->home_button_double_click_enabled = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->home_button_long_pressed_blocked = false;
- applet->home_button_short_pressed_blocked = false;
- applet->home_button_double_click_enabled = false;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto kind = rp.PopEnum<LaunchParameterKind>();
-
- LOG_INFO(Service_AM, "called, kind={:08X}", kind);
-
- std::scoped_lock lk{applet->lock};
-
- auto& channel = kind == LaunchParameterKind::UserChannel
- ? applet->user_channel_launch_parameter
- : applet->preselected_user_launch_parameter;
-
- if (channel.empty()) {
- LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(AM::ResultNoDataInChannel);
- return;
- }
-
- auto data = channel.back();
- channel.pop_back();
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IStorage>(system, std::move(data));
-}
-
-void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- u128 user_id = rp.PopRaw<u128>();
-
- LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
-
- FileSys::SaveDataAttribute attribute{};
- attribute.title_id = applet->program_id;
- attribute.user_id = user_id;
- attribute.type = FileSys::SaveDataType::SaveData;
-
- FileSys::VirtualDir save_data{};
- const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
- &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.Push<u64>(0);
-}
-
-void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) {
- // Takes an input u32 Result, no output.
- // For example, in some cases official apps use this with error 0x2A2 then
- // uses svcBreak.
-
- IPC::RequestParser rp{ctx};
- u32 result = rp.Pop<u32>();
- LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
-
- std::scoped_lock lk{applet->lock};
- applet->terminate_result = Result(result);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- std::array<u8, 0x10> version_string{};
-
- const auto res = [this] {
- const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
- system.GetContentProvider()};
- auto metadata = pm.GetControlMetadata();
- if (metadata.first != nullptr) {
- return metadata;
- }
-
- const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
- system.GetFileSystemController(),
- system.GetContentProvider()};
- return pm_update.GetControlMetadata();
- }();
-
- if (res.first != nullptr) {
- const auto& version = res.first->GetVersionString();
- std::copy(version.begin(), version.end(), version_string.begin());
- } else {
- static constexpr char default_version[]{"1.0.0"};
- std::memcpy(version_string.data(), default_version, sizeof(default_version));
- }
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.PushRaw(version_string);
-}
-
-void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
- // TODO(bunnei): This should be configurable
- LOG_DEBUG(Service_AM, "called");
-
- // Get supported languages from NACP, if possible
- // Default to 0 (all languages supported)
- u32 supported_languages = 0;
-
- const auto res = [this] {
- const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
- system.GetContentProvider()};
- auto metadata = pm.GetControlMetadata();
- if (metadata.first != nullptr) {
- return metadata;
- }
-
- const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
- system.GetFileSystemController(),
- system.GetContentProvider()};
- return pm_update.GetControlMetadata();
- }();
-
- if (res.first != nullptr) {
- supported_languages = res.first->GetSupportedLanguages();
- }
-
- // Call IApplicationManagerInterface implementation.
- auto& service_manager = system.ServiceManager();
- auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
- auto app_man = ns_am2->GetApplicationManagerInterface();
-
- // Get desired application language
- u8 desired_language{};
- const auto res_lang =
- app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
- if (res_lang != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res_lang);
- return;
- }
-
- // Convert to settings language code.
- u64 language_code{};
- const auto res_code =
- app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
- if (res_code != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res_code);
- return;
- }
-
- LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(language_code);
-}
-
-void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(applet->gameplay_recording_supported);
-}
-
-void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
-
- std::scoped_lock lk{applet->lock};
- applet->gameplay_recording_state = rp.PopRaw<GameplayRecordingState>();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->is_running = true;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
-}
-
-void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
-
- // Returns a 128-bit UUID
- rb.Push<u64>(0);
- rb.Push<u64>(0);
-}
-
-void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
- struct Parameters {
- FileSys::SaveDataType type;
- u128 user_id;
- u64 new_normal_size;
- u64 new_journal_size;
- };
- static_assert(sizeof(Parameters) == 40);
-
- IPC::RequestParser rp{ctx};
- const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>();
-
- LOG_DEBUG(Service_AM,
- "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
- "new_journal={:016X}",
- static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
-
- system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
- type, applet->program_id, user_id, {new_normal_size, new_journal_size});
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
-
- // The following value is used upon failure to help the system recover.
- // Since we always succeed, this should be 0.
- rb.Push<u64>(0);
-}
-
-void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
- struct Parameters {
- FileSys::SaveDataType type;
- u128 user_id;
- };
- static_assert(sizeof(Parameters) == 24);
-
- IPC::RequestParser rp{ctx};
- const auto [type, user_id] = rp.PopRaw<Parameters>();
-
- LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
- user_id[0]);
-
- const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
- type, applet->program_id, user_id);
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.Push(size.normal);
- rb.Push(size.journal);
-}
-
-void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
- struct InputParameters {
- u16 index;
- s64 size;
- s64 journal_size;
- };
- static_assert(sizeof(InputParameters) == 24);
-
- struct OutputParameters {
- u32 storage_target;
- u64 required_size;
- };
- static_assert(sizeof(OutputParameters) == 16);
-
- IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<InputParameters>();
-
- LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
- params.index, params.size, params.journal_size);
-
- const OutputParameters resp{
- .storage_target = 1,
- .required_size = 0,
- };
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.PushRaw(resp);
-}
-
-void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- constexpr u64 size_max_normal = 0xFFFFFFF;
- constexpr u64 size_max_journal = 0xFFFFFFF;
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.Push(size_max_normal);
- rb.Push(size_max_journal);
-}
-
-void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
-}
-
-void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
-}
-
-void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
- [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
- [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
- const auto program_index = rp.Pop<u64>();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-
- // Swap user channel ownership into the system so that it will be preserved
- system.GetUserChannel().swap(applet->user_channel_launch_parameter);
- system.ExecuteProgram(program_index);
-}
-
-void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- applet->user_channel_launch_parameter.clear();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::RequestParser rp{ctx};
- const auto storage = rp.PopIpcInterface<IStorage>().lock();
- if (storage) {
- applet->user_channel_launch_parameter.push_back(storage->GetData());
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<s32>(applet->previous_program_index);
-}
-
-void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle());
-}
-
-void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle());
-}
-
-void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(AM::ResultNoDataInChannel);
-}
-
-void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle());
-}
-
-void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle());
-}
-
-void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->jit_service_launched = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_functions.h b/src/core/hle/service/am/application_functions.h
deleted file mode 100644
index 55eb21d39..000000000
--- a/src/core/hle/service/am/application_functions.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
-public:
- explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~IApplicationFunctions() override;
-
-private:
- void PopLaunchParameter(HLERequestContext& ctx);
- void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx);
- void EnsureSaveData(HLERequestContext& ctx);
- void SetTerminateResult(HLERequestContext& ctx);
- void GetDisplayVersion(HLERequestContext& ctx);
- void GetDesiredLanguage(HLERequestContext& ctx);
- void IsGamePlayRecordingSupported(HLERequestContext& ctx);
- void InitializeGamePlayRecording(HLERequestContext& ctx);
- void SetGamePlayRecordingState(HLERequestContext& ctx);
- void NotifyRunning(HLERequestContext& ctx);
- void GetPseudoDeviceId(HLERequestContext& ctx);
- void ExtendSaveData(HLERequestContext& ctx);
- void GetSaveDataSize(HLERequestContext& ctx);
- void CreateCacheStorage(HLERequestContext& ctx);
- void GetSaveDataSizeMax(HLERequestContext& ctx);
- void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
- void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
- void BeginBlockingHomeButton(HLERequestContext& ctx);
- void EndBlockingHomeButton(HLERequestContext& ctx);
- void EnableApplicationCrashReport(HLERequestContext& ctx);
- void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx);
- void SetApplicationCopyrightImage(HLERequestContext& ctx);
- void SetApplicationCopyrightVisibility(HLERequestContext& ctx);
- void QueryApplicationPlayStatistics(HLERequestContext& ctx);
- void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx);
- void ExecuteProgram(HLERequestContext& ctx);
- void ClearUserChannel(HLERequestContext& ctx);
- void UnpopToUserChannel(HLERequestContext& ctx);
- void GetPreviousProgramIndex(HLERequestContext& ctx);
- void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx);
- void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx);
- void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx);
- void GetNotificationStorageChannelEvent(HLERequestContext& ctx);
- void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
- void PrepareForJit(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_proxy.cpp b/src/core/hle/service/am/application_proxy.cpp
deleted file mode 100644
index a6fd6d37f..000000000
--- a/src/core/hle/service/am/application_proxy.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet_common_functions.h"
-#include "core/hle/service/am/application_functions.h"
-#include "core/hle/service/am/application_proxy.h"
-#include "core/hle/service/am/audio_controller.h"
-#include "core/hle/service/am/common_state_getter.h"
-#include "core/hle/service/am/debug_functions.h"
-#include "core/hle/service/am/display_controller.h"
-#include "core/hle/service/am/library_applet_creator.h"
-#include "core/hle/service/am/library_applet_self_accessor.h"
-#include "core/hle/service/am/process_winding_controller.h"
-#include "core/hle/service/am/self_controller.h"
-#include "core/hle/service/am/window_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
- std::shared_ptr<Applet> applet_, Core::System& system_)
- : ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
- applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
- {1, &IApplicationProxy::GetSelfController, "GetSelfController"},
- {2, &IApplicationProxy::GetWindowController, "GetWindowController"},
- {3, &IApplicationProxy::GetAudioController, "GetAudioController"},
- {4, &IApplicationProxy::GetDisplayController, "GetDisplayController"},
- {10, &IApplicationProxy::GetProcessWindingController, "GetProcessWindingController"},
- {11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
- {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
- {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IApplicationProxy::~IApplicationProxy() = default;
-
-void IApplicationProxy::GetAudioController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioController>(system);
-}
-
-void IApplicationProxy::GetDisplayController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDisplayController>(system, applet);
-}
-
-void IApplicationProxy::GetProcessWindingController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IProcessWindingController>(system, applet);
-}
-
-void IApplicationProxy::GetDebugFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDebugFunctions>(system);
-}
-
-void IApplicationProxy::GetWindowController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IWindowController>(system, applet);
-}
-
-void IApplicationProxy::GetSelfController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
-}
-
-void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ICommonStateGetter>(system, applet);
-}
-
-void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
-}
-
-void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationFunctions>(system, applet);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_proxy.h b/src/core/hle/service/am/application_proxy.h
deleted file mode 100644
index eb98b095c..000000000
--- a/src/core/hle/service/am/application_proxy.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
-public:
- explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
- std::shared_ptr<Applet> msg_queue_, Core::System& system_);
- ~IApplicationProxy();
-
-private:
- void GetAudioController(HLERequestContext& ctx);
- void GetDisplayController(HLERequestContext& ctx);
- void GetProcessWindingController(HLERequestContext& ctx);
- void GetDebugFunctions(HLERequestContext& ctx);
- void GetWindowController(HLERequestContext& ctx);
- void GetSelfController(HLERequestContext& ctx);
- void GetCommonStateGetter(HLERequestContext& ctx);
- void GetLibraryAppletCreator(HLERequestContext& ctx);
- void GetApplicationFunctions(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nvnflinger;
- std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/audio_controller.cpp b/src/core/hle/service/am/audio_controller.cpp
deleted file mode 100644
index ae75db174..000000000
--- a/src/core/hle/service/am/audio_controller.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/audio_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IAudioController::IAudioController(Core::System& system_)
- : ServiceFramework{system_, "IAudioController"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
- {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
- {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
- {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
- {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IAudioController::~IAudioController() = default;
-
-void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const float main_applet_volume_tmp = rp.Pop<float>();
- const float library_applet_volume_tmp = rp.Pop<float>();
-
- LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
- main_applet_volume_tmp, library_applet_volume_tmp);
-
- // Ensure the volume values remain within the 0-100% range
- main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
- library_applet_volume =
- std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(main_applet_volume);
-}
-
-void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(library_applet_volume);
-}
-
-void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) {
- struct Parameters {
- float volume;
- s64 fade_time_ns;
- };
- static_assert(sizeof(Parameters) == 16);
-
- IPC::RequestParser rp{ctx};
- const auto parameters = rp.PopRaw<Parameters>();
-
- LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
- parameters.fade_time_ns);
-
- main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
- fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const float transparent_volume_rate_tmp = rp.Pop<float>();
-
- LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
-
- // Clamp volume range to 0-100%.
- transparent_volume_rate =
- std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/audio_controller.h b/src/core/hle/service/am/audio_controller.h
deleted file mode 100644
index a47e3bad8..000000000
--- a/src/core/hle/service/am/audio_controller.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class IAudioController final : public ServiceFramework<IAudioController> {
-public:
- explicit IAudioController(Core::System& system_);
- ~IAudioController() override;
-
-private:
- void SetExpectedMasterVolume(HLERequestContext& ctx);
- void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx);
- void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx);
- void ChangeMainAppletMasterVolume(HLERequestContext& ctx);
- void SetTransparentAudioRate(HLERequestContext& ctx);
-
- static constexpr float min_allowed_volume = 0.0f;
- static constexpr float max_allowed_volume = 1.0f;
-
- float main_applet_volume{0.25f};
- float library_applet_volume{max_allowed_volume};
- float transparent_volume_rate{min_allowed_volume};
-
- // Volume transition fade time in nanoseconds.
- // e.g. If the main applet volume was 0% and was changed to 50%
- // with a fade of 50ns, then over the course of 50ns,
- // the volume will gradually fade up to 50%
- std::chrono::nanoseconds fade_time_ns{0};
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/common_state_getter.cpp b/src/core/hle/service/am/common_state_getter.cpp
deleted file mode 100644
index 937ac0beb..000000000
--- a/src/core/hle/service/am/common_state_getter.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/settings.h"
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/applet.h"
-#include "core/hle/service/am/common_state_getter.h"
-#include "core/hle/service/am/lock_accessor.h"
-#include "core/hle/service/apm/apm_controller.h"
-#include "core/hle/service/apm/apm_interface.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/pm/pm.h"
-#include "core/hle/service/sm/sm.h"
-#include "core/hle/service/vi/vi.h"
-
-namespace Service::AM {
-
-ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "ICommonStateGetter"}, applet{std::move(applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
- {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
- {2, nullptr, "GetThisAppletKind"},
- {3, nullptr, "AllowToEnterSleep"},
- {4, nullptr, "DisallowToEnterSleep"},
- {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
- {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
- {7, nullptr, "GetCradleStatus"},
- {8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
- {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
- {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"},
- {11, nullptr, "ReleaseSleepLock"},
- {12, nullptr, "ReleaseSleepLockTransiently"},
- {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"},
- {14, nullptr, "GetWakeupCount"},
- {20, nullptr, "PushToGeneralChannel"},
- {30, nullptr, "GetHomeButtonReaderLockAccessor"},
- {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"},
- {32, nullptr, "GetWriterLockAccessorEx"},
- {40, nullptr, "GetCradleFwVersion"},
- {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
- {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
- {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
- {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
- {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
- {55, nullptr, "IsInControllerFirmwareUpdateSection"},
- {59, nullptr, "SetVrPositionForDebug"},
- {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
- {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
- {62, nullptr, "GetHdcpAuthenticationState"},
- {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
- {64, nullptr, "SetTvPowerStateMatchingMode"},
- {65, nullptr, "GetApplicationIdByContentActionName"},
- {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
- {67, nullptr, "CancelCpuBoostMode"},
- {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"},
- {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
- {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
- {91, nullptr, "GetCurrentPerformanceConfiguration"},
- {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
- {110, nullptr, "OpenMyGpuErrorHandler"},
- {120, &ICommonStateGetter::GetAppletLaunchedHistory, "GetAppletLaunchedHistory"},
- {200, nullptr, "GetOperationModeSystemInfo"},
- {300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"},
- {400, nullptr, "ActivateMigrationService"},
- {401, nullptr, "DeactivateMigrationService"},
- {500, nullptr, "DisableSleepTillShutdown"},
- {501, nullptr, "SuppressDisablingSleepTemporarily"},
- {502, nullptr, "IsSleepEnabled"},
- {503, nullptr, "IsDisablingSleepSuppressed"},
- {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ICommonStateGetter::~ICommonStateGetter() = default;
-
-void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
-}
-
-void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->message_queue.GetMessageReceiveEvent());
-}
-
-void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- const auto message = applet->message_queue.PopMessage();
- IPC::ResponseBuilder rb{ctx, 3};
-
- if (message == AppletMessageQueue::AppletMessage::None) {
- LOG_ERROR(Service_AM, "Message queue is empty");
- rb.Push(AM::ResultNoMessages);
- rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
- return;
- }
-
- rb.Push(ResultSuccess);
- rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
-}
-
-void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u8>(applet->focus_state));
-}
-
-void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
- const bool use_docked_mode{Settings::IsDockedMode()};
- LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
-}
-
-void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode());
-}
-
-void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- // Sleep lock is acquired immediately.
- applet->sleep_lock_event.Signal();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto unknown = rp.Pop<u32>();
-
- LOG_INFO(Service_AM, "called, unknown={}", unknown);
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILockAccessor>(system);
-}
-
-void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->sleep_lock_event.GetHandle());
-}
-
-void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- std::scoped_lock lk{applet->lock};
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(applet->vr_mode_enabled);
-}
-
-void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- std::scoped_lock lk{applet->lock};
- applet->vr_mode_enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "VR Mode is {}", applet->vr_mode_enabled ? "on" : "off");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
-
- LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
- is_lcd_backlight_off_enabled);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->vr_mode_enabled = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->vr_mode_enabled = false;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->message_queue.GetOperationModeChangedEvent());
-}
-
-void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
-
- if (Settings::IsDockedMode()) {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
- } else {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
- }
-}
-
-void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS");
-
- const auto& sm = system.ServiceManager();
- const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys");
- ASSERT(apm_sys != nullptr);
-
- apm_sys->SetCpuBoostMode(ctx);
-}
-
-void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(0);
-}
-
-void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto system_button{rp.PopEnum<SystemButtonType>()};
-
- LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ICommonStateGetter::GetAppletLaunchedHistory(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::shared_ptr<Applet> current_applet = applet;
- std::vector<AppletId> result;
-
- const size_t count = ctx.GetWriteBufferNumElements<AppletId>();
- size_t i;
-
- for (i = 0; i < count && current_applet != nullptr; i++) {
- result.push_back(current_applet->applet_id);
- current_applet = current_applet->caller_applet.lock();
- }
-
- ctx.WriteBuffer(result);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(i));
-}
-
-void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(SysPlatformRegion::Global);
-}
-
-void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
- HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->request_exit_to_library_applet_at_execute_next_program_enabled = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/common_state_getter.h b/src/core/hle/service/am/common_state_getter.h
deleted file mode 100644
index bf652790c..000000000
--- a/src/core/hle/service/am/common_state_getter.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/service.h"
-
-#include "core/hle/service/am/applet_message_queue.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
-public:
- explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~ICommonStateGetter() override;
-
-private:
- // This is nn::oe::FocusState
- enum class FocusState : u8 {
- InFocus = 1,
- NotInFocus = 2,
- Background = 3,
- };
-
- // This is nn::oe::OperationMode
- enum class OperationMode : u8 {
- Handheld = 0,
- Docked = 1,
- };
-
- // This is nn::am::service::SystemButtonType
- enum class SystemButtonType {
- None,
- HomeButtonShortPressing,
- HomeButtonLongPressing,
- PowerButtonShortPressing,
- PowerButtonLongPressing,
- ShutdownSystem,
- CaptureButtonShortPressing,
- CaptureButtonLongPressing,
- };
-
- enum class SysPlatformRegion : s32 {
- Global = 1,
- Terra = 2,
- };
-
- void GetEventHandle(HLERequestContext& ctx);
- void ReceiveMessage(HLERequestContext& ctx);
- void GetCurrentFocusState(HLERequestContext& ctx);
- void RequestToAcquireSleepLock(HLERequestContext& ctx);
- void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
- void GetReaderLockAccessorEx(HLERequestContext& ctx);
- void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
- void GetOperationMode(HLERequestContext& ctx);
- void GetPerformanceMode(HLERequestContext& ctx);
- void GetBootMode(HLERequestContext& ctx);
- void IsVrModeEnabled(HLERequestContext& ctx);
- void SetVrModeEnabled(HLERequestContext& ctx);
- void SetLcdBacklighOffEnabled(HLERequestContext& ctx);
- void BeginVrModeEx(HLERequestContext& ctx);
- void EndVrModeEx(HLERequestContext& ctx);
- void GetDefaultDisplayResolution(HLERequestContext& ctx);
- void SetCpuBoostMode(HLERequestContext& ctx);
- void GetBuiltInDisplayType(HLERequestContext& ctx);
- void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
- void GetAppletLaunchedHistory(HLERequestContext& ctx);
- void GetSettingsPlatformRegion(HLERequestContext& ctx);
- void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/debug_functions.cpp b/src/core/hle/service/am/debug_functions.cpp
deleted file mode 100644
index f80b970f2..000000000
--- a/src/core/hle/service/am/debug_functions.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/debug_functions.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IDebugFunctions::IDebugFunctions(Core::System& system_)
- : ServiceFramework{system_, "IDebugFunctions"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
- {1, nullptr, "OpenMainApplication"},
- {10, nullptr, "PerformSystemButtonPressing"},
- {20, nullptr, "InvalidateTransitionLayer"},
- {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
- {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"},
- {40, nullptr, "GetAppletResourceUsageInfo"},
- {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"},
- {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"},
- {100, nullptr, "SetCpuBoostModeForApplet"},
- {101, nullptr, "CancelCpuBoostModeForApplet"},
- {110, nullptr, "PushToAppletBoundChannelForDebug"},
- {111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
- {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
- {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"},
- {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"},
- {130, nullptr, "FriendInvitationSetApplicationParameter"},
- {131, nullptr, "FriendInvitationClearApplicationParameter"},
- {132, nullptr, "FriendInvitationPushApplicationParameter"},
- {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
- {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"},
- {300, nullptr, "TerminateAllRunningApplicationsForDebug"},
- {900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IDebugFunctions::~IDebugFunctions() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_controller.cpp b/src/core/hle/service/am/display_controller.cpp
deleted file mode 100644
index 4d6858348..000000000
--- a/src/core/hle/service/am/display_controller.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet.h"
-#include "core/hle/service/am/display_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-namespace {
-struct OutputParameters {
- bool was_written;
- s32 fbshare_layer_index;
-};
-
-static_assert(sizeof(OutputParameters) == 8, "OutputParameters has wrong size");
-} // namespace
-
-IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetLastForegroundCaptureImage"},
- {1, nullptr, "UpdateLastForegroundCaptureImage"},
- {2, nullptr, "GetLastApplicationCaptureImage"},
- {3, nullptr, "GetCallerAppletCaptureImage"},
- {4, nullptr, "UpdateCallerAppletCaptureImage"},
- {5, nullptr, "GetLastForegroundCaptureImageEx"},
- {6, nullptr, "GetLastApplicationCaptureImageEx"},
- {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"},
- {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
- {9, nullptr, "CopyBetweenCaptureBuffers"},
- {10, nullptr, "AcquireLastApplicationCaptureBuffer"},
- {11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
- {12, nullptr, "AcquireLastForegroundCaptureBuffer"},
- {13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
- {14, nullptr, "AcquireCallerAppletCaptureBuffer"},
- {15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
- {16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
- {17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
- {18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
- {20, nullptr, "ClearCaptureBuffer"},
- {21, nullptr, "ClearAppletTransitionBuffer"},
- {22, &IDisplayController::AcquireLastApplicationCaptureSharedBuffer, "AcquireLastApplicationCaptureSharedBuffer"},
- {23, &IDisplayController::ReleaseLastApplicationCaptureSharedBuffer, "ReleaseLastApplicationCaptureSharedBuffer"},
- {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
- {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
- {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
- {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
- {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IDisplayController::~IDisplayController() = default;
-
-void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- OutputParameters params{};
- const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
- &params.was_written, &params.fbshare_layer_index);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.PushRaw(params);
-}
-
-void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IDisplayController::AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- OutputParameters params{};
- const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
- &params.was_written, &params.fbshare_layer_index);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.PushRaw(params);
-}
-
-void IDisplayController::ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- OutputParameters params{};
- const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
- &params.was_written, &params.fbshare_layer_index);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.PushRaw(params);
-}
-
-void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- OutputParameters params{};
- const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
- &params.was_written, &params.fbshare_layer_index);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.PushRaw(params);
-}
-
-void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_controller.h b/src/core/hle/service/am/display_controller.h
deleted file mode 100644
index 75172580c..000000000
--- a/src/core/hle/service/am/display_controller.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IDisplayController final : public ServiceFramework<IDisplayController> {
-public:
- explicit IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~IDisplayController() override;
-
-private:
- void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
- void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
- void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
- void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
- void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
- void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
- void AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx);
- void ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp
new file mode 100644
index 000000000..85ff6fb88
--- /dev/null
+++ b/src/core/hle/service/am/display_layer_manager.cpp
@@ -0,0 +1,151 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/am/display_layer_manager.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/vi/application_display_service.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/manager_display_service.h"
+#include "core/hle/service/vi/manager_root_service.h"
+#include "core/hle/service/vi/shared_buffer_manager.h"
+#include "core/hle/service/vi/vi_results.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::AM {
+
+DisplayLayerManager::DisplayLayerManager() = default;
+DisplayLayerManager::~DisplayLayerManager() {
+ this->Finalize();
+}
+
+void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* process,
+ AppletId applet_id, LibraryAppletMode mode) {
+ R_ASSERT(system.ServiceManager()
+ .GetService<VI::IManagerRootService>("vi:m", true)
+ ->GetDisplayService(&m_display_service, VI::Policy::Compositor));
+ R_ASSERT(m_display_service->GetManagerDisplayService(&m_manager_display_service));
+
+ m_process = process;
+ m_system_shared_buffer_id = 0;
+ m_system_shared_layer_id = 0;
+ m_applet_id = applet_id;
+ m_buffer_sharing_enabled = false;
+ m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
+ mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
+}
+
+void DisplayLayerManager::Finalize() {
+ if (!m_manager_display_service) {
+ return;
+ }
+
+ // Clean up managed layers.
+ for (const auto& layer : m_managed_display_layers) {
+ m_manager_display_service->DestroyManagedLayer(layer);
+ }
+
+ for (const auto& layer : m_managed_display_recording_layers) {
+ m_manager_display_service->DestroyManagedLayer(layer);
+ }
+
+ // Clean up shared layers.
+ if (m_buffer_sharing_enabled) {
+ m_manager_display_service->DestroySharedLayerSession(m_process);
+ }
+
+ m_manager_display_service = nullptr;
+ m_display_service = nullptr;
+}
+
+Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
+ R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
+
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
+ u64 display_id;
+ R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
+ R_TRY(m_manager_display_service->CreateManagedLayer(
+ out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
+
+ m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
+ m_managed_display_layers.emplace(*out_layer_id);
+
+ R_SUCCEED();
+}
+
+Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer_id,
+ u64* out_recording_layer_id) {
+ R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
+
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
+ // This calls nn::vi::CreateRecordingLayer() which creates another layer.
+ // Currently we do not support more than 1 layer per display, output 1 layer id for now.
+ // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
+ // side effects.
+ *out_recording_layer_id = 0;
+ R_RETURN(this->CreateManagedDisplayLayer(out_layer_id));
+}
+
+Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
+ // Succeed if already enabled.
+ R_SUCCEED_IF(m_buffer_sharing_enabled);
+
+ // Ensure we can access shared layers.
+ R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
+ R_UNLESS(m_applet_id != AppletId::Application, VI::ResultPermissionDenied);
+
+ // Create the shared layer.
+ u64 display_id;
+ R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
+ R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id,
+ &m_system_shared_layer_id, display_id,
+ m_blending_enabled));
+
+ // We succeeded, so set up remaining state.
+ m_buffer_sharing_enabled = true;
+ m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
+ R_SUCCEED();
+}
+
+Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
+ u64* out_system_shared_layer_id) {
+ R_TRY(this->IsSystemBufferSharingEnabled());
+
+ *out_system_shared_buffer_id = m_system_shared_buffer_id;
+ *out_system_shared_layer_id = m_system_shared_layer_id;
+
+ R_SUCCEED();
+}
+
+void DisplayLayerManager::SetWindowVisibility(bool visible) {
+ if (m_visible == visible) {
+ return;
+ }
+
+ m_visible = visible;
+
+ if (m_manager_display_service) {
+ if (m_system_shared_layer_id) {
+ m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
+ }
+
+ for (const auto layer_id : m_managed_display_layers) {
+ m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
+ }
+ }
+}
+
+bool DisplayLayerManager::GetWindowVisibility() const {
+ return m_visible;
+}
+
+Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
+ s32* out_fbshare_layer_index) {
+ R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
+ R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
+ out_was_written, out_fbshare_layer_index));
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_layer_manager.h b/src/core/hle/service/am/display_layer_manager.h
new file mode 100644
index 000000000..a66509c04
--- /dev/null
+++ b/src/core/hle/service/am/display_layer_manager.h
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <set>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/am_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KProcess;
+}
+
+namespace Service::VI {
+class IApplicationDisplayService;
+class IManagerDisplayService;
+} // namespace Service::VI
+
+namespace Service::AM {
+
+class DisplayLayerManager {
+public:
+ explicit DisplayLayerManager();
+ ~DisplayLayerManager();
+
+ void Initialize(Core::System& system, Kernel::KProcess* process, AppletId applet_id,
+ LibraryAppletMode mode);
+ void Finalize();
+
+ Result CreateManagedDisplayLayer(u64* out_layer_id);
+ Result CreateManagedDisplaySeparableLayer(u64* out_layer_id, u64* out_recording_layer_id);
+
+ Result IsSystemBufferSharingEnabled();
+ Result GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
+ u64* out_system_shared_layer_id);
+
+ void SetWindowVisibility(bool visible);
+ bool GetWindowVisibility() const;
+
+ Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
+
+private:
+ Kernel::KProcess* m_process{};
+ std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
+ std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
+ std::set<u64> m_managed_display_layers{};
+ std::set<u64> m_managed_display_recording_layers{};
+ u64 m_system_shared_buffer_id{};
+ u64 m_system_shared_layer_id{};
+ AppletId m_applet_id{};
+ bool m_buffer_sharing_enabled{};
+ bool m_blending_enabled{};
+ bool m_visible{true};
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/frontend/applet_cabinet.cpp b/src/core/hle/service/am/frontend/applet_cabinet.cpp
index 0862c81b6..4cbc80d63 100644
--- a/src/core/hle/service/am/frontend/applet_cabinet.cpp
+++ b/src/core/hle/service/am/frontend/applet_cabinet.cpp
@@ -9,7 +9,7 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_cabinet.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfc/common/device.h"
#include "hid_core/hid_core.h"
diff --git a/src/core/hle/service/am/frontend/applet_controller.cpp b/src/core/hle/service/am/frontend/applet_controller.cpp
index bd3e49fc4..66f52686d 100644
--- a/src/core/hle/service/am/frontend/applet_controller.cpp
+++ b/src/core/hle/service/am/frontend/applet_controller.cpp
@@ -12,7 +12,7 @@
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_controller.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
diff --git a/src/core/hle/service/am/frontend/applet_error.cpp b/src/core/hle/service/am/frontend/applet_error.cpp
index b97a5f3ea..34ec7013b 100644
--- a/src/core/hle/service/am/frontend/applet_error.cpp
+++ b/src/core/hle/service/am/frontend/applet_error.cpp
@@ -10,7 +10,7 @@
#include "core/frontend/applets/error.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_error.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "core/reporter.h"
namespace Service::AM::Frontend {
diff --git a/src/core/hle/service/am/frontend/applet_general.cpp b/src/core/hle/service/am/frontend/applet_general.cpp
index 3c091a602..d2cabb7b5 100644
--- a/src/core/hle/service/am/frontend/applet_general.cpp
+++ b/src/core/hle/service/am/frontend/applet_general.cpp
@@ -10,7 +10,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/frontend/applet_general.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "core/reporter.h"
namespace Service::AM::Frontend {
diff --git a/src/core/hle/service/am/frontend/applet_mii_edit.cpp b/src/core/hle/service/am/frontend/applet_mii_edit.cpp
index e3d19fb3d..0180ab761 100644
--- a/src/core/hle/service/am/frontend/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/frontend/applet_mii_edit.cpp
@@ -7,7 +7,7 @@
#include "core/frontend/applets/mii_edit.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_mii_edit.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/sm/sm.h"
diff --git a/src/core/hle/service/am/frontend/applet_profile_select.cpp b/src/core/hle/service/am/frontend/applet_profile_select.cpp
index efb4053b8..89b5a1eab 100644
--- a/src/core/hle/service/am/frontend/applet_profile_select.cpp
+++ b/src/core/hle/service/am/frontend/applet_profile_select.cpp
@@ -10,7 +10,7 @@
#include "core/hle/service/acc/errors.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_profile_select.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
namespace Service::AM::Frontend {
diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
index fbf75d379..d1bc03018 100644
--- a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
@@ -6,7 +6,7 @@
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_software_keyboard.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
namespace Service::AM::Frontend {
@@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() {
case LibraryAppletMode::AllForeground:
InitializeForeground();
break;
- case LibraryAppletMode::Background:
- case LibraryAppletMode::BackgroundIndirectDisplay:
- InitializeBackground(applet_mode);
+ case LibraryAppletMode::PartialForeground:
+ case LibraryAppletMode::PartialForegroundIndirectDisplay:
+ InitializePartialForeground(applet_mode);
break;
default:
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
InitializeFrontendNormalKeyboard();
}
-void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
+void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) {
LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
is_background = true;
@@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
swkbd_inline_initialize_arg.size());
if (swkbd_initialize_arg.library_applet_mode_flag) {
- ASSERT(library_applet_mode == LibraryAppletMode::Background);
+ ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground);
} else {
- ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay);
+ ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay);
}
}
diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h
index f464b7e15..2a7d01b96 100644
--- a/src/core/hle/service/am/frontend/applet_software_keyboard.h
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h
@@ -62,7 +62,7 @@ private:
void InitializeForeground();
/// Initializes the inline software keyboard.
- void InitializeBackground(LibraryAppletMode library_applet_mode);
+ void InitializePartialForeground(LibraryAppletMode library_applet_mode);
/// Processes the text check sent by the application.
void ProcessTextCheck();
diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp
index 6ee4caf34..835c20c4e 100644
--- a/src/core/hle/service/am/frontend/applet_web_browser.cpp
+++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp
@@ -20,9 +20,9 @@
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_web_browser.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ns/iplatform_service_manager.h"
+#include "core/hle/service/ns/platform_service_manager.h"
#include "core/loader/loader.h"
namespace Service::AM::Frontend {
diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp
index db2b04575..e662c6cd6 100644
--- a/src/core/hle/service/am/frontend/applets.cpp
+++ b/src/core/hle/service/am/frontend/applets.cpp
@@ -15,11 +15,8 @@
#include "core/frontend/applets/web_browser.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/applet_message_queue.h"
-#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/frontend/applet_cabinet.h"
#include "core/hle/service/am/frontend/applet_controller.h"
#include "core/hle/service/am/frontend/applet_error.h"
@@ -29,7 +26,7 @@
#include "core/hle/service/am/frontend/applet_software_keyboard.h"
#include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h"
-#include "core/hle/service/am/storage.h"
+#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend {
diff --git a/src/core/hle/service/am/global_state_controller.cpp b/src/core/hle/service/am/global_state_controller.cpp
deleted file mode 100644
index ed0eb7108..000000000
--- a/src/core/hle/service/am/global_state_controller.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/global_state_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IGlobalStateController::IGlobalStateController(Core::System& system_)
- : ServiceFramework{system_, "IGlobalStateController"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestToEnterSleep"},
- {1, nullptr, "EnterSleep"},
- {2, nullptr, "StartSleepSequence"},
- {3, nullptr, "StartShutdownSequence"},
- {4, nullptr, "StartRebootSequence"},
- {9, nullptr, "IsAutoPowerDownRequested"},
- {10, nullptr, "LoadAndApplyIdlePolicySettings"},
- {11, nullptr, "NotifyCecSettingsChanged"},
- {12, nullptr, "SetDefaultHomeButtonLongPressTime"},
- {13, nullptr, "UpdateDefaultDisplayResolution"},
- {14, nullptr, "ShouldSleepOnBoot"},
- {15, nullptr, "GetHdcpAuthenticationFailedEvent"},
- {30, nullptr, "OpenCradleFirmwareUpdater"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IGlobalStateController::~IGlobalStateController() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/global_state_controller.h b/src/core/hle/service/am/global_state_controller.h
deleted file mode 100644
index 7125464a1..000000000
--- a/src/core/hle/service/am/global_state_controller.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
-public:
- explicit IGlobalStateController(Core::System& system_);
- ~IGlobalStateController() override;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/home_menu_functions.cpp b/src/core/hle/service/am/home_menu_functions.cpp
deleted file mode 100644
index 640e9fbb7..000000000
--- a/src/core/hle/service/am/home_menu_functions.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/home_menu_functions.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
- : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system,
- "IHomeMenuFunctions"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
- {11, nullptr, "LockForeground"},
- {12, nullptr, "UnlockForeground"},
- {20, nullptr, "PopFromGeneralChannel"},
- {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"},
- {30, nullptr, "GetHomeButtonWriterLockAccessor"},
- {31, nullptr, "GetWriterLockAccessorEx"},
- {40, nullptr, "IsSleepEnabled"},
- {41, nullptr, "IsRebootEnabled"},
- {50, nullptr, "LaunchSystemApplet"},
- {51, nullptr, "LaunchStarter"},
- {100, nullptr, "PopRequestLaunchApplicationForDebug"},
- {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"},
- {200, nullptr, "LaunchDevMenu"},
- {1000, nullptr, "SetLastApplicationExitReason"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-
- pop_from_general_channel_event =
- service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent");
-}
-
-IHomeMenuFunctions::~IHomeMenuFunctions() {
- service_context.CloseEvent(pop_from_general_channel_event);
-}
-
-void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent());
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/home_menu_functions.h b/src/core/hle/service/am/home_menu_functions.h
deleted file mode 100644
index e082d5d73..000000000
--- a/src/core/hle/service/am/home_menu_functions.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
-public:
- explicit IHomeMenuFunctions(Core::System& system_);
- ~IHomeMenuFunctions() override;
-
-private:
- void RequestToGetForeground(HLERequestContext& ctx);
- void GetPopFromGeneralChannelEvent(HLERequestContext& ctx);
-
- KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* pop_from_general_channel_event;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
deleted file mode 100644
index 603515284..000000000
--- a/src/core/hle/service/am/idle.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/idle.h"
-
-namespace Service::AM {
-
-IdleSys::IdleSys(Core::System& system_) : ServiceFramework{system_, "idle:sys"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetAutoPowerDownEvent"},
- {1, nullptr, "IsAutoPowerDownRequested"},
- {2, nullptr, "Unknown2"},
- {3, nullptr, "SetHandlingContext"},
- {4, nullptr, "LoadAndApplySettings"},
- {5, nullptr, "ReportUserIsActive"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IdleSys::~IdleSys() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h
deleted file mode 100644
index 15b31f67e..000000000
--- a/src/core/hle/service/am/idle.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::AM {
-
-class IdleSys final : public ServiceFramework<IdleSys> {
-public:
- explicit IdleSys(Core::System& system_);
- ~IdleSys() override;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_accessor.cpp b/src/core/hle/service/am/library_applet_accessor.cpp
deleted file mode 100644
index 6b20814f8..000000000
--- a/src/core/hle/service/am/library_applet_accessor.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/scope_exit.h"
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/applet_data_broker.h"
-#include "core/hle/service/am/frontend/applets.h"
-#include "core/hle/service/am/library_applet_accessor.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
- std::shared_ptr<AppletDataBroker> broker_,
- std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "ILibraryAppletAccessor"}, broker{std::move(broker_)},
- applet{std::move(applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
- {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
- {10, &ILibraryAppletAccessor::Start, "Start"},
- {20, &ILibraryAppletAccessor::RequestExit, "RequestExit"},
- {25, nullptr, "Terminate"},
- {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
- {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
- {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
- {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
- {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
- {102, nullptr, "PushExtraStorage"},
- {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
- {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
- {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
- {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
- {110, nullptr, "NeedsToExitProcess"},
- {120, nullptr, "GetLibraryAppletInfo"},
- {150, nullptr, "RequestForAppletToGetForeground"},
- {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ILibraryAppletAccessor::~ILibraryAppletAccessor() = default;
-
-void ILibraryAppletAccessor::GetAppletStateChangedEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(broker->GetStateChangedEvent().GetHandle());
-}
-
-void ILibraryAppletAccessor::IsCompleted(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- std::scoped_lock lk{applet->lock};
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(broker->IsCompleted());
-}
-
-void ILibraryAppletAccessor::GetResult(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(applet->terminate_result);
-}
-
-void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletAccessor::Start(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- applet->process->Run();
- FrontendExecute();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletAccessor::RequestExit(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- ASSERT(applet != nullptr);
- applet->message_queue.RequestExit();
- FrontendRequestExit();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::RequestParser rp{ctx};
- broker->GetInData().Push(rp.PopIpcInterface<IStorage>().lock());
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletAccessor::PopOutData(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- std::shared_ptr<IStorage> data;
- const auto res = broker->GetOutData().Pop(&data);
-
- if (res.IsSuccess()) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(res);
- rb.PushIpcInterface(std::move(data));
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
- }
-}
-
-void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::RequestParser rp{ctx};
- broker->GetInteractiveInData().Push(rp.PopIpcInterface<IStorage>().lock());
- FrontendExecuteInteractive();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletAccessor::PopInteractiveOutData(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- std::shared_ptr<IStorage> data;
- const auto res = broker->GetInteractiveOutData().Pop(&data);
-
- if (res.IsSuccess()) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(res);
- rb.PushIpcInterface(std::move(data));
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
- }
-}
-
-void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(broker->GetOutData().GetEvent());
-}
-
-void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(broker->GetInteractiveOutData().GetEvent());
-}
-
-void ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
- // actually used anywhere
- constexpr u64 handle = 0xdeadbeef;
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(handle);
-}
-
-void ILibraryAppletAccessor::FrontendExecute() {
- if (applet->frontend) {
- applet->frontend->Initialize();
- applet->frontend->Execute();
- }
-}
-
-void ILibraryAppletAccessor::FrontendExecuteInteractive() {
- if (applet->frontend) {
- applet->frontend->ExecuteInteractive();
- applet->frontend->Execute();
- }
-}
-
-void ILibraryAppletAccessor::FrontendRequestExit() {
- if (applet->frontend) {
- applet->frontend->RequestExit();
- }
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_accessor.h b/src/core/hle/service/am/library_applet_accessor.h
deleted file mode 100644
index 8be29e003..000000000
--- a/src/core/hle/service/am/library_applet_accessor.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class AppletDataBroker;
-struct Applet;
-
-class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
-public:
- explicit ILibraryAppletAccessor(Core::System& system_,
- std::shared_ptr<AppletDataBroker> broker_,
- std::shared_ptr<Applet> applet_);
- ~ILibraryAppletAccessor();
-
-protected:
- void GetAppletStateChangedEvent(HLERequestContext& ctx);
- void IsCompleted(HLERequestContext& ctx);
- void GetResult(HLERequestContext& ctx);
- void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx);
- void Start(HLERequestContext& ctx);
- void RequestExit(HLERequestContext& ctx);
- void PushInData(HLERequestContext& ctx);
- void PopOutData(HLERequestContext& ctx);
- void PushInteractiveInData(HLERequestContext& ctx);
- void PopInteractiveOutData(HLERequestContext& ctx);
- void GetPopOutDataEvent(HLERequestContext& ctx);
- void GetPopInteractiveOutDataEvent(HLERequestContext& ctx);
- void GetIndirectLayerConsumerHandle(HLERequestContext& ctx);
-
- void FrontendExecute();
- void FrontendExecuteInteractive();
- void FrontendRequestExit();
-
- const std::shared_ptr<AppletDataBroker> broker;
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp
deleted file mode 100644
index 47bab7528..000000000
--- a/src/core/hle/service/am/library_applet_creator.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/kernel/k_transfer_memory.h"
-#include "core/hle/service/am/applet_data_broker.h"
-#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/frontend/applets.h"
-#include "core/hle/service/am/library_applet_accessor.h"
-#include "core/hle/service/am/library_applet_creator.h"
-#include "core/hle/service/am/library_applet_storage.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace Service::AM {
-
-namespace {
-
-AppletProgramId AppletIdToProgramId(AppletId applet_id) {
- switch (applet_id) {
- case AppletId::OverlayDisplay:
- return AppletProgramId::OverlayDisplay;
- case AppletId::QLaunch:
- return AppletProgramId::QLaunch;
- case AppletId::Starter:
- return AppletProgramId::Starter;
- case AppletId::Auth:
- return AppletProgramId::Auth;
- case AppletId::Cabinet:
- return AppletProgramId::Cabinet;
- case AppletId::Controller:
- return AppletProgramId::Controller;
- case AppletId::DataErase:
- return AppletProgramId::DataErase;
- case AppletId::Error:
- return AppletProgramId::Error;
- case AppletId::NetConnect:
- return AppletProgramId::NetConnect;
- case AppletId::ProfileSelect:
- return AppletProgramId::ProfileSelect;
- case AppletId::SoftwareKeyboard:
- return AppletProgramId::SoftwareKeyboard;
- case AppletId::MiiEdit:
- return AppletProgramId::MiiEdit;
- case AppletId::Web:
- return AppletProgramId::Web;
- case AppletId::Shop:
- return AppletProgramId::Shop;
- case AppletId::PhotoViewer:
- return AppletProgramId::PhotoViewer;
- case AppletId::Settings:
- return AppletProgramId::Settings;
- case AppletId::OfflineWeb:
- return AppletProgramId::OfflineWeb;
- case AppletId::LoginShare:
- return AppletProgramId::LoginShare;
- case AppletId::WebAuth:
- return AppletProgramId::WebAuth;
- case AppletId::MyPage:
- return AppletProgramId::MyPage;
- default:
- return static_cast<AppletProgramId>(0);
- }
-}
-
-[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(
- Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
- LibraryAppletMode mode) {
- const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
- if (program_id == 0) {
- // Unknown applet
- return {};
- }
-
- auto process = std::make_unique<Process>(system);
- if (!process->Initialize(program_id)) {
- // Couldn't initialize the guest process
- return {};
- }
-
- const auto applet = std::make_shared<Applet>(system, std::move(process));
- applet->program_id = program_id;
- applet->applet_id = applet_id;
- applet->type = AppletType::LibraryApplet;
- applet->library_applet_mode = mode;
-
- // Set focus state
- switch (mode) {
- case LibraryAppletMode::AllForeground:
- case LibraryAppletMode::NoUI:
- applet->focus_state = FocusState::InFocus;
- applet->hid_registration.EnableAppletToGetInput(true);
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
- break;
- case LibraryAppletMode::AllForegroundInitiallyHidden:
- applet->system_buffer_manager.SetWindowVisibility(false);
- applet->focus_state = FocusState::NotInFocus;
- applet->hid_registration.EnableAppletToGetInput(false);
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
- break;
- case LibraryAppletMode::Background:
- case LibraryAppletMode::BackgroundIndirectDisplay:
- default:
- applet->focus_state = FocusState::Background;
- applet->hid_registration.EnableAppletToGetInput(true);
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
- break;
- }
-
- auto broker = std::make_shared<AppletDataBroker>(system);
- applet->caller_applet = caller_applet;
- applet->caller_applet_broker = broker;
-
- system.GetAppletManager().InsertApplet(applet);
-
- return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
-}
-
-[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(
- Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
- LibraryAppletMode mode) {
- const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
-
- auto process = std::make_unique<Process>(system);
- auto applet = std::make_shared<Applet>(system, std::move(process));
- applet->program_id = program_id;
- applet->applet_id = applet_id;
- applet->type = AppletType::LibraryApplet;
- applet->library_applet_mode = mode;
-
- auto storage = std::make_shared<AppletDataBroker>(system);
- applet->caller_applet = caller_applet;
- applet->caller_applet_broker = storage;
- applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
-
- return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
-}
-
-} // namespace
-
-ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} {
- static const FunctionInfo functions[] = {
- {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
- {1, nullptr, "TerminateAllLibraryApplets"},
- {2, nullptr, "AreAnyLibraryAppletsLeft"},
- {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
- {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
- {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"},
- };
- RegisterHandlers(functions);
-}
-
-ILibraryAppletCreator::~ILibraryAppletCreator() = default;
-
-void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto applet_id = rp.PopRaw<AppletId>();
- const auto applet_mode = rp.PopRaw<LibraryAppletMode>();
-
- LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
- applet_mode);
-
- auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
- if (!library_applet) {
- LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- // Applet is created, can now be launched.
- applet->library_applet_launchable_event.Signal();
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletAccessor>(library_applet);
-}
-
-void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const s64 size{rp.Pop<s64>()};
-
- LOG_DEBUG(Service_AM, "called, size={}", size);
-
- if (size <= 0) {
- LOG_ERROR(Service_AM, "size is less than or equal to 0");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- std::vector<u8> data(size);
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IStorage>(system, AM::CreateStorage(std::move(data)));
-}
-
-void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- struct Parameters {
- bool is_writable;
- s64 size;
- };
-
- const auto params{rp.PopRaw<Parameters>()};
- const auto handle{ctx.GetCopyHandle(0)};
-
- LOG_DEBUG(Service_AM, "called, is_writable={}, size={}, handle={:08X}", params.is_writable,
- params.size, handle);
-
- if (params.size <= 0) {
- LOG_ERROR(Service_AM, "size is less than or equal to 0");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
-
- if (transfer_mem.IsNull()) {
- LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IStorage>(
- system, AM::CreateTransferMemoryStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(),
- params.is_writable, params.size));
-}
-
-void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const s64 size{rp.Pop<s64>()};
- const auto handle{ctx.GetCopyHandle(0)};
-
- LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle);
-
- if (size <= 0) {
- LOG_ERROR(Service_AM, "size is less than or equal to 0");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
-
- if (transfer_mem.IsNull()) {
- LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IStorage>(
- system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size));
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_creator.h b/src/core/hle/service/am/library_applet_creator.h
deleted file mode 100644
index 551f287bd..000000000
--- a/src/core/hle/service/am/library_applet_creator.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
-public:
- explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~ILibraryAppletCreator() override;
-
-private:
- void CreateLibraryApplet(HLERequestContext& ctx);
- void CreateStorage(HLERequestContext& ctx);
- void CreateTransferMemoryStorage(HLERequestContext& ctx);
- void CreateHandleStorage(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_proxy.cpp b/src/core/hle/service/am/library_applet_proxy.cpp
deleted file mode 100644
index d6108fba3..000000000
--- a/src/core/hle/service/am/library_applet_proxy.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet_common_functions.h"
-#include "core/hle/service/am/audio_controller.h"
-#include "core/hle/service/am/common_state_getter.h"
-#include "core/hle/service/am/debug_functions.h"
-#include "core/hle/service/am/display_controller.h"
-#include "core/hle/service/am/global_state_controller.h"
-#include "core/hle/service/am/home_menu_functions.h"
-#include "core/hle/service/am/library_applet_creator.h"
-#include "core/hle/service/am/library_applet_proxy.h"
-#include "core/hle/service/am/library_applet_self_accessor.h"
-#include "core/hle/service/am/process_winding_controller.h"
-#include "core/hle/service/am/self_controller.h"
-#include "core/hle/service/am/window_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
- std::shared_ptr<Applet> applet_, Core::System& system_)
- : ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
- applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
- {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
- {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"},
- {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"},
- {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"},
- {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
- {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
- {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
- {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
- {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
- {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
- {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ILibraryAppletProxy::~ILibraryAppletProxy() = default;
-
-void ILibraryAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ICommonStateGetter>(system, applet);
-}
-
-void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
-}
-
-void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IWindowController>(system, applet);
-}
-
-void ILibraryAppletProxy::GetAudioController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioController>(system);
-}
-
-void ILibraryAppletProxy::GetDisplayController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDisplayController>(system, applet);
-}
-
-void ILibraryAppletProxy::GetProcessWindingController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IProcessWindingController>(system, applet);
-}
-
-void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
-}
-
-void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system, applet);
-}
-
-void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
-}
-
-void ILibraryAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHomeMenuFunctions>(system);
-}
-
-void ILibraryAppletProxy::GetGlobalStateController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IGlobalStateController>(system);
-}
-
-void ILibraryAppletProxy::GetDebugFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDebugFunctions>(system);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_proxy.h b/src/core/hle/service/am/library_applet_proxy.h
deleted file mode 100644
index 8f7a25897..000000000
--- a/src/core/hle/service/am/library_applet_proxy.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
-public:
- explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
- std::shared_ptr<Applet> applet_, Core::System& system_);
- ~ILibraryAppletProxy();
-
-private:
- void GetCommonStateGetter(HLERequestContext& ctx);
- void GetSelfController(HLERequestContext& ctx);
- void GetWindowController(HLERequestContext& ctx);
- void GetAudioController(HLERequestContext& ctx);
- void GetDisplayController(HLERequestContext& ctx);
- void GetProcessWindingController(HLERequestContext& ctx);
- void GetLibraryAppletCreator(HLERequestContext& ctx);
- void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx);
- void GetAppletCommonFunctions(HLERequestContext& ctx);
- void GetHomeMenuFunctions(HLERequestContext& ctx);
- void GetGlobalStateController(HLERequestContext& ctx);
- void GetDebugFunctions(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nvnflinger;
- std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_self_accessor.cpp b/src/core/hle/service/am/library_applet_self_accessor.cpp
deleted file mode 100644
index b560f580b..000000000
--- a/src/core/hle/service/am/library_applet_self_accessor.cpp
+++ /dev/null
@@ -1,338 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/scope_exit.h"
-#include "core/core_timing.h"
-#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/patch_manager.h"
-#include "core/file_sys/registered_cache.h"
-#include "core/hle/service/acc/profile_manager.h"
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/applet_data_broker.h"
-#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/frontend/applet_cabinet.h"
-#include "core/hle/service/am/frontend/applet_controller.h"
-#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
-#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
-#include "core/hle/service/am/frontend/applets.h"
-#include "core/hle/service/am/library_applet_self_accessor.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ns/ns.h"
-#include "core/hle/service/sm/sm.h"
-#include "hid_core/hid_types.h"
-
-namespace Service::AM {
-
-namespace {
-
-AppletIdentityInfo GetCallerIdentity(std::shared_ptr<Applet> applet) {
- if (const auto caller_applet = applet->caller_applet.lock(); caller_applet) {
- // TODO: is this actually the application ID?
- return {
- .applet_id = caller_applet->applet_id,
- .application_id = caller_applet->program_id,
- };
- } else {
- return {
- .applet_id = AppletId::QLaunch,
- .application_id = 0x0100000000001000ull,
- };
- }
-}
-
-} // namespace
-
-ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
- std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, applet{std::move(applet_)},
- broker{applet->caller_applet_broker} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"},
- {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"},
- {2, &ILibraryAppletSelfAccessor::PopInteractiveInData, "PopInteractiveInData"},
- {3, &ILibraryAppletSelfAccessor::PushInteractiveOutData, "PushInteractiveOutData"},
- {5, &ILibraryAppletSelfAccessor::GetPopInDataEvent, "GetPopInDataEvent"},
- {6, &ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent, "GetPopInteractiveInDataEvent"},
- {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
- {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
- {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
- {13, &ILibraryAppletSelfAccessor::CanUseApplicationCore, "CanUseApplicationCore"},
- {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
- {15, nullptr, "GetMainAppletApplicationControlProperty"},
- {16, nullptr, "GetMainAppletStorageId"},
- {17, nullptr, "GetCallerAppletIdentityInfoStack"},
- {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"},
- {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"},
- {20, nullptr, "PopExtraStorage"},
- {25, nullptr, "GetPopExtraStorageEvent"},
- {30, nullptr, "UnpopInData"},
- {31, nullptr, "UnpopExtraStorage"},
- {40, nullptr, "GetIndirectLayerProducerHandle"},
- {50, nullptr, "ReportVisibleError"},
- {51, nullptr, "ReportVisibleErrorWithErrorContext"},
- {60, &ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage, "GetMainAppletApplicationDesiredLanguage"},
- {70, &ILibraryAppletSelfAccessor::GetCurrentApplicationId, "GetCurrentApplicationId"},
- {80, nullptr, "RequestExitToSelf"},
- {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"},
- {100, nullptr, "CreateGameMovieTrimmer"},
- {101, nullptr, "ReserveResourceForMovieOperation"},
- {102, nullptr, "UnreserveResourceForMovieOperation"},
- {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"},
- {120, nullptr, "GetLaunchStorageInfoForDebug"},
- {130, nullptr, "GetGpuErrorDetectedSystemEvent"},
- {140, nullptr, "SetApplicationMemoryReservation"},
- {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"},
- {160, &ILibraryAppletSelfAccessor::Cmd160, "Cmd160"},
- };
- // clang-format on
- RegisterHandlers(functions);
-}
-
-ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
-
-void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- std::shared_ptr<IStorage> data;
- const auto res = broker->GetInData().Pop(&data);
-
- if (res.IsSuccess()) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(res);
- rb.PushIpcInterface(std::move(data));
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
- }
-}
-
-void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- IPC::RequestParser rp{ctx};
- broker->GetOutData().Push(rp.PopIpcInterface<IStorage>().lock());
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletSelfAccessor::PopInteractiveInData(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- std::shared_ptr<IStorage> data;
- const auto res = broker->GetInteractiveInData().Pop(&data);
-
- if (res.IsSuccess()) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(res);
- rb.PushIpcInterface(std::move(data));
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
- }
-}
-
-void ILibraryAppletSelfAccessor::PushInteractiveOutData(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- IPC::RequestParser rp{ctx};
- broker->GetInteractiveOutData().Push(rp.PopIpcInterface<IStorage>().lock());
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletSelfAccessor::GetPopInDataEvent(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(broker->GetInData().GetEvent());
-}
-
-void ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(broker->GetInteractiveInData().GetEvent());
-}
-
-void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- system.GetAppletManager().TerminateAndRemoveApplet(applet->aruid);
- broker->SignalCompletion();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
- struct LibraryAppletInfo {
- AppletId applet_id;
- LibraryAppletMode library_applet_mode;
- };
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- const LibraryAppletInfo applet_info{
- .applet_id = applet->applet_id,
- .library_applet_mode = applet->library_applet_mode,
- };
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushRaw(applet_info);
-}
-
-void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- const AppletIdentityInfo applet_info{
- .applet_id = AppletId::QLaunch,
- .application_id = 0x0100000000001000ull,
- };
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.PushRaw(applet_info);
-}
-
-void ILibraryAppletSelfAccessor::CanUseApplicationCore(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- // TODO: This appears to read the NPDM from state and check the core mask of the applet.
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
-}
-
-void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.PushRaw(GetCallerIdentity(applet));
-}
-
-void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
-}
-
-void ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx) {
- // FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage
- auto identity = GetCallerIdentity(applet);
-
- // TODO(bunnei): This should be configurable
- LOG_DEBUG(Service_AM, "called");
-
- // Get supported languages from NACP, if possible
- // Default to 0 (all languages supported)
- u32 supported_languages = 0;
-
- const auto res = [this, identity] {
- const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(),
- system.GetContentProvider()};
- auto metadata = pm.GetControlMetadata();
- if (metadata.first != nullptr) {
- return metadata;
- }
-
- const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id),
- system.GetFileSystemController(),
- system.GetContentProvider()};
- return pm_update.GetControlMetadata();
- }();
-
- if (res.first != nullptr) {
- supported_languages = res.first->GetSupportedLanguages();
- }
-
- // Call IApplicationManagerInterface implementation.
- auto& service_manager = system.ServiceManager();
- auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
- auto app_man = ns_am2->GetApplicationManagerInterface();
-
- // Get desired application language
- u8 desired_language{};
- const auto res_lang =
- app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
- if (res_lang != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res_lang);
- return;
- }
-
- // Convert to settings language code.
- u64 language_code{};
- const auto res_code =
- app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
- if (res_code != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res_code);
- return;
- }
-
- LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(language_code);
-}
-
-void ILibraryAppletSelfAccessor::GetCurrentApplicationId(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- u64 application_id = 0;
- if (auto caller_applet = applet->caller_applet.lock(); caller_applet) {
- application_id = caller_applet->program_id;
- }
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(application_id);
-}
-
-void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) {
- const Service::Account::ProfileManager manager{};
- bool is_empty{true};
- s32 user_count{-1};
-
- LOG_INFO(Service_AM, "called");
-
- if (manager.GetUserCount() > 0) {
- is_empty = false;
- user_count = static_cast<s32>(manager.GetUserCount());
- ctx.WriteBuffer(manager.GetAllUsers());
- }
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u8>(is_empty);
- rb.Push(user_count);
-}
-
-void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
-}
-
-void ILibraryAppletSelfAccessor::Cmd160(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(0);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_self_accessor.h b/src/core/hle/service/am/library_applet_self_accessor.h
deleted file mode 100644
index 8717a989a..000000000
--- a/src/core/hle/service/am/library_applet_self_accessor.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <deque>
-#include <vector>
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class AppletDataBroker;
-struct Applet;
-
-class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
-public:
- explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~ILibraryAppletSelfAccessor() override;
-
-private:
- void PopInData(HLERequestContext& ctx);
- void PushOutData(HLERequestContext& ctx);
- void PopInteractiveInData(HLERequestContext& ctx);
- void PushInteractiveOutData(HLERequestContext& ctx);
- void GetPopInDataEvent(HLERequestContext& ctx);
- void GetPopInteractiveInDataEvent(HLERequestContext& ctx);
- void GetLibraryAppletInfo(HLERequestContext& ctx);
- void GetMainAppletIdentityInfo(HLERequestContext& ctx);
- void CanUseApplicationCore(HLERequestContext& ctx);
- void ExitProcessAndReturn(HLERequestContext& ctx);
- void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
- void GetDesirableKeyboardLayout(HLERequestContext& ctx);
- void GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx);
- void GetCurrentApplicationId(HLERequestContext& ctx);
- void GetMainAppletAvailableUsers(HLERequestContext& ctx);
- void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx);
- void Cmd160(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
- const std::shared_ptr<AppletDataBroker> broker;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp
index 46e6c0111..0412c215d 100644
--- a/src/core/hle/service/am/library_applet_storage.cpp
+++ b/src/core/hle/service/am/library_applet_storage.cpp
@@ -70,7 +70,7 @@ public:
Result Read(s64 offset, void* buffer, size_t size) override {
R_TRY(ValidateOffset(offset, size, m_size));
- m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size);
+ m_memory.ReadBlock(m_trmem->GetSourceAddress() + offset, buffer, size);
R_SUCCEED();
}
@@ -79,7 +79,7 @@ public:
R_UNLESS(m_is_writable, ResultUnknown);
R_TRY(ValidateOffset(offset, size, m_size));
- m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size);
+ m_memory.WriteBlock(m_trmem->GetSourceAddress() + offset, buffer, size);
R_SUCCEED();
}
diff --git a/src/core/hle/service/am/lock_accessor.cpp b/src/core/hle/service/am/lock_accessor.cpp
deleted file mode 100644
index d0bd8d95e..000000000
--- a/src/core/hle/service/am/lock_accessor.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/lock_accessor.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-ILockAccessor::ILockAccessor(Core::System& system_)
- : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {1, &ILockAccessor::TryLock, "TryLock"},
- {2, &ILockAccessor::Unlock, "Unlock"},
- {3, &ILockAccessor::GetEvent, "GetEvent"},
- {4,&ILockAccessor::IsLocked, "IsLocked"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-
- lock_event = service_context.CreateEvent("ILockAccessor::LockEvent");
-}
-
-ILockAccessor::~ILockAccessor() {
- service_context.CloseEvent(lock_event);
-};
-
-void ILockAccessor::TryLock(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto return_handle = rp.Pop<bool>();
-
- LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle);
-
- // TODO: When return_handle is true this function should return the lock handle
-
- is_locked = true;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(is_locked);
-}
-
-void ILockAccessor::Unlock(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- is_locked = false;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ILockAccessor::GetEvent(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- lock_event->Signal();
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(lock_event->GetReadableEvent());
-}
-
-void ILockAccessor::IsLocked(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- rb.Push<u8>(is_locked);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/lock_accessor.h b/src/core/hle/service/am/lock_accessor.h
deleted file mode 100644
index 626f60e07..000000000
--- a/src/core/hle/service/am/lock_accessor.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class ILockAccessor final : public ServiceFramework<ILockAccessor> {
-public:
- explicit ILockAccessor(Core::System& system_);
- ~ILockAccessor() override;
-
-private:
- void TryLock(HLERequestContext& ctx);
- void Unlock(HLERequestContext& ctx);
- void GetEvent(HLERequestContext& ctx);
- void IsLocked(HLERequestContext& ctx);
-
- bool is_locked{};
-
- Kernel::KEvent* lock_event;
- KernelHelpers::ServiceContext service_context;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp
deleted file mode 100644
index 61eb8641a..000000000
--- a/src/core/hle/service/am/managed_layer_holder.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/managed_layer_holder.h"
-#include "core/hle/service/nvnflinger/nvnflinger.h"
-
-namespace Service::AM {
-
-ManagedLayerHolder::ManagedLayerHolder() = default;
-ManagedLayerHolder::~ManagedLayerHolder() {
- if (!m_nvnflinger) {
- return;
- }
-
- for (const auto& layer : m_managed_display_layers) {
- m_nvnflinger->DestroyLayer(layer);
- }
-
- for (const auto& layer : m_managed_display_recording_layers) {
- m_nvnflinger->DestroyLayer(layer);
- }
-
- m_nvnflinger = nullptr;
-}
-
-void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) {
- m_nvnflinger = nvnflinger;
-}
-
-void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) {
- // TODO(Subv): Find out how AM determines the display to use, for now just
- // create the layer in the Default display.
- const auto display_id = m_nvnflinger->OpenDisplay("Default");
- const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
-
- m_managed_display_layers.emplace(*layer_id);
-
- *out_layer = *layer_id;
-}
-
-void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer,
- u64* out_recording_layer) {
- // TODO(Subv): Find out how AM determines the display to use, for now just
- // create the layer in the Default display.
- // This calls nn::vi::CreateRecordingLayer() which creates another layer.
- // Currently we do not support more than 1 layer per display, output 1 layer id for now.
- // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
- // side effects.
- // TODO: Support multiple layers
- const auto display_id = m_nvnflinger->OpenDisplay("Default");
- const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
-
- m_managed_display_layers.emplace(*layer_id);
-
- *out_layer = *layer_id;
- *out_recording_layer = 0;
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h
deleted file mode 100644
index f7fe03f24..000000000
--- a/src/core/hle/service/am/managed_layer_holder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <set>
-
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-
-namespace Service::Nvnflinger {
-class Nvnflinger;
-}
-
-namespace Service::AM {
-
-class ManagedLayerHolder {
-public:
- ManagedLayerHolder();
- ~ManagedLayerHolder();
-
- void Initialize(Nvnflinger::Nvnflinger* nvnflinger);
- void CreateManagedDisplayLayer(u64* out_layer);
- void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
-
-private:
- Nvnflinger::Nvnflinger* m_nvnflinger{};
- std::set<u64> m_managed_display_layers{};
- std::set<u64> m_managed_display_recording_layers{};
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
deleted file mode 100644
index 66824e495..000000000
--- a/src/core/hle/service/am/omm.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/omm.h"
-
-namespace Service::AM {
-
-OMM::OMM(Core::System& system_) : ServiceFramework{system_, "omm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetOperationMode"},
- {1, nullptr, "GetOperationModeChangeEvent"},
- {2, nullptr, "EnableAudioVisual"},
- {3, nullptr, "DisableAudioVisual"},
- {4, nullptr, "EnterSleepAndWait"},
- {5, nullptr, "GetCradleStatus"},
- {6, nullptr, "FadeInDisplay"},
- {7, nullptr, "FadeOutDisplay"},
- {8, nullptr, "GetCradleFwVersion"},
- {9, nullptr, "NotifyCecSettingsChanged"},
- {10, nullptr, "SetOperationModePolicy"},
- {11, nullptr, "GetDefaultDisplayResolution"},
- {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
- {13, nullptr, "UpdateDefaultDisplayResolution"},
- {14, nullptr, "ShouldSleepOnBoot"},
- {15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
- {16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
- {17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
- {18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
- {19, nullptr, "GetHdcpAuthenticationFailedEvent"},
- {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
- {21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
- {22, nullptr, "GetHdcpStateChangeEvent"},
- {23, nullptr, "GetHdcpState"},
- {24, nullptr, "ShowCardUpdateProcessing"},
- {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
- {26, nullptr, "GetOperationModeSystemInfo"},
- {27, nullptr, "GetAppletFullAwakingSystemEvent"},
- {28, nullptr, "CreateCradleFirmwareUpdater"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-OMM::~OMM() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h
deleted file mode 100644
index 73d0c82d5..000000000
--- a/src/core/hle/service/am/omm.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::AM {
-
-class OMM final : public ServiceFramework<OMM> {
-public:
- explicit OMM(Core::System& system_);
- ~OMM() override;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp
index 16b685f86..388d2045c 100644
--- a/src/core/hle/service/am/process.cpp
+++ b/src/core/hle/service/am/process.cpp
@@ -3,6 +3,7 @@
#include "common/scope_exit.h"
+#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/kernel/k_process.h"
@@ -20,7 +21,7 @@ Process::~Process() {
this->Finalize();
}
-bool Process::Initialize(u64 program_id) {
+bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
// First, ensure we are not holding another process.
this->Finalize();
@@ -29,21 +30,33 @@ bool Process::Initialize(u64 program_id) {
// Attempt to load program NCA.
const FileSys::RegisteredCache* bis_system{};
- FileSys::VirtualFile nca{};
+ FileSys::VirtualFile nca_raw{};
// Get the program NCA from built-in storage.
bis_system = fsc.GetSystemNANDContents();
if (bis_system) {
- nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
+ nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
}
// Ensure we retrieved a program NCA.
- if (!nca) {
+ if (!nca_raw) {
return false;
}
+ // Ensure we have a suitable version.
+ if (minimum_key_generation > 0) {
+ FileSys::NCA nca(nca_raw);
+ if (nca.GetStatus() == Loader::ResultStatus::Success &&
+ (nca.GetKeyGeneration() < minimum_key_generation ||
+ nca.GetKeyGeneration() > maximum_key_generation)) {
+ LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
+ nca.GetKeyGeneration());
+ return false;
+ }
+ }
+
// Get the appropriate loader to parse this NCA.
- auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0);
+ auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0);
// Ensure we have a loader which can parse the NCA.
if (!app_loader) {
@@ -55,7 +68,9 @@ bool Process::Initialize(u64 program_id) {
Kernel::KProcess::Register(m_system.Kernel(), process);
// On exit, ensure we free the additional reference to the process.
- SCOPE_EXIT({ process->Close(); });
+ SCOPE_EXIT {
+ process->Close();
+ };
// Insert process modules into memory.
const auto [load_result, load_parameters] = app_loader->Load(*process, m_system);
diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h
index 4b908ade4..4b8102fb6 100644
--- a/src/core/hle/service/am/process.h
+++ b/src/core/hle/service/am/process.h
@@ -21,7 +21,7 @@ public:
explicit Process(Core::System& system);
~Process();
- bool Initialize(u64 program_id);
+ bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
void Finalize();
bool Run();
diff --git a/src/core/hle/service/am/process_winding_controller.cpp b/src/core/hle/service/am/process_winding_controller.cpp
deleted file mode 100644
index b48b52797..000000000
--- a/src/core/hle/service/am/process_winding_controller.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/frontend/applets.h"
-#include "core/hle/service/am/library_applet_accessor.h"
-#include "core/hle/service/am/process_winding_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IProcessWindingController::IProcessWindingController(Core::System& system_,
- std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "IProcessWindingController"}, applet{std::move(applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"},
- {11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"},
- {21, nullptr, "PushContext"},
- {22, nullptr, "PopContext"},
- {23, nullptr, "CancelWindingReservation"},
- {30, nullptr, "WindAndDoReserved"},
- {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
- {41, nullptr, "ReserveToStartAndWait"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IProcessWindingController::~IProcessWindingController() = default;
-
-void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(applet->launch_reason);
-}
-
-void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
- const auto caller_applet = applet->caller_applet.lock();
- if (caller_applet == nullptr) {
- LOG_ERROR(Service_AM, "No calling applet available");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultUnknown);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet->caller_applet_broker,
- caller_applet);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/process_winding_controller.h b/src/core/hle/service/am/process_winding_controller.h
deleted file mode 100644
index 71ae4c4f5..000000000
--- a/src/core/hle/service/am/process_winding_controller.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
-public:
- explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~IProcessWindingController() override;
-
-private:
- void GetLaunchReason(HLERequestContext& ctx);
- void OpenCallingLibraryApplet(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp
deleted file mode 100644
index 0289f5cf1..000000000
--- a/src/core/hle/service/am/self_controller.cpp
+++ /dev/null
@@ -1,456 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/frontend/applets.h"
-#include "core/hle/service/am/self_controller.h"
-#include "core/hle/service/caps/caps_su.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
-#include "core/hle/service/nvnflinger/nvnflinger.h"
-#include "core/hle/service/sm/sm.h"
-#include "core/hle/service/vi/vi_results.h"
-
-namespace Service::AM {
-
-ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
- Nvnflinger::Nvnflinger& nvnflinger_)
- : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, applet{std::move(
- applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ISelfController::Exit, "Exit"},
- {1, &ISelfController::LockExit, "LockExit"},
- {2, &ISelfController::UnlockExit, "UnlockExit"},
- {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
- {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
- {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
- {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
- {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
- {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
- {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
- {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
- {15, &ISelfController::SetScreenShotAppletIdentityInfo, "SetScreenShotAppletIdentityInfo"},
- {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
- {17, nullptr, "SetControllerFirmwareUpdateSection"},
- {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
- {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
- {20, nullptr, "SetDesirableKeyboardLayout"},
- {21, nullptr, "GetScreenShotProgramId"},
- {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
- {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"},
- {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"},
- {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"},
- {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
- {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
- {46, nullptr, "SetRecordingLayerCompositionEnabled"},
- {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
- {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
- {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
- {61, nullptr, "SetMediaPlaybackState"},
- {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
- {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
- {64, nullptr, "SetInputDetectionSourceSet"},
- {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
- {66, nullptr, "GetCurrentIlluminance"},
- {67, nullptr, "IsIlluminanceAvailable"},
- {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
- {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"},
- {70, nullptr, "ReportMultimediaError"},
- {71, nullptr, "GetCurrentIlluminanceEx"},
- {72, nullptr, "SetInputDetectionPolicy"},
- {80, nullptr, "SetWirelessPriorityMode"},
- {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"},
- {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
- {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
- {110, nullptr, "SetApplicationAlbumUserData"},
- {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
- {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"},
- {1000, nullptr, "GetDebugStorageChannel"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ISelfController::~ISelfController() = default;
-
-void ISelfController::Exit(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-
- // TODO
- system.Exit();
-}
-
-void ISelfController::LockExit(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- system.SetExitLocked(true);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::UnlockExit(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- system.SetExitLocked(false);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-
- if (system.GetExitRequested()) {
- system.Exit();
- }
-}
-
-void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
-
- std::scoped_lock lk{applet->lock};
- applet->fatal_section_count++;
- LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", applet->fatal_section_count);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::LeaveFatalSection(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called.");
-
- // Entry and exit of fatal sections must be balanced.
- std::scoped_lock lk{applet->lock};
- if (applet->fatal_section_count == 0) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(AM::ResultFatalSectionCountImbalance);
- return;
- }
-
- applet->fatal_section_count--;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- applet->library_applet_launchable_event.Signal();
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->library_applet_launchable_event.GetHandle());
-}
-
-void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto permission = rp.PopEnum<ScreenshotPermission>();
- LOG_DEBUG(Service_AM, "called, permission={}", permission);
-
- std::scoped_lock lk{applet->lock};
- applet->screenshot_permission = permission;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const bool notification_enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
-
- std::scoped_lock lk{applet->lock};
- applet->operation_mode_changed_notification_enabled = notification_enabled;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const bool notification_enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
-
- std::scoped_lock lk{applet->lock};
- applet->performance_mode_changed_notification_enabled = notification_enabled;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto flags = rp.PopRaw<FocusHandlingMode>();
-
- LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
- flags.unknown0, flags.unknown1, flags.unknown2);
-
- std::scoped_lock lk{applet->lock};
- applet->focus_handling_mode = flags;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
- applet->restart_message_enabled = true;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetScreenShotAppletIdentityInfo(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
- std::scoped_lock lk{applet->lock};
- applet->screen_shot_identity = rp.PopRaw<AppletIdentityInfo>();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const bool enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
-
- std::scoped_lock lk{applet->lock};
- ASSERT(applet->type == AppletType::Application);
- applet->out_of_focus_suspension_enabled = enabled;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto orientation = rp.PopRaw<Capture::AlbumImageOrientation>();
- LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", static_cast<s32>(orientation));
-
- std::scoped_lock lk{applet->lock};
- applet->album_image_orientation = orientation;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- u64 layer_id{};
- applet->managed_layer_holder.Initialize(&nvnflinger);
- applet->managed_layer_holder.CreateManagedDisplayLayer(&layer_id);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(layer_id);
-}
-
-void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
-}
-
-void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- u64 buffer_id, layer_id;
- applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
- rb.Push<s64>(buffer_id);
- rb.Push<s64>(layer_id);
-}
-
-void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- u64 buffer_id, layer_id;
- applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
- rb.Push<s64>(buffer_id);
-}
-
-Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
- if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) {
- return ResultSuccess;
- }
-
- return VI::ResultOperationFailed;
-}
-
-void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- u64 layer_id{};
- u64 recording_layer_id{};
- applet->managed_layer_holder.Initialize(&nvnflinger);
- applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(&layer_id, &recording_layer_id);
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.Push(layer_id);
- rb.Push(recording_layer_id);
-}
-
-void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto extension = rp.PopRaw<IdleTimeDetectionExtension>();
- LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", extension);
-
- std::scoped_lock lk{applet->lock};
- applet->idle_time_detection_extension = extension;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- std::scoped_lock lk{applet->lock};
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw<IdleTimeDetectionExtension>(applet->idle_time_detection_extension);
-}
-
-void ISelfController::ReportUserIsActive(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- std::scoped_lock lk{applet->lock};
- applet->auto_sleep_disabled = rp.Pop<bool>();
-
- // On the system itself, if the previous state of is_auto_sleep_disabled
- // differed from the current value passed in, it'd signify the internal
- // window manager to update (and also increment some statistics like update counts)
- //
- // It'd also indicate this change to an idle handling context.
- //
- // However, given we're emulating this behavior, most of this can be ignored
- // and it's sufficient to simply set the member variable for querying via
- // IsAutoSleepDisabled().
-
- LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", applet->auto_sleep_disabled);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called.");
-
- std::scoped_lock lk{applet->lock};
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(applet->auto_sleep_disabled);
-}
-
-void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called.");
-
- std::scoped_lock lk{applet->lock};
- // This command returns the total number of system ticks since ISelfController creation
- // where the game was suspended. Since Yuzu doesn't implement game suspension, this command
- // can just always return 0 ticks.
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(applet->suspended_ticks);
-}
-
-void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called.");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(applet->accumulated_suspended_tick_changed_event.GetHandle());
-}
-
-void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- // This service call sets an internal flag whether a notification is shown when an image is
- // captured. Currently we do not support capturing images via the capture button, so this can be
- // stubbed for now.
- const bool enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
-
- std::scoped_lock lk{applet->lock};
- applet->album_image_taken_notification_enabled = enabled;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto report_option = rp.PopEnum<Capture::AlbumReportOption>();
-
- LOG_INFO(Service_AM, "called, report_option={}", report_option);
-
- const auto screenshot_service =
- system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>(
- "caps:su");
-
- if (screenshot_service) {
- screenshot_service->CaptureAndSaveScreenshot(report_option);
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
-
- std::scoped_lock lk{applet->lock};
- applet->record_volume_muted = enabled;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h
deleted file mode 100644
index a63bc2e74..000000000
--- a/src/core/hle/service/am/self_controller.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class ISelfController final : public ServiceFramework<ISelfController> {
-public:
- explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
- Nvnflinger::Nvnflinger& nvnflinger_);
- ~ISelfController() override;
-
-private:
- void Exit(HLERequestContext& ctx);
- void LockExit(HLERequestContext& ctx);
- void UnlockExit(HLERequestContext& ctx);
- void EnterFatalSection(HLERequestContext& ctx);
- void LeaveFatalSection(HLERequestContext& ctx);
- void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx);
- void SetScreenShotPermission(HLERequestContext& ctx);
- void SetOperationModeChangedNotification(HLERequestContext& ctx);
- void SetPerformanceModeChangedNotification(HLERequestContext& ctx);
- void SetFocusHandlingMode(HLERequestContext& ctx);
- void SetRestartMessageEnabled(HLERequestContext& ctx);
- void SetScreenShotAppletIdentityInfo(HLERequestContext& ctx);
- void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
- void SetAlbumImageOrientation(HLERequestContext& ctx);
- void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
- void GetSystemSharedBufferHandle(HLERequestContext& ctx);
- void GetSystemSharedLayerHandle(HLERequestContext& ctx);
- void CreateManagedDisplayLayer(HLERequestContext& ctx);
- void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
- void SetHandlesRequestToDisplay(HLERequestContext& ctx);
- void ApproveToDisplay(HLERequestContext& ctx);
- void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
- void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
- void ReportUserIsActive(HLERequestContext& ctx);
- void SetAutoSleepDisabled(HLERequestContext& ctx);
- void IsAutoSleepDisabled(HLERequestContext& ctx);
- void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx);
- void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx);
- void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx);
- void SaveCurrentScreenshot(HLERequestContext& ctx);
- void SetRecordVolumeMuted(HLERequestContext& ctx);
-
- Result EnsureBufferSharingEnabled(Kernel::KProcess* process);
-
- Nvnflinger::Nvnflinger& nvnflinger;
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp
new file mode 100644
index 000000000..21747783a
--- /dev/null
+++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/service/all_system_applet_proxies_service.h"
+#include "core/hle/service/am/service/library_applet_proxy.h"
+#include "core/hle/service/am/service/system_applet_proxy.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_)
+ : ServiceFramework{system_, "appletAE"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"},
+ {200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"},
+ {201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"},
+ {300, nullptr, "OpenOverlayAppletProxy"},
+ {350, nullptr, "OpenSystemApplicationProxy"},
+ {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
+ {410, nullptr, "GetSystemAppletControllerForDebug"},
+ {1000, nullptr, "GetDebugFunctions"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IAllSystemAppletProxiesService::~IAllSystemAppletProxiesService() = default;
+
+Result IAllSystemAppletProxiesService::OpenSystemAppletProxy(
+ Out<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy, ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle) {
+ LOG_DEBUG(Service_AM, "called");
+
+ if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
+ *out_system_applet_proxy =
+ std::make_shared<ISystemAppletProxy>(system, applet, process_handle.Get());
+ R_SUCCEED();
+ } else {
+ UNIMPLEMENTED();
+ R_THROW(ResultUnknown);
+ }
+}
+
+Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
+ Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle,
+ InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
+ LOG_DEBUG(Service_AM, "called");
+
+ if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
+ *out_library_applet_proxy =
+ std::make_shared<ILibraryAppletProxy>(system, applet, process_handle.Get());
+ R_SUCCEED();
+ } else {
+ UNIMPLEMENTED();
+ R_THROW(ResultUnknown);
+ }
+}
+
+Result IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld(
+ Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle) {
+ LOG_DEBUG(Service_AM, "called");
+
+ AppletAttribute attribute{};
+ R_RETURN(
+ this->OpenLibraryAppletProxy(out_library_applet_proxy, pid, process_handle, attribute));
+}
+
+std::shared_ptr<Applet> IAllSystemAppletProxiesService::GetAppletFromProcessId(
+ ProcessId process_id) {
+ return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid);
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.h b/src/core/hle/service/am/service/all_system_applet_proxies_service.h
new file mode 100644
index 000000000..0e2dcb86d
--- /dev/null
+++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.h
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+
+namespace AM {
+
+struct Applet;
+struct AppletAttribute;
+class ILibraryAppletProxy;
+class ISystemAppletProxy;
+
+class IAllSystemAppletProxiesService final
+ : public ServiceFramework<IAllSystemAppletProxiesService> {
+public:
+ explicit IAllSystemAppletProxiesService(Core::System& system_);
+ ~IAllSystemAppletProxiesService() override;
+
+private:
+ Result OpenSystemAppletProxy(Out<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy,
+ ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle);
+ Result OpenLibraryAppletProxy(Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy,
+ ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle,
+ InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
+ Result OpenLibraryAppletProxyOld(
+ Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle);
+
+private:
+ std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
+};
+
+} // namespace AM
+} // namespace Service
diff --git a/src/core/hle/service/am/service/applet_common_functions.cpp b/src/core/hle/service/am/service/applet_common_functions.cpp
new file mode 100644
index 000000000..0f29ab285
--- /dev/null
+++ b/src/core/hle/service/am/service/applet_common_functions.cpp
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/service/applet_common_functions.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
+ std::shared_ptr<Applet> applet_)
+ : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "SetTerminateResult"},
+ {10, nullptr, "ReadThemeStorage"},
+ {11, nullptr, "WriteThemeStorage"},
+ {20, nullptr, "PushToAppletBoundChannel"},
+ {21, nullptr, "TryPopFromAppletBoundChannel"},
+ {40, nullptr, "GetDisplayLogicalResolution"},
+ {42, nullptr, "SetDisplayMagnification"},
+ {50, nullptr, "SetHomeButtonDoubleClickEnabled"},
+ {51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"},
+ {52, nullptr, "IsHomeButtonShortPressedBlocked"},
+ {60, nullptr, "IsVrModeCurtainRequired"},
+ {61, nullptr, "IsSleepRequiredByHighTemperature"},
+ {62, nullptr, "IsSleepRequiredByLowBattery"},
+ {70, D<&IAppletCommonFunctions::SetCpuBoostRequestPriority>, "SetCpuBoostRequestPriority"},
+ {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
+ {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
+ {90, nullptr, "OpenNamedChannelAsParent"},
+ {91, nullptr, "OpenNamedChannelAsChild"},
+ {100, nullptr, "SetApplicationCoreUsageMode"},
+ {300, D<&IAppletCommonFunctions::GetCurrentApplicationId>, "GetCurrentApplicationId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IAppletCommonFunctions::~IAppletCommonFunctions() = default;
+
+Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled(
+ Out<bool> out_home_button_double_click_enabled) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_home_button_double_click_enabled = false;
+ R_SUCCEED();
+}
+
+Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ std::scoped_lock lk{applet->lock};
+ applet->cpu_boost_request_priority = priority;
+ R_SUCCEED();
+}
+
+Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/applet_common_functions.h b/src/core/hle/service/am/service/applet_common_functions.h
new file mode 100644
index 000000000..4424fc83d
--- /dev/null
+++ b/src/core/hle/service/am/service/applet_common_functions.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+
+class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
+public:
+ explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
+ ~IAppletCommonFunctions() override;
+
+private:
+ Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
+ Result SetCpuBoostRequestPriority(s32 priority);
+ Result GetCurrentApplicationId(Out<u64> out_application_id);
+
+ const std::shared_ptr<Applet> applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_accessor.cpp b/src/core/hle/service/am/service/application_accessor.cpp
new file mode 100644
index 000000000..6e7d110e8
--- /dev/null
+++ b/src/core/hle/service/am/service/application_accessor.cpp
@@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/result.h"
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/applet_data_broker.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/service/application_accessor.h"
+#include "core/hle/service/am/service/library_applet_accessor.h"
+#include "core/hle/service/am/service/storage.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "IApplicationAccessor"}, m_applet(std::move(applet)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IApplicationAccessor::GetAppletStateChangedEvent>, "GetAppletStateChangedEvent"},
+ {1, nullptr, "IsCompleted"},
+ {10, D<&IApplicationAccessor::Start>, "Start"},
+ {20, D<&IApplicationAccessor::RequestExit>, "RequestExit"},
+ {25, D<&IApplicationAccessor::Terminate>, "Terminate"},
+ {30, D<&IApplicationAccessor::GetResult>, "GetResult"},
+ {101, D<&IApplicationAccessor::RequestForApplicationToGetForeground>, "RequestForApplicationToGetForeground"},
+ {110, nullptr, "TerminateAllLibraryApplets"},
+ {111, nullptr, "AreAnyLibraryAppletsLeft"},
+ {112, D<&IApplicationAccessor::GetCurrentLibraryApplet>, "GetCurrentLibraryApplet"},
+ {120, nullptr, "GetApplicationId"},
+ {121, D<&IApplicationAccessor::PushLaunchParameter>, "PushLaunchParameter"},
+ {122, D<&IApplicationAccessor::GetApplicationControlProperty>, "GetApplicationControlProperty"},
+ {123, nullptr, "GetApplicationLaunchProperty"},
+ {124, nullptr, "GetApplicationLaunchRequestInfo"},
+ {130, D<&IApplicationAccessor::SetUsers>, "SetUsers"},
+ {131, D<&IApplicationAccessor::CheckRightsEnvironmentAvailable>, "CheckRightsEnvironmentAvailable"},
+ {132, D<&IApplicationAccessor::GetNsRightsEnvironmentHandle>, "GetNsRightsEnvironmentHandle"},
+ {140, nullptr, "GetDesirableUids"},
+ {150, D<&IApplicationAccessor::ReportApplicationExitTimeout>, "ReportApplicationExitTimeout"},
+ {160, nullptr, "SetApplicationAttribute"},
+ {170, nullptr, "HasSaveDataAccessPermission"},
+ {180, nullptr, "PushToFriendInvitationStorageChannel"},
+ {190, nullptr, "PushToNotificationStorageChannel"},
+ {200, nullptr, "RequestApplicationSoftReset"},
+ {201, nullptr, "RestartApplicationTimer"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationAccessor::~IApplicationAccessor() = default;
+
+Result IApplicationAccessor::Start() {
+ LOG_INFO(Service_AM, "called");
+ m_applet->process->Run();
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::RequestExit() {
+ LOG_INFO(Service_AM, "called");
+ m_applet->message_queue.RequestExit();
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::Terminate() {
+ LOG_INFO(Service_AM, "called");
+ m_applet->process->Terminate();
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::GetResult() {
+ LOG_INFO(Service_AM, "called");
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::GetAppletStateChangedEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_AM, "called");
+ *out_event = m_applet->caller_applet_broker->GetStateChangedEvent().GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::PushLaunchParameter(LaunchParameterKind kind,
+ SharedPointer<IStorage> storage) {
+ LOG_INFO(Service_AM, "called, kind={}", kind);
+
+ switch (kind) {
+ case LaunchParameterKind::AccountPreselectedUser:
+ m_applet->preselected_user_launch_parameter.push_back(storage->GetData());
+ R_SUCCEED();
+ default:
+ R_THROW(ResultUnknown);
+ }
+}
+
+Result IApplicationAccessor::GetApplicationControlProperty(
+ OutBuffer<BufferAttr_HipcMapAlias> out_control_property) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_THROW(ResultUnknown);
+}
+
+Result IApplicationAccessor::SetUsers(bool enable,
+ InArray<Common::UUID, BufferAttr_HipcMapAlias> user_ids) {
+ LOG_INFO(Service_AM, "called, enable={} user_id_count={}", enable, user_ids.size());
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::GetCurrentLibraryApplet(
+ Out<SharedPointer<ILibraryAppletAccessor>> out_accessor) {
+ LOG_INFO(Service_AM, "(STUBBED) called");
+ *out_accessor = nullptr;
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::RequestForApplicationToGetForeground() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_THROW(ResultUnknown);
+}
+
+Result IApplicationAccessor::CheckRightsEnvironmentAvailable(Out<bool> out_is_available) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_is_available = true;
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::GetNsRightsEnvironmentHandle(Out<u64> out_handle) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_handle = 0xdeadbeef;
+ R_SUCCEED();
+}
+
+Result IApplicationAccessor::ReportApplicationExitTimeout() {
+ LOG_ERROR(Service_AM, "called");
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_accessor.h b/src/core/hle/service/am/service/application_accessor.h
new file mode 100644
index 000000000..39a9b2153
--- /dev/null
+++ b/src/core/hle/service/am/service/application_accessor.h
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+class ILibraryAppletAccessor;
+class IStorage;
+
+class IApplicationAccessor final : public ServiceFramework<IApplicationAccessor> {
+public:
+ explicit IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet);
+ ~IApplicationAccessor() override;
+
+private:
+ Result Start();
+ Result RequestExit();
+ Result Terminate();
+ Result GetResult();
+ Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result PushLaunchParameter(LaunchParameterKind kind, SharedPointer<IStorage> storage);
+ Result GetApplicationControlProperty(OutBuffer<BufferAttr_HipcMapAlias> out_control_property);
+ Result SetUsers(bool enable, InArray<Common::UUID, BufferAttr_HipcMapAlias> user_ids);
+ Result GetCurrentLibraryApplet(Out<SharedPointer<ILibraryAppletAccessor>> out_accessor);
+ Result RequestForApplicationToGetForeground();
+ Result CheckRightsEnvironmentAvailable(Out<bool> out_is_available);
+ Result GetNsRightsEnvironmentHandle(Out<u64> out_handle);
+ Result ReportApplicationExitTimeout();
+
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp
new file mode 100644
index 000000000..568bb0122
--- /dev/null
+++ b/src/core/hle/service/am/service/application_creator.cpp
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/service/application_accessor.h"
+#include "core/hle/service/am/service/application_creator.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IApplicationCreator::IApplicationCreator(Core::System& system_)
+ : ServiceFramework{system_, "IApplicationCreator"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"},
+ {1, nullptr, "PopLaunchRequestedApplication"},
+ {10, nullptr, "CreateSystemApplication"},
+ {100, nullptr, "PopFloatingApplicationForDevelopment"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationCreator::~IApplicationCreator() = default;
+
+Result IApplicationCreator::CreateApplication(
+ Out<SharedPointer<IApplicationAccessor>> out_application_accessor, u64 application_id) {
+ LOG_ERROR(Service_NS, "called, application_id={:x}", application_id);
+ R_THROW(ResultUnknown);
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_creator.h b/src/core/hle/service/am/service/application_creator.h
new file mode 100644
index 000000000..9f939ebf6
--- /dev/null
+++ b/src/core/hle/service/am/service/application_creator.h
@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class IApplicationAccessor;
+struct Applet;
+
+class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
+public:
+ explicit IApplicationCreator(Core::System& system_);
+ ~IApplicationCreator() override;
+
+private:
+ Result CreateApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id);
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp
new file mode 100644
index 000000000..cb53b07e0
--- /dev/null
+++ b/src/core/hle/service/am/service/application_functions.cpp
@@ -0,0 +1,485 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "common/uuid.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/service/application_functions.h"
+#include "core/hle/service/am/service/storage.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/filesystem/save_data_controller.h"
+#include "core/hle/service/glue/glue_manager.h"
+#include "core/hle/service/ns/application_manager_interface.h"
+#include "core/hle/service/ns/service_getter_interface.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::AM {
+
+IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, D<&IApplicationFunctions::PopLaunchParameter>, "PopLaunchParameter"},
+ {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
+ {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
+ {12, nullptr, "CreateApplicationAndRequestToStart"},
+ {13, nullptr, "CreateApplicationAndRequestToStartForQuest"},
+ {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
+ {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
+ {20, D<&IApplicationFunctions::EnsureSaveData>, "EnsureSaveData"},
+ {21, D<&IApplicationFunctions::GetDesiredLanguage>, "GetDesiredLanguage"},
+ {22, D<&IApplicationFunctions::SetTerminateResult>, "SetTerminateResult"},
+ {23, D<&IApplicationFunctions::GetDisplayVersion>, "GetDisplayVersion"},
+ {24, nullptr, "GetLaunchStorageInfoForDebug"},
+ {25, D<&IApplicationFunctions::ExtendSaveData>, "ExtendSaveData"},
+ {26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"},
+ {27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"},
+ {28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"},
+ {29, D<&IApplicationFunctions::GetCacheStorageMax>, "GetCacheStorageMax"},
+ {30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"},
+ {31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"},
+ {32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"},
+ {33, D<&IApplicationFunctions::EndBlockingHomeButton>, "EndBlockingHomeButton"},
+ {34, nullptr, "SelectApplicationLicense"},
+ {35, nullptr, "GetDeviceSaveDataSizeMax"},
+ {36, nullptr, "GetLimitedApplicationLicense"},
+ {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
+ {40, D<&IApplicationFunctions::NotifyRunning>, "NotifyRunning"},
+ {50, D<&IApplicationFunctions::GetPseudoDeviceId>, "GetPseudoDeviceId"},
+ {60, nullptr, "SetMediaPlaybackStateForApplication"},
+ {65, D<&IApplicationFunctions::IsGamePlayRecordingSupported>, "IsGamePlayRecordingSupported"},
+ {66, D<&IApplicationFunctions::InitializeGamePlayRecording>, "InitializeGamePlayRecording"},
+ {67, D<&IApplicationFunctions::SetGamePlayRecordingState>, "SetGamePlayRecordingState"},
+ {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
+ {70, nullptr, "RequestToShutdown"},
+ {71, nullptr, "RequestToReboot"},
+ {72, nullptr, "RequestToSleep"},
+ {80, nullptr, "ExitAndRequestToShowThanksMessage"},
+ {90, D<&IApplicationFunctions::EnableApplicationCrashReport>, "EnableApplicationCrashReport"},
+ {100, D<&IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer>, "InitializeApplicationCopyrightFrameBuffer"},
+ {101, D<&IApplicationFunctions::SetApplicationCopyrightImage>, "SetApplicationCopyrightImage"},
+ {102, D<&IApplicationFunctions::SetApplicationCopyrightVisibility>, "SetApplicationCopyrightVisibility"},
+ {110, D<&IApplicationFunctions::QueryApplicationPlayStatistics>, "QueryApplicationPlayStatistics"},
+ {111, D<&IApplicationFunctions::QueryApplicationPlayStatisticsByUid>, "QueryApplicationPlayStatisticsByUid"},
+ {120, D<&IApplicationFunctions::ExecuteProgram>, "ExecuteProgram"},
+ {121, D<&IApplicationFunctions::ClearUserChannel>, "ClearUserChannel"},
+ {122, D<&IApplicationFunctions::UnpopToUserChannel>, "UnpopToUserChannel"},
+ {123, D<&IApplicationFunctions::GetPreviousProgramIndex>, "GetPreviousProgramIndex"},
+ {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
+ {130, D<&IApplicationFunctions::GetGpuErrorDetectedSystemEvent>, "GetGpuErrorDetectedSystemEvent"},
+ {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
+ {140, D<&IApplicationFunctions::GetFriendInvitationStorageChannelEvent>, "GetFriendInvitationStorageChannelEvent"},
+ {141, D<&IApplicationFunctions::TryPopFromFriendInvitationStorageChannel>, "TryPopFromFriendInvitationStorageChannel"},
+ {150, D<&IApplicationFunctions::GetNotificationStorageChannelEvent>, "GetNotificationStorageChannelEvent"},
+ {151, nullptr, "TryPopFromNotificationStorageChannel"},
+ {160, D<&IApplicationFunctions::GetHealthWarningDisappearedSystemEvent>, "GetHealthWarningDisappearedSystemEvent"},
+ {170, nullptr, "SetHdcpAuthenticationActivated"},
+ {180, nullptr, "GetLaunchRequiredVersion"},
+ {181, nullptr, "UpgradeLaunchRequiredVersion"},
+ {190, nullptr, "SendServerMaintenanceOverlayNotification"},
+ {200, nullptr, "GetLastApplicationExitReason"},
+ {500, nullptr, "StartContinuousRecordingFlushForDebug"},
+ {1000, nullptr, "CreateMovieMaker"},
+ {1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationFunctions::~IApplicationFunctions() = default;
+
+Result IApplicationFunctions::PopLaunchParameter(Out<SharedPointer<IStorage>> out_storage,
+ LaunchParameterKind launch_parameter_kind) {
+ LOG_INFO(Service_AM, "called, kind={}", launch_parameter_kind);
+
+ std::scoped_lock lk{m_applet->lock};
+
+ auto& channel = launch_parameter_kind == LaunchParameterKind::UserChannel
+ ? m_applet->user_channel_launch_parameter
+ : m_applet->preselected_user_launch_parameter;
+
+ if (channel.empty()) {
+ LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!",
+ launch_parameter_kind);
+ R_THROW(AM::ResultNoDataInChannel);
+ }
+
+ auto data = channel.back();
+ channel.pop_back();
+
+ *out_storage = std::make_shared<IStorage>(system, std::move(data));
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID user_id) {
+ LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
+
+ FileSys::SaveDataAttribute attribute{};
+ attribute.title_id = m_applet->program_id;
+ attribute.user_id = user_id.AsU128();
+ attribute.type = FileSys::SaveDataType::SaveData;
+
+ FileSys::VirtualDir save_data{};
+ R_TRY(system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
+ &save_data, FileSys::SaveDataSpaceId::NandUser, attribute));
+
+ *out_size = 0;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
+ // FIXME: all of this stuff belongs to ns
+ // TODO(bunnei): This should be configurable
+ LOG_DEBUG(Service_AM, "called");
+
+ // Get supported languages from NACP, if possible
+ // Default to 0 (all languages supported)
+ u32 supported_languages = 0;
+
+ const auto res = [this] {
+ const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ auto metadata = pm.GetControlMetadata();
+ if (metadata.first != nullptr) {
+ return metadata;
+ }
+
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ return pm_update.GetControlMetadata();
+ }();
+
+ if (res.first != nullptr) {
+ supported_languages = res.first->GetSupportedLanguages();
+ }
+
+ // Call IApplicationManagerInterface implementation.
+ auto& service_manager = system.ServiceManager();
+ auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2");
+
+ std::shared_ptr<NS::IApplicationManagerInterface> app_man;
+ R_TRY(ns_am2->GetApplicationManagerInterface(&app_man));
+
+ // Get desired application language
+ NS::ApplicationLanguage desired_language{};
+ R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
+
+ // Convert to settings language code.
+ R_TRY(app_man->ConvertApplicationLanguageToLanguageCode(out_language_code, desired_language));
+
+ LOG_DEBUG(Service_AM, "got desired_language={:016X}", *out_language_code);
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
+ LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({}-{})", terminate_result.GetInnerValue(),
+ static_cast<u32>(terminate_result.GetModule()) + 2000,
+ terminate_result.GetDescription());
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->terminate_result = terminate_result;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
+ LOG_DEBUG(Service_AM, "called");
+
+ const auto res = [this] {
+ const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ auto metadata = pm.GetControlMetadata();
+ if (metadata.first != nullptr) {
+ return metadata;
+ }
+
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ return pm_update.GetControlMetadata();
+ }();
+
+ if (res.first != nullptr) {
+ const auto& version = res.first->GetVersionString();
+ std::memcpy(out_display_version->string.data(), version.data(),
+ std::min(version.size(), out_display_version->string.size()));
+ } else {
+ static constexpr char default_version[]{"1.0.0"};
+ std::memcpy(out_display_version->string.data(), default_version, sizeof(default_version));
+ }
+
+ out_display_version->string[out_display_version->string.size() - 1] = '\0';
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type,
+ Common::UUID user_id, u64 normal_size,
+ u64 journal_size) {
+ LOG_DEBUG(Service_AM, "called with type={} user_id={} normal={:#x} journal={:#x}",
+ static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
+
+ system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
+ type, m_applet->program_id, user_id.AsU128(), {normal_size, journal_size});
+
+ // The following value is used to indicate the amount of space remaining on failure
+ // due to running out of space. Since we always succeed, this should be 0.
+ *out_required_size = 0;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size,
+ FileSys::SaveDataType type, Common::UUID user_id) {
+ LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
+
+ const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
+ type, m_applet->program_id, user_id.AsU128());
+
+ *out_normal_size = size.normal;
+ *out_journal_size = size.journal;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::CreateCacheStorage(Out<u32> out_target_media,
+ Out<u64> out_required_size, u16 index,
+ u64 normal_size, u64 journal_size) {
+ LOG_WARNING(Service_AM, "(STUBBED) called with index={} size={:#x} journal_size={:#x}", index,
+ normal_size, journal_size);
+
+ *out_target_media = 1; // Nand
+ *out_required_size = 0;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetSaveDataSizeMax(Out<u64> out_max_normal_size,
+ Out<u64> out_max_journal_size) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ *out_max_normal_size = 0xFFFFFFF;
+ *out_max_journal_size = 0xFFFFFFF;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetCacheStorageMax(Out<u32> out_cache_storage_index_max,
+ Out<u64> out_max_journal_size) {
+ LOG_DEBUG(Service_AM, "called");
+
+ std::vector<u8> nacp;
+ R_TRY(system.GetARPManager().GetControlProperty(&nacp, m_applet->program_id));
+
+ auto raw_nacp = std::make_unique<FileSys::RawNACP>();
+ std::memcpy(raw_nacp.get(), nacp.data(), std::min(sizeof(*raw_nacp), nacp.size()));
+
+ *out_cache_storage_index_max = static_cast<u32>(raw_nacp->cache_storage_max_index);
+ *out_max_journal_size = static_cast<u64>(raw_nacp->cache_storage_data_and_journal_max_size);
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->home_button_long_pressed_blocked = true;
+ m_applet->home_button_short_pressed_blocked = true;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->home_button_long_pressed_blocked = false;
+ m_applet->home_button_short_pressed_blocked = false;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::BeginBlockingHomeButton(s64 timeout_ns) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, timeout_ns={}", timeout_ns);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->home_button_long_pressed_blocked = true;
+ m_applet->home_button_short_pressed_blocked = true;
+ m_applet->home_button_double_click_enabled = true;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::EndBlockingHomeButton() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->home_button_long_pressed_blocked = false;
+ m_applet->home_button_short_pressed_blocked = false;
+ m_applet->home_button_double_click_enabled = false;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_became_running = true;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_pseudo_device_id = {};
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::IsGamePlayRecordingSupported(
+ Out<bool> out_is_game_play_recording_supported) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_is_game_play_recording_supported = m_applet->game_play_recording_supported;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::InitializeGamePlayRecording(
+ u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetGamePlayRecordingState(
+ GamePlayRecordingState game_play_recording_state) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->game_play_recording_state = game_play_recording_state;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::EnableApplicationCrashReport(bool enabled) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->application_crash_report_enabled = enabled;
+
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(
+ s32 width, s32 height, u64 transfer_memory_size,
+ InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetApplicationCopyrightImage(
+ s32 x, s32 y, s32 width, s32 height, WindowOriginMode window_origin_mode,
+ InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> image_data) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetApplicationCopyrightVisibility(bool visible) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", visible);
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::QueryApplicationPlayStatistics(
+ Out<s32> out_entries,
+ OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+ InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_entries = 0;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::QueryApplicationPlayStatisticsByUid(
+ Out<s32> out_entries,
+ OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+ Common::UUID user_id, InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_entries = 0;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, kind={}, value={}", kind, value);
+ ASSERT(kind == ProgramSpecifyKind::ExecuteProgram ||
+ kind == ProgramSpecifyKind::RestartProgram);
+
+ // Copy user channel ownership into the system so that it will be preserved
+ system.GetUserChannel() = m_applet->user_channel_launch_parameter;
+ system.ExecuteProgram(value);
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::ClearUserChannel() {
+ LOG_DEBUG(Service_AM, "called");
+ m_applet->user_channel_launch_parameter.clear();
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::UnpopToUserChannel(SharedPointer<IStorage> storage) {
+ LOG_DEBUG(Service_AM, "called");
+ m_applet->user_channel_launch_parameter.push_back(storage->GetData());
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetPreviousProgramIndex(Out<s32> out_previous_program_index) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_previous_program_index = m_applet->previous_program_index;
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetGpuErrorDetectedSystemEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_event = m_applet->gpu_error_detected_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetFriendInvitationStorageChannelEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = m_applet->friend_invitation_storage_channel_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
+ Out<SharedPointer<IStorage>> out_storage) {
+ LOG_INFO(Service_AM, "(STUBBED) called");
+ R_THROW(AM::ResultNoDataInChannel);
+}
+
+Result IApplicationFunctions::GetNotificationStorageChannelEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = m_applet->notification_storage_channel_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = m_applet->health_warning_disappeared_system_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationFunctions::PrepareForJit() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->jit_service_launched = true;
+
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h
new file mode 100644
index 000000000..10025a152
--- /dev/null
+++ b/src/core/hle/service/am/service/application_functions.h
@@ -0,0 +1,84 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace FileSys {
+enum class SaveDataType : u8;
+}
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::AM {
+
+struct Applet;
+class IStorage;
+
+class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
+public:
+ explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet);
+ ~IApplicationFunctions() override;
+
+private:
+ Result PopLaunchParameter(Out<SharedPointer<IStorage>> out_storage,
+ LaunchParameterKind launch_parameter_kind);
+ Result EnsureSaveData(Out<u64> out_size, Common::UUID user_id);
+ Result GetDesiredLanguage(Out<u64> out_language_code);
+ Result SetTerminateResult(Result terminate_result);
+ Result GetDisplayVersion(Out<DisplayVersion> out_display_version);
+ Result ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type,
+ Common::UUID user_id, u64 normal_size, u64 journal_size);
+ Result GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size,
+ FileSys::SaveDataType type, Common::UUID user_id);
+ Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index,
+ u64 normal_size, u64 journal_size);
+ Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size);
+ Result GetCacheStorageMax(Out<u32> out_cache_storage_index_max, Out<u64> out_max_journal_size);
+ Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused);
+ Result EndBlockingHomeButtonShortAndLongPressed();
+ Result BeginBlockingHomeButton(s64 timeout_ns);
+ Result EndBlockingHomeButton();
+ Result NotifyRunning(Out<bool> out_became_running);
+ Result GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id);
+ Result IsGamePlayRecordingSupported(Out<bool> out_is_game_play_recording_supported);
+ Result InitializeGamePlayRecording(
+ u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
+ Result SetGamePlayRecordingState(GamePlayRecordingState game_play_recording_state);
+ Result EnableApplicationCrashReport(bool enabled);
+ Result InitializeApplicationCopyrightFrameBuffer(
+ s32 width, s32 height, u64 transfer_memory_size,
+ InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
+ Result SetApplicationCopyrightImage(
+ s32 x, s32 y, s32 width, s32 height, WindowOriginMode window_origin_mode,
+ InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> image_data);
+ Result SetApplicationCopyrightVisibility(bool visible);
+ Result QueryApplicationPlayStatistics(
+ Out<s32> out_entries,
+ OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+ InArray<u64, BufferAttr_HipcMapAlias> application_ids);
+ Result QueryApplicationPlayStatisticsByUid(
+ Out<s32> out_entries,
+ OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+ Common::UUID user_id, InArray<u64, BufferAttr_HipcMapAlias> application_ids);
+ Result ExecuteProgram(ProgramSpecifyKind kind, u64 value);
+ Result ClearUserChannel();
+ Result UnpopToUserChannel(SharedPointer<IStorage> storage);
+ Result GetPreviousProgramIndex(Out<s32> out_previous_program_index);
+ Result GetGpuErrorDetectedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetFriendInvitationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result TryPopFromFriendInvitationStorageChannel(Out<SharedPointer<IStorage>> out_storage);
+ Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result PrepareForJit();
+
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp
new file mode 100644
index 000000000..19d6a3b89
--- /dev/null
+++ b/src/core/hle/service/am/service/application_proxy.cpp
@@ -0,0 +1,105 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/applet_common_functions.h"
+#include "core/hle/service/am/service/application_functions.h"
+#include "core/hle/service/am/service/application_proxy.h"
+#include "core/hle/service/am/service/audio_controller.h"
+#include "core/hle/service/am/service/common_state_getter.h"
+#include "core/hle/service/am/service/debug_functions.h"
+#include "core/hle/service/am/service/display_controller.h"
+#include "core/hle/service/am/service/library_applet_creator.h"
+#include "core/hle/service/am/service/process_winding_controller.h"
+#include "core/hle/service/am/service/self_controller.h"
+#include "core/hle/service/am/service/window_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process)
+ : ServiceFramework{system_, "IApplicationProxy"}, m_process{process}, m_applet{
+ std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
+ {1, D<&IApplicationProxy::GetSelfController>, "GetSelfController"},
+ {2, D<&IApplicationProxy::GetWindowController>, "GetWindowController"},
+ {3, D<&IApplicationProxy::GetAudioController>, "GetAudioController"},
+ {4, D<&IApplicationProxy::GetDisplayController>, "GetDisplayController"},
+ {10, D<&IApplicationProxy::GetProcessWindingController>, "GetProcessWindingController"},
+ {11, D<&IApplicationProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
+ {20, D<&IApplicationProxy::GetApplicationFunctions>, "GetApplicationFunctions"},
+ {1000, D<&IApplicationProxy::GetDebugFunctions>, "GetDebugFunctions"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationProxy::~IApplicationProxy() = default;
+
+Result IApplicationProxy::GetAudioController(
+ Out<SharedPointer<IAudioController>> out_audio_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_audio_controller = std::make_shared<IAudioController>(system);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetDisplayController(
+ Out<SharedPointer<IDisplayController>> out_display_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_display_controller = std::make_shared<IDisplayController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetProcessWindingController(
+ Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetDebugFunctions(
+ Out<SharedPointer<IDebugFunctions>> out_debug_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_debug_functions = std::make_shared<IDebugFunctions>(system);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetWindowController(
+ Out<SharedPointer<IWindowController>> out_window_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_window_controller = std::make_shared<IWindowController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetSelfController(
+ Out<SharedPointer<ISelfController>> out_self_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetCommonStateGetter(
+ Out<SharedPointer<ICommonStateGetter>> out_common_state_getter) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetLibraryAppletCreator(
+ Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result IApplicationProxy::GetApplicationFunctions(
+ Out<SharedPointer<IApplicationFunctions>> out_application_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_application_functions = std::make_shared<IApplicationFunctions>(system, m_applet);
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h
new file mode 100644
index 000000000..6da350df7
--- /dev/null
+++ b/src/core/hle/service/am/service/application_proxy.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+class IAudioController;
+class IApplicationFunctions;
+class ICommonStateGetter;
+class IDebugFunctions;
+class IDisplayController;
+class ILibraryAppletCreator;
+class IProcessWindingController;
+class ISelfController;
+class IWindowController;
+
+class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
+public:
+ explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process);
+ ~IApplicationProxy();
+
+private:
+ Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
+ Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
+ Result GetProcessWindingController(
+ Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
+ Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
+ Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
+ Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
+ Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
+ Result GetLibraryAppletCreator(
+ Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
+ Result GetApplicationFunctions(
+ Out<SharedPointer<IApplicationFunctions>> out_application_functions);
+
+private:
+ Kernel::KProcess* const m_process;
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp
new file mode 100644
index 000000000..fd66e77b9
--- /dev/null
+++ b/src/core/hle/service/am/service/application_proxy_service.cpp
@@ -0,0 +1,42 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/service/application_proxy.h"
+#include "core/hle/service/am/service/application_proxy_service.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IApplicationProxyService::IApplicationProxyService(Core::System& system_)
+ : ServiceFramework{system_, "appletOE"} {
+ static const FunctionInfo functions[] = {
+ {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"},
+ };
+ RegisterHandlers(functions);
+}
+
+IApplicationProxyService::~IApplicationProxyService() = default;
+
+Result IApplicationProxyService::OpenApplicationProxy(
+ Out<SharedPointer<IApplicationProxy>> out_application_proxy, ClientProcessId pid,
+ InCopyHandle<Kernel::KProcess> process_handle) {
+ LOG_DEBUG(Service_AM, "called");
+
+ if (const auto applet = this->GetAppletFromProcessId(pid)) {
+ *out_application_proxy =
+ std::make_shared<IApplicationProxy>(system, applet, process_handle.Get());
+ R_SUCCEED();
+ } else {
+ UNIMPLEMENTED();
+ R_THROW(ResultUnknown);
+ }
+}
+
+std::shared_ptr<Applet> IApplicationProxyService::GetAppletFromProcessId(ProcessId process_id) {
+ return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid);
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_proxy_service.h b/src/core/hle/service/am/service/application_proxy_service.h
new file mode 100644
index 000000000..8efafa31a
--- /dev/null
+++ b/src/core/hle/service/am/service/application_proxy_service.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+
+namespace AM {
+
+struct Applet;
+class IApplicationProxy;
+
+class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> {
+public:
+ explicit IApplicationProxyService(Core::System& system_);
+ ~IApplicationProxyService() override;
+
+private:
+ Result OpenApplicationProxy(Out<SharedPointer<IApplicationProxy>> out_application_proxy,
+ ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
+
+private:
+ std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
+};
+
+} // namespace AM
+} // namespace Service
diff --git a/src/core/hle/service/am/service/audio_controller.cpp b/src/core/hle/service/am/service/audio_controller.cpp
new file mode 100644
index 000000000..ad731c7bd
--- /dev/null
+++ b/src/core/hle/service/am/service/audio_controller.cpp
@@ -0,0 +1,69 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/audio_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IAudioController::IAudioController(Core::System& system_)
+ : ServiceFramework{system_, "IAudioController"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IAudioController::SetExpectedMasterVolume>, "SetExpectedMasterVolume"},
+ {1, D<&IAudioController::GetMainAppletExpectedMasterVolume>, "GetMainAppletExpectedMasterVolume"},
+ {2, D<&IAudioController::GetLibraryAppletExpectedMasterVolume>, "GetLibraryAppletExpectedMasterVolume"},
+ {3, D<&IAudioController::ChangeMainAppletMasterVolume>, "ChangeMainAppletMasterVolume"},
+ {4, D<&IAudioController::SetTransparentVolumeRate>, "SetTransparentVolumeRate"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IAudioController::~IAudioController() = default;
+
+Result IAudioController::SetExpectedMasterVolume(f32 main_applet_volume,
+ f32 library_applet_volume) {
+ LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
+ main_applet_volume, library_applet_volume);
+
+ // Ensure the volume values remain within the 0-100% range
+ m_main_applet_volume = std::clamp(main_applet_volume, MinAllowedVolume, MaxAllowedVolume);
+ m_library_applet_volume = std::clamp(library_applet_volume, MinAllowedVolume, MaxAllowedVolume);
+
+ R_SUCCEED();
+}
+
+Result IAudioController::GetMainAppletExpectedMasterVolume(Out<f32> out_main_applet_volume) {
+ LOG_DEBUG(Service_AM, "called. main_applet_volume={}", m_main_applet_volume);
+ *out_main_applet_volume = m_main_applet_volume;
+ R_SUCCEED();
+}
+
+Result IAudioController::GetLibraryAppletExpectedMasterVolume(Out<f32> out_library_applet_volume) {
+ LOG_DEBUG(Service_AM, "called. library_applet_volume={}", m_library_applet_volume);
+ *out_library_applet_volume = m_library_applet_volume;
+ R_SUCCEED();
+}
+
+Result IAudioController::ChangeMainAppletMasterVolume(f32 volume, s64 fade_time_ns) {
+ LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", volume, fade_time_ns);
+
+ m_main_applet_volume = std::clamp(volume, MinAllowedVolume, MaxAllowedVolume);
+ m_fade_time_ns = std::chrono::nanoseconds{fade_time_ns};
+
+ R_SUCCEED();
+}
+
+Result IAudioController::SetTransparentVolumeRate(f32 transparent_volume_rate) {
+ LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate);
+
+ // Clamp volume range to 0-100%.
+ m_transparent_volume_rate =
+ std::clamp(transparent_volume_rate, MinAllowedVolume, MaxAllowedVolume);
+
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/audio_controller.h b/src/core/hle/service/am/service/audio_controller.h
new file mode 100644
index 000000000..4b0f3f9ae
--- /dev/null
+++ b/src/core/hle/service/am/service/audio_controller.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class IAudioController final : public ServiceFramework<IAudioController> {
+public:
+ explicit IAudioController(Core::System& system_);
+ ~IAudioController() override;
+
+private:
+ Result SetExpectedMasterVolume(f32 main_applet_volume, f32 library_applet_volume);
+ Result GetMainAppletExpectedMasterVolume(Out<f32> out_main_applet_volume);
+ Result GetLibraryAppletExpectedMasterVolume(Out<f32> out_library_applet_volume);
+ Result ChangeMainAppletMasterVolume(f32 volume, s64 fade_time_ns);
+ Result SetTransparentVolumeRate(f32 transparent_volume_rate);
+
+ static constexpr float MinAllowedVolume = 0.0f;
+ static constexpr float MaxAllowedVolume = 1.0f;
+
+ float m_main_applet_volume{0.25f};
+ float m_library_applet_volume{MaxAllowedVolume};
+ float m_transparent_volume_rate{MinAllowedVolume};
+
+ // Volume transition fade time in nanoseconds.
+ // e.g. If the main applet volume was 0% and was changed to 50%
+ // with a fade of 50ns, then over the course of 50ns,
+ // the volume will gradually fade up to 50%
+ std::chrono::nanoseconds m_fade_time_ns{0};
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/common_state_getter.cpp b/src/core/hle/service/am/service/common_state_getter.cpp
new file mode 100644
index 000000000..548498e83
--- /dev/null
+++ b/src/core/hle/service/am/service/common_state_getter.cpp
@@ -0,0 +1,278 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/service/common_state_getter.h"
+#include "core/hle/service/am/service/lock_accessor.h"
+#include "core/hle/service/apm/apm_interface.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/pm/pm.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::AM {
+
+ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "ICommonStateGetter"}, m_applet{std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ICommonStateGetter::GetEventHandle>, "GetEventHandle"},
+ {1, D<&ICommonStateGetter::ReceiveMessage>, "ReceiveMessage"},
+ {2, nullptr, "GetThisAppletKind"},
+ {3, nullptr, "AllowToEnterSleep"},
+ {4, nullptr, "DisallowToEnterSleep"},
+ {5, D<&ICommonStateGetter::GetOperationMode>, "GetOperationMode"},
+ {6, D<&ICommonStateGetter::GetPerformanceMode>, "GetPerformanceMode"},
+ {7, nullptr, "GetCradleStatus"},
+ {8, D<&ICommonStateGetter::GetBootMode>, "GetBootMode"},
+ {9, D<&ICommonStateGetter::GetCurrentFocusState>, "GetCurrentFocusState"},
+ {10, D<&ICommonStateGetter::RequestToAcquireSleepLock>, "RequestToAcquireSleepLock"},
+ {11, nullptr, "ReleaseSleepLock"},
+ {12, nullptr, "ReleaseSleepLockTransiently"},
+ {13, D<&ICommonStateGetter::GetAcquiredSleepLockEvent>, "GetAcquiredSleepLockEvent"},
+ {14, nullptr, "GetWakeupCount"},
+ {20, nullptr, "PushToGeneralChannel"},
+ {30, nullptr, "GetHomeButtonReaderLockAccessor"},
+ {31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"},
+ {32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"},
+ {40, nullptr, "GetCradleFwVersion"},
+ {50, D<&ICommonStateGetter::IsVrModeEnabled>, "IsVrModeEnabled"},
+ {51, D<&ICommonStateGetter::SetVrModeEnabled>, "SetVrModeEnabled"},
+ {52, D<&ICommonStateGetter::SetLcdBacklighOffEnabled>, "SetLcdBacklighOffEnabled"},
+ {53, D<&ICommonStateGetter::BeginVrModeEx>, "BeginVrModeEx"},
+ {54, D<&ICommonStateGetter::EndVrModeEx>, "EndVrModeEx"},
+ {55, D<&ICommonStateGetter::IsInControllerFirmwareUpdateSection>, "IsInControllerFirmwareUpdateSection"},
+ {59, nullptr, "SetVrPositionForDebug"},
+ {60, D<&ICommonStateGetter::GetDefaultDisplayResolution>, "GetDefaultDisplayResolution"},
+ {61, D<&ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent>, "GetDefaultDisplayResolutionChangeEvent"},
+ {62, nullptr, "GetHdcpAuthenticationState"},
+ {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
+ {64, nullptr, "SetTvPowerStateMatchingMode"},
+ {65, nullptr, "GetApplicationIdByContentActionName"},
+ {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
+ {67, nullptr, "CancelCpuBoostMode"},
+ {68, D<&ICommonStateGetter::GetBuiltInDisplayType>, "GetBuiltInDisplayType"},
+ {80, D<&ICommonStateGetter::PerformSystemButtonPressingIfInFocus>, "PerformSystemButtonPressingIfInFocus"},
+ {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
+ {91, nullptr, "GetCurrentPerformanceConfiguration"},
+ {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
+ {110, nullptr, "OpenMyGpuErrorHandler"},
+ {120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"},
+ {200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
+ {300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
+ {400, nullptr, "ActivateMigrationService"},
+ {401, nullptr, "DeactivateMigrationService"},
+ {500, nullptr, "DisableSleepTillShutdown"},
+ {501, nullptr, "SuppressDisablingSleepTemporarily"},
+ {502, nullptr, "IsSleepEnabled"},
+ {503, nullptr, "IsDisablingSleepSuppressed"},
+ {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ICommonStateGetter::~ICommonStateGetter() = default;
+
+Result ICommonStateGetter::GetEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = &m_applet->message_queue.GetMessageReceiveEvent();
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::ReceiveMessage(Out<AppletMessage> out_applet_message) {
+ LOG_DEBUG(Service_AM, "called");
+
+ *out_applet_message = m_applet->message_queue.PopMessage();
+ if (*out_applet_message == AppletMessage::None) {
+ LOG_ERROR(Service_AM, "Tried to pop message but none was available!");
+ R_THROW(AM::ResultNoMessages);
+ }
+
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetCurrentFocusState(Out<FocusState> out_focus_state) {
+ LOG_DEBUG(Service_AM, "called");
+
+ std::scoped_lock lk{m_applet->lock};
+ *out_focus_state = m_applet->focus_state;
+
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::RequestToAcquireSleepLock() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // Sleep lock is acquired immediately.
+ m_applet->sleep_lock_event.Signal();
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetAcquiredSleepLockEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_AM, "called");
+ *out_event = m_applet->sleep_lock_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetReaderLockAccessorEx(
+ Out<SharedPointer<ILockAccessor>> out_lock_accessor, u32 button_type) {
+ LOG_INFO(Service_AM, "called, button_type={}", button_type);
+ *out_lock_accessor = std::make_shared<ILockAccessor>(system);
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetWriterLockAccessorEx(
+ Out<SharedPointer<ILockAccessor>> out_lock_accessor, u32 button_type) {
+ LOG_INFO(Service_AM, "called, button_type={}", button_type);
+ *out_lock_accessor = std::make_shared<ILockAccessor>(system);
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = &m_applet->message_queue.GetOperationModeChangedEvent();
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetOperationMode(Out<OperationMode> out_operation_mode) {
+ const bool use_docked_mode{Settings::IsDockedMode()};
+ LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
+ *out_operation_mode = use_docked_mode ? OperationMode::Docked : OperationMode::Handheld;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_performance_mode = system.GetAPMController().GetCurrentPerformanceMode();
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetBootMode(Out<PM::SystemBootMode> out_boot_mode) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_boot_mode = Service::PM::SystemBootMode::Normal;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled) {
+ LOG_DEBUG(Service_AM, "called");
+
+ std::scoped_lock lk{m_applet->lock};
+ *out_is_vr_mode_enabled = m_applet->vr_mode_enabled;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::SetVrModeEnabled(bool is_vr_mode_enabled) {
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->vr_mode_enabled = is_vr_mode_enabled;
+ LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled ? "on" : "off");
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::SetLcdBacklighOffEnabled(bool is_lcd_backlight_off_enabled) {
+ LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
+ is_lcd_backlight_off_enabled);
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::BeginVrModeEx() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->vr_mode_enabled = true;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::EndVrModeEx() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->vr_mode_enabled = false;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::IsInControllerFirmwareUpdateSection(
+ Out<bool> out_is_in_controller_firmware_update_section) {
+ LOG_INFO(Service_AM, "called");
+ *out_is_in_controller_firmware_update_section = false;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height) {
+ LOG_DEBUG(Service_AM, "called");
+
+ if (Settings::IsDockedMode()) {
+ *out_width = static_cast<u32>(Service::VI::DisplayResolution::DockedWidth);
+ *out_height = static_cast<u32>(Service::VI::DisplayResolution::DockedHeight);
+ } else {
+ *out_width = static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth);
+ *out_height = static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight);
+ }
+
+ R_SUCCEED();
+}
+
+void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS");
+
+ const auto& sm = system.ServiceManager();
+ const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys");
+ ASSERT(apm_sys != nullptr);
+
+ apm_sys->SetCpuBoostMode(ctx);
+}
+
+Result ICommonStateGetter::GetBuiltInDisplayType(Out<s32> out_display_type) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_display_type = 0;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType type) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, type={}", type);
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_operation_mode_system_info = 0;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetAppletLaunchedHistory(
+ Out<s32> out_count, OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids) {
+ LOG_INFO(Service_AM, "called");
+
+ std::shared_ptr<Applet> current_applet = m_applet;
+
+ for (*out_count = 0;
+ *out_count < static_cast<s32>(out_applet_ids.size()) && current_applet != nullptr;
+ /* ... */) {
+ out_applet_ids[(*out_count)++] = current_applet->applet_id;
+ current_applet = current_applet->caller_applet.lock();
+ }
+
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::GetSettingsPlatformRegion(
+ Out<SysPlatformRegion> out_settings_platform_region) {
+ LOG_INFO(Service_AM, "called");
+ *out_settings_platform_region = SysPlatformRegion::Global;
+ R_SUCCEED();
+}
+
+Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->request_exit_to_library_applet_at_execute_next_program_enabled = true;
+
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/common_state_getter.h b/src/core/hle/service/am/service/common_state_getter.h
new file mode 100644
index 000000000..5a8dca3d6
--- /dev/null
+++ b/src/core/hle/service/am/service/common_state_getter.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/apm/apm_controller.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/pm/pm.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::AM {
+
+struct Applet;
+class ILockAccessor;
+
+class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
+public:
+ explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_);
+ ~ICommonStateGetter() override;
+
+private:
+ Result GetEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result ReceiveMessage(Out<AppletMessage> out_applet_message);
+ Result GetCurrentFocusState(Out<FocusState> out_focus_state);
+ Result RequestToAcquireSleepLock();
+ Result GetAcquiredSleepLockEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetReaderLockAccessorEx(Out<SharedPointer<ILockAccessor>> out_lock_accessor,
+ u32 button_type);
+ Result GetWriterLockAccessorEx(Out<SharedPointer<ILockAccessor>> out_lock_accessor,
+ u32 button_type);
+ Result GetDefaultDisplayResolutionChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetOperationMode(Out<OperationMode> out_operation_mode);
+ Result GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode);
+ Result GetBootMode(Out<PM::SystemBootMode> out_boot_mode);
+ Result IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled);
+ Result SetVrModeEnabled(bool is_vr_mode_enabled);
+ Result SetLcdBacklighOffEnabled(bool is_lcd_backlight_off_enabled);
+ Result BeginVrModeEx();
+ Result EndVrModeEx();
+ Result IsInControllerFirmwareUpdateSection(
+ Out<bool> out_is_in_controller_firmware_update_section);
+ Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
+ Result GetBuiltInDisplayType(Out<s32> out_display_type);
+ Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
+ Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
+ Result GetAppletLaunchedHistory(Out<s32> out_count,
+ OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
+ Result GetSettingsPlatformRegion(Out<SysPlatformRegion> out_settings_platform_region);
+ Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
+
+ void SetCpuBoostMode(HLERequestContext& ctx);
+
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/cradle_firmware_updater.cpp b/src/core/hle/service/am/service/cradle_firmware_updater.cpp
new file mode 100644
index 000000000..0a8af0858
--- /dev/null
+++ b/src/core/hle/service/am/service/cradle_firmware_updater.cpp
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/cradle_firmware_updater.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+ICradleFirmwareUpdater::ICradleFirmwareUpdater(Core::System& system_)
+ : ServiceFramework{system_, "ICradleFirmwareUpdater"},
+ m_context{system, "ICradleFirmwareUpdater"}, m_cradle_device_info_event{m_context} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ICradleFirmwareUpdater::StartUpdate>, "StartUpdate"},
+ {1, D<&ICradleFirmwareUpdater::FinishUpdate>, "FinishUpdate"},
+ {2, D<&ICradleFirmwareUpdater::GetCradleDeviceInfo>, "GetCradleDeviceInfo"},
+ {3, D<&ICradleFirmwareUpdater::GetCradleDeviceInfoChangeEvent>, "GetCradleDeviceInfoChangeEvent"},
+ {4, nullptr, "GetUpdateProgressInfo"},
+ {5, nullptr, "GetLastInternalResult"},
+
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ICradleFirmwareUpdater::~ICradleFirmwareUpdater() = default;
+
+Result ICradleFirmwareUpdater::StartUpdate() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result ICradleFirmwareUpdater::FinishUpdate() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result ICradleFirmwareUpdater::GetCradleDeviceInfo(Out<CradleDeviceInfo> out_cradle_device_info) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_cradle_device_info = {};
+ R_SUCCEED();
+}
+
+Result ICradleFirmwareUpdater::GetCradleDeviceInfoChangeEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_event = m_cradle_device_info_event.GetHandle();
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/cradle_firmware_updater.h b/src/core/hle/service/am/service/cradle_firmware_updater.h
new file mode 100644
index 000000000..3e803f0ae
--- /dev/null
+++ b/src/core/hle/service/am/service/cradle_firmware_updater.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct CradleDeviceInfo {
+ bool unknown0;
+ bool unknown1;
+ bool unknown2;
+ u64 unknown3;
+};
+static_assert(sizeof(CradleDeviceInfo) == 0x10, "CradleDeviceInfo has incorrect size");
+
+class ICradleFirmwareUpdater final : public ServiceFramework<ICradleFirmwareUpdater> {
+public:
+ explicit ICradleFirmwareUpdater(Core::System& system_);
+ ~ICradleFirmwareUpdater() override;
+
+private:
+ Result StartUpdate();
+ Result FinishUpdate();
+ Result GetCradleDeviceInfo(Out<CradleDeviceInfo> out_cradle_device_info);
+ Result GetCradleDeviceInfoChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+private:
+ KernelHelpers::ServiceContext m_context;
+ Event m_cradle_device_info_event;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/debug_functions.cpp b/src/core/hle/service/am/service/debug_functions.cpp
new file mode 100644
index 000000000..fcac4776d
--- /dev/null
+++ b/src/core/hle/service/am/service/debug_functions.cpp
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/debug_functions.h"
+
+namespace Service::AM {
+
+IDebugFunctions::IDebugFunctions(Core::System& system_)
+ : ServiceFramework{system_, "IDebugFunctions"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
+ {1, nullptr, "OpenMainApplication"},
+ {10, nullptr, "PerformSystemButtonPressing"},
+ {20, nullptr, "InvalidateTransitionLayer"},
+ {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
+ {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"},
+ {40, nullptr, "GetAppletResourceUsageInfo"},
+ {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"},
+ {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"},
+ {100, nullptr, "SetCpuBoostModeForApplet"},
+ {101, nullptr, "CancelCpuBoostModeForApplet"},
+ {110, nullptr, "PushToAppletBoundChannelForDebug"},
+ {111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
+ {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
+ {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"},
+ {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"},
+ {130, nullptr, "FriendInvitationSetApplicationParameter"},
+ {131, nullptr, "FriendInvitationClearApplicationParameter"},
+ {132, nullptr, "FriendInvitationPushApplicationParameter"},
+ {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
+ {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"},
+ {300, nullptr, "TerminateAllRunningApplicationsForDebug"},
+ {900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDebugFunctions::~IDebugFunctions() = default;
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/debug_functions.h b/src/core/hle/service/am/service/debug_functions.h
index d55968743..d55968743 100644
--- a/src/core/hle/service/am/debug_functions.h
+++ b/src/core/hle/service/am/service/debug_functions.h
diff --git a/src/core/hle/service/am/service/display_controller.cpp b/src/core/hle/service/am/service/display_controller.cpp
new file mode 100644
index 000000000..ed71f9093
--- /dev/null
+++ b/src/core/hle/service/am/service/display_controller.cpp
@@ -0,0 +1,105 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/result.h"
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/service/display_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_)
+ : ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetLastForegroundCaptureImage"},
+ {1, nullptr, "UpdateLastForegroundCaptureImage"},
+ {2, nullptr, "GetLastApplicationCaptureImage"},
+ {3, nullptr, "GetCallerAppletCaptureImage"},
+ {4, nullptr, "UpdateCallerAppletCaptureImage"},
+ {5, nullptr, "GetLastForegroundCaptureImageEx"},
+ {6, nullptr, "GetLastApplicationCaptureImageEx"},
+ {7, D<&IDisplayController::GetCallerAppletCaptureImageEx>, "GetCallerAppletCaptureImageEx"},
+ {8, D<&IDisplayController::TakeScreenShotOfOwnLayer>, "TakeScreenShotOfOwnLayer"},
+ {9, nullptr, "CopyBetweenCaptureBuffers"},
+ {10, nullptr, "AcquireLastApplicationCaptureBuffer"},
+ {11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
+ {12, nullptr, "AcquireLastForegroundCaptureBuffer"},
+ {13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
+ {14, nullptr, "AcquireCallerAppletCaptureBuffer"},
+ {15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
+ {16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
+ {17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
+ {18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
+ {20, D<&IDisplayController::ClearCaptureBuffer>, "ClearCaptureBuffer"},
+ {21, nullptr, "ClearAppletTransitionBuffer"},
+ {22, D<&IDisplayController::AcquireLastApplicationCaptureSharedBuffer>, "AcquireLastApplicationCaptureSharedBuffer"},
+ {23, D<&IDisplayController::ReleaseLastApplicationCaptureSharedBuffer>, "ReleaseLastApplicationCaptureSharedBuffer"},
+ {24, D<&IDisplayController::AcquireLastForegroundCaptureSharedBuffer>, "AcquireLastForegroundCaptureSharedBuffer"},
+ {25, D<&IDisplayController::ReleaseLastForegroundCaptureSharedBuffer>, "ReleaseLastForegroundCaptureSharedBuffer"},
+ {26, D<&IDisplayController::AcquireCallerAppletCaptureSharedBuffer>, "AcquireCallerAppletCaptureSharedBuffer"},
+ {27, D<&IDisplayController::ReleaseCallerAppletCaptureSharedBuffer>, "ReleaseCallerAppletCaptureSharedBuffer"},
+ {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDisplayController::~IDisplayController() = default;
+
+Result IDisplayController::GetCallerAppletCaptureImageEx(
+ Out<bool> out_was_written, OutBuffer<BufferAttr_HipcMapAlias> out_image_data) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_was_written = true;
+ R_SUCCEED();
+}
+
+Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_layer_index) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
+ unknown0, fbshare_layer_index, color);
+ R_SUCCEED();
+}
+
+Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
+ Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
+ out_fbshare_layer_index));
+}
+
+Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
+ Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
+ out_fbshare_layer_index));
+}
+
+Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
+ Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
+ out_fbshare_layer_index));
+}
+
+Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/display_controller.h b/src/core/hle/service/am/service/display_controller.h
new file mode 100644
index 000000000..406fae21a
--- /dev/null
+++ b/src/core/hle/service/am/service/display_controller.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+
+class IDisplayController final : public ServiceFramework<IDisplayController> {
+public:
+ explicit IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_);
+ ~IDisplayController() override;
+
+private:
+ Result GetCallerAppletCaptureImageEx(Out<bool> out_was_written,
+ OutBuffer<BufferAttr_HipcMapAlias> out_image_data);
+ Result TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_layer_index);
+ Result ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color);
+ Result AcquireLastForegroundCaptureSharedBuffer(Out<bool> out_was_written,
+ Out<s32> out_fbshare_layer_index);
+ Result ReleaseLastForegroundCaptureSharedBuffer();
+ Result AcquireCallerAppletCaptureSharedBuffer(Out<bool> out_was_written,
+ Out<s32> out_fbshare_layer_index);
+ Result ReleaseCallerAppletCaptureSharedBuffer();
+ Result AcquireLastApplicationCaptureSharedBuffer(Out<bool> out_was_written,
+ Out<s32> out_fbshare_layer_index);
+ Result ReleaseLastApplicationCaptureSharedBuffer();
+
+ const std::shared_ptr<Applet> applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/global_state_controller.cpp b/src/core/hle/service/am/service/global_state_controller.cpp
new file mode 100644
index 000000000..dba5d3613
--- /dev/null
+++ b/src/core/hle/service/am/service/global_state_controller.cpp
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/cradle_firmware_updater.h"
+#include "core/hle/service/am/service/global_state_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IGlobalStateController::IGlobalStateController(Core::System& system_)
+ : ServiceFramework{system_, "IGlobalStateController"},
+ m_context{system_, "IGlobalStateController"}, m_hdcp_authentication_failed_event{m_context} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "RequestToEnterSleep"},
+ {1, nullptr, "EnterSleep"},
+ {2, nullptr, "StartSleepSequence"},
+ {3, nullptr, "StartShutdownSequence"},
+ {4, nullptr, "StartRebootSequence"},
+ {9, nullptr, "IsAutoPowerDownRequested"},
+ {10, D<&IGlobalStateController::LoadAndApplyIdlePolicySettings>, "LoadAndApplyIdlePolicySettings"},
+ {11, nullptr, "NotifyCecSettingsChanged"},
+ {12, nullptr, "SetDefaultHomeButtonLongPressTime"},
+ {13, nullptr, "UpdateDefaultDisplayResolution"},
+ {14, D<&IGlobalStateController::ShouldSleepOnBoot>, "ShouldSleepOnBoot"},
+ {15, D<&IGlobalStateController::GetHdcpAuthenticationFailedEvent>, "GetHdcpAuthenticationFailedEvent"},
+ {30, D<&IGlobalStateController::OpenCradleFirmwareUpdater>, "OpenCradleFirmwareUpdater"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IGlobalStateController::~IGlobalStateController() = default;
+
+Result IGlobalStateController::LoadAndApplyIdlePolicySettings() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IGlobalStateController::ShouldSleepOnBoot(Out<bool> out_should_sleep_on_boot) {
+ LOG_INFO(Service_AM, "called");
+ *out_should_sleep_on_boot = false;
+ R_SUCCEED();
+}
+
+Result IGlobalStateController::GetHdcpAuthenticationFailedEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_AM, "called");
+ *out_event = m_hdcp_authentication_failed_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IGlobalStateController::OpenCradleFirmwareUpdater(
+ Out<SharedPointer<ICradleFirmwareUpdater>> out_cradle_firmware_updater) {
+ LOG_INFO(Service_AM, "called");
+ *out_cradle_firmware_updater = std::make_shared<ICradleFirmwareUpdater>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/global_state_controller.h b/src/core/hle/service/am/service/global_state_controller.h
new file mode 100644
index 000000000..67c753513
--- /dev/null
+++ b/src/core/hle/service/am/service/global_state_controller.h
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class ICradleFirmwareUpdater;
+
+class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
+public:
+ explicit IGlobalStateController(Core::System& system_);
+ ~IGlobalStateController() override;
+
+private:
+ Result LoadAndApplyIdlePolicySettings();
+ Result ShouldSleepOnBoot(Out<bool> out_should_sleep_on_boot);
+ Result GetHdcpAuthenticationFailedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result OpenCradleFirmwareUpdater(
+ Out<SharedPointer<ICradleFirmwareUpdater>> out_cradle_firmware_updater);
+
+ KernelHelpers::ServiceContext m_context;
+ Event m_hdcp_authentication_failed_event;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/home_menu_functions.cpp b/src/core/hle/service/am/service/home_menu_functions.cpp
new file mode 100644
index 000000000..0c4d24b58
--- /dev/null
+++ b/src/core/hle/service/am/service/home_menu_functions.cpp
@@ -0,0 +1,74 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/result.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/service/home_menu_functions.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "IHomeMenuFunctions"}, m_applet{std::move(applet)},
+ m_context{system, "IHomeMenuFunctions"}, m_pop_from_general_channel_event{m_context} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"},
+ {11, D<&IHomeMenuFunctions::LockForeground>, "LockForeground"},
+ {12, D<&IHomeMenuFunctions::UnlockForeground>, "UnlockForeground"},
+ {20, nullptr, "PopFromGeneralChannel"},
+ {21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"},
+ {30, nullptr, "GetHomeButtonWriterLockAccessor"},
+ {31, nullptr, "GetWriterLockAccessorEx"},
+ {40, nullptr, "IsSleepEnabled"},
+ {41, D<&IHomeMenuFunctions::IsRebootEnabled>, "IsRebootEnabled"},
+ {50, nullptr, "LaunchSystemApplet"},
+ {51, nullptr, "LaunchStarter"},
+ {100, nullptr, "PopRequestLaunchApplicationForDebug"},
+ {110, D<&IHomeMenuFunctions::IsForceTerminateApplicationDisabledForDebug>, "IsForceTerminateApplicationDisabledForDebug"},
+ {200, nullptr, "LaunchDevMenu"},
+ {1000, nullptr, "SetLastApplicationExitReason"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IHomeMenuFunctions::~IHomeMenuFunctions() = default;
+
+Result IHomeMenuFunctions::RequestToGetForeground() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IHomeMenuFunctions::LockForeground() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IHomeMenuFunctions::UnlockForeground() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IHomeMenuFunctions::GetPopFromGeneralChannelEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_AM, "called");
+ *out_event = m_pop_from_general_channel_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IHomeMenuFunctions::IsRebootEnabled(Out<bool> out_is_reboot_enbaled) {
+ LOG_INFO(Service_AM, "called");
+ *out_is_reboot_enbaled = true;
+ R_SUCCEED();
+}
+
+Result IHomeMenuFunctions::IsForceTerminateApplicationDisabledForDebug(
+ Out<bool> out_is_force_terminate_application_disabled_for_debug) {
+ LOG_INFO(Service_AM, "called");
+ *out_is_force_terminate_application_disabled_for_debug = false;
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/home_menu_functions.h b/src/core/hle/service/am/service/home_menu_functions.h
new file mode 100644
index 000000000..caf6fbaab
--- /dev/null
+++ b/src/core/hle/service/am/service/home_menu_functions.h
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+
+class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
+public:
+ explicit IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet);
+ ~IHomeMenuFunctions() override;
+
+private:
+ Result RequestToGetForeground();
+ Result LockForeground();
+ Result UnlockForeground();
+ Result GetPopFromGeneralChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result IsRebootEnabled(Out<bool> out_is_reboot_enbaled);
+ Result IsForceTerminateApplicationDisabledForDebug(
+ Out<bool> out_is_force_terminate_application_disabled_for_debug);
+
+ const std::shared_ptr<Applet> m_applet;
+ KernelHelpers::ServiceContext m_context;
+ Event m_pop_from_general_channel_event;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp
new file mode 100644
index 000000000..0c2426d4b
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_accessor.cpp
@@ -0,0 +1,157 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/applet_data_broker.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/frontend/applets.h"
+#include "core/hle/service/am/service/library_applet_accessor.h"
+#include "core/hle/service/am/service/storage.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
+ std::shared_ptr<AppletDataBroker> broker,
+ std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "ILibraryAppletAccessor"}, m_broker{std::move(broker)},
+ m_applet{std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ILibraryAppletAccessor::GetAppletStateChangedEvent>, "GetAppletStateChangedEvent"},
+ {1, D<&ILibraryAppletAccessor::IsCompleted>, "IsCompleted"},
+ {10, D<&ILibraryAppletAccessor::Start>, "Start"},
+ {20, D<&ILibraryAppletAccessor::RequestExit>, "RequestExit"},
+ {25, D<&ILibraryAppletAccessor::Terminate>, "Terminate"},
+ {30, D<&ILibraryAppletAccessor::GetResult>, "GetResult"},
+ {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
+ {60, D<&ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero>, "PresetLibraryAppletGpuTimeSliceZero"},
+ {100, D<&ILibraryAppletAccessor::PushInData>, "PushInData"},
+ {101, D<&ILibraryAppletAccessor::PopOutData>, "PopOutData"},
+ {102, nullptr, "PushExtraStorage"},
+ {103, D<&ILibraryAppletAccessor::PushInteractiveInData>, "PushInteractiveInData"},
+ {104, D<&ILibraryAppletAccessor::PopInteractiveOutData>, "PopInteractiveOutData"},
+ {105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"},
+ {106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"},
+ {110, nullptr, "NeedsToExitProcess"},
+ {120, nullptr, "GetLibraryAppletInfo"},
+ {150, nullptr, "RequestForAppletToGetForeground"},
+ {160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ILibraryAppletAccessor::~ILibraryAppletAccessor() = default;
+
+Result ILibraryAppletAccessor::GetAppletStateChangedEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = m_broker->GetStateChangedEvent().GetHandle();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::IsCompleted(Out<bool> out_is_completed) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_is_completed = m_broker->IsCompleted();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::GetResult(Out<Result> out_result) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_result = m_applet->terminate_result;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero() {
+ LOG_INFO(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::Start() {
+ LOG_DEBUG(Service_AM, "called");
+ m_applet->process->Run();
+ FrontendExecute();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::RequestExit() {
+ LOG_DEBUG(Service_AM, "called");
+ m_applet->message_queue.RequestExit();
+ FrontendRequestExit();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::Terminate() {
+ LOG_DEBUG(Service_AM, "called");
+ m_applet->process->Terminate();
+ FrontendRequestExit();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
+ LOG_DEBUG(Service_AM, "called");
+ m_broker->GetInData().Push(storage);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
+ LOG_DEBUG(Service_AM, "called");
+ R_RETURN(m_broker->GetOutData().Pop(out_storage.Get()));
+}
+
+Result ILibraryAppletAccessor::PushInteractiveInData(SharedPointer<IStorage> storage) {
+ LOG_DEBUG(Service_AM, "called");
+ m_broker->GetInteractiveInData().Push(storage);
+ FrontendExecuteInteractive();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::PopInteractiveOutData(Out<SharedPointer<IStorage>> out_storage) {
+ LOG_DEBUG(Service_AM, "called");
+ R_RETURN(m_broker->GetInteractiveOutData().Pop(out_storage.Get()));
+}
+
+Result ILibraryAppletAccessor::GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = m_broker->GetOutData().GetEvent();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_event = m_broker->GetInteractiveOutData().GetEvent();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handle) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
+ // actually used anywhere
+ *out_handle = 0xdeadbeef;
+ R_SUCCEED();
+}
+
+void ILibraryAppletAccessor::FrontendExecute() {
+ if (m_applet->frontend) {
+ m_applet->frontend->Initialize();
+ m_applet->frontend->Execute();
+ }
+}
+
+void ILibraryAppletAccessor::FrontendExecuteInteractive() {
+ if (m_applet->frontend) {
+ m_applet->frontend->ExecuteInteractive();
+ m_applet->frontend->Execute();
+ }
+}
+
+void ILibraryAppletAccessor::FrontendRequestExit() {
+ if (m_applet->frontend) {
+ m_applet->frontend->RequestExit();
+ }
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_accessor.h b/src/core/hle/service/am/service/library_applet_accessor.h
new file mode 100644
index 000000000..97d3b6c8a
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_accessor.h
@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class AppletDataBroker;
+struct Applet;
+class IStorage;
+
+class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
+public:
+ explicit ILibraryAppletAccessor(Core::System& system_, std::shared_ptr<AppletDataBroker> broker,
+ std::shared_ptr<Applet> applet);
+ ~ILibraryAppletAccessor();
+
+private:
+ Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result IsCompleted(Out<bool> out_is_completed);
+ Result GetResult(Out<Result> out_result);
+ Result PresetLibraryAppletGpuTimeSliceZero();
+ Result Start();
+ Result RequestExit();
+ Result Terminate();
+ Result PushInData(SharedPointer<IStorage> storage);
+ Result PopOutData(Out<SharedPointer<IStorage>> out_storage);
+ Result PushInteractiveInData(SharedPointer<IStorage> storage);
+ Result PopInteractiveOutData(Out<SharedPointer<IStorage>> out_storage);
+ Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
+
+ void FrontendExecute();
+ void FrontendExecuteInteractive();
+ void FrontendRequestExit();
+
+ const std::shared_ptr<AppletDataBroker> m_broker;
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp
new file mode 100644
index 000000000..c97358d81
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_creator.cpp
@@ -0,0 +1,268 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/am/applet_data_broker.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/frontend/applets.h"
+#include "core/hle/service/am/library_applet_storage.h"
+#include "core/hle/service/am/service/library_applet_accessor.h"
+#include "core/hle/service/am/service/library_applet_creator.h"
+#include "core/hle/service/am/service/storage.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::AM {
+
+namespace {
+
+bool ShouldCreateGuestApplet(AppletId applet_id) {
+#define X(Name, name) \
+ if (applet_id == AppletId::Name && \
+ Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \
+ return false; \
+ }
+
+ X(Cabinet, cabinet)
+ X(Controller, controller)
+ X(DataErase, data_erase)
+ X(Error, error)
+ X(NetConnect, net_connect)
+ X(ProfileSelect, player_select)
+ X(SoftwareKeyboard, swkbd)
+ X(MiiEdit, mii_edit)
+ X(Web, web)
+ X(Shop, shop)
+ X(PhotoViewer, photo_viewer)
+ X(OfflineWeb, offline_web)
+ X(LoginShare, login_share)
+ X(WebAuth, wifi_web_auth)
+ X(MyPage, my_page)
+
+#undef X
+
+ return true;
+}
+
+AppletProgramId AppletIdToProgramId(AppletId applet_id) {
+ switch (applet_id) {
+ case AppletId::OverlayDisplay:
+ return AppletProgramId::OverlayDisplay;
+ case AppletId::QLaunch:
+ return AppletProgramId::QLaunch;
+ case AppletId::Starter:
+ return AppletProgramId::Starter;
+ case AppletId::Auth:
+ return AppletProgramId::Auth;
+ case AppletId::Cabinet:
+ return AppletProgramId::Cabinet;
+ case AppletId::Controller:
+ return AppletProgramId::Controller;
+ case AppletId::DataErase:
+ return AppletProgramId::DataErase;
+ case AppletId::Error:
+ return AppletProgramId::Error;
+ case AppletId::NetConnect:
+ return AppletProgramId::NetConnect;
+ case AppletId::ProfileSelect:
+ return AppletProgramId::ProfileSelect;
+ case AppletId::SoftwareKeyboard:
+ return AppletProgramId::SoftwareKeyboard;
+ case AppletId::MiiEdit:
+ return AppletProgramId::MiiEdit;
+ case AppletId::Web:
+ return AppletProgramId::Web;
+ case AppletId::Shop:
+ return AppletProgramId::Shop;
+ case AppletId::PhotoViewer:
+ return AppletProgramId::PhotoViewer;
+ case AppletId::Settings:
+ return AppletProgramId::Settings;
+ case AppletId::OfflineWeb:
+ return AppletProgramId::OfflineWeb;
+ case AppletId::LoginShare:
+ return AppletProgramId::LoginShare;
+ case AppletId::WebAuth:
+ return AppletProgramId::WebAuth;
+ case AppletId::MyPage:
+ return AppletProgramId::MyPage;
+ default:
+ return static_cast<AppletProgramId>(0);
+ }
+}
+
+std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
+ std::shared_ptr<Applet> caller_applet,
+ AppletId applet_id,
+ LibraryAppletMode mode) {
+ const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
+ if (program_id == 0) {
+ // Unknown applet
+ return {};
+ }
+
+ // TODO: enable other versions of applets
+ enum : u8 {
+ Firmware1400 = 14,
+ Firmware1500 = 15,
+ Firmware1600 = 16,
+ Firmware1700 = 17,
+ };
+
+ auto process = std::make_unique<Process>(system);
+ if (!process->Initialize(program_id, Firmware1400, Firmware1700)) {
+ // Couldn't initialize the guest process
+ return {};
+ }
+
+ const auto applet = std::make_shared<Applet>(system, std::move(process));
+ applet->program_id = program_id;
+ applet->applet_id = applet_id;
+ applet->type = AppletType::LibraryApplet;
+ applet->library_applet_mode = mode;
+
+ // Set focus state
+ switch (mode) {
+ case LibraryAppletMode::AllForeground:
+ case LibraryAppletMode::NoUi:
+ case LibraryAppletMode::PartialForeground:
+ case LibraryAppletMode::PartialForegroundIndirectDisplay:
+ applet->hid_registration.EnableAppletToGetInput(true);
+ applet->focus_state = FocusState::InFocus;
+ applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);
+ break;
+ case LibraryAppletMode::AllForegroundInitiallyHidden:
+ applet->hid_registration.EnableAppletToGetInput(false);
+ applet->focus_state = FocusState::NotInFocus;
+ applet->display_layer_manager.SetWindowVisibility(false);
+ applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground);
+ break;
+ }
+
+ auto broker = std::make_shared<AppletDataBroker>(system);
+ applet->caller_applet = caller_applet;
+ applet->caller_applet_broker = broker;
+
+ system.GetAppletManager().InsertApplet(applet);
+
+ return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
+}
+
+std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
+ std::shared_ptr<Applet> caller_applet,
+ AppletId applet_id,
+ LibraryAppletMode mode) {
+ const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
+
+ auto process = std::make_unique<Process>(system);
+ auto applet = std::make_shared<Applet>(system, std::move(process));
+ applet->program_id = program_id;
+ applet->applet_id = applet_id;
+ applet->type = AppletType::LibraryApplet;
+ applet->library_applet_mode = mode;
+
+ auto storage = std::make_shared<AppletDataBroker>(system);
+ applet->caller_applet = caller_applet;
+ applet->caller_applet_broker = storage;
+ applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
+
+ return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
+}
+
+} // namespace
+
+ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "ILibraryAppletCreator"}, m_applet{std::move(applet)} {
+ static const FunctionInfo functions[] = {
+ {0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},
+ {1, nullptr, "TerminateAllLibraryApplets"},
+ {2, nullptr, "AreAnyLibraryAppletsLeft"},
+ {10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"},
+ {11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"},
+ {12, D<&ILibraryAppletCreator::CreateHandleStorage>, "CreateHandleStorage"},
+ };
+ RegisterHandlers(functions);
+}
+
+ILibraryAppletCreator::~ILibraryAppletCreator() = default;
+
+Result ILibraryAppletCreator::CreateLibraryApplet(
+ Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
+ LibraryAppletMode library_applet_mode) {
+ LOG_DEBUG(Service_AM, "called with applet_id={} applet_mode={}", applet_id,
+ library_applet_mode);
+
+ std::shared_ptr<ILibraryAppletAccessor> library_applet;
+ if (ShouldCreateGuestApplet(applet_id)) {
+ library_applet = CreateGuestApplet(system, m_applet, applet_id, library_applet_mode);
+ }
+ if (!library_applet) {
+ library_applet = CreateFrontendApplet(system, m_applet, applet_id, library_applet_mode);
+ }
+ if (!library_applet) {
+ LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
+ R_THROW(ResultUnknown);
+ }
+
+ // Applet is created, can now be launched.
+ m_applet->library_applet_launchable_event.Signal();
+ *out_library_applet_accessor = library_applet;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletCreator::CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size) {
+ LOG_DEBUG(Service_AM, "called, size={}", size);
+
+ if (size <= 0) {
+ LOG_ERROR(Service_AM, "size is less than or equal to 0");
+ R_THROW(ResultUnknown);
+ }
+
+ *out_storage = std::make_shared<IStorage>(system, AM::CreateStorage(std::vector<u8>(size)));
+ R_SUCCEED();
+}
+
+Result ILibraryAppletCreator::CreateTransferMemoryStorage(
+ Out<SharedPointer<IStorage>> out_storage, bool is_writable, s64 size,
+ InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) {
+ LOG_DEBUG(Service_AM, "called, is_writable={} size={}", is_writable, size);
+
+ if (size <= 0) {
+ LOG_ERROR(Service_AM, "size is less than or equal to 0");
+ R_THROW(ResultUnknown);
+ }
+
+ if (!transfer_memory_handle) {
+ LOG_ERROR(Service_AM, "transfer_memory_handle is null");
+ R_THROW(ResultUnknown);
+ }
+
+ *out_storage = std::make_shared<IStorage>(
+ system, AM::CreateTransferMemoryStorage(transfer_memory_handle->GetOwner()->GetMemory(),
+ transfer_memory_handle.Get(), is_writable, size));
+ R_SUCCEED();
+}
+
+Result ILibraryAppletCreator::CreateHandleStorage(
+ Out<SharedPointer<IStorage>> out_storage, s64 size,
+ InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) {
+ LOG_DEBUG(Service_AM, "called, size={}", size);
+
+ if (size <= 0) {
+ LOG_ERROR(Service_AM, "size is less than or equal to 0");
+ R_THROW(ResultUnknown);
+ }
+
+ if (!transfer_memory_handle) {
+ LOG_ERROR(Service_AM, "transfer_memory_handle is null");
+ R_THROW(ResultUnknown);
+ }
+
+ *out_storage = std::make_shared<IStorage>(
+ system, AM::CreateHandleStorage(transfer_memory_handle->GetOwner()->GetMemory(),
+ transfer_memory_handle.Get(), size));
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_creator.h b/src/core/hle/service/am/service/library_applet_creator.h
new file mode 100644
index 000000000..fe6d40eb3
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_creator.h
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+class ILibraryAppletAccessor;
+class IStorage;
+
+class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
+public:
+ explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet);
+ ~ILibraryAppletCreator() override;
+
+private:
+ Result CreateLibraryApplet(
+ Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
+ LibraryAppletMode library_applet_mode);
+ Result CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size);
+ Result CreateTransferMemoryStorage(
+ Out<SharedPointer<IStorage>> out_storage, bool is_writable, s64 size,
+ InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
+ Result CreateHandleStorage(Out<SharedPointer<IStorage>> out_storage, s64 size,
+ InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
+
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_proxy.cpp b/src/core/hle/service/am/service/library_applet_proxy.cpp
new file mode 100644
index 000000000..58e709347
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_proxy.cpp
@@ -0,0 +1,132 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/applet_common_functions.h"
+#include "core/hle/service/am/service/audio_controller.h"
+#include "core/hle/service/am/service/common_state_getter.h"
+#include "core/hle/service/am/service/debug_functions.h"
+#include "core/hle/service/am/service/display_controller.h"
+#include "core/hle/service/am/service/global_state_controller.h"
+#include "core/hle/service/am/service/home_menu_functions.h"
+#include "core/hle/service/am/service/library_applet_creator.h"
+#include "core/hle/service/am/service/library_applet_proxy.h"
+#include "core/hle/service/am/service/library_applet_self_accessor.h"
+#include "core/hle/service/am/service/process_winding_controller.h"
+#include "core/hle/service/am/service/self_controller.h"
+#include "core/hle/service/am/service/window_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process)
+ : ServiceFramework{system_, "ILibraryAppletProxy"}, m_process{process}, m_applet{
+ std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
+ {1, D<&ILibraryAppletProxy::GetSelfController>, "GetSelfController"},
+ {2, D<&ILibraryAppletProxy::GetWindowController>, "GetWindowController"},
+ {3, D<&ILibraryAppletProxy::GetAudioController>, "GetAudioController"},
+ {4, D<&ILibraryAppletProxy::GetDisplayController>, "GetDisplayController"},
+ {10, D<&ILibraryAppletProxy::GetProcessWindingController>, "GetProcessWindingController"},
+ {11, D<&ILibraryAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
+ {20, D<&ILibraryAppletProxy::OpenLibraryAppletSelfAccessor>, "OpenLibraryAppletSelfAccessor"},
+ {21, D<&ILibraryAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"},
+ {22, D<&ILibraryAppletProxy::GetHomeMenuFunctions>, "GetHomeMenuFunctions"},
+ {23, D<&ILibraryAppletProxy::GetGlobalStateController>, "GetGlobalStateController"},
+ {1000, D<&ILibraryAppletProxy::GetDebugFunctions>, "GetDebugFunctions"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ILibraryAppletProxy::~ILibraryAppletProxy() = default;
+
+Result ILibraryAppletProxy::GetAudioController(
+ Out<SharedPointer<IAudioController>> out_audio_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_audio_controller = std::make_shared<IAudioController>(system);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetDisplayController(
+ Out<SharedPointer<IDisplayController>> out_display_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_display_controller = std::make_shared<IDisplayController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetProcessWindingController(
+ Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetDebugFunctions(
+ Out<SharedPointer<IDebugFunctions>> out_debug_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_debug_functions = std::make_shared<IDebugFunctions>(system);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetWindowController(
+ Out<SharedPointer<IWindowController>> out_window_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_window_controller = std::make_shared<IWindowController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetSelfController(
+ Out<SharedPointer<ISelfController>> out_self_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetCommonStateGetter(
+ Out<SharedPointer<ICommonStateGetter>> out_common_state_getter) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetLibraryAppletCreator(
+ Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(
+ Out<SharedPointer<ILibraryAppletSelfAccessor>> out_library_applet_self_accessor) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_library_applet_self_accessor =
+ std::make_shared<ILibraryAppletSelfAccessor>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetAppletCommonFunctions(
+ Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetHomeMenuFunctions(
+ Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_home_menu_functions = std::make_shared<IHomeMenuFunctions>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletProxy::GetGlobalStateController(
+ Out<SharedPointer<IGlobalStateController>> out_global_state_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_global_state_controller = std::make_shared<IGlobalStateController>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_proxy.h b/src/core/hle/service/am/service/library_applet_proxy.h
new file mode 100644
index 000000000..7d0714b85
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_proxy.h
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+class IAppletCommonFunctions;
+class IAudioController;
+class ICommonStateGetter;
+class IDebugFunctions;
+class IDisplayController;
+class IHomeMenuFunctions;
+class IGlobalStateController;
+class ILibraryAppletCreator;
+class ILibraryAppletSelfAccessor;
+class IProcessWindingController;
+class ISelfController;
+class IWindowController;
+
+class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
+public:
+ explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process);
+ ~ILibraryAppletProxy();
+
+private:
+ Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
+ Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
+ Result GetProcessWindingController(
+ Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
+ Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
+ Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
+ Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
+ Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
+ Result GetLibraryAppletCreator(
+ Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
+ Result OpenLibraryAppletSelfAccessor(
+ Out<SharedPointer<ILibraryAppletSelfAccessor>> out_library_applet_self_accessor);
+ Result GetAppletCommonFunctions(
+ Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions);
+ Result GetHomeMenuFunctions(Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions);
+ Result GetGlobalStateController(
+ Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
+
+ Kernel::KProcess* const m_process;
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp
new file mode 100644
index 000000000..330eb26f0
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp
@@ -0,0 +1,325 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/am/applet_data_broker.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/frontend/applets.h"
+#include "core/hle/service/am/service/library_applet_self_accessor.h"
+#include "core/hle/service/am/service/storage.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/glue/glue_manager.h"
+#include "core/hle/service/ns/application_manager_interface.h"
+#include "core/hle/service/ns/service_getter_interface.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::AM {
+
+namespace {
+
+AppletIdentityInfo GetCallerIdentity(Applet& applet) {
+ if (const auto caller_applet = applet.caller_applet.lock(); caller_applet) {
+ // TODO: is this actually the application ID?
+ return {
+ .applet_id = caller_applet->applet_id,
+ .application_id = caller_applet->program_id,
+ };
+ } else {
+ return {
+ .applet_id = AppletId::QLaunch,
+ .application_id = 0x0100000000001000ull,
+ };
+ }
+}
+
+} // namespace
+
+ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
+ std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)},
+ m_broker{m_applet->caller_applet_broker} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ILibraryAppletSelfAccessor::PopInData>, "PopInData"},
+ {1, D<&ILibraryAppletSelfAccessor::PushOutData>, "PushOutData"},
+ {2, D<&ILibraryAppletSelfAccessor::PopInteractiveInData>, "PopInteractiveInData"},
+ {3, D<&ILibraryAppletSelfAccessor::PushInteractiveOutData>, "PushInteractiveOutData"},
+ {5, D<&ILibraryAppletSelfAccessor::GetPopInDataEvent>, "GetPopInDataEvent"},
+ {6, D<&ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent>, "GetPopInteractiveInDataEvent"},
+ {10, D<&ILibraryAppletSelfAccessor::ExitProcessAndReturn>, "ExitProcessAndReturn"},
+ {11, D<&ILibraryAppletSelfAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"},
+ {12, D<&ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo>, "GetMainAppletIdentityInfo"},
+ {13, D<&ILibraryAppletSelfAccessor::CanUseApplicationCore>, "CanUseApplicationCore"},
+ {14, D<&ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo>, "GetCallerAppletIdentityInfo"},
+ {15, D<&ILibraryAppletSelfAccessor::GetMainAppletApplicationControlProperty>, "GetMainAppletApplicationControlProperty"},
+ {16, D<&ILibraryAppletSelfAccessor::GetMainAppletStorageId>, "GetMainAppletStorageId"},
+ {17, D<&ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfoStack>, "GetCallerAppletIdentityInfoStack"},
+ {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"},
+ {19, D<&ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout>, "GetDesirableKeyboardLayout"},
+ {20, nullptr, "PopExtraStorage"},
+ {25, nullptr, "GetPopExtraStorageEvent"},
+ {30, nullptr, "UnpopInData"},
+ {31, nullptr, "UnpopExtraStorage"},
+ {40, nullptr, "GetIndirectLayerProducerHandle"},
+ {50, D<&ILibraryAppletSelfAccessor::ReportVisibleError>, "ReportVisibleError"},
+ {51, D<&ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext>, "ReportVisibleErrorWithErrorContext"},
+ {60, D<&ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage>, "GetMainAppletApplicationDesiredLanguage"},
+ {70, D<&ILibraryAppletSelfAccessor::GetCurrentApplicationId>, "GetCurrentApplicationId"},
+ {80, nullptr, "RequestExitToSelf"},
+ {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"},
+ {100, nullptr, "CreateGameMovieTrimmer"},
+ {101, nullptr, "ReserveResourceForMovieOperation"},
+ {102, nullptr, "UnreserveResourceForMovieOperation"},
+ {110, D<&ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers>, "GetMainAppletAvailableUsers"},
+ {120, nullptr, "GetLaunchStorageInfoForDebug"},
+ {130, nullptr, "GetGpuErrorDetectedSystemEvent"},
+ {140, nullptr, "SetApplicationMemoryReservation"},
+ {150, D<&ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually>, "ShouldSetGpuTimeSliceManually"},
+ {160, D<&ILibraryAppletSelfAccessor::Cmd160>, "Cmd160"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+}
+
+ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
+
+Result ILibraryAppletSelfAccessor::PopInData(Out<SharedPointer<IStorage>> out_storage) {
+ LOG_INFO(Service_AM, "called");
+ R_RETURN(m_broker->GetInData().Pop(out_storage));
+}
+
+Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer<IStorage> storage) {
+ LOG_INFO(Service_AM, "called");
+ m_broker->GetOutData().Push(storage);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::PopInteractiveInData(Out<SharedPointer<IStorage>> out_storage) {
+ LOG_INFO(Service_AM, "called");
+ R_RETURN(m_broker->GetInteractiveInData().Pop(out_storage));
+}
+
+Result ILibraryAppletSelfAccessor::PushInteractiveOutData(SharedPointer<IStorage> storage) {
+ LOG_INFO(Service_AM, "called");
+ m_broker->GetInteractiveOutData().Push(storage);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetPopInDataEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_AM, "called");
+ *out_event = m_broker->GetInData().GetEvent();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_AM, "called");
+ *out_event = m_broker->GetInteractiveInData().GetEvent();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetLibraryAppletInfo(
+ Out<LibraryAppletInfo> out_library_applet_info) {
+ LOG_INFO(Service_AM, "called");
+ *out_library_applet_info = {
+ .applet_id = m_applet->applet_id,
+ .library_applet_mode = m_applet->library_applet_mode,
+ };
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(
+ Out<AppletIdentityInfo> out_identity_info) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_identity_info = {
+ .applet_id = AppletId::QLaunch,
+ .application_id = 0x0100000000001000ull,
+ };
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::CanUseApplicationCore(Out<bool> out_can_use_application_core) {
+ // TODO: This appears to read the NPDM from state and check the core mask of the applet.
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_can_use_application_core = false;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetMainAppletApplicationControlProperty(
+ OutLargeData<std::array<u8, 0x4000>, BufferAttr_HipcMapAlias> out_nacp) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // TODO: this should be the main applet, not the caller applet
+ const auto application = GetCallerIdentity(*m_applet);
+ std::vector<u8> nacp;
+ const auto result =
+ system.GetARPManager().GetControlProperty(&nacp, application.application_id);
+
+ if (R_SUCCEEDED(result)) {
+ std::memcpy(out_nacp->data(), nacp.data(), std::min(nacp.size(), out_nacp->size()));
+ }
+
+ R_RETURN(result);
+}
+
+Result ILibraryAppletSelfAccessor::GetMainAppletStorageId(Out<FileSys::StorageId> out_storage_id) {
+ LOG_INFO(Service_AM, "(STUBBED) called");
+ *out_storage_id = FileSys::StorageId::NandUser;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::ExitProcessAndReturn() {
+ LOG_INFO(Service_AM, "called");
+ system.GetAppletManager().TerminateAndRemoveApplet(m_applet->aruid);
+ m_broker->SignalCompletion();
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(
+ Out<AppletIdentityInfo> out_identity_info) {
+ LOG_INFO(Service_AM, "called");
+ *out_identity_info = GetCallerIdentity(*m_applet);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfoStack(
+ Out<s32> out_count, OutArray<AppletIdentityInfo, BufferAttr_HipcMapAlias> out_identity_info) {
+ LOG_INFO(Service_AM, "called");
+
+ std::shared_ptr<Applet> applet = m_applet;
+ *out_count = 0;
+
+ do {
+ if (*out_count >= static_cast<s32>(out_identity_info.size())) {
+ break;
+ }
+ out_identity_info[(*out_count)++] = GetCallerIdentity(*applet);
+ } while ((applet = applet->caller_applet.lock()));
+
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(Out<u32> out_desirable_layout) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_desirable_layout = 0;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::ReportVisibleError(ErrorCode error_code) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, error {}-{}", error_code.category,
+ error_code.number);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
+ ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, error {}-{}", error_code.category,
+ error_code.number);
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(
+ Out<u64> out_desired_language) {
+ // FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage
+ // FIXME: all of this stuff belongs to ns
+ auto identity = GetCallerIdentity(*m_applet);
+
+ // TODO(bunnei): This should be configurable
+ LOG_DEBUG(Service_AM, "called");
+
+ // Get supported languages from NACP, if possible
+ // Default to 0 (all languages supported)
+ u32 supported_languages = 0;
+
+ const auto res = [this, identity] {
+ const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ auto metadata = pm.GetControlMetadata();
+ if (metadata.first != nullptr) {
+ return metadata;
+ }
+
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ return pm_update.GetControlMetadata();
+ }();
+
+ if (res.first != nullptr) {
+ supported_languages = res.first->GetSupportedLanguages();
+ }
+
+ // Call IApplicationManagerInterface implementation.
+ auto& service_manager = system.ServiceManager();
+ auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2");
+
+ std::shared_ptr<NS::IApplicationManagerInterface> app_man;
+ R_TRY(ns_am2->GetApplicationManagerInterface(&app_man));
+
+ // Get desired application language
+ NS::ApplicationLanguage desired_language{};
+ R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
+
+ // Convert to settings language code.
+ u64 language_code{};
+ R_TRY(app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language));
+
+ LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
+
+ *out_desired_language = language_code;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetCurrentApplicationId(Out<u64> out_application_id) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // TODO: this should be the main applet, not the caller applet
+ const auto main_applet = GetCallerIdentity(*m_applet);
+ *out_application_id = main_applet.application_id;
+
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(
+ Out<bool> out_can_select_any_user, Out<s32> out_users_count,
+ OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users) {
+ const Service::Account::ProfileManager manager{};
+
+ *out_can_select_any_user = false;
+ *out_users_count = -1;
+
+ LOG_INFO(Service_AM, "called");
+
+ if (manager.GetUserCount() > 0) {
+ *out_can_select_any_user = true;
+ *out_users_count = static_cast<s32>(manager.GetUserCount());
+
+ const auto users = manager.GetAllUsers();
+ for (size_t i = 0; i < users.size() && i < out_users.size(); i++) {
+ out_users[i] = users[i];
+ }
+ }
+
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(
+ Out<bool> out_should_set_gpu_time_slice_manually) {
+ LOG_INFO(Service_AM, "(STUBBED) called");
+ *out_should_set_gpu_time_slice_manually = false;
+ R_SUCCEED();
+}
+
+Result ILibraryAppletSelfAccessor::Cmd160(Out<u64> out_unknown0) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_unknown0 = 0;
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.h b/src/core/hle/service/am/service/library_applet_self_accessor.h
new file mode 100644
index 000000000..3e60393c2
--- /dev/null
+++ b/src/core/hle/service/am/service/library_applet_self_accessor.h
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace FileSys {
+enum class StorageId : u8;
+}
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::AM {
+
+class AppletDataBroker;
+struct Applet;
+class IStorage;
+
+struct LibraryAppletInfo {
+ AppletId applet_id;
+ LibraryAppletMode library_applet_mode;
+};
+static_assert(sizeof(LibraryAppletInfo) == 0x8, "LibraryAppletInfo has incorrect size.");
+
+struct ErrorCode {
+ u32 category;
+ u32 number;
+};
+static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
+
+struct ErrorContext {
+ u8 type;
+ INSERT_PADDING_BYTES_NOINIT(0x7);
+ std::array<u8, 0x1f4> data;
+ Result result;
+};
+static_assert(sizeof(ErrorContext) == 0x200, "ErrorContext has incorrect size.");
+
+class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
+public:
+ explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet);
+ ~ILibraryAppletSelfAccessor() override;
+
+private:
+ Result PopInData(Out<SharedPointer<IStorage>> out_storage);
+ Result PushOutData(SharedPointer<IStorage> storage);
+ Result PopInteractiveInData(Out<SharedPointer<IStorage>> out_storage);
+ Result PushInteractiveOutData(SharedPointer<IStorage> storage);
+ Result GetPopInDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetPopInteractiveInDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info);
+ Result GetMainAppletIdentityInfo(Out<AppletIdentityInfo> out_identity_info);
+ Result CanUseApplicationCore(Out<bool> out_can_use_application_core);
+ Result GetMainAppletApplicationControlProperty(
+ OutLargeData<std::array<u8, 0x4000>, BufferAttr_HipcMapAlias> out_nacp);
+ Result GetMainAppletStorageId(Out<FileSys::StorageId> out_storage_id);
+ Result ExitProcessAndReturn();
+ Result GetCallerAppletIdentityInfo(Out<AppletIdentityInfo> out_identity_info);
+ Result GetCallerAppletIdentityInfoStack(
+ Out<s32> out_count,
+ OutArray<AppletIdentityInfo, BufferAttr_HipcMapAlias> out_identity_info);
+ Result GetDesirableKeyboardLayout(Out<u32> out_desirable_layout);
+ Result ReportVisibleError(ErrorCode error_code);
+ Result ReportVisibleErrorWithErrorContext(
+ ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
+ Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
+ Result GetCurrentApplicationId(Out<u64> out_application_id);
+ Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
+ OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users);
+ Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually);
+ Result Cmd160(Out<u64> out_unknown0);
+
+ const std::shared_ptr<Applet> m_applet;
+ const std::shared_ptr<AppletDataBroker> m_broker;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/lock_accessor.cpp b/src/core/hle/service/am/service/lock_accessor.cpp
new file mode 100644
index 000000000..8e556fdd6
--- /dev/null
+++ b/src/core/hle/service/am/service/lock_accessor.cpp
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/lock_accessor.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+ILockAccessor::ILockAccessor(Core::System& system_)
+ : ServiceFramework{system_, "ILockAccessor"}, m_context{system_, "ILockAccessor"},
+ m_event{m_context} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, D<&ILockAccessor::TryLock>, "TryLock"},
+ {2, D<&ILockAccessor::Unlock>, "Unlock"},
+ {3, D<&ILockAccessor::GetEvent>, "GetEvent"},
+ {4, D<&ILockAccessor::IsLocked>, "IsLocked"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ m_event.Signal();
+}
+
+ILockAccessor::~ILockAccessor() = default;
+
+Result ILockAccessor::TryLock(Out<bool> out_is_locked,
+ OutCopyHandle<Kernel::KReadableEvent> out_handle,
+ bool return_handle) {
+ LOG_INFO(Service_AM, "called, return_handle={}", return_handle);
+
+ {
+ std::scoped_lock lk{m_mutex};
+ if (m_is_locked) {
+ *out_is_locked = false;
+ } else {
+ m_is_locked = true;
+ *out_is_locked = true;
+ }
+ }
+
+ if (return_handle) {
+ *out_handle = m_event.GetHandle();
+ }
+
+ R_SUCCEED();
+}
+
+Result ILockAccessor::Unlock() {
+ LOG_INFO(Service_AM, "called");
+
+ {
+ std::scoped_lock lk{m_mutex};
+ m_is_locked = false;
+ }
+
+ m_event.Signal();
+ R_SUCCEED();
+}
+
+Result ILockAccessor::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_handle) {
+ LOG_INFO(Service_AM, "called");
+ *out_handle = m_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result ILockAccessor::IsLocked(Out<bool> out_is_locked) {
+ LOG_INFO(Service_AM, "called");
+ std::scoped_lock lk{m_mutex};
+ *out_is_locked = m_is_locked;
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/lock_accessor.h b/src/core/hle/service/am/service/lock_accessor.h
new file mode 100644
index 000000000..9bfb5c050
--- /dev/null
+++ b/src/core/hle/service/am/service/lock_accessor.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class ILockAccessor final : public ServiceFramework<ILockAccessor> {
+public:
+ explicit ILockAccessor(Core::System& system_);
+ ~ILockAccessor() override;
+
+private:
+ Result TryLock(Out<bool> out_is_locked, OutCopyHandle<Kernel::KReadableEvent> out_handle,
+ bool return_handle);
+ Result Unlock();
+ Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_handle);
+ Result IsLocked(Out<bool> out_is_locked);
+
+private:
+ KernelHelpers::ServiceContext m_context;
+ Event m_event;
+ std::mutex m_mutex{};
+ bool m_is_locked{};
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/process_winding_controller.cpp b/src/core/hle/service/am/service/process_winding_controller.cpp
new file mode 100644
index 000000000..10df830d7
--- /dev/null
+++ b/src/core/hle/service/am/service/process_winding_controller.cpp
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/frontend/applets.h"
+#include "core/hle/service/am/service/library_applet_accessor.h"
+#include "core/hle/service/am/service/process_winding_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IProcessWindingController::IProcessWindingController(Core::System& system_,
+ std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "IProcessWindingController"}, m_applet{std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IProcessWindingController::GetLaunchReason>, "GetLaunchReason"},
+ {11, D<&IProcessWindingController::OpenCallingLibraryApplet>, "OpenCallingLibraryApplet"},
+ {21, nullptr, "PushContext"},
+ {22, nullptr, "PopContext"},
+ {23, nullptr, "CancelWindingReservation"},
+ {30, nullptr, "WindAndDoReserved"},
+ {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
+ {41, nullptr, "ReserveToStartAndWait"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IProcessWindingController::~IProcessWindingController() = default;
+
+Result IProcessWindingController::GetLaunchReason(
+ Out<AppletProcessLaunchReason> out_launch_reason) {
+ LOG_INFO(Service_AM, "called");
+ *out_launch_reason = m_applet->launch_reason;
+ R_SUCCEED();
+}
+
+Result IProcessWindingController::OpenCallingLibraryApplet(
+ Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) {
+ LOG_INFO(Service_AM, "called");
+
+ const auto caller_applet = m_applet->caller_applet.lock();
+ if (caller_applet == nullptr) {
+ LOG_ERROR(Service_AM, "No caller applet available");
+ R_THROW(ResultUnknown);
+ }
+
+ *out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>(
+ system, m_applet->caller_applet_broker, caller_applet);
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/process_winding_controller.h b/src/core/hle/service/am/service/process_winding_controller.h
new file mode 100644
index 000000000..4408af1f1
--- /dev/null
+++ b/src/core/hle/service/am/service/process_winding_controller.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+class ILibraryAppletAccessor;
+
+class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
+public:
+ explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_);
+ ~IProcessWindingController() override;
+
+private:
+ Result GetLaunchReason(Out<AppletProcessLaunchReason> out_launch_reason);
+ Result OpenCallingLibraryApplet(
+ Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet);
+
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp
new file mode 100644
index 000000000..06314407c
--- /dev/null
+++ b/src/core/hle/service/am/service/self_controller.cpp
@@ -0,0 +1,394 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/frontend/applets.h"
+#include "core/hle/service/am/service/self_controller.h"
+#include "core/hle/service/caps/caps_su.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/nvnflinger/nvnflinger.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/vi/vi_results.h"
+
+namespace Service::AM {
+
+ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process)
+ : ServiceFramework{system_, "ISelfController"}, m_process{process}, m_applet{
+ std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ISelfController::Exit>, "Exit"},
+ {1, D<&ISelfController::LockExit>, "LockExit"},
+ {2, D<&ISelfController::UnlockExit>, "UnlockExit"},
+ {3, D<&ISelfController::EnterFatalSection>, "EnterFatalSection"},
+ {4, D<&ISelfController::LeaveFatalSection>, "LeaveFatalSection"},
+ {9, D<&ISelfController::GetLibraryAppletLaunchableEvent>, "GetLibraryAppletLaunchableEvent"},
+ {10, D<&ISelfController::SetScreenShotPermission>, "SetScreenShotPermission"},
+ {11, D<&ISelfController::SetOperationModeChangedNotification>, "SetOperationModeChangedNotification"},
+ {12, D<&ISelfController::SetPerformanceModeChangedNotification>, "SetPerformanceModeChangedNotification"},
+ {13, D<&ISelfController::SetFocusHandlingMode>, "SetFocusHandlingMode"},
+ {14, D<&ISelfController::SetRestartMessageEnabled>, "SetRestartMessageEnabled"},
+ {15, D<&ISelfController::SetScreenShotAppletIdentityInfo>, "SetScreenShotAppletIdentityInfo"},
+ {16, D<&ISelfController::SetOutOfFocusSuspendingEnabled>, "SetOutOfFocusSuspendingEnabled"},
+ {17, nullptr, "SetControllerFirmwareUpdateSection"},
+ {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
+ {19, D<&ISelfController::SetAlbumImageOrientation>, "SetAlbumImageOrientation"},
+ {20, nullptr, "SetDesirableKeyboardLayout"},
+ {21, nullptr, "GetScreenShotProgramId"},
+ {40, D<&ISelfController::CreateManagedDisplayLayer>, "CreateManagedDisplayLayer"},
+ {41, D<&ISelfController::IsSystemBufferSharingEnabled>, "IsSystemBufferSharingEnabled"},
+ {42, D<&ISelfController::GetSystemSharedLayerHandle>, "GetSystemSharedLayerHandle"},
+ {43, D<&ISelfController::GetSystemSharedBufferHandle>, "GetSystemSharedBufferHandle"},
+ {44, D<&ISelfController::CreateManagedDisplaySeparableLayer>, "CreateManagedDisplaySeparableLayer"},
+ {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
+ {46, nullptr, "SetRecordingLayerCompositionEnabled"},
+ {50, D<&ISelfController::SetHandlesRequestToDisplay>, "SetHandlesRequestToDisplay"},
+ {51, D<&ISelfController::ApproveToDisplay>, "ApproveToDisplay"},
+ {60, D<&ISelfController::OverrideAutoSleepTimeAndDimmingTime>, "OverrideAutoSleepTimeAndDimmingTime"},
+ {61, D<&ISelfController::SetMediaPlaybackState>, "SetMediaPlaybackState"},
+ {62, D<&ISelfController::SetIdleTimeDetectionExtension>, "SetIdleTimeDetectionExtension"},
+ {63, D<&ISelfController::GetIdleTimeDetectionExtension>, "GetIdleTimeDetectionExtension"},
+ {64, nullptr, "SetInputDetectionSourceSet"},
+ {65, D<&ISelfController::ReportUserIsActive>, "ReportUserIsActive"},
+ {66, nullptr, "GetCurrentIlluminance"},
+ {67, nullptr, "IsIlluminanceAvailable"},
+ {68, D<&ISelfController::SetAutoSleepDisabled>, "SetAutoSleepDisabled"},
+ {69, D<&ISelfController::IsAutoSleepDisabled>, "IsAutoSleepDisabled"},
+ {70, nullptr, "ReportMultimediaError"},
+ {71, nullptr, "GetCurrentIlluminanceEx"},
+ {72, D<&ISelfController::SetInputDetectionPolicy>, "SetInputDetectionPolicy"},
+ {80, nullptr, "SetWirelessPriorityMode"},
+ {90, D<&ISelfController::GetAccumulatedSuspendedTickValue>, "GetAccumulatedSuspendedTickValue"},
+ {91, D<&ISelfController::GetAccumulatedSuspendedTickChangedEvent>, "GetAccumulatedSuspendedTickChangedEvent"},
+ {100, D<&ISelfController::SetAlbumImageTakenNotificationEnabled>, "SetAlbumImageTakenNotificationEnabled"},
+ {110, nullptr, "SetApplicationAlbumUserData"},
+ {120, D<&ISelfController::SaveCurrentScreenshot>, "SaveCurrentScreenshot"},
+ {130, D<&ISelfController::SetRecordVolumeMuted>, "SetRecordVolumeMuted"},
+ {1000, nullptr, "GetDebugStorageChannel"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->display_layer_manager.Initialize(system, m_process, m_applet->applet_id,
+ m_applet->library_applet_mode);
+}
+
+ISelfController::~ISelfController() {
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->display_layer_manager.Finalize();
+}
+
+Result ISelfController::Exit() {
+ LOG_DEBUG(Service_AM, "called");
+
+ // TODO
+ system.Exit();
+
+ R_SUCCEED();
+}
+
+Result ISelfController::LockExit() {
+ LOG_DEBUG(Service_AM, "called");
+
+ system.SetExitLocked(true);
+
+ R_SUCCEED();
+}
+
+Result ISelfController::UnlockExit() {
+ LOG_DEBUG(Service_AM, "called");
+
+ system.SetExitLocked(false);
+
+ if (system.GetExitRequested()) {
+ system.Exit();
+ }
+
+ R_SUCCEED();
+}
+
+Result ISelfController::EnterFatalSection() {
+ std::scoped_lock lk{m_applet->lock};
+
+ m_applet->fatal_section_count++;
+ LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", m_applet->fatal_section_count);
+
+ R_SUCCEED();
+}
+
+Result ISelfController::LeaveFatalSection() {
+ LOG_DEBUG(Service_AM, "called");
+
+ // Entry and exit of fatal sections must be balanced.
+ std::scoped_lock lk{m_applet->lock};
+ R_UNLESS(m_applet->fatal_section_count > 0, AM::ResultFatalSectionCountImbalance);
+ m_applet->fatal_section_count--;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::GetLibraryAppletLaunchableEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ m_applet->library_applet_launchable_event.Signal();
+ *out_event = m_applet->library_applet_launchable_event.GetHandle();
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetScreenShotPermission(ScreenshotPermission screen_shot_permission) {
+ LOG_DEBUG(Service_AM, "called, permission={}", screen_shot_permission);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->screenshot_permission = screen_shot_permission;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetOperationModeChangedNotification(bool enabled) {
+ LOG_INFO(Service_AM, "called, enabled={}", enabled);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->operation_mode_changed_notification_enabled = enabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetPerformanceModeChangedNotification(bool enabled) {
+ LOG_INFO(Service_AM, "called, enabled={}", enabled);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->performance_mode_changed_notification_enabled = enabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetFocusHandlingMode(bool notify, bool background, bool suspend) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, notify={} background={} suspend={}", notify,
+ background, suspend);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->focus_handling_mode = {notify, background, suspend};
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetRestartMessageEnabled(bool enabled) {
+ LOG_INFO(Service_AM, "called, enabled={}", enabled);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->restart_message_enabled = enabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetScreenShotAppletIdentityInfo(
+ AppletIdentityInfo screen_shot_applet_identity_info) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->screen_shot_identity = screen_shot_applet_identity_info;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetOutOfFocusSuspendingEnabled(bool enabled) {
+ LOG_INFO(Service_AM, "called, enabled={}", enabled);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->out_of_focus_suspension_enabled = enabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetAlbumImageOrientation(
+ Capture::AlbumImageOrientation album_image_orientation) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", album_image_orientation);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->album_image_orientation = album_image_orientation;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::IsSystemBufferSharingEnabled() {
+ LOG_INFO(Service_AM, "called");
+
+ std::scoped_lock lk{m_applet->lock};
+ R_RETURN(m_applet->display_layer_manager.IsSystemBufferSharingEnabled());
+}
+
+Result ISelfController::GetSystemSharedBufferHandle(Out<u64> out_buffer_id) {
+ LOG_INFO(Service_AM, "called");
+
+ u64 layer_id;
+
+ std::scoped_lock lk{m_applet->lock};
+ R_RETURN(m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, &layer_id));
+}
+
+Result ISelfController::GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id) {
+ LOG_INFO(Service_AM, "called");
+
+ std::scoped_lock lk{m_applet->lock};
+ R_RETURN(
+ m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, out_layer_id));
+}
+
+Result ISelfController::CreateManagedDisplayLayer(Out<u64> out_layer_id) {
+ LOG_INFO(Service_AM, "called");
+
+ std::scoped_lock lk{m_applet->lock};
+ R_RETURN(m_applet->display_layer_manager.CreateManagedDisplayLayer(out_layer_id));
+}
+
+Result ISelfController::CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id,
+ Out<u64> out_recording_layer_id) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ R_RETURN(m_applet->display_layer_manager.CreateManagedDisplaySeparableLayer(
+ out_layer_id, out_recording_layer_id));
+}
+
+Result ISelfController::SetHandlesRequestToDisplay(bool enable) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, enable={}", enable);
+ R_SUCCEED();
+}
+
+Result ISelfController::ApproveToDisplay() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result ISelfController::SetMediaPlaybackState(bool state) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state);
+ R_SUCCEED();
+}
+
+Result ISelfController::OverrideAutoSleepTimeAndDimmingTime(s32 a, s32 b, s32 c, s32 d) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, a={}, b={}, c={}, d={}", a, b, c, d);
+ R_SUCCEED();
+}
+
+Result ISelfController::SetIdleTimeDetectionExtension(
+ IdleTimeDetectionExtension idle_time_detection_extension) {
+ LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", idle_time_detection_extension);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->idle_time_detection_extension = idle_time_detection_extension;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::GetIdleTimeDetectionExtension(
+ Out<IdleTimeDetectionExtension> out_idle_time_detection_extension) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ std::scoped_lock lk{m_applet->lock};
+ *out_idle_time_detection_extension = m_applet->idle_time_detection_extension;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::ReportUserIsActive() {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result ISelfController::SetAutoSleepDisabled(bool is_auto_sleep_disabled) {
+ LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled);
+
+ // On the system itself, if the previous state of is_auto_sleep_disabled
+ // differed from the current value passed in, it'd signify the internal
+ // window manager to update (and also increment some statistics like update counts)
+ //
+ // It'd also indicate this change to an idle handling context.
+ //
+ // However, given we're emulating this behavior, most of this can be ignored
+ // and it's sufficient to simply set the member variable for querying via
+ // IsAutoSleepDisabled().
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->auto_sleep_disabled = is_auto_sleep_disabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled) {
+ LOG_DEBUG(Service_AM, "called.");
+
+ std::scoped_lock lk{m_applet->lock};
+ *out_is_auto_sleep_disabled = m_applet->auto_sleep_disabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result ISelfController::GetAccumulatedSuspendedTickValue(
+ Out<u64> out_accumulated_suspended_tick_value) {
+ LOG_DEBUG(Service_AM, "called.");
+
+ // This command returns the total number of system ticks since ISelfController creation
+ // where the game was suspended. Since Yuzu doesn't implement game suspension, this command
+ // can just always return 0 ticks.
+ std::scoped_lock lk{m_applet->lock};
+ *out_accumulated_suspended_tick_value = m_applet->suspended_ticks;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::GetAccumulatedSuspendedTickChangedEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_DEBUG(Service_AM, "called.");
+
+ *out_event = m_applet->accumulated_suspended_tick_changed_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result ISelfController::SetAlbumImageTakenNotificationEnabled(bool enabled) {
+ LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
+
+ // This service call sets an internal flag whether a notification is shown when an image is
+ // captured. Currently we do not support capturing images via the capture button, so this can be
+ // stubbed for now.
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->album_image_taken_notification_enabled = enabled;
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option) {
+ LOG_INFO(Service_AM, "called, report_option={}", album_report_option);
+
+ const auto screenshot_service =
+ system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>(
+ "caps:su");
+
+ if (screenshot_service) {
+ screenshot_service->CaptureAndSaveScreenshot(album_report_option);
+ }
+
+ R_SUCCEED();
+}
+
+Result ISelfController::SetRecordVolumeMuted(bool muted) {
+ LOG_WARNING(Service_AM, "(STUBBED) called. muted={}", muted);
+
+ std::scoped_lock lk{m_applet->lock};
+ m_applet->record_volume_muted = muted;
+
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h
new file mode 100644
index 000000000..eca083cfe
--- /dev/null
+++ b/src/core/hle/service/am/service/self_controller.h
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::Capture {
+enum class AlbumImageOrientation;
+enum class AlbumReportOption;
+} // namespace Service::Capture
+
+namespace Service::AM {
+
+struct Applet;
+
+class ISelfController final : public ServiceFramework<ISelfController> {
+public:
+ explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process);
+ ~ISelfController() override;
+
+private:
+ Result Exit();
+ Result LockExit();
+ Result UnlockExit();
+ Result EnterFatalSection();
+ Result LeaveFatalSection();
+ Result GetLibraryAppletLaunchableEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result SetScreenShotPermission(ScreenshotPermission screen_shot_permission);
+ Result SetOperationModeChangedNotification(bool enabled);
+ Result SetPerformanceModeChangedNotification(bool enabled);
+ Result SetFocusHandlingMode(bool notify, bool background, bool suspend);
+ Result SetRestartMessageEnabled(bool enabled);
+ Result SetScreenShotAppletIdentityInfo(AppletIdentityInfo screen_shot_applet_identity_info);
+ Result SetOutOfFocusSuspendingEnabled(bool enabled);
+ Result SetAlbumImageOrientation(Capture::AlbumImageOrientation album_image_orientation);
+ Result IsSystemBufferSharingEnabled();
+ Result GetSystemSharedBufferHandle(Out<u64> out_buffer_id);
+ Result GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id);
+ Result CreateManagedDisplayLayer(Out<u64> out_layer_id);
+ Result CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id,
+ Out<u64> out_recording_layer_id);
+ Result SetHandlesRequestToDisplay(bool enable);
+ Result ApproveToDisplay();
+ Result SetMediaPlaybackState(bool state);
+ Result OverrideAutoSleepTimeAndDimmingTime(s32 a, s32 b, s32 c, s32 d);
+ Result SetIdleTimeDetectionExtension(IdleTimeDetectionExtension idle_time_detection_extension);
+ Result GetIdleTimeDetectionExtension(
+ Out<IdleTimeDetectionExtension> out_idle_time_detection_extension);
+ Result ReportUserIsActive();
+ Result SetAutoSleepDisabled(bool is_auto_sleep_disabled);
+ Result IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled);
+ Result SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy);
+ Result GetAccumulatedSuspendedTickValue(Out<u64> out_accumulated_suspended_tick_value);
+ Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result SetAlbumImageTakenNotificationEnabled(bool enabled);
+ Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option);
+ Result SetRecordVolumeMuted(bool muted);
+
+ Kernel::KProcess* const m_process;
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/storage.cpp b/src/core/hle/service/am/service/storage.cpp
new file mode 100644
index 000000000..25ee0afbd
--- /dev/null
+++ b/src/core/hle/service/am/service/storage.cpp
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/library_applet_storage.h"
+#include "core/hle/service/am/service/storage.h"
+#include "core/hle/service/am/service/storage_accessor.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IStorage::IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl)
+ : ServiceFramework{system_, "IStorage"}, m_impl{std::move(impl)} {
+ static const FunctionInfo functions[] = {
+ {0, D<&IStorage::Open>, "Open"},
+ {1, D<&IStorage::OpenTransferStorage>, "OpenTransferStorage"},
+ };
+
+ RegisterHandlers(functions);
+}
+
+IStorage::IStorage(Core::System& system_, std::vector<u8>&& data)
+ : IStorage(system_, CreateStorage(std::move(data))) {}
+
+IStorage::~IStorage() = default;
+
+Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) {
+ LOG_DEBUG(Service_AM, "called");
+
+ R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType);
+
+ *out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl);
+ R_SUCCEED();
+}
+
+Result IStorage::OpenTransferStorage(
+ Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
+ R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType);
+
+ *out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl);
+ R_SUCCEED();
+}
+
+std::vector<u8> IStorage::GetData() const {
+ return m_impl->GetData();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/storage.h b/src/core/hle/service/am/service/storage.h
new file mode 100644
index 000000000..cde2ed0ea
--- /dev/null
+++ b/src/core/hle/service/am/service/storage.h
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class LibraryAppletStorage;
+class IStorageAccessor;
+class ITransferStorageAccessor;
+
+class IStorage final : public ServiceFramework<IStorage> {
+public:
+ explicit IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl);
+ explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);
+ ~IStorage() override;
+
+ std::shared_ptr<LibraryAppletStorage> GetImpl() const {
+ return m_impl;
+ }
+
+ std::vector<u8> GetData() const;
+
+private:
+ Result Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor);
+ Result OpenTransferStorage(
+ Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor);
+
+ const std::shared_ptr<LibraryAppletStorage> m_impl;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/storage_accessor.cpp b/src/core/hle/service/am/service/storage_accessor.cpp
new file mode 100644
index 000000000..84577fee4
--- /dev/null
+++ b/src/core/hle/service/am/service/storage_accessor.cpp
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/am/library_applet_storage.h"
+#include "core/hle/service/am/service/storage_accessor.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IStorageAccessor::IStorageAccessor(Core::System& system_,
+ std::shared_ptr<LibraryAppletStorage> impl)
+ : ServiceFramework{system_, "IStorageAccessor"}, m_impl{std::move(impl)} {
+ static const FunctionInfo functions[] = {
+ {0, D<&IStorageAccessor::GetSize>, "GetSize"},
+ {10, D<&IStorageAccessor::Write>, "Write"},
+ {11, D<&IStorageAccessor::Read>, "Read"},
+ };
+
+ RegisterHandlers(functions);
+}
+
+IStorageAccessor::~IStorageAccessor() = default;
+
+Result IStorageAccessor::GetSize(Out<s64> out_size) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_size = m_impl->GetSize();
+ R_SUCCEED();
+}
+
+Result IStorageAccessor::Write(InBuffer<BufferAttr_HipcAutoSelect> buffer, s64 offset) {
+ LOG_DEBUG(Service_AM, "called, offset={} size={}", offset, buffer.size());
+ R_RETURN(m_impl->Write(offset, buffer.data(), buffer.size()));
+}
+
+Result IStorageAccessor::Read(OutBuffer<BufferAttr_HipcAutoSelect> out_buffer, s64 offset) {
+ LOG_DEBUG(Service_AM, "called, offset={} size={}", offset, out_buffer.size());
+ R_RETURN(m_impl->Read(offset, out_buffer.data(), out_buffer.size()));
+}
+
+ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_,
+ std::shared_ptr<LibraryAppletStorage> impl)
+ : ServiceFramework{system_, "ITransferStorageAccessor"}, m_impl{std::move(impl)} {
+ static const FunctionInfo functions[] = {
+ {0, D<&ITransferStorageAccessor::GetSize>, "GetSize"},
+ {1, D<&ITransferStorageAccessor::GetHandle>, "GetHandle"},
+ };
+
+ RegisterHandlers(functions);
+}
+
+ITransferStorageAccessor::~ITransferStorageAccessor() = default;
+
+Result ITransferStorageAccessor::GetSize(Out<s64> out_size) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_size = m_impl->GetSize();
+ R_SUCCEED();
+}
+
+Result ITransferStorageAccessor::GetHandle(Out<s64> out_size,
+ OutCopyHandle<Kernel::KTransferMemory> out_handle) {
+ LOG_INFO(Service_AM, "called");
+ *out_size = m_impl->GetSize();
+ *out_handle = m_impl->GetHandle();
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/storage_accessor.h b/src/core/hle/service/am/service/storage_accessor.h
new file mode 100644
index 000000000..1a01730e0
--- /dev/null
+++ b/src/core/hle/service/am/service/storage_accessor.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/am/library_applet_storage.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
+public:
+ explicit IStorageAccessor(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl);
+ ~IStorageAccessor() override;
+
+private:
+ Result GetSize(Out<s64> out_size);
+ Result Write(InBuffer<BufferAttr_HipcAutoSelect> buffer, s64 offset);
+ Result Read(OutBuffer<BufferAttr_HipcAutoSelect> out_buffer, s64 offset);
+
+ const std::shared_ptr<LibraryAppletStorage> m_impl;
+};
+
+class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> {
+public:
+ explicit ITransferStorageAccessor(Core::System& system_,
+ std::shared_ptr<LibraryAppletStorage> impl);
+ ~ITransferStorageAccessor() override;
+
+private:
+ Result GetSize(Out<s64> out_size);
+ Result GetHandle(Out<s64> out_size, OutCopyHandle<Kernel::KTransferMemory> out_handle);
+
+ const std::shared_ptr<LibraryAppletStorage> m_impl;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp
new file mode 100644
index 000000000..d1871ef9b
--- /dev/null
+++ b/src/core/hle/service/am/service/system_applet_proxy.cpp
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/service/applet_common_functions.h"
+#include "core/hle/service/am/service/application_creator.h"
+#include "core/hle/service/am/service/audio_controller.h"
+#include "core/hle/service/am/service/common_state_getter.h"
+#include "core/hle/service/am/service/debug_functions.h"
+#include "core/hle/service/am/service/display_controller.h"
+#include "core/hle/service/am/service/global_state_controller.h"
+#include "core/hle/service/am/service/home_menu_functions.h"
+#include "core/hle/service/am/service/library_applet_creator.h"
+#include "core/hle/service/am/service/process_winding_controller.h"
+#include "core/hle/service/am/service/self_controller.h"
+#include "core/hle/service/am/service/system_applet_proxy.h"
+#include "core/hle/service/am/service/window_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process)
+ : ServiceFramework{system_, "ISystemAppletProxy"}, m_process{process}, m_applet{
+ std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
+ {1, D<&ISystemAppletProxy::GetSelfController>, "GetSelfController"},
+ {2, D<&ISystemAppletProxy::GetWindowController>, "GetWindowController"},
+ {3, D<&ISystemAppletProxy::GetAudioController>, "GetAudioController"},
+ {4, D<&ISystemAppletProxy::GetDisplayController>, "GetDisplayController"},
+ {10, D<&ISystemAppletProxy::GetProcessWindingController>, "GetProcessWindingController"},
+ {11, D<&ISystemAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
+ {20, D<&ISystemAppletProxy::GetHomeMenuFunctions>, "GetHomeMenuFunctions"},
+ {21, D<&ISystemAppletProxy::GetGlobalStateController>, "GetGlobalStateController"},
+ {22, D<&ISystemAppletProxy::GetApplicationCreator>, "GetApplicationCreator"},
+ {23, D<&ISystemAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"},
+ {1000, D<&ISystemAppletProxy::GetDebugFunctions>, "GetDebugFunctions"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISystemAppletProxy::~ISystemAppletProxy() = default;
+
+Result ISystemAppletProxy::GetAudioController(
+ Out<SharedPointer<IAudioController>> out_audio_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_audio_controller = std::make_shared<IAudioController>(system);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetDisplayController(
+ Out<SharedPointer<IDisplayController>> out_display_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_display_controller = std::make_shared<IDisplayController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetProcessWindingController(
+ Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetDebugFunctions(
+ Out<SharedPointer<IDebugFunctions>> out_debug_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_debug_functions = std::make_shared<IDebugFunctions>(system);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetWindowController(
+ Out<SharedPointer<IWindowController>> out_window_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_window_controller = std::make_shared<IWindowController>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetSelfController(
+ Out<SharedPointer<ISelfController>> out_self_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetCommonStateGetter(
+ Out<SharedPointer<ICommonStateGetter>> out_common_state_getter) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetLibraryAppletCreator(
+ Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetApplicationCreator(
+ Out<SharedPointer<IApplicationCreator>> out_application_creator) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_application_creator = std::make_shared<IApplicationCreator>(system);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetAppletCommonFunctions(
+ Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetHomeMenuFunctions(
+ Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_home_menu_functions = std::make_shared<IHomeMenuFunctions>(system, m_applet);
+ R_SUCCEED();
+}
+
+Result ISystemAppletProxy::GetGlobalStateController(
+ Out<SharedPointer<IGlobalStateController>> out_global_state_controller) {
+ LOG_DEBUG(Service_AM, "called");
+ *out_global_state_controller = std::make_shared<IGlobalStateController>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/system_applet_proxy.h b/src/core/hle/service/am/service/system_applet_proxy.h
new file mode 100644
index 000000000..67cd50e03
--- /dev/null
+++ b/src/core/hle/service/am/service/system_applet_proxy.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+class IAppletCommonFunctions;
+class IApplicationCreator;
+class IAudioController;
+class ICommonStateGetter;
+class IDebugFunctions;
+class IDisplayController;
+class IHomeMenuFunctions;
+class IGlobalStateController;
+class ILibraryAppletCreator;
+class IProcessWindingController;
+class ISelfController;
+class IWindowController;
+
+class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
+public:
+ explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet,
+ Kernel::KProcess* process);
+ ~ISystemAppletProxy();
+
+private:
+ Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
+ Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
+ Result GetProcessWindingController(
+ Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
+ Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
+ Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
+ Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
+ Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
+ Result GetLibraryAppletCreator(
+ Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
+ Result GetApplicationCreator(Out<SharedPointer<IApplicationCreator>> out_application_creator);
+ Result GetAppletCommonFunctions(
+ Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions);
+ Result GetHomeMenuFunctions(Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions);
+ Result GetGlobalStateController(
+ Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
+
+ Kernel::KProcess* const m_process;
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp
new file mode 100644
index 000000000..99a4f50a2
--- /dev/null
+++ b/src/core/hle/service/am/service/window_controller.cpp
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/applet_manager.h"
+#include "core/hle/service/am/service/window_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::AM {
+
+IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet)
+ : ServiceFramework{system_, "IWindowController"}, m_applet{std::move(applet)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CreateWindow"},
+ {1, D<&IWindowController::GetAppletResourceUserId>, "GetAppletResourceUserId"},
+ {2, D<&IWindowController::GetAppletResourceUserIdOfCallerApplet>, "GetAppletResourceUserIdOfCallerApplet"},
+ {10, D<&IWindowController::AcquireForegroundRights>, "AcquireForegroundRights"},
+ {11, D<&IWindowController::ReleaseForegroundRights>, "ReleaseForegroundRights"},
+ {12, D<&IWindowController::RejectToChangeIntoBackground>, "RejectToChangeIntoBackground"},
+ {20, D<&IWindowController::SetAppletWindowVisibility>, "SetAppletWindowVisibility"},
+ {21, D<&IWindowController::SetAppletGpuTimeSlice>, "SetAppletGpuTimeSlice"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IWindowController::~IWindowController() = default;
+
+Result IWindowController::GetAppletResourceUserId(Out<AppletResourceUserId> out_aruid) {
+ LOG_INFO(Service_AM, "called");
+ *out_aruid = m_applet->aruid;
+ R_SUCCEED();
+}
+
+Result IWindowController::GetAppletResourceUserIdOfCallerApplet(
+ Out<AppletResourceUserId> out_aruid) {
+ LOG_INFO(Service_AM, "called");
+
+ if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet != nullptr) {
+ *out_aruid = caller_applet->aruid;
+ } else {
+ *out_aruid = AppletResourceUserId{};
+ }
+
+ R_SUCCEED();
+}
+
+Result IWindowController::AcquireForegroundRights() {
+ LOG_INFO(Service_AM, "called");
+ R_SUCCEED();
+}
+
+Result IWindowController::ReleaseForegroundRights() {
+ LOG_INFO(Service_AM, "called");
+ R_SUCCEED();
+}
+
+Result IWindowController::RejectToChangeIntoBackground() {
+ LOG_INFO(Service_AM, "called");
+ R_SUCCEED();
+}
+
+Result IWindowController::SetAppletWindowVisibility(bool visible) {
+ m_applet->display_layer_manager.SetWindowVisibility(visible);
+ m_applet->hid_registration.EnableAppletToGetInput(visible);
+
+ if (visible) {
+ m_applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);
+ m_applet->focus_state = FocusState::InFocus;
+ } else {
+ m_applet->focus_state = FocusState::NotInFocus;
+ }
+
+ m_applet->message_queue.PushMessage(AppletMessage::FocusStateChanged);
+
+ R_SUCCEED();
+}
+
+Result IWindowController::SetAppletGpuTimeSlice(s64 time_slice) {
+ LOG_WARNING(Service_AM, "(STUBBED) called, time_slice={}", time_slice);
+ R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/window_controller.h b/src/core/hle/service/am/service/window_controller.h
new file mode 100644
index 000000000..bfbad9bcc
--- /dev/null
+++ b/src/core/hle/service/am/service/window_controller.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+struct Applet;
+
+class IWindowController final : public ServiceFramework<IWindowController> {
+public:
+ explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet);
+ ~IWindowController() override;
+
+private:
+ Result GetAppletResourceUserId(Out<AppletResourceUserId> out_aruid);
+ Result GetAppletResourceUserIdOfCallerApplet(Out<AppletResourceUserId> out_aruid);
+ Result AcquireForegroundRights();
+ Result ReleaseForegroundRights();
+ Result RejectToChangeIntoBackground();
+ Result SetAppletWindowVisibility(bool visible);
+ Result SetAppletGpuTimeSlice(s64 time_slice);
+
+ const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp
deleted file mode 100644
index ec581e32b..000000000
--- a/src/core/hle/service/am/spsm.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/spsm.h"
-
-namespace Service::AM {
-
-SPSM::SPSM(Core::System& system_) : ServiceFramework{system_, "spsm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetState"},
- {1, nullptr, "EnterSleep"},
- {2, nullptr, "GetLastWakeReason"},
- {3, nullptr, "Shutdown"},
- {4, nullptr, "GetNotificationMessageEventHandle"},
- {5, nullptr, "ReceiveNotificationMessage"},
- {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"},
- {7, nullptr, "ResetEventLog"},
- {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"},
- {9, nullptr, "ChangeHomeButtonLongPressingTime"},
- {10, nullptr, "PutErrorState"},
- {11, nullptr, "InvalidateCurrentHomeButtonPressing"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-SPSM::~SPSM() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h
deleted file mode 100644
index 922f8863e..000000000
--- a/src/core/hle/service/am/spsm.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::AM {
-
-class SPSM final : public ServiceFramework<SPSM> {
-public:
- explicit SPSM(Core::System& system_);
- ~SPSM() override;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage.cpp b/src/core/hle/service/am/storage.cpp
deleted file mode 100644
index 4e82afd1c..000000000
--- a/src/core/hle/service/am/storage.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/library_applet_storage.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/am/storage_accessor.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IStorage::IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_)
- : ServiceFramework{system_, "IStorage"}, impl{std::move(impl_)} {
- static const FunctionInfo functions[] = {
- {0, &IStorage::Open, "Open"},
- {1, &IStorage::OpenTransferStorage, "OpenTransferStorage"},
- };
-
- RegisterHandlers(functions);
-}
-
-IStorage::IStorage(Core::System& system_, std::vector<u8>&& data)
- : IStorage(system_, CreateStorage(std::move(data))) {}
-
-IStorage::~IStorage() = default;
-
-void IStorage::Open(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- if (impl->GetHandle() != nullptr) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(AM::ResultInvalidStorageType);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IStorageAccessor>(system, impl);
-}
-
-void IStorage::OpenTransferStorage(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- if (impl->GetHandle() == nullptr) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(AM::ResultInvalidStorageType);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ITransferStorageAccessor>(system, impl);
-}
-
-std::vector<u8> IStorage::GetData() const {
- return impl->GetData();
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage.h b/src/core/hle/service/am/storage.h
deleted file mode 100644
index 10d00b141..000000000
--- a/src/core/hle/service/am/storage.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class LibraryAppletStorage;
-
-class IStorage final : public ServiceFramework<IStorage> {
-public:
- explicit IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_);
- explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);
- ~IStorage() override;
-
- std::shared_ptr<LibraryAppletStorage> GetImpl() const {
- return impl;
- }
-
- std::vector<u8> GetData() const;
-
-private:
- void Open(HLERequestContext& ctx);
- void OpenTransferStorage(HLERequestContext& ctx);
-
- const std::shared_ptr<LibraryAppletStorage> impl;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage_accessor.cpp b/src/core/hle/service/am/storage_accessor.cpp
deleted file mode 100644
index a1184b065..000000000
--- a/src/core/hle/service/am/storage_accessor.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/kernel/k_transfer_memory.h"
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/library_applet_storage.h"
-#include "core/hle/service/am/storage_accessor.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IStorageAccessor::IStorageAccessor(Core::System& system_,
- std::shared_ptr<LibraryAppletStorage> impl_)
- : ServiceFramework{system_, "IStorageAccessor"}, impl{std::move(impl_)} {
- static const FunctionInfo functions[] = {
- {0, &IStorageAccessor::GetSize, "GetSize"},
- {10, &IStorageAccessor::Write, "Write"},
- {11, &IStorageAccessor::Read, "Read"},
- };
-
- RegisterHandlers(functions);
-}
-
-IStorageAccessor::~IStorageAccessor() = default;
-
-void IStorageAccessor::GetSize(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 4};
-
- rb.Push(ResultSuccess);
- rb.Push(impl->GetSize());
-}
-
-void IStorageAccessor::Write(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const s64 offset{rp.Pop<s64>()};
- const auto data{ctx.ReadBuffer()};
- LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
-
- const auto res{impl->Write(offset, data.data(), data.size())};
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
-}
-
-void IStorageAccessor::Read(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const s64 offset{rp.Pop<s64>()};
- std::vector<u8> data(ctx.GetWriteBufferSize());
-
- LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
-
- const auto res{impl->Read(offset, data.data(), data.size())};
-
- ctx.WriteBuffer(data);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
-}
-
-ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_,
- std::shared_ptr<LibraryAppletStorage> impl_)
- : ServiceFramework{system_, "ITransferStorageAccessor"}, impl{std::move(impl_)} {
- static const FunctionInfo functions[] = {
- {0, &ITransferStorageAccessor::GetSize, "GetSize"},
- {1, &ITransferStorageAccessor::GetHandle, "GetHandle"},
- };
-
- RegisterHandlers(functions);
-}
-
-ITransferStorageAccessor::~ITransferStorageAccessor() = default;
-
-void ITransferStorageAccessor::GetSize(HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(impl->GetSize());
-}
-
-void ITransferStorageAccessor::GetHandle(HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 4, 1};
- rb.Push(ResultSuccess);
- rb.Push(impl->GetSize());
- rb.PushCopyObjects(impl->GetHandle());
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage_accessor.h b/src/core/hle/service/am/storage_accessor.h
deleted file mode 100644
index b9aa85a66..000000000
--- a/src/core/hle/service/am/storage_accessor.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
-public:
- explicit IStorageAccessor(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_);
- ~IStorageAccessor() override;
-
-private:
- void GetSize(HLERequestContext& ctx);
- void Write(HLERequestContext& ctx);
- void Read(HLERequestContext& ctx);
-
- const std::shared_ptr<LibraryAppletStorage> impl;
-};
-
-class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> {
-public:
- explicit ITransferStorageAccessor(Core::System& system_,
- std::shared_ptr<LibraryAppletStorage> impl_);
- ~ITransferStorageAccessor() override;
-
-private:
- void GetSize(HLERequestContext& ctx);
- void GetHandle(HLERequestContext& ctx);
-
- const std::shared_ptr<LibraryAppletStorage> impl;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_applet_proxy.cpp b/src/core/hle/service/am/system_applet_proxy.cpp
deleted file mode 100644
index 38643408e..000000000
--- a/src/core/hle/service/am/system_applet_proxy.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet_common_functions.h"
-#include "core/hle/service/am/application_creator.h"
-#include "core/hle/service/am/audio_controller.h"
-#include "core/hle/service/am/common_state_getter.h"
-#include "core/hle/service/am/debug_functions.h"
-#include "core/hle/service/am/display_controller.h"
-#include "core/hle/service/am/global_state_controller.h"
-#include "core/hle/service/am/home_menu_functions.h"
-#include "core/hle/service/am/library_applet_creator.h"
-#include "core/hle/service/am/library_applet_self_accessor.h"
-#include "core/hle/service/am/process_winding_controller.h"
-#include "core/hle/service/am/self_controller.h"
-#include "core/hle/service/am/system_applet_proxy.h"
-#include "core/hle/service/am/window_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-ISystemAppletProxy::ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
- std::shared_ptr<Applet> applet_, Core::System& system_)
- : ServiceFramework{system_, "ISystemAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
- applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
- {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
- {2, &ISystemAppletProxy::GetWindowController, "GetWindowController"},
- {3, &ISystemAppletProxy::GetAudioController, "GetAudioController"},
- {4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"},
- {10, nullptr, "GetProcessWindingController"},
- {11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
- {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
- {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
- {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
- {23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
- {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ISystemAppletProxy::~ISystemAppletProxy() = default;
-
-void ISystemAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ICommonStateGetter>(system, applet);
-}
-
-void ISystemAppletProxy::GetSelfController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
-}
-
-void ISystemAppletProxy::GetWindowController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IWindowController>(system, applet);
-}
-
-void ISystemAppletProxy::GetAudioController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioController>(system);
-}
-
-void ISystemAppletProxy::GetDisplayController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDisplayController>(system, applet);
-}
-
-void ISystemAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
-}
-
-void ISystemAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHomeMenuFunctions>(system);
-}
-
-void ISystemAppletProxy::GetGlobalStateController(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IGlobalStateController>(system);
-}
-
-void ISystemAppletProxy::GetApplicationCreator(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationCreator>(system);
-}
-
-void ISystemAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
-}
-
-void ISystemAppletProxy::GetDebugFunctions(HLERequestContext& ctx) {
- LOG_DEBUG(Service_AM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDebugFunctions>(system);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_applet_proxy.h b/src/core/hle/service/am/system_applet_proxy.h
deleted file mode 100644
index 0390cd1e5..000000000
--- a/src/core/hle/service/am/system_applet_proxy.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/am/applet_message_queue.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
-public:
- explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
- std::shared_ptr<Applet> applet_, Core::System& system_);
- ~ISystemAppletProxy();
-
-private:
- void GetCommonStateGetter(HLERequestContext& ctx);
- void GetSelfController(HLERequestContext& ctx);
- void GetWindowController(HLERequestContext& ctx);
- void GetAudioController(HLERequestContext& ctx);
- void GetDisplayController(HLERequestContext& ctx);
- void GetLibraryAppletCreator(HLERequestContext& ctx);
- void GetHomeMenuFunctions(HLERequestContext& ctx);
- void GetGlobalStateController(HLERequestContext& ctx);
- void GetApplicationCreator(HLERequestContext& ctx);
- void GetAppletCommonFunctions(HLERequestContext& ctx);
- void GetDebugFunctions(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nvnflinger;
- std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp
deleted file mode 100644
index 60a9afc9d..000000000
--- a/src/core/hle/service/am/system_buffer_manager.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/system_buffer_manager.h"
-#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
-#include "core/hle/service/nvnflinger/nvnflinger.h"
-#include "core/hle/service/vi/vi_results.h"
-
-namespace Service::AM {
-
-SystemBufferManager::SystemBufferManager() = default;
-
-SystemBufferManager::~SystemBufferManager() {
- if (!m_nvnflinger) {
- return;
- }
-
- // Clean up shared layers.
- if (m_buffer_sharing_enabled) {
- }
-}
-
-bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
- AppletId applet_id) {
- if (m_nvnflinger) {
- return m_buffer_sharing_enabled;
- }
-
- m_process = process;
- m_nvnflinger = nvnflinger;
- m_buffer_sharing_enabled = false;
- m_system_shared_buffer_id = 0;
- m_system_shared_layer_id = 0;
-
- if (applet_id <= AppletId::Application) {
- return false;
- }
-
- const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
- const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
- &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id);
-
- if (res.IsSuccess()) {
- m_buffer_sharing_enabled = true;
- m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
- }
-
- return m_buffer_sharing_enabled;
-}
-
-void SystemBufferManager::SetWindowVisibility(bool visible) {
- if (m_visible == visible) {
- return;
- }
-
- m_visible = visible;
-
- if (m_nvnflinger) {
- m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
- }
-}
-
-Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
- s32* out_fbshare_layer_index) {
- // TODO
- R_SUCCEED();
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h
deleted file mode 100644
index 98c3cf055..000000000
--- a/src/core/hle/service/am/system_buffer_manager.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <set>
-
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-
-#include "core/hle/service/am/am_types.h"
-
-namespace Kernel {
-class KProcess;
-}
-
-namespace Service::Nvnflinger {
-class Nvnflinger;
-}
-
-union Result;
-
-namespace Service::AM {
-
-class SystemBufferManager {
-public:
- SystemBufferManager();
- ~SystemBufferManager();
-
- bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id);
-
- void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
- u64* out_system_shared_layer_id) {
- *out_system_shared_buffer_id = m_system_shared_buffer_id;
- *out_system_shared_layer_id = m_system_shared_layer_id;
- }
-
- void SetWindowVisibility(bool visible);
-
- Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
-
-private:
- Kernel::KProcess* m_process{};
- Nvnflinger::Nvnflinger* m_nvnflinger{};
- bool m_buffer_sharing_enabled{};
- bool m_visible{true};
- u64 m_system_shared_buffer_id{};
- u64 m_system_shared_layer_id{};
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp
deleted file mode 100644
index f00957f83..000000000
--- a/src/core/hle/service/am/window_controller.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/applet.h"
-#include "core/hle/service/am/window_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-
-namespace Service::AM {
-
-IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_)
- : ServiceFramework{system_, "IWindowController"}, applet{std::move(applet_)} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "CreateWindow"},
- {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
- {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
- {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
- {11, nullptr, "ReleaseForegroundRights"},
- {12, nullptr, "RejectToChangeIntoBackground"},
- {20, &IWindowController::SetAppletWindowVisibility, "SetAppletWindowVisibility"},
- {21, &IWindowController::SetAppletGpuTimeSlice, "SetAppletGpuTimeSlice"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IWindowController::~IWindowController() = default;
-
-void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(applet->aruid);
-}
-
-void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
- u64 aruid = 0;
- if (auto caller = applet->caller_applet.lock(); caller) {
- aruid = caller->aruid;
- }
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(aruid);
-}
-
-void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
- LOG_INFO(Service_AM, "called");
-
- IPC::RequestParser rp{ctx};
- const bool visible = rp.Pop<bool>();
-
- applet->system_buffer_manager.SetWindowVisibility(visible);
- applet->hid_registration.EnableAppletToGetInput(visible);
-
- if (visible) {
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
- applet->focus_state = FocusState::InFocus;
- } else {
- applet->focus_state = FocusState::NotInFocus;
- }
- applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IWindowController::SetAppletGpuTimeSlice(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto time_slice = rp.Pop<s64>();
-
- LOG_WARNING(Service_AM, "(STUBBED) called, time_slice={}", time_slice);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/window_controller.h b/src/core/hle/service/am/window_controller.h
deleted file mode 100644
index a28219abe..000000000
--- a/src/core/hle/service/am/window_controller.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IWindowController final : public ServiceFramework<IWindowController> {
-public:
- explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_);
- ~IWindowController() override;
-
-private:
- void GetAppletResourceUserId(HLERequestContext& ctx);
- void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
- void AcquireForegroundRights(HLERequestContext& ctx);
- void SetAppletWindowVisibility(HLERequestContext& ctx);
- void SetAppletGpuTimeSlice(HLERequestContext& ctx);
-
- const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
deleted file mode 100644
index 3101cf447..000000000
--- a/src/core/hle/service/audio/audctl.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/logging/log.h"
-#include "core/hle/service/audio/audctl.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/set/system_settings_server.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace Service::Audio {
-
-AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetTargetVolume"},
- {1, nullptr, "SetTargetVolume"},
- {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"},
- {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"},
- {4, nullptr, "IsTargetMute"},
- {5, nullptr, "SetTargetMute"},
- {6, nullptr, "IsTargetConnected"},
- {7, nullptr, "SetDefaultTarget"},
- {8, nullptr, "GetDefaultTarget"},
- {9, &AudCtl::GetAudioOutputMode, "GetAudioOutputMode"},
- {10, &AudCtl::SetAudioOutputMode, "SetAudioOutputMode"},
- {11, nullptr, "SetForceMutePolicy"},
- {12, &AudCtl::GetForceMutePolicy, "GetForceMutePolicy"},
- {13, &AudCtl::GetOutputModeSetting, "GetOutputModeSetting"},
- {14, &AudCtl::SetOutputModeSetting, "SetOutputModeSetting"},
- {15, nullptr, "SetOutputTarget"},
- {16, nullptr, "SetInputTargetForceEnabled"},
- {17, &AudCtl::SetHeadphoneOutputLevelMode, "SetHeadphoneOutputLevelMode"},
- {18, &AudCtl::GetHeadphoneOutputLevelMode, "GetHeadphoneOutputLevelMode"},
- {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
- {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
- {21, nullptr, "GetAudioOutputTargetForPlayReport"},
- {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
- {23, nullptr, "SetSystemOutputMasterVolume"},
- {24, nullptr, "GetSystemOutputMasterVolume"},
- {25, nullptr, "GetAudioVolumeDataForPlayReport"},
- {26, nullptr, "UpdateHeadphoneSettings"},
- {27, nullptr, "SetVolumeMappingTableForDev"},
- {28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
- {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
- {30, &AudCtl::SetSpeakerAutoMuteEnabled, "SetSpeakerAutoMuteEnabled"},
- {31, &AudCtl::IsSpeakerAutoMuteEnabled, "IsSpeakerAutoMuteEnabled"},
- {32, nullptr, "GetActiveOutputTarget"},
- {33, nullptr, "GetTargetDeviceInfo"},
- {34, nullptr, "AcquireTargetNotification"},
- {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
- {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
- {37, nullptr, "SetHearingProtectionSafeguardEnabled"},
- {38, nullptr, "IsHearingProtectionSafeguardEnabled"},
- {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
- {40, nullptr, "GetSystemInformationForDebug"},
- {41, nullptr, "SetVolumeButtonLongPressTime"},
- {42, nullptr, "SetNativeVolumeForDebug"},
- {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
- {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
- {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
- {10100, nullptr, "GetAudioVolumeDataForPlayReport"},
- {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
- {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
- {10103, nullptr, "GetAudioOutputTargetForPlayReport"},
- {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
- {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
- {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
- {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-
- m_set_sys =
- system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
-}
-
-AudCtl::~AudCtl() = default;
-
-void AudCtl::GetTargetVolumeMin(HLERequestContext& ctx) {
- LOG_DEBUG(Audio, "called.");
-
- // This service function is currently hardcoded on the
- // actual console to this value (as of 8.0.0).
- constexpr s32 target_min_volume = 0;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(target_min_volume);
-}
-
-void AudCtl::GetTargetVolumeMax(HLERequestContext& ctx) {
- LOG_DEBUG(Audio, "called.");
-
- // This service function is currently hardcoded on the
- // actual console to this value (as of 8.0.0).
- constexpr s32 target_max_volume = 15;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(target_max_volume);
-}
-
-void AudCtl::GetAudioOutputMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
-
- Set::AudioOutputMode output_mode{};
- const auto result = m_set_sys->GetAudioOutputMode(output_mode, target);
-
- LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(result);
- rb.PushEnum(output_mode);
-}
-
-void AudCtl::SetAudioOutputMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
- const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()};
-
- const auto result = m_set_sys->SetAudioOutputMode(target, output_mode);
-
- LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
-}
-
-void AudCtl::GetForceMutePolicy(HLERequestContext& ctx) {
- LOG_WARNING(Audio, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(ForceMutePolicy::Disable);
-}
-
-void AudCtl::GetOutputModeSetting(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
-
- LOG_WARNING(Audio, "(STUBBED) called, target={}", target);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(Set::AudioOutputMode::ch_7_1);
-}
-
-void AudCtl::SetOutputModeSetting(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
- const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()};
-
- LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void AudCtl::SetHeadphoneOutputLevelMode(HLERequestContext& ctx) {
- LOG_WARNING(Audio, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void AudCtl::GetHeadphoneOutputLevelMode(HLERequestContext& ctx) {
- LOG_WARNING(Audio, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(HeadphoneOutputLevelMode::Normal);
-}
-
-void AudCtl::SetSpeakerAutoMuteEnabled(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto is_speaker_auto_mute_enabled{rp.Pop<bool>()};
-
- LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}",
- is_speaker_auto_mute_enabled);
-
- const auto result = m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
-}
-
-void AudCtl::IsSpeakerAutoMuteEnabled(HLERequestContext& ctx) {
- bool is_speaker_auto_mute_enabled{};
- const auto result = m_set_sys->GetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled);
-
- LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}",
- is_speaker_auto_mute_enabled);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(result);
- rb.Push<u8>(is_speaker_auto_mute_enabled);
-}
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
deleted file mode 100644
index 4c90ead70..000000000
--- a/src/core/hle/service/audio/audctl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Set {
-class ISystemSettingsServer;
-}
-
-namespace Service::Audio {
-
-class AudCtl final : public ServiceFramework<AudCtl> {
-public:
- explicit AudCtl(Core::System& system_);
- ~AudCtl() override;
-
-private:
- enum class ForceMutePolicy {
- Disable,
- SpeakerMuteOnHeadphoneUnplugged,
- };
-
- enum class HeadphoneOutputLevelMode {
- Normal,
- HighPower,
- };
-
- void GetTargetVolumeMin(HLERequestContext& ctx);
- void GetTargetVolumeMax(HLERequestContext& ctx);
- void GetAudioOutputMode(HLERequestContext& ctx);
- void SetAudioOutputMode(HLERequestContext& ctx);
- void GetForceMutePolicy(HLERequestContext& ctx);
- void GetOutputModeSetting(HLERequestContext& ctx);
- void SetOutputModeSetting(HLERequestContext& ctx);
- void SetHeadphoneOutputLevelMode(HLERequestContext& ctx);
- void GetHeadphoneOutputLevelMode(HLERequestContext& ctx);
- void SetSpeakerAutoMuteEnabled(HLERequestContext& ctx);
- void IsSpeakerAutoMuteEnabled(HLERequestContext& ctx);
- void AcquireTargetNotification(HLERequestContext& ctx);
-
- std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
-};
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index dccd16309..44af030eb 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
-#include "core/hle/service/audio/audctl.h"
#include "core/hle/service/audio/audin_u.h"
#include "core/hle/service/audio/audio.h"
+#include "core/hle/service/audio/audio_controller.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/audrec_a.h"
#include "core/hle/service/audio/audrec_u.h"
@@ -18,7 +18,7 @@ namespace Service::Audio {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- server_manager->RegisterNamedService("audctl", std::make_shared<AudCtl>(system));
+ server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system));
server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system));
server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system));
server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system));
diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp
new file mode 100644
index 000000000..a6da66d0f
--- /dev/null
+++ b/src/core/hle/service/audio/audio_controller.cpp
@@ -0,0 +1,174 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/audio/audio_controller.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/set/system_settings_server.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::Audio {
+
+IAudioController::IAudioController(Core::System& system_)
+ : ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetTargetVolume"},
+ {1, nullptr, "SetTargetVolume"},
+ {2, C<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
+ {3, C<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
+ {4, nullptr, "IsTargetMute"},
+ {5, nullptr, "SetTargetMute"},
+ {6, nullptr, "IsTargetConnected"},
+ {7, nullptr, "SetDefaultTarget"},
+ {8, nullptr, "GetDefaultTarget"},
+ {9, C<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
+ {10, C<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
+ {11, nullptr, "SetForceMutePolicy"},
+ {12, C<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
+ {13, C<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
+ {14, C<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
+ {15, nullptr, "SetOutputTarget"},
+ {16, nullptr, "SetInputTargetForceEnabled"},
+ {17, C<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
+ {18, C<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
+ {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
+ {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
+ {21, nullptr, "GetAudioOutputTargetForPlayReport"},
+ {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
+ {23, nullptr, "SetSystemOutputMasterVolume"},
+ {24, nullptr, "GetSystemOutputMasterVolume"},
+ {25, nullptr, "GetAudioVolumeDataForPlayReport"},
+ {26, nullptr, "UpdateHeadphoneSettings"},
+ {27, nullptr, "SetVolumeMappingTableForDev"},
+ {28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
+ {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
+ {30, C<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
+ {31, C<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
+ {32, nullptr, "GetActiveOutputTarget"},
+ {33, nullptr, "GetTargetDeviceInfo"},
+ {34, C<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
+ {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
+ {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
+ {37, nullptr, "SetHearingProtectionSafeguardEnabled"},
+ {38, nullptr, "IsHearingProtectionSafeguardEnabled"},
+ {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
+ {40, nullptr, "GetSystemInformationForDebug"},
+ {41, nullptr, "SetVolumeButtonLongPressTime"},
+ {42, nullptr, "SetNativeVolumeForDebug"},
+ {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
+ {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
+ {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
+ {10100, nullptr, "GetAudioVolumeDataForPlayReport"},
+ {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
+ {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
+ {10103, nullptr, "GetAudioOutputTargetForPlayReport"},
+ {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
+ {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
+ {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
+ {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ m_set_sys =
+ system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
+ notification_event = service_context.CreateEvent("IAudioController:NotificationEvent");
+}
+
+IAudioController::~IAudioController() {
+ service_context.CloseEvent(notification_event);
+};
+
+Result IAudioController::GetTargetVolumeMin(Out<s32> out_target_min_volume) {
+ LOG_DEBUG(Audio, "called.");
+
+ // This service function is currently hardcoded on the
+ // actual console to this value (as of 8.0.0).
+ *out_target_min_volume = 0;
+ R_SUCCEED();
+}
+
+Result IAudioController::GetTargetVolumeMax(Out<s32> out_target_max_volume) {
+ LOG_DEBUG(Audio, "called.");
+
+ // This service function is currently hardcoded on the
+ // actual console to this value (as of 8.0.0).
+ *out_target_max_volume = 15;
+ R_SUCCEED();
+}
+
+Result IAudioController::GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
+ Set::AudioOutputModeTarget target) {
+ const auto result = m_set_sys->GetAudioOutputMode(out_output_mode, target);
+
+ LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, *out_output_mode);
+ R_RETURN(result);
+}
+
+Result IAudioController::SetAudioOutputMode(Set::AudioOutputModeTarget target,
+ Set::AudioOutputMode output_mode) {
+ LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
+
+ R_RETURN(m_set_sys->SetAudioOutputMode(target, output_mode));
+}
+
+Result IAudioController::GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy) {
+ LOG_WARNING(Audio, "(STUBBED) called");
+
+ // Removed on FW 13.2.1+
+ *out_mute_policy = ForceMutePolicy::Disable;
+ R_SUCCEED();
+}
+
+Result IAudioController::GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode,
+ Set::AudioOutputModeTarget target) {
+ LOG_WARNING(Audio, "(STUBBED) called, target={}", target);
+
+ *out_output_mode = Set::AudioOutputMode::ch_7_1;
+ R_SUCCEED();
+}
+
+Result IAudioController::SetOutputModeSetting(Set::AudioOutputModeTarget target,
+ Set::AudioOutputMode output_mode) {
+ LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
+ R_SUCCEED();
+}
+
+Result IAudioController::SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode) {
+ LOG_WARNING(Audio, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IAudioController::GetHeadphoneOutputLevelMode(
+ Out<HeadphoneOutputLevelMode> out_output_level_mode) {
+ LOG_INFO(Audio, "called");
+
+ *out_output_level_mode = HeadphoneOutputLevelMode::Normal;
+ R_SUCCEED();
+}
+
+Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) {
+ LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", is_speaker_auto_mute_enabled);
+
+ R_RETURN(m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled));
+}
+
+Result IAudioController::IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled) {
+ const auto result = m_set_sys->GetSpeakerAutoMuteFlag(out_is_speaker_auto_mute_enabled);
+
+ LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", *out_is_speaker_auto_mute_enabled);
+ R_RETURN(result);
+}
+
+Result IAudioController::AcquireTargetNotification(
+ OutCopyHandle<Kernel::KReadableEvent> out_notification_event) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ *out_notification_event = &notification_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h
new file mode 100644
index 000000000..9e8514373
--- /dev/null
+++ b/src/core/hle/service/audio/audio_controller.h
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/set/settings_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Set {
+class ISystemSettingsServer;
+}
+
+namespace Service::Audio {
+
+class IAudioController final : public ServiceFramework<IAudioController> {
+public:
+ explicit IAudioController(Core::System& system_);
+ ~IAudioController() override;
+
+private:
+ enum class ForceMutePolicy {
+ Disable,
+ SpeakerMuteOnHeadphoneUnplugged,
+ };
+
+ enum class HeadphoneOutputLevelMode {
+ Normal,
+ HighPower,
+ };
+
+ Result GetTargetVolumeMin(Out<s32> out_target_min_volume);
+ Result GetTargetVolumeMax(Out<s32> out_target_max_volume);
+ Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
+ Set::AudioOutputModeTarget target);
+ Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode);
+ Result GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy);
+ Result GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode,
+ Set::AudioOutputModeTarget target);
+ Result SetOutputModeSetting(Set::AudioOutputModeTarget target,
+ Set::AudioOutputMode output_mode);
+ Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode);
+ Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode);
+ Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled);
+ Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled);
+ Result AcquireTargetNotification(OutCopyHandle<Kernel::KReadableEvent> out_notification_event);
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* notification_event;
+ std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
+};
+
+} // namespace Service::Audio
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
index 5be167fce..ed393f7a2 100644
--- a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
+++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
@@ -12,7 +12,7 @@ INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_)
"INewlyArrivedEventHolder"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, C<&INewlyArrivedEventHolder::Get>, "Get"},
+ {0, D<&INewlyArrivedEventHolder::Get>, "Get"},
};
// clang-format on
diff --git a/src/core/hle/service/bcat/news/news_database_service.cpp b/src/core/hle/service/bcat/news/news_database_service.cpp
index 18109f9b0..b94ef0636 100644
--- a/src/core/hle/service/bcat/news/news_database_service.cpp
+++ b/src/core/hle/service/bcat/news/news_database_service.cpp
@@ -11,12 +11,12 @@ INewsDatabaseService::INewsDatabaseService(Core::System& system_)
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetListV1"},
- {1, C<&INewsDatabaseService::Count>, "Count"},
+ {1, D<&INewsDatabaseService::Count>, "Count"},
{2, nullptr, "CountWithKey"},
{3, nullptr, "UpdateIntegerValue"},
- {4, nullptr, "UpdateIntegerValueWithAddition"},
+ {4, D<&INewsDatabaseService::UpdateIntegerValueWithAddition>, "UpdateIntegerValueWithAddition"},
{5, nullptr, "UpdateStringValue"},
- {1000, nullptr, "GetList"},
+ {1000, D<&INewsDatabaseService::GetList>, "GetList"},
};
// clang-format on
@@ -32,4 +32,22 @@ Result INewsDatabaseService::Count(Out<s32> out_count,
R_SUCCEED();
}
+Result INewsDatabaseService::UpdateIntegerValueWithAddition(
+ u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}",
+ value, buffer_data_1.size(), buffer_data_2.size());
+ R_SUCCEED();
+}
+
+Result INewsDatabaseService::GetList(Out<s32> out_count, u32 value,
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}",
+ value, buffer_data_1.size(), buffer_data_2.size());
+ *out_count = 0;
+ R_SUCCEED();
+}
+
} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_database_service.h b/src/core/hle/service/bcat/news/news_database_service.h
index f5916634b..860b7074c 100644
--- a/src/core/hle/service/bcat/news/news_database_service.h
+++ b/src/core/hle/service/bcat/news/news_database_service.h
@@ -19,6 +19,14 @@ public:
private:
Result Count(Out<s32> out_count, InBuffer<BufferAttr_HipcPointer> buffer_data);
+
+ Result UpdateIntegerValueWithAddition(u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2);
+
+ Result GetList(Out<s32> out_count, u32 value,
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_1,
+ InBuffer<BufferAttr_HipcPointer> buffer_data_2);
};
} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_service.cpp b/src/core/hle/service/bcat/news/news_service.cpp
index e19cea7b5..bc6c2afd2 100644
--- a/src/core/hle/service/bcat/news/news_service.cpp
+++ b/src/core/hle/service/bcat/news/news_service.cpp
@@ -11,10 +11,10 @@ INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "I
static const FunctionInfo functions[] = {
{10100, nullptr, "PostLocalNews"},
{20100, nullptr, "SetPassphrase"},
- {30100, C<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"},
+ {30100, D<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"},
{30101, nullptr, "GetTopicList"},
{30110, nullptr, "Unknown30110"},
- {30200, nullptr, "IsSystemUpdateRequired"},
+ {30200, D<&INewsService::IsSystemUpdateRequired>, "IsSystemUpdateRequired"},
{30201, nullptr, "Unknown30201"},
{30210, nullptr, "Unknown30210"},
{30300, nullptr, "RequestImmediateReception"},
@@ -24,7 +24,7 @@ INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "I
{30901, nullptr, "Unknown30901"},
{30902, nullptr, "Unknown30902"},
{40100, nullptr, "SetSubscriptionStatus"},
- {40101, nullptr, "RequestAutoSubscription"},
+ {40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"},
{40200, nullptr, "ClearStorage"},
{40201, nullptr, "ClearSubscriptionStatusAll"},
{90100, nullptr, "GetNewsDatabaseDump"},
@@ -43,4 +43,15 @@ Result INewsService::GetSubscriptionStatus(Out<u32> out_status,
R_SUCCEED();
}
+Result INewsService::IsSystemUpdateRequired(Out<bool> out_is_system_update_required) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called");
+ *out_is_system_update_required = false;
+ R_SUCCEED();
+}
+
+Result INewsService::RequestAutoSubscription(u64 value) {
+ LOG_WARNING(Service_BCAT, "(STUBBED) called");
+ R_SUCCEED();
+}
+
} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_service.h b/src/core/hle/service/bcat/news/news_service.h
index 8d06be9d6..f1716a302 100644
--- a/src/core/hle/service/bcat/news/news_service.h
+++ b/src/core/hle/service/bcat/news/news_service.h
@@ -19,6 +19,10 @@ public:
private:
Result GetSubscriptionStatus(Out<u32> out_status, InBuffer<BufferAttr_HipcPointer> buffer_data);
+
+ Result IsSystemUpdateRequired(Out<bool> out_is_system_update_required);
+
+ Result RequestAutoSubscription(u64 value);
};
} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
index c32a5ca8f..1712971e4 100644
--- a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
+++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
@@ -11,7 +11,7 @@ IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_)
"IOverwriteEventHolder"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, C<&IOverwriteEventHolder::Get>, "Get"},
+ {0, D<&IOverwriteEventHolder::Get>, "Get"},
};
// clang-format on
diff --git a/src/core/hle/service/bcat/news/service_creator.cpp b/src/core/hle/service/bcat/news/service_creator.cpp
index d5ba5dff7..a1b22c004 100644
--- a/src/core/hle/service/bcat/news/service_creator.cpp
+++ b/src/core/hle/service/bcat/news/service_creator.cpp
@@ -15,11 +15,11 @@ IServiceCreator::IServiceCreator(Core::System& system_, u32 permissions_, const
: ServiceFramework{system_, name_}, permissions{permissions_} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, C<&IServiceCreator::CreateNewsService>, "CreateNewsService"},
- {1, C<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"},
- {2, C<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"},
- {3, C<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"},
- {4, C<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"},
+ {0, D<&IServiceCreator::CreateNewsService>, "CreateNewsService"},
+ {1, D<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"},
+ {2, D<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"},
+ {3, D<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"},
+ {4, D<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"},
};
// clang-format on
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 2dc23e674..d120dade8 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -3,141 +3,18 @@
#include <memory>
-#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/k_event.h"
#include "core/hle/service/btm/btm.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/btm/btm_debug.h"
+#include "core/hle/service/btm/btm_system.h"
+#include "core/hle/service/btm/btm_user.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::BTM {
-class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
+class IBtm final : public ServiceFramework<IBtm> {
public:
- explicit IBtmUserCore(Core::System& system_)
- : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
- {1, nullptr, "GetBleScanFilterParameter"},
- {2, nullptr, "GetBleScanFilterParameter2"},
- {3, nullptr, "StartBleScanForGeneral"},
- {4, nullptr, "StopBleScanForGeneral"},
- {5, nullptr, "GetBleScanResultsForGeneral"},
- {6, nullptr, "StartBleScanForPaired"},
- {7, nullptr, "StopBleScanForPaired"},
- {8, nullptr, "StartBleScanForSmartDevice"},
- {9, nullptr, "StopBleScanForSmartDevice"},
- {10, nullptr, "GetBleScanResultsForSmartDevice"},
- {17, &IBtmUserCore::AcquireBleConnectionEvent, "AcquireBleConnectionEvent"},
- {18, nullptr, "BleConnect"},
- {19, nullptr, "BleDisconnect"},
- {20, nullptr, "BleGetConnectionState"},
- {21, nullptr, "AcquireBlePairingEvent"},
- {22, nullptr, "BlePairDevice"},
- {23, nullptr, "BleUnPairDevice"},
- {24, nullptr, "BleUnPairDevice2"},
- {25, nullptr, "BleGetPairedDevices"},
- {26, &IBtmUserCore::AcquireBleServiceDiscoveryEvent, "AcquireBleServiceDiscoveryEvent"},
- {27, nullptr, "GetGattServices"},
- {28, nullptr, "GetGattService"},
- {29, nullptr, "GetGattIncludedServices"},
- {30, nullptr, "GetBelongingGattService"},
- {31, nullptr, "GetGattCharacteristics"},
- {32, nullptr, "GetGattDescriptors"},
- {33, &IBtmUserCore::AcquireBleMtuConfigEvent, "AcquireBleMtuConfigEvent"},
- {34, nullptr, "ConfigureBleMtu"},
- {35, nullptr, "GetBleMtu"},
- {36, nullptr, "RegisterBleGattDataPath"},
- {37, nullptr, "UnregisterBleGattDataPath"},
- };
- // clang-format on
- RegisterHandlers(functions);
-
- scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent");
- connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent");
- service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent");
- config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent");
- }
-
- ~IBtmUserCore() override {
- service_context.CloseEvent(scan_event);
- service_context.CloseEvent(connection_event);
- service_context.CloseEvent(service_discovery_event);
- service_context.CloseEvent(config_event);
- }
-
-private:
- void AcquireBleScanEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(scan_event->GetReadableEvent());
- }
-
- void AcquireBleConnectionEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(connection_event->GetReadableEvent());
- }
-
- void AcquireBleServiceDiscoveryEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(service_discovery_event->GetReadableEvent());
- }
-
- void AcquireBleMtuConfigEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(config_event->GetReadableEvent());
- }
-
- KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* scan_event;
- Kernel::KEvent* connection_event;
- Kernel::KEvent* service_discovery_event;
- Kernel::KEvent* config_event;
-};
-
-class BTM_USR final : public ServiceFramework<BTM_USR> {
-public:
- explicit BTM_USR(Core::System& system_) : ServiceFramework{system_, "btm:u"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &BTM_USR::GetCore, "GetCore"},
- };
- // clang-format on
- RegisterHandlers(functions);
- }
-
-private:
- void GetCore(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IBtmUserCore>(system);
- }
-};
-
-class BTM final : public ServiceFramework<BTM> {
-public:
- explicit BTM(Core::System& system_) : ServiceFramework{system_, "btm"} {
+ explicit IBtm(Core::System& system_) : ServiceFramework{system_, "btm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetState"},
@@ -232,144 +109,13 @@ public:
}
};
-class BTM_DBG final : public ServiceFramework<BTM_DBG> {
-public:
- explicit BTM_DBG(Core::System& system_) : ServiceFramework{system_, "btm:dbg"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "AcquireDiscoveryEvent"},
- {1, nullptr, "StartDiscovery"},
- {2, nullptr, "CancelDiscovery"},
- {3, nullptr, "GetDeviceProperty"},
- {4, nullptr, "CreateBond"},
- {5, nullptr, "CancelBond"},
- {6, nullptr, "SetTsiMode"},
- {7, nullptr, "GeneralTest"},
- {8, nullptr, "HidConnect"},
- {9, nullptr, "GeneralGet"},
- {10, nullptr, "GetGattClientDisconnectionReason"},
- {11, nullptr, "GetBleConnectionParameter"},
- {12, nullptr, "GetBleConnectionParameterRequest"},
- {13, nullptr, "Unknown13"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> {
-public:
- explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
- {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
- {2, nullptr, "ClearGamepadPairingDatabase"},
- {3, nullptr, "GetPairedGamepadCount"},
- {4, nullptr, "EnableRadio"},
- {5, nullptr, "DisableRadio"},
- {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
- {7, nullptr, "AcquireRadioEvent"},
- {8, nullptr, "AcquireGamepadPairingEvent"},
- {9, nullptr, "IsGamepadPairingStarted"},
- {10, nullptr, "StartAudioDeviceDiscovery"},
- {11, nullptr, "StopAudioDeviceDiscovery"},
- {12, nullptr, "IsDiscoveryingAudioDevice"},
- {13, nullptr, "GetDiscoveredAudioDevice"},
- {14, nullptr, "AcquireAudioDeviceConnectionEvent"},
- {15, nullptr, "ConnectAudioDevice"},
- {16, nullptr, "IsConnectingAudioDevice"},
- {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
- {18, nullptr, "DisconnectAudioDevice"},
- {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
- {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"},
- {21, nullptr, "RemoveAudioDevicePairing"},
- {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
- {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void IsRadioEnabled(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(true);
- }
-
- void StartGamepadPairing(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void CancelGamepadPairing(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetConnectedAudioDevices(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
- }
-
- void GetPairedAudioDevices(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
- }
-
- void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-};
-
-class BTM_SYS final : public ServiceFramework<BTM_SYS> {
-public:
- explicit BTM_SYS(Core::System& system_) : ServiceFramework{system_, "btm:sys"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &BTM_SYS::GetCore, "GetCore"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void GetCore(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IBtmSystemCore>(system);
- }
-};
-
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- server_manager->RegisterNamedService("btm", std::make_shared<BTM>(system));
- server_manager->RegisterNamedService("btm:dbg", std::make_shared<BTM_DBG>(system));
- server_manager->RegisterNamedService("btm:sys", std::make_shared<BTM_SYS>(system));
- server_manager->RegisterNamedService("btm:u", std::make_shared<BTM_USR>(system));
+ server_manager->RegisterNamedService("btm", std::make_shared<IBtm>(system));
+ server_manager->RegisterNamedService("btm:dbg", std::make_shared<IBtmDebug>(system));
+ server_manager->RegisterNamedService("btm:sys", std::make_shared<IBtmSystem>(system));
+ server_manager->RegisterNamedService("btm:u", std::make_shared<IBtmUser>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h
index a99b34364..0bf77d053 100644
--- a/src/core/hle/service/btm/btm.h
+++ b/src/core/hle/service/btm/btm.h
@@ -3,10 +3,6 @@
#pragma once
-namespace Service::SM {
-class ServiceManager;
-}
-
namespace Core {
class System;
};
diff --git a/src/core/hle/service/btm/btm_debug.cpp b/src/core/hle/service/btm/btm_debug.cpp
new file mode 100644
index 000000000..4d61d2641
--- /dev/null
+++ b/src/core/hle/service/btm/btm_debug.cpp
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/btm/btm_debug.h"
+
+namespace Service::BTM {
+
+IBtmDebug::IBtmDebug(Core::System& system_) : ServiceFramework{system_, "btm:dbg"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "AcquireDiscoveryEvent"},
+ {1, nullptr, "StartDiscovery"},
+ {2, nullptr, "CancelDiscovery"},
+ {3, nullptr, "GetDeviceProperty"},
+ {4, nullptr, "CreateBond"},
+ {5, nullptr, "CancelBond"},
+ {6, nullptr, "SetTsiMode"},
+ {7, nullptr, "GeneralTest"},
+ {8, nullptr, "HidConnect"},
+ {9, nullptr, "GeneralGet"},
+ {10, nullptr, "GetGattClientDisconnectionReason"},
+ {11, nullptr, "GetBleConnectionParameter"},
+ {12, nullptr, "GetBleConnectionParameterRequest"},
+ {13, nullptr, "Unknown13"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IBtmDebug::~IBtmDebug() = default;
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_debug.h b/src/core/hle/service/btm/btm_debug.h
new file mode 100644
index 000000000..bf4f7e14f
--- /dev/null
+++ b/src/core/hle/service/btm/btm_debug.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+
+class IBtmDebug final : public ServiceFramework<IBtmDebug> {
+public:
+ explicit IBtmDebug(Core::System& system_);
+ ~IBtmDebug() override;
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system.cpp b/src/core/hle/service/btm/btm_system.cpp
new file mode 100644
index 000000000..99718a7b0
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/btm/btm_system.h"
+#include "core/hle/service/btm/btm_system_core.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/service.h"
+
+namespace Service::BTM {
+
+IBtmSystem::IBtmSystem(Core::System& system_) : ServiceFramework{system_, "btm:sys"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmSystem::GetCore>, "GetCore"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IBtmSystem::~IBtmSystem() = default;
+
+Result IBtmSystem::GetCore(OutInterface<IBtmSystemCore> out_interface) {
+ LOG_WARNING(Service_BTM, "called");
+
+ *out_interface = std::make_shared<IBtmSystemCore>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system.h b/src/core/hle/service/btm/btm_system.h
new file mode 100644
index 000000000..fe1c6dbd7
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+class IBtmSystemCore;
+
+class IBtmSystem final : public ServiceFramework<IBtmSystem> {
+public:
+ explicit IBtmSystem(Core::System& system_);
+ ~IBtmSystem() override;
+
+private:
+ Result GetCore(OutInterface<IBtmSystemCore> out_interface);
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system_core.cpp b/src/core/hle/service/btm/btm_system_core.cpp
new file mode 100644
index 000000000..4bc8a9e8b
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system_core.cpp
@@ -0,0 +1,127 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/btm/btm_system_core.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/set/system_settings_server.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::BTM {
+
+IBtmSystemCore::IBtmSystemCore(Core::System& system_)
+ : ServiceFramework{system_, "IBtmSystemCore"}, service_context{system_, "IBtmSystemCore"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmSystemCore::StartGamepadPairing>, "StartGamepadPairing"},
+ {1, C<&IBtmSystemCore::CancelGamepadPairing>, "CancelGamepadPairing"},
+ {2, nullptr, "ClearGamepadPairingDatabase"},
+ {3, nullptr, "GetPairedGamepadCount"},
+ {4, C<&IBtmSystemCore::EnableRadio>, "EnableRadio"},
+ {5, C<&IBtmSystemCore::DisableRadio>, "DisableRadio"},
+ {6, C<&IBtmSystemCore::IsRadioEnabled>, "IsRadioEnabled"},
+ {7, C<&IBtmSystemCore::AcquireRadioEvent>, "AcquireRadioEvent"},
+ {8, nullptr, "AcquireGamepadPairingEvent"},
+ {9, nullptr, "IsGamepadPairingStarted"},
+ {10, nullptr, "StartAudioDeviceDiscovery"},
+ {11, nullptr, "StopAudioDeviceDiscovery"},
+ {12, nullptr, "IsDiscoveryingAudioDevice"},
+ {13, nullptr, "GetDiscoveredAudioDevice"},
+ {14, C<&IBtmSystemCore::AcquireAudioDeviceConnectionEvent>, "AcquireAudioDeviceConnectionEvent"},
+ {15, nullptr, "ConnectAudioDevice"},
+ {16, nullptr, "IsConnectingAudioDevice"},
+ {17, C<&IBtmSystemCore::GetConnectedAudioDevices>, "GetConnectedAudioDevices"},
+ {18, nullptr, "DisconnectAudioDevice"},
+ {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
+ {20, C<&IBtmSystemCore::GetPairedAudioDevices>, "GetPairedAudioDevices"},
+ {21, nullptr, "RemoveAudioDevicePairing"},
+ {22, C<&IBtmSystemCore::RequestAudioDeviceConnectionRejection>, "RequestAudioDeviceConnectionRejection"},
+ {23, C<&IBtmSystemCore::CancelAudioDeviceConnectionRejection>, "CancelAudioDeviceConnectionRejection"}
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ radio_event = service_context.CreateEvent("IBtmSystemCore::RadioEvent");
+ audio_device_connection_event =
+ service_context.CreateEvent("IBtmSystemCore::AudioDeviceConnectionEvent");
+
+ m_set_sys =
+ system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
+}
+
+IBtmSystemCore::~IBtmSystemCore() {
+ service_context.CloseEvent(radio_event);
+ service_context.CloseEvent(audio_device_connection_event);
+}
+
+Result IBtmSystemCore::StartGamepadPairing() {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::CancelGamepadPairing() {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::EnableRadio() {
+ LOG_DEBUG(Service_BTM, "called");
+
+ R_RETURN(m_set_sys->SetBluetoothEnableFlag(true));
+}
+Result IBtmSystemCore::DisableRadio() {
+ LOG_DEBUG(Service_BTM, "called");
+
+ R_RETURN(m_set_sys->SetBluetoothEnableFlag(false));
+}
+
+Result IBtmSystemCore::IsRadioEnabled(Out<bool> out_is_enabled) {
+ LOG_DEBUG(Service_BTM, "called");
+
+ R_RETURN(m_set_sys->GetBluetoothEnableFlag(out_is_enabled));
+}
+
+Result IBtmSystemCore::AcquireRadioEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &radio_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::AcquireAudioDeviceConnectionEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_event = &audio_device_connection_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::GetConnectedAudioDevices(
+ Out<s32> out_count, OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_count = 0;
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::GetPairedAudioDevices(
+ Out<s32> out_count, OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_count = 0;
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::RequestAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::CancelAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system_core.h b/src/core/hle/service/btm/btm_system_core.h
new file mode 100644
index 000000000..06498b21e
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system_core.h
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Core {
+class System;
+}
+
+namespace Service::Set {
+class ISystemSettingsServer;
+}
+
+namespace Service::BTM {
+
+class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> {
+public:
+ explicit IBtmSystemCore(Core::System& system_);
+ ~IBtmSystemCore() override;
+
+private:
+ Result StartGamepadPairing();
+ Result CancelGamepadPairing();
+ Result EnableRadio();
+ Result DisableRadio();
+ Result IsRadioEnabled(Out<bool> out_is_enabled);
+
+ Result AcquireRadioEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireAudioDeviceConnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result GetConnectedAudioDevices(
+ Out<s32> out_count,
+ OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices);
+
+ Result GetPairedAudioDevices(
+ Out<s32> out_count,
+ OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices);
+
+ Result RequestAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid);
+ Result CancelAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid);
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* radio_event;
+ Kernel::KEvent* audio_device_connection_event;
+ std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user.cpp b/src/core/hle/service/btm/btm_user.cpp
new file mode 100644
index 000000000..d2e228f8d
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user.cpp
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/btm/btm_user.h"
+#include "core/hle/service/btm/btm_user_core.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BTM {
+
+IBtmUser::IBtmUser(Core::System& system_) : ServiceFramework{system_, "btm:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmUser::GetCore>, "GetCore"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IBtmUser::~IBtmUser() = default;
+
+Result IBtmUser::GetCore(OutInterface<IBtmUserCore> out_interface) {
+ LOG_WARNING(Service_BTM, "called");
+
+ *out_interface = std::make_shared<IBtmUserCore>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user.h b/src/core/hle/service/btm/btm_user.h
new file mode 100644
index 000000000..d9ee5db45
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+class IBtmUserCore;
+
+class IBtmUser final : public ServiceFramework<IBtmUser> {
+public:
+ explicit IBtmUser(Core::System& system_);
+ ~IBtmUser() override;
+
+private:
+ Result GetCore(OutInterface<IBtmUserCore> out_interface);
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user_core.cpp b/src/core/hle/service/btm/btm_user_core.cpp
new file mode 100644
index 000000000..6f9fa589b
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user_core.cpp
@@ -0,0 +1,103 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/btm/btm_user_core.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BTM {
+
+IBtmUserCore::IBtmUserCore(Core::System& system_)
+ : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmUserCore::AcquireBleScanEvent>, "AcquireBleScanEvent"},
+ {1, nullptr, "GetBleScanFilterParameter"},
+ {2, nullptr, "GetBleScanFilterParameter2"},
+ {3, nullptr, "StartBleScanForGeneral"},
+ {4, nullptr, "StopBleScanForGeneral"},
+ {5, nullptr, "GetBleScanResultsForGeneral"},
+ {6, nullptr, "StartBleScanForPaired"},
+ {7, nullptr, "StopBleScanForPaired"},
+ {8, nullptr, "StartBleScanForSmartDevice"},
+ {9, nullptr, "StopBleScanForSmartDevice"},
+ {10, nullptr, "GetBleScanResultsForSmartDevice"},
+ {17, C<&IBtmUserCore::AcquireBleConnectionEvent>, "AcquireBleConnectionEvent"},
+ {18, nullptr, "BleConnect"},
+ {19, nullptr, "BleDisconnect"},
+ {20, nullptr, "BleGetConnectionState"},
+ {21, nullptr, "AcquireBlePairingEvent"},
+ {22, nullptr, "BlePairDevice"},
+ {23, nullptr, "BleUnPairDevice"},
+ {24, nullptr, "BleUnPairDevice2"},
+ {25, nullptr, "BleGetPairedDevices"},
+ {26, C<&IBtmUserCore::AcquireBleServiceDiscoveryEvent>, "AcquireBleServiceDiscoveryEvent"},
+ {27, nullptr, "GetGattServices"},
+ {28, nullptr, "GetGattService"},
+ {29, nullptr, "GetGattIncludedServices"},
+ {30, nullptr, "GetBelongingGattService"},
+ {31, nullptr, "GetGattCharacteristics"},
+ {32, nullptr, "GetGattDescriptors"},
+ {33, C<&IBtmUserCore::AcquireBleMtuConfigEvent>, "AcquireBleMtuConfigEvent"},
+ {34, nullptr, "ConfigureBleMtu"},
+ {35, nullptr, "GetBleMtu"},
+ {36, nullptr, "RegisterBleGattDataPath"},
+ {37, nullptr, "UnregisterBleGattDataPath"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+
+ scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent");
+ connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent");
+ service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent");
+ config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent");
+}
+
+IBtmUserCore::~IBtmUserCore() {
+ service_context.CloseEvent(scan_event);
+ service_context.CloseEvent(connection_event);
+ service_context.CloseEvent(service_discovery_event);
+ service_context.CloseEvent(config_event);
+}
+
+Result IBtmUserCore::AcquireBleScanEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &scan_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmUserCore::AcquireBleConnectionEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &connection_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmUserCore::AcquireBleServiceDiscoveryEvent(
+ Out<bool> out_is_valid, OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &service_discovery_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmUserCore::AcquireBleMtuConfigEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &config_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user_core.h b/src/core/hle/service/btm/btm_user_core.h
new file mode 100644
index 000000000..dc0a22e81
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user_core.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+
+class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
+public:
+ explicit IBtmUserCore(Core::System& system_);
+ ~IBtmUserCore() override;
+
+private:
+ Result AcquireBleScanEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireBleConnectionEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireBleServiceDiscoveryEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireBleMtuConfigEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* scan_event;
+ Kernel::KEvent* connection_event;
+ Kernel::KEvent* service_discovery_event;
+ Kernel::KEvent* config_event;
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 47ff072c5..52228b830 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -16,7 +16,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAlbumFileCount"},
- {1, nullptr, "GetAlbumFileList"},
+ {1, C<&IAlbumAccessorService::GetAlbumFileList>, "GetAlbumFileList"},
{2, nullptr, "LoadAlbumFile"},
{3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"},
{4, nullptr, "StorageCopyAlbumFile"},
@@ -62,6 +62,15 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
IAlbumAccessorService::~IAlbumAccessorService() = default;
+Result IAlbumAccessorService::GetAlbumFileList(
+ Out<u64> out_count, AlbumStorage storage,
+ OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
+ LOG_INFO(Service_Capture, "called, storage={}", storage);
+
+ const Result result = manager->GetAlbumFileList(out_entries, *out_count, storage, 0);
+ R_RETURN(TranslateResult(result));
+}
+
Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
file_id.application_id, file_id.storage, file_id.type);
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index 2cb9b4547..c7a5208e3 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -21,6 +21,9 @@ public:
~IAlbumAccessorService() override;
private:
+ Result GetAlbumFileList(Out<u64> out_count, AlbumStorage storage,
+ OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);
+
Result DeleteAlbumFile(AlbumFileId file_id);
Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage);
diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp
index 3ea862fad..39ae3a723 100644
--- a/src/core/hle/service/erpt/erpt.cpp
+++ b/src/core/hle/service/erpt/erpt.cpp
@@ -3,6 +3,8 @@
#include <memory>
+#include "common/logging/log.h"
+#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/erpt/erpt.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
@@ -15,7 +17,7 @@ public:
explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "SubmitContext"},
+ {0, C<&ErrorReportContext::SubmitContext>, "SubmitContext"},
{1, nullptr, "CreateReportV0"},
{2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
{3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
@@ -36,6 +38,14 @@ public:
RegisterHandlers(functions);
}
+
+private:
+ Result SubmitContext(InBuffer<BufferAttr_HipcMapAlias> buffer_a,
+ InBuffer<BufferAttr_HipcMapAlias> buffer_b) {
+ LOG_WARNING(Service_SET, "(STUBBED) called, buffer_a_size={}, buffer_b_size={}",
+ buffer_a.size(), buffer_b.size());
+ R_SUCCEED();
+ }
};
class ErrorReportSession final : public ServiceFramework<ErrorReportSession> {
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
index 63c2d3a58..2d49f30c8 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
@@ -336,7 +336,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{1012, nullptr, "GetFsStackUsage"},
{1013, nullptr, "UnsetSaveDataRootPath"},
{1014, nullptr, "OutputMultiProgramTagAccessLog"},
- {1016, nullptr, "FlushAccessLogOnSdCard"},
+ {1016, &FSP_SRV::FlushAccessLogOnSdCard, "FlushAccessLogOnSdCard"},
{1017, nullptr, "OutputApplicationInfoAccessLog"},
{1018, nullptr, "SetDebugOption"},
{1019, nullptr, "UnsetDebugOption"},
@@ -706,6 +706,13 @@ void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) {
rb.Push(access_log_program_index);
}
+void FSP_SRV::FlushAccessLogOnSdCard(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto index{rp.Pop<s32>()};
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h
index 26980af99..59406e6f9 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h
@@ -58,6 +58,7 @@ private:
void SetGlobalAccessLogMode(HLERequestContext& ctx);
void GetGlobalAccessLogMode(HLERequestContext& ctx);
void OutputAccessLogToSdCard(HLERequestContext& ctx);
+ void FlushAccessLogOnSdCard(HLERequestContext& ctx);
void GetProgramIndexForAccessLog(HLERequestContext& ctx);
void OpenMultiCommitManager(HLERequestContext& ctx);
void GetCacheStorageSize(HLERequestContext& ctx);
diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp
index 0c27e8029..059ac3fc9 100644
--- a/src/core/hle/service/glue/time/manager.cpp
+++ b/src/core/hle/service/glue/time/manager.cpp
@@ -21,19 +21,6 @@
namespace Service::Glue::Time {
namespace {
-
-template <typename T>
-T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
- const char* category, const char* name) {
- std::vector<u8> interval_buf;
- auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
- ASSERT(res == ResultSuccess);
-
- T v{};
- std::memcpy(&v, interval_buf.data(), sizeof(T));
- return v;
-}
-
s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
constexpr auto is_leap = [](s32 year) -> bool {
return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
@@ -65,13 +52,15 @@ s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
Service::PSC::Time::CalendarTime calendar{
- .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"),
+ .year = 2000,
.month = 1,
.day = 1,
.hour = 0,
.minute = 0,
.second = 0,
};
+ set_sys->GetSettingsItemValueImpl<s16>(calendar.year, "time",
+ "standard_user_clock_initial_year");
return CalendarTimeToEpoch(calendar);
}
@@ -124,7 +113,7 @@ TimeManager::TimeManager(Core::System& system)
ASSERT(res == ResultSuccess);
Service::PSC::Time::SystemClockContext user_clock_context{};
- res = m_set_sys->GetUserSystemClockContext(user_clock_context);
+ res = m_set_sys->GetUserSystemClockContext(&user_clock_context);
ASSERT(res == ResultSuccess);
// TODO the local clock should initialise with this epoch time, and be updated somewhere else on
@@ -140,11 +129,12 @@ TimeManager::TimeManager(Core::System& system)
ASSERT(res == ResultSuccess);
Service::PSC::Time::SystemClockContext network_clock_context{};
- res = m_set_sys->GetNetworkSystemClockContext(network_clock_context);
+ res = m_set_sys->GetNetworkSystemClockContext(&network_clock_context);
ASSERT(res == ResultSuccess);
- auto network_accuracy_m{GetSettingsItemValue<s32>(
- m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")};
+ s32 network_accuracy_m{};
+ m_set_sys->GetSettingsItemValueImpl<s32>(network_accuracy_m, "time",
+ "standard_network_clock_sufficient_accuracy_minutes");
auto one_minute_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};
@@ -153,12 +143,12 @@ TimeManager::TimeManager(Core::System& system)
ASSERT(res == ResultSuccess);
bool is_automatic_correction_enabled{};
- res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+ res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(&is_automatic_correction_enabled);
ASSERT(res == ResultSuccess);
Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
- automatic_correction_time_point);
+ &automatic_correction_time_point);
ASSERT(res == ResultSuccess);
res = m_time_m->SetupStandardUserSystemClockCore(is_automatic_correction_enabled,
@@ -196,13 +186,17 @@ TimeManager::TimeManager(Core::System& system)
}
}
+TimeManager::~TimeManager() {
+ ResetTimeZoneBinary();
+}
+
Result TimeManager::SetupStandardSteadyClockCore() {
Common::UUID external_clock_source_id{};
- auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id);
+ auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id);
ASSERT(res == ResultSuccess);
s64 external_steady_clock_internal_offset_s{};
- res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s);
+ res = m_set_sys->GetExternalSteadyClockInternalOffset(&external_steady_clock_internal_offset_s);
ASSERT(res == ResultSuccess);
auto one_second_ns{
@@ -210,8 +204,9 @@ Result TimeManager::SetupStandardSteadyClockCore() {
s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
one_second_ns};
- s32 standard_steady_clock_test_offset_m{
- GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")};
+ s32 standard_steady_clock_test_offset_m{};
+ m_set_sys->GetSettingsItemValueImpl<s32>(standard_steady_clock_test_offset_m, "time",
+ "standard_steady_clock_test_offset_minutes");
auto one_minute_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};
@@ -237,7 +232,7 @@ Result TimeManager::SetupStandardSteadyClockCore() {
Result TimeManager::SetupTimeZoneServiceCore() {
Service::PSC::Time::LocationName name{};
- auto res = m_set_sys->GetDeviceTimeZoneLocationName(name);
+ auto res = m_set_sys->GetDeviceTimeZoneLocationName(&name);
ASSERT(res == ResultSuccess);
auto configured_zone = GetTimeZoneString(name);
@@ -255,7 +250,7 @@ Result TimeManager::SetupTimeZoneServiceCore() {
}
Service::PSC::Time::SteadyClockTimePoint time_point{};
- res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point);
+ res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(&time_point);
ASSERT(res == ResultSuccess);
auto location_count = GetTimeZoneCount();
diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h
index 1de93f8f9..bb4b65049 100644
--- a/src/core/hle/service/glue/time/manager.h
+++ b/src/core/hle/service/glue/time/manager.h
@@ -26,6 +26,7 @@ namespace Service::Glue::Time {
class TimeManager {
public:
explicit TimeManager(Core::System& system);
+ ~TimeManager();
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp
index f8c1218f3..b801faef2 100644
--- a/src/core/hle/service/glue/time/static.cpp
+++ b/src/core/hle/service/glue/time/static.cpp
@@ -20,19 +20,6 @@
#include "core/hle/service/sm/sm.h"
namespace Service::Glue::Time {
-namespace {
-template <typename T>
-T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
- const char* category, const char* name) {
- std::vector<u8> interval_buf;
- auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
- ASSERT(res == ResultSuccess);
-
- T v{};
- std::memcpy(&v, interval_buf.data(), sizeof(T));
- return v;
-}
-} // namespace
StaticService::StaticService(Core::System& system_,
Service::PSC::Time::StaticServiceSetupInfo setup_info,
@@ -155,16 +142,18 @@ Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
}
Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value);
+ };
R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(*out_rtc_value));
}
Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
Out<bool> out_automatic_correction) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. out_automatic_correction={}", *out_automatic_correction);
- });
+ };
R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
out_automatic_correction));
@@ -179,21 +168,27 @@ Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
}
Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_year={}", *out_year); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_year={}", *out_year);
+ };
- *out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year");
- R_SUCCEED();
+ R_RETURN(m_set_sys->GetSettingsItemValueImpl<s32>(*out_year, "time",
+ "standard_user_clock_initial_year"));
}
Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient);
+ };
R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
}
Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
+ };
R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
out_time_point));
@@ -201,15 +196,18 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time);
+ };
R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
}
Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot,
Service::PSC::Time::TimeType type) {
- SCOPE_EXIT(
- { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot);
+ };
R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
}
@@ -218,11 +216,11 @@ Result StaticService::GetClockSnapshotFromSystemClockContext(
Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot,
const Service::PSC::Time::SystemClockContext& user_context,
const Service::PSC::Time::SystemClockContext& network_context) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. type={} out_snapshot={} user_context={} network_context={}", type,
*out_snapshot, user_context, network_context);
- });
+ };
R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(
type, out_snapshot, user_context, network_context));
@@ -231,14 +229,18 @@ Result StaticService::GetClockSnapshotFromSystemClockContext(
Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_time,
InClockSnapshot a,
InClockSnapshot b) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
+ };
R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
}
Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a,
InClockSnapshot b) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
+ };
R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
}
diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp
index 36f163419..f4d0c87d5 100644
--- a/src/core/hle/service/glue/time/time_zone.cpp
+++ b/src/core/hle/service/glue/time/time_zone.cpp
@@ -57,7 +57,9 @@ TimeZoneService::~TimeZoneService() = default;
Result TimeZoneService::GetDeviceLocationName(
Out<Service::PSC::Time::LocationName> out_location_name) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name);
+ };
R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
}
@@ -94,7 +96,9 @@ Result TimeZoneService::SetDeviceLocationName(
}
Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count);
+ };
R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
}
@@ -102,10 +106,10 @@ Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
Result TimeZoneService::LoadLocationNameList(
Out<u32> out_count,
OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}",
index, *out_count, out_names[0], out_names[1]);
- });
+ };
std::scoped_lock l{m_mutex};
R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index));
@@ -124,7 +128,9 @@ Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule,
Result TimeZoneService::GetTimeZoneRuleVersion(
Out<Service::PSC::Time::RuleVersion> out_rule_version) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version);
+ };
R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
}
@@ -132,10 +138,10 @@ Result TimeZoneService::GetTimeZoneRuleVersion(
Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
Out<Service::PSC::Time::LocationName> location_name,
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. location_name={} out_time_point={}", *location_name,
*out_time_point);
- });
+ };
R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(location_name, out_time_point));
}
@@ -178,10 +184,10 @@ Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
Result TimeZoneService::ToCalendarTime(
Out<Service::PSC::Time::CalendarTime> out_calendar_time,
Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time, InRule rule) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
*out_calendar_time, *out_additional_info);
- });
+ };
R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
}
@@ -189,10 +195,10 @@ Result TimeZoneService::ToCalendarTime(
Result TimeZoneService::ToCalendarTimeWithMyRule(
Out<Service::PSC::Time::CalendarTime> out_calendar_time,
Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
*out_calendar_time, *out_additional_info);
- });
+ };
R_RETURN(
m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
@@ -202,11 +208,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const Service::PSC::Time::CalendarTime& calendar_time,
InRule rule) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times[0], out_times[1]);
- });
+ };
R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
}
@@ -214,11 +220,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
Result TimeZoneService::ToPosixTimeWithMyRule(
Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
const Service::PSC::Time::CalendarTime& calendar_time) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times[0], out_times[1]);
- });
+ };
R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time));
}
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp
index 8787f2dcd..b28569b68 100644
--- a/src/core/hle/service/glue/time/worker.cpp
+++ b/src/core/hle/service/glue/time/worker.cpp
@@ -27,7 +27,7 @@ template <typename T>
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
const char* category, const char* name) {
std::vector<u8> interval_buf;
- auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
+ auto res = set_sys->GetSettingsItemValueImpl(interval_buf, category, name);
ASSERT(res == ResultSuccess);
T v{};
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
index 8f3c04550..b9db19618 100644
--- a/src/core/hle/service/ldn/lan_discovery.cpp
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
@@ -85,15 +85,14 @@ Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
}
Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
- std::vector<NodeLatestUpdate>& out_updates,
- std::size_t buffer_count) {
- if (buffer_count > NodeCountMax) {
+ std::span<NodeLatestUpdate> out_updates) {
+ if (out_updates.size() > NodeCountMax) {
return ResultInvalidBufferCount;
}
if (state == State::AccessPointCreated || state == State::StationConnected) {
std::memcpy(&out_network, &network_info, sizeof(network_info));
- for (std::size_t i = 0; i < buffer_count; i++) {
+ for (std::size_t i = 0; i < out_updates.size(); i++) {
out_updates[i].state_change = node_changes[i].state_change;
node_changes[i].state_change = NodeStateChange::None;
}
@@ -107,15 +106,8 @@ DisconnectReason LANDiscovery::GetDisconnectReason() const {
return disconnect_reason;
}
-Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
+Result LANDiscovery::Scan(std::span<NetworkInfo> out_networks, s16& out_count,
const ScanFilter& filter) {
- if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) ||
- filter.network_type <= NetworkType::All) {
- if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) {
- return ResultBadInput;
- }
- }
-
{
std::scoped_lock lock{packet_mutex};
scan_results.clear();
@@ -128,7 +120,7 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
std::scoped_lock lock{packet_mutex};
for (const auto& [key, info] : scan_results) {
- if (count >= networks.size()) {
+ if (out_count >= static_cast<s16>(out_networks.size())) {
break;
}
@@ -159,7 +151,7 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
}
}
- networks[count++] = info;
+ out_networks[out_count++] = info;
}
return ResultSuccess;
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
index 3833cd764..8f7a8dfc4 100644
--- a/src/core/hle/service/ldn/lan_discovery.h
+++ b/src/core/hle/service/ldn/lan_discovery.h
@@ -54,11 +54,10 @@ public:
void SetState(State new_state);
Result GetNetworkInfo(NetworkInfo& out_network) const;
- Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates,
- std::size_t buffer_count);
+ Result GetNetworkInfo(NetworkInfo& out_network, std::span<NodeLatestUpdate> out_updates);
DisconnectReason GetDisconnectReason() const;
- Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter);
+ Result Scan(std::span<NetworkInfo> out_networks, s16& out_count, const ScanFilter& filter);
Result SetAdvertiseData(std::span<const u8> data);
Result OpenAccessPoint();
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 961f89a14..f2d638c30 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -1,36 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <memory>
-
#include "core/core.h"
-#include "core/hle/service/ldn/lan_discovery.h"
+#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn.h"
-#include "core/hle/service/ldn/ldn_results.h"
-#include "core/hle/service/ldn/ldn_types.h"
-#include "core/hle/service/server_manager.h"
-#include "core/internal_network/network.h"
-#include "core/internal_network/network_interface.h"
-#include "network/network.h"
-
-// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
-#undef CreateEvent
+#include "core/hle/service/ldn/monitor_service.h"
+#include "core/hle/service/ldn/sf_monitor_service.h"
+#include "core/hle/service/ldn/sf_service.h"
+#include "core/hle/service/ldn/sf_service_monitor.h"
+#include "core/hle/service/ldn/system_local_communication_service.h"
+#include "core/hle/service/ldn/user_local_communication_service.h"
namespace Service::LDN {
-class IMonitorService final : public ServiceFramework<IMonitorService> {
+class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
public:
- explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} {
+ explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &IMonitorService::GetStateForMonitor, "GetStateForMonitor"},
- {1, nullptr, "GetNetworkInfoForMonitor"},
- {2, nullptr, "GetIpv4AddressForMonitor"},
- {3, nullptr, "GetDisconnectReasonForMonitor"},
- {4, nullptr, "GetSecurityParameterForMonitor"},
- {5, nullptr, "GetNetworkConfigForMonitor"},
- {100, &IMonitorService::InitializeMonitor, "InitializeMonitor"},
- {101, nullptr, "FinalizeMonitor"},
+ {0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
};
// clang-format on
@@ -38,84 +26,20 @@ public:
}
private:
- void GetStateForMonitor(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(state);
- }
-
- void InitializeMonitor(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- state = State::Initialized;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- State state{State::None};
-};
-
-class LDNM final : public ServiceFramework<LDNM> {
-public:
- explicit LDNM(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &LDNM::CreateMonitorService, "CreateMonitorService"}
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- void CreateMonitorService(HLERequestContext& ctx) {
+ Result CreateMonitorService(OutInterface<IMonitorService> out_interface) {
LOG_DEBUG(Service_LDN, "called");
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IMonitorService>(system);
+ *out_interface = std::make_shared<IMonitorService>(system);
+ R_SUCCEED();
}
};
-class ISystemLocalCommunicationService final
- : public ServiceFramework<ISystemLocalCommunicationService> {
+class ISystemServiceCreator final : public ServiceFramework<ISystemServiceCreator> {
public:
- explicit ISystemLocalCommunicationService(Core::System& system_)
- : ServiceFramework{system_, "ISystemLocalCommunicationService"} {
+ explicit ISystemServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:s"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetState"},
- {1, nullptr, "GetNetworkInfo"},
- {2, nullptr, "GetIpv4Address"},
- {3, nullptr, "GetDisconnectReason"},
- {4, nullptr, "GetSecurityParameter"},
- {5, nullptr, "GetNetworkConfig"},
- {100, nullptr, "AttachStateChangeEvent"},
- {101, nullptr, "GetNetworkInfoLatestUpdate"},
- {102, nullptr, "Scan"},
- {103, nullptr, "ScanPrivate"},
- {104, nullptr, "SetWirelessControllerRestriction"},
- {200, nullptr, "OpenAccessPoint"},
- {201, nullptr, "CloseAccessPoint"},
- {202, nullptr, "CreateNetwork"},
- {203, nullptr, "CreateNetworkPrivate"},
- {204, nullptr, "DestroyNetwork"},
- {205, nullptr, "Reject"},
- {206, nullptr, "SetAdvertiseData"},
- {207, nullptr, "SetStationAcceptPolicy"},
- {208, nullptr, "AddAcceptFilterEntry"},
- {209, nullptr, "ClearAcceptFilter"},
- {300, nullptr, "OpenStation"},
- {301, nullptr, "CloseStation"},
- {302, nullptr, "Connect"},
- {303, nullptr, "ConnectPrivate"},
- {304, nullptr, "Disconnect"},
- {400, nullptr, "InitializeSystem"},
- {401, nullptr, "FinalizeSystem"},
- {402, nullptr, "SetOperationMode"},
- {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
+ {0, C<&ISystemServiceCreator::CreateSystemLocalCommunicationService>, "CreateSystemLocalCommunicationService"},
};
// clang-format on
@@ -123,687 +47,78 @@ public:
}
private:
- void InitializeSystem2(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-};
-
-class IUserLocalCommunicationService final
- : public ServiceFramework<IUserLocalCommunicationService> {
-public:
- explicit IUserLocalCommunicationService(Core::System& system_)
- : ServiceFramework{system_, "IUserLocalCommunicationService"},
- service_context{system, "IUserLocalCommunicationService"},
- room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IUserLocalCommunicationService::GetState, "GetState"},
- {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
- {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"},
- {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
- {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
- {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
- {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
- {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
- {102, &IUserLocalCommunicationService::Scan, "Scan"},
- {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
- {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"},
- {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
- {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
- {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
- {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
- {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
- {205, nullptr, "Reject"},
- {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
- {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
- {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
- {209, nullptr, "ClearAcceptFilter"},
- {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
- {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
- {302, &IUserLocalCommunicationService::Connect, "Connect"},
- {303, nullptr, "ConnectPrivate"},
- {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
- {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
- {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
- {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-
- state_change_event =
- service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
- }
-
- ~IUserLocalCommunicationService() {
- if (is_initialized) {
- if (auto room_member = room_network.GetRoomMember().lock()) {
- room_member->Unbind(ldn_packet_received);
- }
- }
-
- service_context.CloseEvent(state_change_event);
- }
-
- /// Callback to parse and handle a received LDN packet.
- void OnLDNPacketReceived(const Network::LDNPacket& packet) {
- lan_discovery.ReceivePacket(packet);
- }
-
- void OnEventFired() {
- state_change_event->Signal();
- }
-
- void GetState(HLERequestContext& ctx) {
- State state = State::Error;
-
- if (is_initialized) {
- state = lan_discovery.GetState();
- }
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(state);
- }
-
- void GetNetworkInfo(HLERequestContext& ctx) {
- const auto write_buffer_size = ctx.GetWriteBufferSize();
-
- if (write_buffer_size != sizeof(NetworkInfo)) {
- LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultBadInput);
- return;
- }
-
- NetworkInfo network_info{};
- const auto rc = lan_discovery.GetNetworkInfo(network_info);
- if (rc.IsError()) {
- LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- return;
- }
-
- ctx.WriteBuffer<NetworkInfo>(network_info);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetIpv4Address(HLERequestContext& ctx) {
- const auto network_interface = Network::GetSelectedNetworkInterface();
-
- if (!network_interface) {
- LOG_ERROR(Service_LDN, "No network interface available");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNoIpAddress);
- return;
- }
-
- Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)};
- Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)};
-
- // When we're connected to a room, spoof the hosts IP address
- if (auto room_member = room_network.GetRoomMember().lock()) {
- if (room_member->IsConnected()) {
- current_address = room_member->GetFakeIpAddress();
- }
- }
-
- std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
- std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushRaw(current_address);
- rb.PushRaw(subnet_mask);
- }
-
- void GetDisconnectReason(HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(lan_discovery.GetDisconnectReason());
- }
-
- void GetSecurityParameter(HLERequestContext& ctx) {
- SecurityParameter security_parameter{};
- NetworkInfo info{};
- const Result rc = lan_discovery.GetNetworkInfo(info);
-
- if (rc.IsError()) {
- LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- return;
- }
-
- security_parameter.session_id = info.network_id.session_id;
- std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
- sizeof(SecurityParameter::data));
-
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(rc);
- rb.PushRaw<SecurityParameter>(security_parameter);
- }
-
- void GetNetworkConfig(HLERequestContext& ctx) {
- NetworkConfig config{};
- NetworkInfo info{};
- const Result rc = lan_discovery.GetNetworkInfo(info);
-
- if (rc.IsError()) {
- LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- return;
- }
-
- config.intent_id = info.network_id.intent_id;
- config.channel = info.common.channel;
- config.node_count_max = info.ldn.node_count_max;
- config.local_communication_version = info.ldn.nodes[0].local_communication_version;
-
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(rc);
- rb.PushRaw<NetworkConfig>(config);
- }
-
- void AttachStateChangeEvent(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(state_change_event->GetReadableEvent());
- }
-
- void GetNetworkInfoLatestUpdate(HLERequestContext& ctx) {
- const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
- const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements<NodeLatestUpdate>(1);
-
- if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
- LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
- node_buffer_count);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultBadInput);
- return;
- }
-
- NetworkInfo info{};
- std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
-
- const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size());
- if (rc.IsError()) {
- LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- return;
- }
-
- ctx.WriteBuffer(info, 0);
- ctx.WriteBuffer(latest_update, 1);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void Scan(HLERequestContext& ctx) {
- ScanImpl(ctx);
- }
-
- void ScanPrivate(HLERequestContext& ctx) {
- ScanImpl(ctx, true);
- }
-
- void ScanImpl(HLERequestContext& ctx, bool is_private = false) {
- IPC::RequestParser rp{ctx};
- const auto channel{rp.PopEnum<WifiChannel>()};
- const auto scan_filter{rp.PopRaw<ScanFilter>()};
-
- const std::size_t network_info_size = ctx.GetWriteBufferNumElements<NetworkInfo>();
-
- if (network_info_size == 0) {
- LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultBadInput);
- return;
- }
-
- u16 count = 0;
- std::vector<NetworkInfo> network_infos(network_info_size);
- Result rc = lan_discovery.Scan(network_infos, count, scan_filter);
-
- LOG_INFO(Service_LDN,
- "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}",
- channel, scan_filter.flag, scan_filter.network_type, is_private);
-
- ctx.WriteBuffer(network_infos);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(rc);
- rb.Push<u32>(count);
- }
-
- void SetWirelessControllerRestriction(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void OpenAccessPoint(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.OpenAccessPoint());
- }
-
- void CloseAccessPoint(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.CloseAccessPoint());
- }
-
- void CreateNetwork(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- CreateNetworkImpl(ctx);
- }
-
- void CreateNetworkPrivate(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- CreateNetworkImpl(ctx, true);
- }
-
- void CreateNetworkImpl(HLERequestContext& ctx, bool is_private = false) {
- IPC::RequestParser rp{ctx};
-
- const auto security_config{rp.PopRaw<SecurityConfig>()};
- [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>()
- : SecurityParameter{}};
- const auto user_config{rp.PopRaw<UserConfig>()};
- rp.Pop<u32>(); // Padding
- const auto network_Config{rp.PopRaw<NetworkConfig>()};
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config));
- }
-
- void DestroyNetwork(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.DestroyNetwork());
- }
-
- void SetAdvertiseData(HLERequestContext& ctx) {
- const auto read_buffer = ctx.ReadBuffer();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
- }
-
- void SetStationAcceptPolicy(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void AddAcceptFilterEntry(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void OpenStation(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.OpenStation());
- }
-
- void CloseStation(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.CloseStation());
- }
-
- void Connect(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- struct Parameters {
- SecurityConfig security_config;
- UserConfig user_config;
- u32 local_communication_version;
- u32 option;
- };
- static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
-
- const auto parameters{rp.PopRaw<Parameters>()};
-
- LOG_INFO(Service_LDN,
- "called, passphrase_size={}, security_mode={}, "
- "local_communication_version={}",
- parameters.security_config.passphrase_size,
- parameters.security_config.security_mode, parameters.local_communication_version);
-
- const auto read_buffer = ctx.ReadBuffer();
- if (read_buffer.size() != sizeof(NetworkInfo)) {
- LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultBadInput);
- return;
- }
-
- NetworkInfo network_info{};
- std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.Connect(network_info, parameters.user_config,
- static_cast<u16>(parameters.local_communication_version)));
- }
-
- void Disconnect(HLERequestContext& ctx) {
- LOG_INFO(Service_LDN, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.Disconnect());
- }
-
- void Initialize(HLERequestContext& ctx) {
- const auto rc = InitializeImpl(ctx);
- if (rc.IsError()) {
- LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- }
-
- void Finalize(HLERequestContext& ctx) {
- if (auto room_member = room_network.GetRoomMember().lock()) {
- room_member->Unbind(ldn_packet_received);
- }
-
- is_initialized = false;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(lan_discovery.Finalize());
- }
-
- void Initialize2(HLERequestContext& ctx) {
- const auto rc = InitializeImpl(ctx);
- if (rc.IsError()) {
- LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- }
-
- Result InitializeImpl(HLERequestContext& ctx) {
- const auto network_interface = Network::GetSelectedNetworkInterface();
- if (!network_interface) {
- LOG_ERROR(Service_LDN, "No network interface is set");
- return ResultAirplaneModeEnabled;
- }
-
- if (auto room_member = room_network.GetRoomMember().lock()) {
- ldn_packet_received = room_member->BindOnLdnPacketReceived(
- [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
- } else {
- LOG_ERROR(Service_LDN, "Couldn't bind callback!");
- return ResultAirplaneModeEnabled;
- }
-
- lan_discovery.Initialize([&]() { OnEventFired(); });
- is_initialized = true;
- return ResultSuccess;
- }
-
- KernelHelpers::ServiceContext service_context;
- Kernel::KEvent* state_change_event;
- Network::RoomNetwork& room_network;
- LANDiscovery lan_discovery;
-
- // Callback identifier for the OnLDNPacketReceived event.
- Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
-
- bool is_initialized{};
-};
-
-class LDNS final : public ServiceFramework<LDNS> {
-public:
- explicit LDNS(Core::System& system_) : ServiceFramework{system_, "ldn:s"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &LDNS::CreateSystemLocalCommunicationService, "CreateSystemLocalCommunicationService"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- void CreateSystemLocalCommunicationService(HLERequestContext& ctx) {
+ Result CreateSystemLocalCommunicationService(
+ OutInterface<ISystemLocalCommunicationService> out_interface) {
LOG_DEBUG(Service_LDN, "called");
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISystemLocalCommunicationService>(system);
+ *out_interface = std::make_shared<ISystemLocalCommunicationService>(system);
+ R_SUCCEED();
}
};
-class LDNU final : public ServiceFramework<LDNU> {
+class IUserServiceCreator final : public ServiceFramework<IUserServiceCreator> {
public:
- explicit LDNU(Core::System& system_) : ServiceFramework{system_, "ldn:u"} {
+ explicit IUserServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:u"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &LDNU::CreateUserLocalCommunicationService, "CreateUserLocalCommunicationService"},
+ {0, C<&IUserServiceCreator::CreateUserLocalCommunicationService>, "CreateUserLocalCommunicationService"},
};
// clang-format on
RegisterHandlers(functions);
}
- void CreateUserLocalCommunicationService(HLERequestContext& ctx) {
+private:
+ Result CreateUserLocalCommunicationService(
+ OutInterface<IUserLocalCommunicationService> out_interface) {
LOG_DEBUG(Service_LDN, "called");
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IUserLocalCommunicationService>(system);
+ *out_interface = std::make_shared<IUserLocalCommunicationService>(system);
+ R_SUCCEED();
}
};
-class INetworkService final : public ServiceFramework<INetworkService> {
+class ISfServiceCreator final : public ServiceFramework<ISfServiceCreator> {
public:
- explicit INetworkService(Core::System& system_) : ServiceFramework{system_, "INetworkService"} {
+ explicit ISfServiceCreator(Core::System& system_, bool is_system_, const char* name_)
+ : ServiceFramework{system_, name_}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Initialize"},
- {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
- {264, nullptr, "GetNetworkInterfaceLastError"},
- {272, nullptr, "GetRole"},
- {280, nullptr, "GetAdvertiseData"},
- {288, nullptr, "GetGroupInfo"},
- {296, nullptr, "GetGroupInfo2"},
- {304, nullptr, "GetGroupOwner"},
- {312, nullptr, "GetIpConfig"},
- {320, nullptr, "GetLinkLevel"},
- {512, nullptr, "Scan"},
- {768, nullptr, "CreateGroup"},
- {776, nullptr, "DestroyGroup"},
- {784, nullptr, "SetAdvertiseData"},
- {1536, nullptr, "SendToOtherGroup"},
- {1544, nullptr, "RecvFromOtherGroup"},
- {1552, nullptr, "AddAcceptableGroupId"},
- {1560, nullptr, "ClearAcceptableGroupId"},
+ {0, C<&ISfServiceCreator::CreateNetworkService>, "CreateNetworkService"},
+ {8, C<&ISfServiceCreator::CreateNetworkServiceMonitor>, "CreateNetworkServiceMonitor"},
};
// clang-format on
RegisterHandlers(functions);
}
-};
-
-class INetworkServiceMonitor final : public ServiceFramework<INetworkServiceMonitor> {
-public:
- explicit INetworkServiceMonitor(Core::System& system_)
- : ServiceFramework{system_, "INetworkServiceMonitor"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &INetworkServiceMonitor::Initialize, "Initialize"},
- {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
- {264, nullptr, "GetNetworkInterfaceLastError"},
- {272, nullptr, "GetRole"},
- {280, nullptr, "GetAdvertiseData"},
- {281, nullptr, "GetAdvertiseData2"},
- {288, nullptr, "GetGroupInfo"},
- {296, nullptr, "GetGroupInfo2"},
- {304, nullptr, "GetGroupOwner"},
- {312, nullptr, "GetIpConfig"},
- {320, nullptr, "GetLinkLevel"},
- {328, nullptr, "AttachJoinEvent"},
- {336, nullptr, "GetMembers"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- void Initialize(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultDisabled);
- }
-};
-
-class LP2PAPP final : public ServiceFramework<LP2PAPP> {
-public:
- explicit LP2PAPP(Core::System& system_) : ServiceFramework{system_, "lp2p:app"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &LP2PAPP::CreateMonitorService, "CreateNetworkService"},
- {8, &LP2PAPP::CreateMonitorService, "CreateNetworkServiceMonitor"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- void CreateNetworkervice(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 reserved_input = rp.Pop<u64>();
- const u32 input = rp.Pop<u32>();
-
- LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
- input);
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkService>(system);
- }
-
- void CreateMonitorService(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 reserved_input = rp.Pop<u64>();
-
- LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkServiceMonitor>(system);
- }
-};
-
-class LP2PSYS final : public ServiceFramework<LP2PSYS> {
-public:
- explicit LP2PSYS(Core::System& system_) : ServiceFramework{system_, "lp2p:sys"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &LP2PSYS::CreateMonitorService, "CreateNetworkService"},
- {8, &LP2PSYS::CreateMonitorService, "CreateNetworkServiceMonitor"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- void CreateNetworkervice(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 reserved_input = rp.Pop<u64>();
- const u32 input = rp.Pop<u32>();
+private:
+ Result CreateNetworkService(OutInterface<ISfService> out_interface, u32 input,
+ u64 reserved_input) {
LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
input);
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkService>(system);
+ *out_interface = std::make_shared<ISfService>(system);
+ R_SUCCEED();
}
- void CreateMonitorService(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 reserved_input = rp.Pop<u64>();
-
+ Result CreateNetworkServiceMonitor(OutInterface<ISfServiceMonitor> out_interface,
+ u64 reserved_input) {
LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkServiceMonitor>(system);
+ *out_interface = std::make_shared<ISfServiceMonitor>(system);
+ R_SUCCEED();
}
-};
-class ISfMonitorService final : public ServiceFramework<ISfMonitorService> {
-public:
- explicit ISfMonitorService(Core::System& system_)
- : ServiceFramework{system_, "ISfMonitorService"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ISfMonitorService::Initialize, "Initialize"},
- {288, &ISfMonitorService::GetGroupInfo, "GetGroupInfo"},
- {320, nullptr, "GetLinkLevel"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void Initialize(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(0);
- }
-
- void GetGroupInfo(HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
-
- struct GroupInfo {
- std::array<u8, 0x200> info;
- };
-
- GroupInfo group_info{};
-
- ctx.WriteBuffer(group_info);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
+ bool is_system{};
};
-class LP2PM final : public ServiceFramework<LP2PM> {
+class ISfMonitorServiceCreator final : public ServiceFramework<ISfMonitorServiceCreator> {
public:
- explicit LP2PM(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} {
+ explicit ISfMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &LP2PM::CreateMonitorService, "CreateMonitorService"},
+ {0, C<&ISfMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"},
};
// clang-format on
@@ -811,28 +126,27 @@ public:
}
private:
- void CreateMonitorService(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 reserved_input = rp.Pop<u64>();
-
+ Result CreateMonitorService(OutInterface<ISfMonitorService> out_interface, u64 reserved_input) {
LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input);
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISfMonitorService>(system);
+ *out_interface = std::make_shared<ISfMonitorService>(system);
+ R_SUCCEED();
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- server_manager->RegisterNamedService("ldn:m", std::make_shared<LDNM>(system));
- server_manager->RegisterNamedService("ldn:s", std::make_shared<LDNS>(system));
- server_manager->RegisterNamedService("ldn:u", std::make_shared<LDNU>(system));
+ server_manager->RegisterNamedService("ldn:m", std::make_shared<IMonitorServiceCreator>(system));
+ server_manager->RegisterNamedService("ldn:s", std::make_shared<ISystemServiceCreator>(system));
+ server_manager->RegisterNamedService("ldn:u", std::make_shared<IUserServiceCreator>(system));
- server_manager->RegisterNamedService("lp2p:app", std::make_shared<LP2PAPP>(system));
- server_manager->RegisterNamedService("lp2p:sys", std::make_shared<LP2PSYS>(system));
- server_manager->RegisterNamedService("lp2p:m", std::make_shared<LP2PM>(system));
+ server_manager->RegisterNamedService(
+ "lp2p:app", std::make_shared<ISfServiceCreator>(system, false, "lp2p:app"));
+ server_manager->RegisterNamedService(
+ "lp2p:sys", std::make_shared<ISfServiceCreator>(system, true, "lp2p:sys"));
+ server_manager->RegisterNamedService("lp2p:m",
+ std::make_shared<ISfMonitorServiceCreator>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index f4a319168..dae037fa8 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -3,12 +3,6 @@
#pragma once
-#include "core/hle/kernel/k_event.h"
-#include "core/hle/result.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/sm/sm.h"
-
namespace Core {
class System;
}
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
index 44c2c773b..6198aa07b 100644
--- a/src/core/hle/service/ldn/ldn_types.h
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -123,6 +123,18 @@ enum class NodeStatus : u8 {
Connected,
};
+enum class WirelessControllerRestriction : u32 {
+ None,
+ Default,
+};
+
+struct ConnectOption {
+ union {
+ u32 raw;
+ };
+};
+static_assert(sizeof(ConnectOption) == 0x4, "ConnectOption is an invalid size");
+
struct NodeLatestUpdate {
NodeStateChange state_change;
INSERT_PADDING_BYTES(0x7); // Unknown
@@ -139,9 +151,9 @@ static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
struct IntentId {
u64 local_communication_id;
- INSERT_PADDING_BYTES(0x2); // Reserved
+ INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved
u16 scene_id;
- INSERT_PADDING_BYTES(0x4); // Reserved
+ INSERT_PADDING_BYTES_NOINIT(0x4); // Reserved
};
static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
@@ -152,13 +164,14 @@ struct NetworkId {
static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
struct Ssid {
- u8 length{};
- std::array<char, SsidLengthMax + 1> raw{};
+ u8 length;
+ std::array<char, SsidLengthMax + 1> raw;
Ssid() = default;
constexpr explicit Ssid(std::string_view data) {
length = static_cast<u8>(std::min(data.size(), SsidLengthMax));
+ raw = {};
data.copy(raw.data(), length);
raw[length] = 0;
}
@@ -181,7 +194,7 @@ using Ipv4Address = std::array<u8, 4>;
static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
struct MacAddress {
- std::array<u8, 6> raw{};
+ std::array<u8, 6> raw;
friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
};
@@ -211,7 +224,7 @@ struct CommonNetworkInfo {
WifiChannel channel;
LinkLevel link_level;
PackedNetworkType network_type;
- INSERT_PADDING_BYTES(0x4);
+ INSERT_PADDING_BYTES_NOINIT(0x4);
};
static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
@@ -221,9 +234,9 @@ struct NodeInfo {
s8 node_id;
u8 is_connected;
std::array<u8, UserNameBytesMax + 1> user_name;
- INSERT_PADDING_BYTES(0x1); // Reserved
+ INSERT_PADDING_BYTES_NOINIT(0x1); // Reserved
s16 local_communication_version;
- INSERT_PADDING_BYTES(0x10); // Reserved
+ INSERT_PADDING_BYTES_NOINIT(0x10); // Reserved
};
static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
@@ -232,14 +245,14 @@ struct LdnNetworkInfo {
SecurityMode security_mode;
AcceptPolicy station_accept_policy;
u8 has_action_frame;
- INSERT_PADDING_BYTES(0x2); // Padding
+ INSERT_PADDING_BYTES_NOINIT(0x2); // Padding
u8 node_count_max;
u8 node_count;
std::array<NodeInfo, NodeCountMax> nodes;
- INSERT_PADDING_BYTES(0x2); // Reserved
+ INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved
u16 advertise_data_size;
std::array<u8, AdvertiseDataSizeMax> advertise_data;
- INSERT_PADDING_BYTES(0x8C); // Reserved
+ INSERT_PADDING_BYTES_NOINIT(0x8C); // Reserved
u64 random_authentication_id;
};
static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
@@ -250,6 +263,7 @@ struct NetworkInfo {
LdnNetworkInfo ldn;
};
static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
+static_assert(std::is_trivial_v<NetworkInfo>, "NetworkInfo type must be trivially copyable.");
struct SecurityConfig {
SecurityMode security_mode;
@@ -303,4 +317,36 @@ struct AddressList {
};
static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
+struct GroupInfo {
+ std::array<u8, 0x200> info;
+};
+
+struct CreateNetworkConfig {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ INSERT_PADDING_BYTES(0x4);
+ NetworkConfig network_config;
+};
+static_assert(sizeof(CreateNetworkConfig) == 0x98, "CreateNetworkConfig is an invalid size");
+
+#pragma pack(push, 4)
+struct CreateNetworkConfigPrivate {
+ SecurityConfig security_config;
+ SecurityParameter security_parameter;
+ UserConfig user_config;
+ INSERT_PADDING_BYTES(0x4);
+ NetworkConfig network_config;
+};
+#pragma pack(pop)
+static_assert(sizeof(CreateNetworkConfigPrivate) == 0xB8,
+ "CreateNetworkConfigPrivate is an invalid size");
+
+struct ConnectNetworkData {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ s32 local_communication_version;
+ ConnectOption option;
+};
+static_assert(sizeof(ConnectNetworkData) == 0x7c, "ConnectNetworkData is an invalid size");
+
} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/monitor_service.cpp b/src/core/hle/service/ldn/monitor_service.cpp
new file mode 100644
index 000000000..3471f69da
--- /dev/null
+++ b/src/core/hle/service/ldn/monitor_service.cpp
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ldn/monitor_service.h"
+
+namespace Service::LDN {
+
+IMonitorService::IMonitorService(Core::System& system_)
+ : ServiceFramework{system_, "IMonitorService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IMonitorService::GetStateForMonitor>, "GetStateForMonitor"},
+ {1, nullptr, "GetNetworkInfoForMonitor"},
+ {2, nullptr, "GetIpv4AddressForMonitor"},
+ {3, nullptr, "GetDisconnectReasonForMonitor"},
+ {4, nullptr, "GetSecurityParameterForMonitor"},
+ {5, nullptr, "GetNetworkConfigForMonitor"},
+ {100, C<&IMonitorService::InitializeMonitor>, "InitializeMonitor"},
+ {101, nullptr, "FinalizeMonitor"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IMonitorService::~IMonitorService() = default;
+
+Result IMonitorService::GetStateForMonitor(Out<State> out_state) {
+ LOG_INFO(Service_LDN, "called");
+
+ *out_state = state;
+ R_SUCCEED();
+}
+
+Result IMonitorService::InitializeMonitor() {
+ LOG_INFO(Service_LDN, "called");
+
+ state = State::Initialized;
+ R_SUCCEED();
+}
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/monitor_service.h b/src/core/hle/service/ldn/monitor_service.h
new file mode 100644
index 000000000..61aacef30
--- /dev/null
+++ b/src/core/hle/service/ldn/monitor_service.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::LDN {
+
+class IMonitorService final : public ServiceFramework<IMonitorService> {
+public:
+ explicit IMonitorService(Core::System& system_);
+ ~IMonitorService() override;
+
+private:
+ Result GetStateForMonitor(Out<State> out_state);
+ Result InitializeMonitor();
+
+ State state{State::None};
+};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_monitor_service.cpp b/src/core/hle/service/ldn/sf_monitor_service.cpp
new file mode 100644
index 000000000..9e6736ff2
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_monitor_service.cpp
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/hle/service/ldn/sf_monitor_service.h"
+
+namespace Service::LDN {
+
+ISfMonitorService::ISfMonitorService(Core::System& system_)
+ : ServiceFramework{system_, "ISfMonitorService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&ISfMonitorService::Initialize>, "Initialize"},
+ {288, C<&ISfMonitorService::GetGroupInfo>, "GetGroupInfo"},
+ {320, nullptr, "GetLinkLevel"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISfMonitorService::~ISfMonitorService() = default;
+
+Result ISfMonitorService::Initialize(Out<u32> out_value) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ *out_value = 0;
+ R_SUCCEED();
+}
+
+Result ISfMonitorService::GetGroupInfo(
+ OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ *out_group_info = GroupInfo{};
+ R_SUCCEED();
+}
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_monitor_service.h b/src/core/hle/service/ldn/sf_monitor_service.h
new file mode 100644
index 000000000..d02115201
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_monitor_service.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::LDN {
+struct GroupInfo;
+
+class ISfMonitorService final : public ServiceFramework<ISfMonitorService> {
+public:
+ explicit ISfMonitorService(Core::System& system_);
+ ~ISfMonitorService() override;
+
+private:
+ Result Initialize(Out<u32> out_value);
+ Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
+};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service.cpp b/src/core/hle/service/ldn/sf_service.cpp
new file mode 100644
index 000000000..61cabe219
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service.cpp
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/ldn/sf_service.h"
+
+namespace Service::LDN {
+
+ISfService::ISfService(Core::System& system_) : ServiceFramework{system_, "ISfService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Initialize"},
+ {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
+ {264, nullptr, "GetNetworkInterfaceLastError"},
+ {272, nullptr, "GetRole"},
+ {280, nullptr, "GetAdvertiseData"},
+ {288, nullptr, "GetGroupInfo"},
+ {296, nullptr, "GetGroupInfo2"},
+ {304, nullptr, "GetGroupOwner"},
+ {312, nullptr, "GetIpConfig"},
+ {320, nullptr, "GetLinkLevel"},
+ {512, nullptr, "Scan"},
+ {768, nullptr, "CreateGroup"},
+ {776, nullptr, "DestroyGroup"},
+ {784, nullptr, "SetAdvertiseData"},
+ {1536, nullptr, "SendToOtherGroup"},
+ {1544, nullptr, "RecvFromOtherGroup"},
+ {1552, nullptr, "AddAcceptableGroupId"},
+ {1560, nullptr, "ClearAcceptableGroupId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISfService::~ISfService() = default;
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service.h b/src/core/hle/service/ldn/sf_service.h
new file mode 100644
index 000000000..05534b567
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::LDN {
+
+class ISfService final : public ServiceFramework<ISfService> {
+public:
+ explicit ISfService(Core::System& system_);
+ ~ISfService() override;
+};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service_monitor.cpp b/src/core/hle/service/ldn/sf_service_monitor.cpp
new file mode 100644
index 000000000..33e3c1d69
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service_monitor.cpp
@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/hle/service/ldn/sf_service_monitor.h"
+
+namespace Service::LDN {
+
+ISfServiceMonitor::ISfServiceMonitor(Core::System& system_)
+ : ServiceFramework{system_, "ISfServiceMonitor"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&ISfServiceMonitor::Initialize>, "Initialize"},
+ {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
+ {264, nullptr, "GetNetworkInterfaceLastError"},
+ {272, nullptr, "GetRole"},
+ {280, nullptr, "GetAdvertiseData"},
+ {281, nullptr, "GetAdvertiseData2"},
+ {288, C<&ISfServiceMonitor::GetGroupInfo>, "GetGroupInfo"},
+ {296, nullptr, "GetGroupInfo2"},
+ {304, nullptr, "GetGroupOwner"},
+ {312, nullptr, "GetIpConfig"},
+ {320, nullptr, "GetLinkLevel"},
+ {328, nullptr, "AttachJoinEvent"},
+ {336, nullptr, "GetMembers"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISfServiceMonitor::~ISfServiceMonitor() = default;
+
+Result ISfServiceMonitor::Initialize(Out<u32> out_value) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ *out_value = 0;
+ R_SUCCEED();
+}
+
+Result ISfServiceMonitor::GetGroupInfo(
+ OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ *out_group_info = GroupInfo{};
+ R_SUCCEED();
+}
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service_monitor.h b/src/core/hle/service/ldn/sf_service_monitor.h
new file mode 100644
index 000000000..3cfc5005e
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service_monitor.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::LDN {
+struct GroupInfo;
+
+class ISfServiceMonitor final : public ServiceFramework<ISfServiceMonitor> {
+public:
+ explicit ISfServiceMonitor(Core::System& system_);
+ ~ISfServiceMonitor() override;
+
+private:
+ Result Initialize(Out<u32> out_value);
+ Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
+};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/system_local_communication_service.cpp b/src/core/hle/service/ldn/system_local_communication_service.cpp
new file mode 100644
index 000000000..7b52223cd
--- /dev/null
+++ b/src/core/hle/service/ldn/system_local_communication_service.cpp
@@ -0,0 +1,56 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ldn/system_local_communication_service.h"
+
+namespace Service::LDN {
+
+ISystemLocalCommunicationService::ISystemLocalCommunicationService(Core::System& system_)
+ : ServiceFramework{system_, "ISystemLocalCommunicationService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetState"},
+ {1, nullptr, "GetNetworkInfo"},
+ {2, nullptr, "GetIpv4Address"},
+ {3, nullptr, "GetDisconnectReason"},
+ {4, nullptr, "GetSecurityParameter"},
+ {5, nullptr, "GetNetworkConfig"},
+ {100, nullptr, "AttachStateChangeEvent"},
+ {101, nullptr, "GetNetworkInfoLatestUpdate"},
+ {102, nullptr, "Scan"},
+ {103, nullptr, "ScanPrivate"},
+ {104, nullptr, "SetWirelessControllerRestriction"},
+ {200, nullptr, "OpenAccessPoint"},
+ {201, nullptr, "CloseAccessPoint"},
+ {202, nullptr, "CreateNetwork"},
+ {203, nullptr, "CreateNetworkPrivate"},
+ {204, nullptr, "DestroyNetwork"},
+ {205, nullptr, "Reject"},
+ {206, nullptr, "SetAdvertiseData"},
+ {207, nullptr, "SetStationAcceptPolicy"},
+ {208, nullptr, "AddAcceptFilterEntry"},
+ {209, nullptr, "ClearAcceptFilter"},
+ {300, nullptr, "OpenStation"},
+ {301, nullptr, "CloseStation"},
+ {302, nullptr, "Connect"},
+ {303, nullptr, "ConnectPrivate"},
+ {304, nullptr, "Disconnect"},
+ {400, nullptr, "InitializeSystem"},
+ {401, nullptr, "FinalizeSystem"},
+ {402, nullptr, "SetOperationMode"},
+ {403, C<&ISystemLocalCommunicationService::InitializeSystem2>, "InitializeSystem2"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISystemLocalCommunicationService::~ISystemLocalCommunicationService() = default;
+
+Result ISystemLocalCommunicationService::InitializeSystem2() {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/system_local_communication_service.h b/src/core/hle/service/ldn/system_local_communication_service.h
new file mode 100644
index 000000000..a02b097ea
--- /dev/null
+++ b/src/core/hle/service/ldn/system_local_communication_service.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::LDN {
+
+class ISystemLocalCommunicationService final
+ : public ServiceFramework<ISystemLocalCommunicationService> {
+public:
+ explicit ISystemLocalCommunicationService(Core::System& system_);
+ ~ISystemLocalCommunicationService() override;
+
+private:
+ Result InitializeSystem2();
+};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/user_local_communication_service.cpp b/src/core/hle/service/ldn/user_local_communication_service.cpp
new file mode 100644
index 000000000..f28368962
--- /dev/null
+++ b/src/core/hle/service/ldn/user_local_communication_service.cpp
@@ -0,0 +1,320 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ldn/ldn_results.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/hle/service/ldn/user_local_communication_service.h"
+#include "core/hle/service/server_manager.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "network/network.h"
+
+// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
+#undef CreateEvent
+
+namespace Service::LDN {
+
+IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& system_)
+ : ServiceFramework{system_, "IUserLocalCommunicationService"},
+ service_context{system, "IUserLocalCommunicationService"},
+ room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IUserLocalCommunicationService::GetState>, "GetState"},
+ {1, C<&IUserLocalCommunicationService::GetNetworkInfo>, "GetNetworkInfo"},
+ {2, C<&IUserLocalCommunicationService::GetIpv4Address>, "GetIpv4Address"},
+ {3, C<&IUserLocalCommunicationService::GetDisconnectReason>, "GetDisconnectReason"},
+ {4, C<&IUserLocalCommunicationService::GetSecurityParameter>, "GetSecurityParameter"},
+ {5, C<&IUserLocalCommunicationService::GetNetworkConfig>, "GetNetworkConfig"},
+ {100, C<&IUserLocalCommunicationService::AttachStateChangeEvent>, "AttachStateChangeEvent"},
+ {101, C<&IUserLocalCommunicationService::GetNetworkInfoLatestUpdate>, "GetNetworkInfoLatestUpdate"},
+ {102, C<&IUserLocalCommunicationService::Scan>, "Scan"},
+ {103, C<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
+ {104, C<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
+ {200, C<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
+ {201, C<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
+ {202, C<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
+ {203, C<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"},
+ {204, C<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"},
+ {205, nullptr, "Reject"},
+ {206, C<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"},
+ {207, C<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"},
+ {208, C<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"},
+ {209, nullptr, "ClearAcceptFilter"},
+ {300, C<&IUserLocalCommunicationService::OpenStation>, "OpenStation"},
+ {301, C<&IUserLocalCommunicationService::CloseStation>, "CloseStation"},
+ {302, C<&IUserLocalCommunicationService::Connect>, "Connect"},
+ {303, nullptr, "ConnectPrivate"},
+ {304, C<&IUserLocalCommunicationService::Disconnect>, "Disconnect"},
+ {400, C<&IUserLocalCommunicationService::Initialize>, "Initialize"},
+ {401, C<&IUserLocalCommunicationService::Finalize>, "Finalize"},
+ {402, C<&IUserLocalCommunicationService::Initialize2>, "Initialize2"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ state_change_event =
+ service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
+}
+
+IUserLocalCommunicationService::~IUserLocalCommunicationService() {
+ if (is_initialized) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ room_member->Unbind(ldn_packet_received);
+ }
+ }
+
+ service_context.CloseEvent(state_change_event);
+}
+
+Result IUserLocalCommunicationService::GetState(Out<State> out_state) {
+ *out_state = State::Error;
+
+ if (is_initialized) {
+ *out_state = lan_discovery.GetState();
+ }
+
+ LOG_INFO(Service_LDN, "called, state={}", *out_state);
+
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::GetNetworkInfo(
+ OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info) {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info));
+}
+
+Result IUserLocalCommunicationService::GetIpv4Address(Out<Ipv4Address> out_current_address,
+ Out<Ipv4Address> out_subnet_mask) {
+ LOG_INFO(Service_LDN, "called");
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+
+ R_UNLESS(network_interface.has_value(), ResultNoIpAddress);
+
+ *out_current_address = {Network::TranslateIPv4(network_interface->ip_address)};
+ *out_subnet_mask = {Network::TranslateIPv4(network_interface->subnet_mask)};
+
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ *out_current_address = room_member->GetFakeIpAddress();
+ }
+ }
+
+ std::reverse(std::begin(*out_current_address), std::end(*out_current_address)); // ntohl
+ std::reverse(std::begin(*out_subnet_mask), std::end(*out_subnet_mask)); // ntohl
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::GetDisconnectReason(
+ Out<DisconnectReason> out_disconnect_reason) {
+ LOG_INFO(Service_LDN, "called");
+
+ *out_disconnect_reason = lan_discovery.GetDisconnectReason();
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::GetSecurityParameter(
+ Out<SecurityParameter> out_security_parameter) {
+ LOG_INFO(Service_LDN, "called");
+
+ NetworkInfo info{};
+ R_TRY(lan_discovery.GetNetworkInfo(info));
+
+ out_security_parameter->session_id = info.network_id.session_id;
+ std::memcpy(out_security_parameter->data.data(), info.ldn.security_parameter.data(),
+ sizeof(SecurityParameter::data));
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::GetNetworkConfig(Out<NetworkConfig> out_network_config) {
+ LOG_INFO(Service_LDN, "called");
+
+ NetworkInfo info{};
+ R_TRY(lan_discovery.GetNetworkInfo(info));
+
+ out_network_config->intent_id = info.network_id.intent_id;
+ out_network_config->channel = info.common.channel;
+ out_network_config->node_count_max = info.ldn.node_count_max;
+ out_network_config->local_communication_version = info.ldn.nodes[0].local_communication_version;
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::AttachStateChangeEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_INFO(Service_LDN, "called");
+
+ *out_event = &state_change_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::GetNetworkInfoLatestUpdate(
+ OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info,
+ OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update) {
+ LOG_INFO(Service_LDN, "called");
+
+ R_UNLESS(!out_node_latest_update.empty(), ResultBadInput);
+
+ R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info, out_node_latest_update));
+}
+
+Result IUserLocalCommunicationService::Scan(
+ Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
+ OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) {
+ LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}",
+ channel, scan_filter.flag, scan_filter.network_type);
+
+ R_UNLESS(!out_network_info.empty(), ResultBadInput);
+ R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter));
+}
+
+Result IUserLocalCommunicationService::ScanPrivate(
+ Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
+ OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) {
+ LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}",
+ channel, scan_filter.flag, scan_filter.network_type);
+
+ R_UNLESS(out_network_info.empty(), ResultBadInput);
+ R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter));
+}
+
+Result IUserLocalCommunicationService::SetWirelessControllerRestriction(
+ WirelessControllerRestriction wireless_restriction) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::OpenAccessPoint() {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.OpenAccessPoint());
+}
+
+Result IUserLocalCommunicationService::CloseAccessPoint() {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.CloseAccessPoint());
+}
+
+Result IUserLocalCommunicationService::CreateNetwork(const CreateNetworkConfig& create_config) {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config,
+ create_config.network_config));
+}
+
+Result IUserLocalCommunicationService::CreateNetworkPrivate(
+ const CreateNetworkConfigPrivate& create_config,
+ InArray<AddressEntry, BufferAttr_HipcPointer> address_list) {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config,
+ create_config.network_config));
+}
+
+Result IUserLocalCommunicationService::DestroyNetwork() {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.DestroyNetwork());
+}
+
+Result IUserLocalCommunicationService::SetAdvertiseData(
+ InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.SetAdvertiseData(buffer_data));
+}
+
+Result IUserLocalCommunicationService::SetStationAcceptPolicy(AcceptPolicy accept_policy) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::AddAcceptFilterEntry(MacAddress mac_address) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::OpenStation() {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.OpenStation());
+}
+
+Result IUserLocalCommunicationService::CloseStation() {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.CloseStation());
+}
+
+Result IUserLocalCommunicationService::Connect(
+ const ConnectNetworkData& connect_data,
+ InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info) {
+ LOG_INFO(Service_LDN,
+ "called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ connect_data.security_config.passphrase_size,
+ connect_data.security_config.security_mode, connect_data.local_communication_version);
+
+ R_RETURN(lan_discovery.Connect(*network_info, connect_data.user_config,
+ static_cast<u16>(connect_data.local_communication_version)));
+}
+
+Result IUserLocalCommunicationService::Disconnect() {
+ LOG_INFO(Service_LDN, "called");
+
+ R_RETURN(lan_discovery.Disconnect());
+}
+
+Result IUserLocalCommunicationService::Initialize(ClientProcessId aruid) {
+ LOG_INFO(Service_LDN, "called, process_id={}", aruid.pid);
+
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ R_UNLESS(network_interface, ResultAirplaneModeEnabled);
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ ldn_packet_received = room_member->BindOnLdnPacketReceived(
+ [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
+ } else {
+ LOG_ERROR(Service_LDN, "Couldn't bind callback!");
+ R_RETURN(ResultAirplaneModeEnabled);
+ }
+
+ lan_discovery.Initialize([&]() { OnEventFired(); });
+ is_initialized = true;
+ R_SUCCEED();
+}
+
+Result IUserLocalCommunicationService::Finalize() {
+ LOG_INFO(Service_LDN, "called");
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ room_member->Unbind(ldn_packet_received);
+ }
+
+ is_initialized = false;
+
+ R_RETURN(lan_discovery.Finalize());
+}
+
+Result IUserLocalCommunicationService::Initialize2(u32 version, ClientProcessId process_id) {
+ LOG_INFO(Service_LDN, "called, version={}, process_id={}", version, process_id.pid);
+ R_RETURN(Initialize(process_id));
+}
+
+void IUserLocalCommunicationService::OnLDNPacketReceived(const Network::LDNPacket& packet) {
+ lan_discovery.ReceivePacket(packet);
+}
+
+void IUserLocalCommunicationService::OnEventFired() {
+ state_change_event->Signal();
+}
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/user_local_communication_service.h b/src/core/hle/service/ldn/user_local_communication_service.h
new file mode 100644
index 000000000..6698d10d2
--- /dev/null
+++ b/src/core/hle/service/ldn/user_local_communication_service.h
@@ -0,0 +1,103 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/ldn/lan_discovery.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Network {
+class RoomNetwork;
+}
+
+namespace Service::LDN {
+
+class IUserLocalCommunicationService final
+ : public ServiceFramework<IUserLocalCommunicationService> {
+public:
+ explicit IUserLocalCommunicationService(Core::System& system_);
+ ~IUserLocalCommunicationService() override;
+
+private:
+ Result GetState(Out<State> out_state);
+
+ Result GetNetworkInfo(OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info);
+
+ Result GetIpv4Address(Out<Ipv4Address> out_current_address, Out<Ipv4Address> out_subnet_mask);
+
+ Result GetDisconnectReason(Out<DisconnectReason> out_disconnect_reason);
+
+ Result GetSecurityParameter(Out<SecurityParameter> out_security_parameter);
+
+ Result GetNetworkConfig(Out<NetworkConfig> out_network_config);
+
+ Result AttachStateChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result GetNetworkInfoLatestUpdate(
+ OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info,
+ OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update);
+
+ Result Scan(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
+ OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);
+
+ Result ScanPrivate(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
+ OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);
+
+ Result SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction);
+
+ Result OpenAccessPoint();
+
+ Result CloseAccessPoint();
+
+ Result CreateNetwork(const CreateNetworkConfig& create_network_Config);
+
+ Result CreateNetworkPrivate(const CreateNetworkConfigPrivate& create_network_Config,
+ InArray<AddressEntry, BufferAttr_HipcPointer> address_list);
+
+ Result DestroyNetwork();
+
+ Result SetAdvertiseData(InBuffer<BufferAttr_HipcAutoSelect> buffer_data);
+
+ Result SetStationAcceptPolicy(AcceptPolicy accept_policy);
+
+ Result AddAcceptFilterEntry(MacAddress mac_address);
+
+ Result OpenStation();
+
+ Result CloseStation();
+
+ Result Connect(const ConnectNetworkData& connect_data,
+ InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info);
+
+ Result Disconnect();
+
+ Result Initialize(ClientProcessId aruid);
+
+ Result Finalize();
+
+ Result Initialize2(u32 version, ClientProcessId aruid);
+
+private:
+ /// Callback to parse and handle a received LDN packet.
+ void OnLDNPacketReceived(const Network::LDNPacket& packet);
+ void OnEventFired();
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* state_change_event;
+ Network::RoomNetwork& room_network;
+ LANDiscovery lan_discovery;
+
+ // Callback identifier for the OnLDNPacketReceived event.
+ Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
+
+ bool is_initialized{};
+};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 0086f82c5..adaaea571 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -207,7 +207,8 @@ private:
Result DestroyFile() {
bool is_db_test_mode_enabled{};
- m_set_sys->GetSettingsItemValue(is_db_test_mode_enabled, "mii", "is_db_test_mode_enabled");
+ m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii",
+ "is_db_test_mode_enabled");
LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly);
@@ -217,7 +218,8 @@ private:
Result DeleteFile() {
bool is_db_test_mode_enabled{};
- m_set_sys->GetSettingsItemValue(is_db_test_mode_enabled, "mii", "is_db_test_mode_enabled");
+ m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii",
+ "is_db_test_mode_enabled");
LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly);
@@ -227,7 +229,8 @@ private:
Result Format() {
bool is_db_test_mode_enabled{};
- m_set_sys->GetSettingsItemValue(is_db_test_mode_enabled, "mii", "is_db_test_mode_enabled");
+ m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii",
+ "is_db_test_mode_enabled");
LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly);
diff --git a/src/core/hle/service/ns/account_proxy_interface.cpp b/src/core/hle/service/ns/account_proxy_interface.cpp
new file mode 100644
index 000000000..e5041af66
--- /dev/null
+++ b/src/core/hle/service/ns/account_proxy_interface.cpp
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/ns/account_proxy_interface.h"
+
+namespace Service::NS {
+
+IAccountProxyInterface::IAccountProxyInterface(Core::System& system_)
+ : ServiceFramework{system_, "IAccountProxyInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CreateUserAccount"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IAccountProxyInterface::~IAccountProxyInterface() = default;
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/account_proxy_interface.h b/src/core/hle/service/ns/account_proxy_interface.h
new file mode 100644
index 000000000..e944d2a75
--- /dev/null
+++ b/src/core/hle/service/ns/account_proxy_interface.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
+public:
+ explicit IAccountProxyInterface(Core::System& system_);
+ ~IAccountProxyInterface() override;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp
new file mode 100644
index 000000000..2e3a44c0d
--- /dev/null
+++ b/src/core/hle/service/ns/application_manager_interface.cpp
@@ -0,0 +1,519 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/ns/application_manager_interface.h"
+#include "core/hle/service/ns/content_management_interface.h"
+#include "core/hle/service/ns/read_only_application_control_data_interface.h"
+
+namespace Service::NS {
+
+IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
+ : ServiceFramework{system_, "IApplicationManagerInterface"},
+ service_context{system, "IApplicationManagerInterface"},
+ record_update_system_event{service_context}, sd_card_mount_status_event{service_context},
+ gamecard_update_detection_event{service_context},
+ gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"},
+ {1, nullptr, "GenerateApplicationRecordCount"},
+ {2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
+ {3, nullptr, "GetApplicationViewDeprecated"},
+ {4, nullptr, "DeleteApplicationEntity"},
+ {5, nullptr, "DeleteApplicationCompletely"},
+ {6, nullptr, "IsAnyApplicationEntityRedundant"},
+ {7, nullptr, "DeleteRedundantApplicationEntity"},
+ {8, nullptr, "IsApplicationEntityMovable"},
+ {9, nullptr, "MoveApplicationEntity"},
+ {11, nullptr, "CalculateApplicationOccupiedSize"},
+ {16, nullptr, "PushApplicationRecord"},
+ {17, nullptr, "ListApplicationRecordContentMeta"},
+ {19, nullptr, "LaunchApplicationOld"},
+ {21, nullptr, "GetApplicationContentPath"},
+ {22, nullptr, "TerminateApplication"},
+ {23, nullptr, "ResolveApplicationContentPath"},
+ {26, nullptr, "BeginInstallApplication"},
+ {27, nullptr, "DeleteApplicationRecord"},
+ {30, nullptr, "RequestApplicationUpdateInfo"},
+ {31, nullptr, "Unknown31"},
+ {32, nullptr, "CancelApplicationDownload"},
+ {33, nullptr, "ResumeApplicationDownload"},
+ {35, nullptr, "UpdateVersionList"},
+ {36, nullptr, "PushLaunchVersion"},
+ {37, nullptr, "ListRequiredVersion"},
+ {38, D<&IApplicationManagerInterface::CheckApplicationLaunchVersion>, "CheckApplicationLaunchVersion"},
+ {39, nullptr, "CheckApplicationLaunchRights"},
+ {40, nullptr, "GetApplicationLogoData"},
+ {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
+ {42, nullptr, "CleanupSdCard"},
+ {43, D<&IApplicationManagerInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
+ {44, D<&IApplicationManagerInterface::GetSdCardMountStatusChangedEvent>, "GetSdCardMountStatusChangedEvent"},
+ {45, nullptr, "GetGameCardAttachmentEvent"},
+ {46, nullptr, "GetGameCardAttachmentInfo"},
+ {47, nullptr, "GetTotalSpaceSize"},
+ {48, D<&IApplicationManagerInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"},
+ {49, nullptr, "GetSdCardRemovedEvent"},
+ {52, D<&IApplicationManagerInterface::GetGameCardUpdateDetectionEvent>, "GetGameCardUpdateDetectionEvent"},
+ {53, nullptr, "DisableApplicationAutoDelete"},
+ {54, nullptr, "EnableApplicationAutoDelete"},
+ {55, D<&IApplicationManagerInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
+ {56, nullptr, "SetApplicationTerminateResult"},
+ {57, nullptr, "ClearApplicationTerminateResult"},
+ {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
+ {59, D<&IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
+ {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
+ {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
+ {62, nullptr, "GetGameCardStopper"},
+ {63, nullptr, "IsSystemProgramInstalled"},
+ {64, nullptr, "StartApplyDeltaTask"},
+ {65, nullptr, "GetRequestServerStopper"},
+ {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
+ {67, nullptr, "CancelApplicationApplyDelta"},
+ {68, nullptr, "ResumeApplicationApplyDelta"},
+ {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
+ {70, D<&IApplicationManagerInterface::ResumeAll>, "ResumeAll"},
+ {71, D<&IApplicationManagerInterface::GetStorageSize>, "GetStorageSize"},
+ {80, nullptr, "RequestDownloadApplication"},
+ {81, nullptr, "RequestDownloadAddOnContent"},
+ {82, nullptr, "DownloadApplication"},
+ {83, nullptr, "CheckApplicationResumeRights"},
+ {84, nullptr, "GetDynamicCommitEvent"},
+ {85, nullptr, "RequestUpdateApplication2"},
+ {86, nullptr, "EnableApplicationCrashReport"},
+ {87, nullptr, "IsApplicationCrashReportEnabled"},
+ {90, nullptr, "BoostSystemMemoryResourceLimit"},
+ {91, nullptr, "DeprecatedLaunchApplication"},
+ {92, nullptr, "GetRunningApplicationProgramId"},
+ {93, nullptr, "GetMainApplicationProgramIndex"},
+ {94, nullptr, "LaunchApplication"},
+ {95, nullptr, "GetApplicationLaunchInfo"},
+ {96, nullptr, "AcquireApplicationLaunchInfo"},
+ {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
+ {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
+ {99, nullptr, "LaunchDevMenu"},
+ {100, nullptr, "ResetToFactorySettings"},
+ {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
+ {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
+ {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
+ {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
+ {105, nullptr, "RequestResetToFactorySettingsSecurely"},
+ {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
+ {200, nullptr, "CalculateUserSaveDataStatistics"},
+ {201, nullptr, "DeleteUserSaveDataAll"},
+ {210, nullptr, "DeleteUserSystemSaveData"},
+ {211, nullptr, "DeleteSaveData"},
+ {220, nullptr, "UnregisterNetworkServiceAccount"},
+ {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
+ {300, nullptr, "GetApplicationShellEvent"},
+ {301, nullptr, "PopApplicationShellEventInfo"},
+ {302, nullptr, "LaunchLibraryApplet"},
+ {303, nullptr, "TerminateLibraryApplet"},
+ {304, nullptr, "LaunchSystemApplet"},
+ {305, nullptr, "TerminateSystemApplet"},
+ {306, nullptr, "LaunchOverlayApplet"},
+ {307, nullptr, "TerminateOverlayApplet"},
+ {400, D<&IApplicationManagerInterface::GetApplicationControlData>, "GetApplicationControlData"},
+ {401, nullptr, "InvalidateAllApplicationControlCache"},
+ {402, nullptr, "RequestDownloadApplicationControlData"},
+ {403, nullptr, "GetMaxApplicationControlCacheCount"},
+ {404, nullptr, "InvalidateApplicationControlCache"},
+ {405, nullptr, "ListApplicationControlCacheEntryInfo"},
+ {406, nullptr, "GetApplicationControlProperty"},
+ {407, nullptr, "ListApplicationTitle"},
+ {408, nullptr, "ListApplicationIcon"},
+ {502, nullptr, "RequestCheckGameCardRegistration"},
+ {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
+ {504, nullptr, "RequestRegisterGameCard"},
+ {505, D<&IApplicationManagerInterface::GetGameCardMountFailureEvent>, "GetGameCardMountFailureEvent"},
+ {506, nullptr, "IsGameCardInserted"},
+ {507, nullptr, "EnsureGameCardAccess"},
+ {508, nullptr, "GetLastGameCardMountFailureResult"},
+ {509, nullptr, "ListApplicationIdOnGameCard"},
+ {510, nullptr, "GetGameCardPlatformRegion"},
+ {600, nullptr, "CountApplicationContentMeta"},
+ {601, nullptr, "ListApplicationContentMetaStatus"},
+ {602, nullptr, "ListAvailableAddOnContent"},
+ {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
+ {604, nullptr, "RegisterContentsExternalKey"},
+ {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
+ {606, nullptr, "GetContentMetaStorage"},
+ {607, nullptr, "ListAvailableAddOnContent"},
+ {609, nullptr, "ListAvailabilityAssuredAddOnContent"},
+ {610, nullptr, "GetInstalledContentMetaStorage"},
+ {611, nullptr, "PrepareAddOnContent"},
+ {700, nullptr, "PushDownloadTaskList"},
+ {701, nullptr, "ClearTaskStatusList"},
+ {702, nullptr, "RequestDownloadTaskList"},
+ {703, nullptr, "RequestEnsureDownloadTask"},
+ {704, nullptr, "ListDownloadTaskStatus"},
+ {705, nullptr, "RequestDownloadTaskListData"},
+ {800, nullptr, "RequestVersionList"},
+ {801, nullptr, "ListVersionList"},
+ {802, nullptr, "RequestVersionListData"},
+ {900, nullptr, "GetApplicationRecord"},
+ {901, nullptr, "GetApplicationRecordProperty"},
+ {902, nullptr, "EnableApplicationAutoUpdate"},
+ {903, nullptr, "DisableApplicationAutoUpdate"},
+ {904, nullptr, "TouchApplication"},
+ {905, nullptr, "RequestApplicationUpdate"},
+ {906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"},
+ {907, nullptr, "WithdrawApplicationUpdateRequest"},
+ {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
+ {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
+ {910, nullptr, "HasApplicationRecord"},
+ {911, nullptr, "SetPreInstalledApplication"},
+ {912, nullptr, "ClearPreInstalledApplicationFlag"},
+ {913, nullptr, "ListAllApplicationRecord"},
+ {914, nullptr, "HideApplicationRecord"},
+ {915, nullptr, "ShowApplicationRecord"},
+ {916, nullptr, "IsApplicationAutoDeleteDisabled"},
+ {1000, nullptr, "RequestVerifyApplicationDeprecated"},
+ {1001, nullptr, "CorruptApplicationForDebug"},
+ {1002, nullptr, "RequestVerifyAddOnContentsRights"},
+ {1003, nullptr, "RequestVerifyApplication"},
+ {1004, nullptr, "CorruptContentForDebug"},
+ {1200, nullptr, "NeedsUpdateVulnerability"},
+ {1300, D<&IApplicationManagerInterface::IsAnyApplicationEntityInstalled>, "IsAnyApplicationEntityInstalled"},
+ {1301, nullptr, "DeleteApplicationContentEntities"},
+ {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
+ {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
+ {1304, nullptr, "DeleteApplicationContentEntity"},
+ {1305, nullptr, "TryDeleteRunningApplicationEntity"},
+ {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
+ {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
+ {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
+ {1309, nullptr, "CleanupUnavailableAddOnContents"},
+ {1310, nullptr, "RequestMoveApplicationEntity"},
+ {1311, nullptr, "EstimateSizeToMove"},
+ {1312, nullptr, "HasMovableEntity"},
+ {1313, nullptr, "CleanupOrphanContents"},
+ {1314, nullptr, "CheckPreconditionSatisfiedToMove"},
+ {1400, nullptr, "PrepareShutdown"},
+ {1500, nullptr, "FormatSdCard"},
+ {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
+ {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
+ {1504, nullptr, "InsertSdCard"},
+ {1505, nullptr, "RemoveSdCard"},
+ {1506, nullptr, "GetSdCardStartupStatus"},
+ {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
+ {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
+ {1700, nullptr, "ListApplicationDownloadingContentMeta"},
+ {1701, D<&IApplicationManagerInterface::GetApplicationView>, "GetApplicationView"},
+ {1702, nullptr, "GetApplicationDownloadTaskStatus"},
+ {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
+ {1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"},
+ {1705, nullptr, "IsPatchAutoDeletableApplication"},
+ {1800, nullptr, "IsNotificationSetupCompleted"},
+ {1801, nullptr, "GetLastNotificationInfoCount"},
+ {1802, nullptr, "ListLastNotificationInfo"},
+ {1803, nullptr, "ListNotificationTask"},
+ {1900, nullptr, "IsActiveAccount"},
+ {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
+ {1902, nullptr, "GetApplicationTicketInfo"},
+ {1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"},
+ {2000, nullptr, "GetSystemDeliveryInfo"},
+ {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
+ {2002, nullptr, "VerifyDeliveryProtocolVersion"},
+ {2003, nullptr, "GetApplicationDeliveryInfo"},
+ {2004, nullptr, "HasAllContentsToDeliver"},
+ {2005, nullptr, "CompareApplicationDeliveryInfo"},
+ {2006, nullptr, "CanDeliverApplication"},
+ {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
+ {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
+ {2009, nullptr, "EstimateRequiredSize"},
+ {2010, nullptr, "RequestReceiveApplication"},
+ {2011, nullptr, "CommitReceiveApplication"},
+ {2012, nullptr, "GetReceiveApplicationProgress"},
+ {2013, nullptr, "RequestSendApplication"},
+ {2014, nullptr, "GetSendApplicationProgress"},
+ {2015, nullptr, "CompareSystemDeliveryInfo"},
+ {2016, nullptr, "ListNotCommittedContentMeta"},
+ {2017, nullptr, "CreateDownloadTask"},
+ {2018, nullptr, "GetApplicationDeliveryInfoHash"},
+ {2050, D<&IApplicationManagerInterface::GetApplicationRightsOnClient>, "GetApplicationRightsOnClient"},
+ {2051, nullptr, "InvalidateRightsIdCache"},
+ {2100, D<&IApplicationManagerInterface::GetApplicationTerminateResult>, "GetApplicationTerminateResult"},
+ {2101, nullptr, "GetRawApplicationTerminateResult"},
+ {2150, nullptr, "CreateRightsEnvironment"},
+ {2151, nullptr, "DestroyRightsEnvironment"},
+ {2152, nullptr, "ActivateRightsEnvironment"},
+ {2153, nullptr, "DeactivateRightsEnvironment"},
+ {2154, nullptr, "ForceActivateRightsContextForExit"},
+ {2155, nullptr, "UpdateRightsEnvironmentStatus"},
+ {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
+ {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
+ {2161, nullptr, "SetUsersToRightsEnvironment"},
+ {2170, nullptr, "GetRightsEnvironmentStatus"},
+ {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
+ {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
+ {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
+ {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
+ {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
+ {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
+ {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
+ {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
+ {2250, nullptr, "RequestReportActiveELicence"},
+ {2300, nullptr, "ListEventLog"},
+ {2350, nullptr, "PerformAutoUpdateByApplicationId"},
+ {2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
+ {2352, nullptr, "RequestResolveNoDownloadRightsError"},
+ {2353, nullptr, "GetApplicationDownloadTaskInfo"},
+ {2354, nullptr, "PrioritizeApplicationBackgroundTask"},
+ {2355, nullptr, "PreferStorageEfficientUpdate"},
+ {2356, nullptr, "RequestStorageEfficientUpdatePreferable"},
+ {2357, nullptr, "EnableMultiCoreDownload"},
+ {2358, nullptr, "DisableMultiCoreDownload"},
+ {2359, nullptr, "IsMultiCoreDownloadEnabled"},
+ {2400, nullptr, "GetPromotionInfo"},
+ {2401, nullptr, "CountPromotionInfo"},
+ {2402, nullptr, "ListPromotionInfo"},
+ {2403, nullptr, "ImportPromotionJsonForDebug"},
+ {2404, nullptr, "ClearPromotionInfoForDebug"},
+ {2500, nullptr, "ConfirmAvailableTime"},
+ {2510, nullptr, "CreateApplicationResource"},
+ {2511, nullptr, "GetApplicationResource"},
+ {2513, nullptr, "LaunchMicroApplication"},
+ {2514, nullptr, "ClearTaskOfAsyncTaskManager"},
+ {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
+ {2516, nullptr, "EnsureApplicationCertificate"},
+ {2517, nullptr, "CreateApplicationInstance"},
+ {2518, nullptr, "UpdateQualificationForDebug"},
+ {2519, nullptr, "IsQualificationTransitionSupported"},
+ {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
+ {2521, nullptr, "GetRightsUserChangedEvent"},
+ {2522, nullptr, "IsRomRedirectionAvailable"},
+ {2800, nullptr, "GetApplicationIdOfPreomia"},
+ {3000, nullptr, "RegisterDeviceLockKey"},
+ {3001, nullptr, "UnregisterDeviceLockKey"},
+ {3002, nullptr, "VerifyDeviceLockKey"},
+ {3003, nullptr, "HideApplicationIcon"},
+ {3004, nullptr, "ShowApplicationIcon"},
+ {3005, nullptr, "HideApplicationTitle"},
+ {3006, nullptr, "ShowApplicationTitle"},
+ {3007, nullptr, "EnableGameCard"},
+ {3008, nullptr, "DisableGameCard"},
+ {3009, nullptr, "EnableLocalContentShare"},
+ {3010, nullptr, "DisableLocalContentShare"},
+ {3011, nullptr, "IsApplicationIconHidden"},
+ {3012, nullptr, "IsApplicationTitleHidden"},
+ {3013, nullptr, "IsGameCardEnabled"},
+ {3014, nullptr, "IsLocalContentShareEnabled"},
+ {3050, nullptr, "ListAssignELicenseTaskResult"},
+ {9999, nullptr, "GetApplicationCertificate"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationManagerInterface::~IApplicationManagerInterface() = default;
+
+Result IApplicationManagerInterface::GetApplicationControlData(
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
+ ApplicationControlSource application_control_source, u64 application_id) {
+ LOG_DEBUG(Service_NS, "called");
+ R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData(
+ out_buffer, out_actual_size, application_control_source, application_id));
+}
+
+Result IApplicationManagerInterface::GetApplicationDesiredLanguage(
+ Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
+ LOG_DEBUG(Service_NS, "called");
+ R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationDesiredLanguage(
+ out_desired_language, supported_languages));
+}
+
+Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
+ Out<u64> out_language_code, ApplicationLanguage application_language) {
+ LOG_DEBUG(Service_NS, "called");
+ R_RETURN(
+ IReadOnlyApplicationControlDataInterface(system).ConvertApplicationLanguageToLanguageCode(
+ out_language_code, application_language));
+}
+
+Result IApplicationManagerInterface::ListApplicationRecord(
+ OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
+ s32 offset) {
+ const auto limit = out_records.size();
+
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ const auto& cache = system.GetContentProviderUnion();
+ const auto installed_games = cache.ListEntriesFilterOrigin(
+ std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
+
+ size_t i = 0;
+ u8 ii = 24;
+
+ for (const auto& [slot, game] : installed_games) {
+ if (i >= limit) {
+ break;
+ }
+ if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
+ continue;
+ }
+ if (offset > 0) {
+ offset--;
+ continue;
+ }
+
+ ApplicationRecord record{};
+ record.application_id = game.title_id;
+ record.type = ApplicationRecordType::Installed;
+ record.unknown = 0; // 2 = needs update
+ record.unknown2 = ii++;
+
+ out_records[i++] = record;
+ }
+
+ *out_count = static_cast<s32>(i);
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+
+ record_update_system_event.Signal();
+ *out_event = record_update_system_event.GetHandle();
+
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetGameCardMountFailureEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_event = gamecard_mount_failure_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
+ Out<bool> out_is_any_application_entity_installed) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_is_any_application_entity_installed = true;
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetApplicationView(
+ OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
+ InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
+ const auto size = std::min(out_application_views.size(), application_ids.size());
+ LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
+
+ for (size_t i = 0; i < size; i++) {
+ ApplicationView view{};
+ view.application_id = application_ids[i];
+ view.unk = 0x70000;
+ view.flags = 0x401f17;
+
+ out_application_views[i] = view;
+ }
+
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
+ OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
+ InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
+ const auto size = std::min(out_application_views.size(), application_ids.size());
+ LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
+
+ for (size_t i = 0; i < size; i++) {
+ ApplicationViewWithPromotionInfo view{};
+ view.view.application_id = application_ids[i];
+ view.view.unk = 0x70000;
+ view.view.flags = 0x401f17;
+ view.promotion = {};
+
+ out_application_views[i] = view;
+ }
+
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetApplicationRightsOnClient(
+ OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
+ Common::UUID account_id, u32 flags, u64 application_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}",
+ flags, application_id, account_id.FormattedString());
+
+ if (!out_rights.empty()) {
+ ApplicationRightsOnClient rights{};
+ rights.application_id = application_id;
+ rights.uid = account_id;
+ rights.flags = 0;
+ rights.flags2 = 0;
+
+ out_rights[0] = rights;
+ *out_count = 1;
+ } else {
+ *out_count = 0;
+ }
+
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::CheckSdCardMountStatus() {
+ LOG_DEBUG(Service_NS, "called");
+ R_RETURN(IContentManagementInterface(system).CheckSdCardMountStatus());
+}
+
+Result IApplicationManagerInterface::GetSdCardMountStatusChangedEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_event = sd_card_mount_status_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetFreeSpaceSize(Out<s64> out_free_space_size,
+ FileSys::StorageId storage_id) {
+ LOG_DEBUG(Service_NS, "called");
+ R_RETURN(IContentManagementInterface(system).GetFreeSpaceSize(out_free_space_size, storage_id));
+}
+
+Result IApplicationManagerInterface::GetGameCardUpdateDetectionEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_event = gamecard_update_detection_event.GetHandle();
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::ResumeAll() {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_size,
+ Out<s64> out_free_space_size,
+ FileSys::StorageId storage_id) {
+ LOG_INFO(Service_NS, "called, storage_id={}", storage_id);
+ *out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id);
+ *out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id);
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out<bool> out_update_required,
+ Out<u32> out_update_version,
+ u64 application_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
+ *out_update_required = false;
+ *out_update_version = 0;
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::CheckApplicationLaunchVersion(u64 application_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
+ R_SUCCEED();
+}
+
+Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> out_result,
+ u64 application_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
+ *out_result = ResultSuccess;
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h
new file mode 100644
index 000000000..350ec37ce
--- /dev/null
+++ b/src/core/hle/service/ns/application_manager_interface.h
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/ns/language.h"
+#include "core/hle/service/ns/ns_types.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
+public:
+ explicit IApplicationManagerInterface(Core::System& system_);
+ ~IApplicationManagerInterface() override;
+
+ Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
+ Out<u32> out_actual_size,
+ ApplicationControlSource application_control_source,
+ u64 application_id);
+ Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language,
+ u32 supported_languages);
+ Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
+ ApplicationLanguage application_language);
+ Result ListApplicationRecord(OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records,
+ Out<s32> out_count, s32 offset);
+ Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
+ Result GetApplicationView(
+ OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
+ InArray<u64, BufferAttr_HipcMapAlias> application_ids);
+ Result GetApplicationViewWithPromotionInfo(
+ OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
+ InArray<u64, BufferAttr_HipcMapAlias> application_ids);
+ Result GetApplicationRightsOnClient(
+ OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
+ Common::UUID account_id, u32 flags, u64 application_id);
+ Result CheckSdCardMountStatus();
+ Result GetSdCardMountStatusChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
+ Result GetGameCardUpdateDetectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+ Result ResumeAll();
+ Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
+ FileSys::StorageId storage_id);
+ Result IsApplicationUpdateRequested(Out<bool> out_update_required, Out<u32> out_update_version,
+ u64 application_id);
+ Result CheckApplicationLaunchVersion(u64 application_id);
+ Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
+
+private:
+ KernelHelpers::ServiceContext service_context;
+ Event record_update_system_event;
+ Event sd_card_mount_status_event;
+ Event gamecard_update_detection_event;
+ Event gamecard_mount_status_event;
+ Event gamecard_mount_failure_event;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_version_interface.cpp b/src/core/hle/service/ns/application_version_interface.cpp
new file mode 100644
index 000000000..b89e127db
--- /dev/null
+++ b/src/core/hle/service/ns/application_version_interface.cpp
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/ns/application_version_interface.h"
+
+namespace Service::NS {
+
+IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_)
+ : ServiceFramework{system_, "IApplicationVersionInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetLaunchRequiredVersion"},
+ {1, nullptr, "UpgradeLaunchRequiredVersion"},
+ {35, nullptr, "UpdateVersionList"},
+ {36, nullptr, "PushLaunchVersion"},
+ {37, nullptr, "ListRequiredVersion"},
+ {800, nullptr, "RequestVersionList"},
+ {801, nullptr, "ListVersionList"},
+ {802, nullptr, "RequestVersionListData"},
+ {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"},
+ {901, nullptr, "ListDefaultAutoUpdatePolicy"},
+ {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"},
+ {1000, nullptr, "PerformAutoUpdate"},
+ {1001, nullptr, "ListAutoUpdateSchedule"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationVersionInterface::~IApplicationVersionInterface() = default;
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_version_interface.h b/src/core/hle/service/ns/application_version_interface.h
new file mode 100644
index 000000000..b288cff1b
--- /dev/null
+++ b/src/core/hle/service/ns/application_version_interface.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
+public:
+ explicit IApplicationVersionInterface(Core::System& system_);
+ ~IApplicationVersionInterface() override;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/content_management_interface.cpp b/src/core/hle/service/ns/content_management_interface.cpp
new file mode 100644
index 000000000..69bb3f6e4
--- /dev/null
+++ b/src/core/hle/service/ns/content_management_interface.cpp
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/common_funcs.h"
+#include "core/core.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/ns/content_management_interface.h"
+#include "core/hle/service/ns/ns_types.h"
+
+namespace Service::NS {
+
+IContentManagementInterface::IContentManagementInterface(Core::System& system_)
+ : ServiceFramework{system_, "IContentManagementInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {11, D<&IContentManagementInterface::CalculateApplicationOccupiedSize>, "CalculateApplicationOccupiedSize"},
+ {43, D<&IContentManagementInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
+ {47, D<&IContentManagementInterface::GetTotalSpaceSize>, "GetTotalSpaceSize"},
+ {48, D<&IContentManagementInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"},
+ {600, nullptr, "CountApplicationContentMeta"},
+ {601, nullptr, "ListApplicationContentMetaStatus"},
+ {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
+ {607, nullptr, "IsAnyApplicationRunning"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IContentManagementInterface::~IContentManagementInterface() = default;
+
+Result IContentManagementInterface::CalculateApplicationOccupiedSize(
+ Out<ApplicationOccupiedSize> out_size, u64 application_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, application_id={:016X}", application_id);
+
+ using namespace Common::Literals;
+
+ constexpr ApplicationOccupiedSizeEntity stub_entity{
+ .storage_id = FileSys::StorageId::SdCard,
+ .app_size = 8_GiB,
+ .patch_size = 2_GiB,
+ .aoc_size = 12_MiB,
+ };
+
+ for (auto& entity : out_size->entities) {
+ entity = stub_entity;
+ }
+
+ R_SUCCEED();
+}
+
+Result IContentManagementInterface::CheckSdCardMountStatus() {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IContentManagementInterface::GetTotalSpaceSize(Out<s64> out_total_space_size,
+ FileSys::StorageId storage_id) {
+ LOG_INFO(Service_NS, "(STUBBED) called, storage_id={}", storage_id);
+ *out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id);
+ R_SUCCEED();
+}
+
+Result IContentManagementInterface::GetFreeSpaceSize(Out<s64> out_free_space_size,
+ FileSys::StorageId storage_id) {
+ LOG_INFO(Service_NS, "(STUBBED) called, storage_id={}", storage_id);
+ *out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id);
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/content_management_interface.h b/src/core/hle/service/ns/content_management_interface.h
new file mode 100644
index 000000000..2894628e5
--- /dev/null
+++ b/src/core/hle/service/ns/content_management_interface.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/ns/ns_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
+public:
+ explicit IContentManagementInterface(Core::System& system_);
+ ~IContentManagementInterface() override;
+
+public:
+ Result CalculateApplicationOccupiedSize(Out<ApplicationOccupiedSize> out_size,
+ u64 application_id);
+ Result CheckSdCardMountStatus();
+ Result GetTotalSpaceSize(Out<s64> out_total_space_size, FileSys::StorageId storage_id);
+ Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/develop_interface.cpp b/src/core/hle/service/ns/develop_interface.cpp
new file mode 100644
index 000000000..880bdbebb
--- /dev/null
+++ b/src/core/hle/service/ns/develop_interface.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/ns/develop_interface.h"
+
+namespace Service::NS {
+
+IDevelopInterface::IDevelopInterface(Core::System& system_) : ServiceFramework{system_, "ns:dev"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "LaunchProgram"},
+ {1, nullptr, "TerminateProcess"},
+ {2, nullptr, "TerminateProgram"},
+ {4, nullptr, "GetShellEvent"},
+ {5, nullptr, "GetShellEventInfo"},
+ {6, nullptr, "TerminateApplication"},
+ {7, nullptr, "PrepareLaunchProgramFromHost"},
+ {8, nullptr, "LaunchApplicationFromHost"},
+ {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
+ {10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
+ {11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
+ {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"},
+ {13, nullptr, "CreateApplicationResourceForDevelop"},
+ {14, nullptr, "IsPreomiaForDevelop"},
+ {15, nullptr, "GetApplicationProgramIdFromHost"},
+ {16, nullptr, "RefreshCachedDebugValues"},
+ {17, nullptr, "PrepareLaunchApplicationFromHost"},
+ {18, nullptr, "GetLaunchEvent"},
+ {19, nullptr, "GetLaunchResult"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDevelopInterface::~IDevelopInterface() = default;
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/develop_interface.h b/src/core/hle/service/ns/develop_interface.h
new file mode 100644
index 000000000..a9f81ccd6
--- /dev/null
+++ b/src/core/hle/service/ns/develop_interface.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IDevelopInterface final : public ServiceFramework<IDevelopInterface> {
+public:
+ explicit IDevelopInterface(Core::System& system_);
+ ~IDevelopInterface() override;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/document_interface.cpp b/src/core/hle/service/ns/document_interface.cpp
new file mode 100644
index 000000000..51a1e46c0
--- /dev/null
+++ b/src/core/hle/service/ns/document_interface.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/document_interface.h"
+
+namespace Service::NS {
+
+IDocumentInterface::IDocumentInterface(Core::System& system_)
+ : ServiceFramework{system_, "IDocumentInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {21, nullptr, "GetApplicationContentPath"},
+ {23, D<&IDocumentInterface::ResolveApplicationContentPath>, "ResolveApplicationContentPath"},
+ {92, D<&IDocumentInterface::GetRunningApplicationProgramId>, "GetRunningApplicationProgramId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDocumentInterface::~IDocumentInterface() = default;
+
+Result IDocumentInterface::ResolveApplicationContentPath(ContentPath content_path) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}",
+ content_path.file_system_proxy_type, content_path.program_id);
+ R_SUCCEED();
+}
+
+Result IDocumentInterface::GetRunningApplicationProgramId(Out<u64> out_program_id,
+ u64 caller_program_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id);
+ *out_program_id = system.GetApplicationProcessProgramID();
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/document_interface.h b/src/core/hle/service/ns/document_interface.h
new file mode 100644
index 000000000..cd461652c
--- /dev/null
+++ b/src/core/hle/service/ns/document_interface.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/ns/ns_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
+public:
+ explicit IDocumentInterface(Core::System& system_);
+ ~IDocumentInterface() override;
+
+private:
+ Result ResolveApplicationContentPath(ContentPath content_path);
+ Result GetRunningApplicationProgramId(Out<u64> out_program_id, u64 caller_program_id);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/download_task_interface.cpp b/src/core/hle/service/ns/download_task_interface.cpp
new file mode 100644
index 000000000..62dc7f187
--- /dev/null
+++ b/src/core/hle/service/ns/download_task_interface.cpp
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/download_task_interface.h"
+
+namespace Service::NS {
+
+IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_)
+ : ServiceFramework{system_, "IDownloadTaskInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {701, nullptr, "ClearTaskStatusList"},
+ {702, nullptr, "RequestDownloadTaskList"},
+ {703, nullptr, "RequestEnsureDownloadTask"},
+ {704, nullptr, "ListDownloadTaskStatus"},
+ {705, nullptr, "RequestDownloadTaskListData"},
+ {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
+ {707, D<&IDownloadTaskInterface::EnableAutoCommit>, "EnableAutoCommit"},
+ {708, D<&IDownloadTaskInterface::DisableAutoCommit>, "DisableAutoCommit"},
+ {709, nullptr, "TriggerDynamicCommitEvent"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDownloadTaskInterface::~IDownloadTaskInterface() = default;
+
+Result IDownloadTaskInterface::EnableAutoCommit() {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ R_SUCCEED();
+}
+Result IDownloadTaskInterface::DisableAutoCommit() {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/download_task_interface.h b/src/core/hle/service/ns/download_task_interface.h
new file mode 100644
index 000000000..b1cb69cb8
--- /dev/null
+++ b/src/core/hle/service/ns/download_task_interface.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
+public:
+ explicit IDownloadTaskInterface(Core::System& system_);
+ ~IDownloadTaskInterface() override;
+
+private:
+ Result EnableAutoCommit();
+ Result DisableAutoCommit();
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/dynamic_rights_interface.cpp b/src/core/hle/service/ns/dynamic_rights_interface.cpp
new file mode 100644
index 000000000..ce81e203f
--- /dev/null
+++ b/src/core/hle/service/ns/dynamic_rights_interface.cpp
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/dynamic_rights_interface.h"
+
+namespace Service::NS {
+
+IDynamicRightsInterface::IDynamicRightsInterface(Core::System& system_)
+ : ServiceFramework{system_, "DynamicRightsInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "RequestApplicationRightsOnServer"},
+ {1, nullptr, "RequestAssignRights"},
+ {4, nullptr, "DeprecatedRequestAssignRightsToResume"},
+ {5, D<&IDynamicRightsInterface::VerifyActivatedRightsOwners>, "VerifyActivatedRightsOwners"},
+ {6, nullptr, "DeprecatedGetApplicationRightsStatus"},
+ {7, nullptr, "RequestPrefetchForDynamicRights"},
+ {8, nullptr, "GetDynamicRightsState"},
+ {9, nullptr, "RequestApplicationRightsOnServerToResume"},
+ {10, nullptr, "RequestAssignRightsToResume"},
+ {11, nullptr, "GetActivatedRightsUsers"},
+ {12, nullptr, "GetApplicationRightsStatus"},
+ {13, D<&IDynamicRightsInterface::GetRunningApplicationStatus>, "GetRunningApplicationStatus"},
+ {14, nullptr, "SelectApplicationLicense"},
+ {15, nullptr, "RequestContentsAuthorizationToken"},
+ {16, nullptr, "QualifyUser"},
+ {17, nullptr, "QualifyUserWithProcessId"},
+ {18, D<&IDynamicRightsInterface::NotifyApplicationRightsCheckStart>, "NotifyApplicationRightsCheckStart"},
+ {19, nullptr, "UpdateUserList"},
+ {20, nullptr, "IsRightsLostUser"},
+ {21, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
+ {22, nullptr, "GetLimitedApplicationLicense"},
+ {23, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
+ {24, nullptr, "NotifyLimitedApplicationLicenseUpgradableEventForDebug"},
+ {25, nullptr, "RequestProceedDynamicRightsState"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDynamicRightsInterface::~IDynamicRightsInterface() = default;
+
+Result IDynamicRightsInterface::NotifyApplicationRightsCheckStart() {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IDynamicRightsInterface::GetRunningApplicationStatus(Out<u32> out_status,
+ u64 rights_handle) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle);
+ *out_status = 0;
+ R_SUCCEED();
+}
+
+Result IDynamicRightsInterface::VerifyActivatedRightsOwners(u64 rights_handle) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle);
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/dynamic_rights_interface.h b/src/core/hle/service/ns/dynamic_rights_interface.h
new file mode 100644
index 000000000..877e009b0
--- /dev/null
+++ b/src/core/hle/service/ns/dynamic_rights_interface.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IDynamicRightsInterface final : public ServiceFramework<IDynamicRightsInterface> {
+public:
+ explicit IDynamicRightsInterface(Core::System& system_);
+ ~IDynamicRightsInterface() override;
+
+private:
+ Result NotifyApplicationRightsCheckStart();
+ Result GetRunningApplicationStatus(Out<u32> out_status, u64 rights_handle);
+ Result VerifyActivatedRightsOwners(u64 rights_handle);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ecommerce_interface.cpp b/src/core/hle/service/ns/ecommerce_interface.cpp
new file mode 100644
index 000000000..76fc425f0
--- /dev/null
+++ b/src/core/hle/service/ns/ecommerce_interface.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/ns/ecommerce_interface.h"
+
+namespace Service::NS {
+
+IECommerceInterface::IECommerceInterface(Core::System& system_)
+ : ServiceFramework{system_, "IECommerceInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "RequestLinkDevice"},
+ {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
+ {2, nullptr, "RequestCleanupPreInstalledApplication"},
+ {3, nullptr, "RequestSyncRights"},
+ {4, nullptr, "RequestUnlinkDevice"},
+ {5, nullptr, "RequestRevokeAllELicense"},
+ {6, nullptr, "RequestSyncRightsBasedOnAssignedELicenses"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IECommerceInterface::~IECommerceInterface() = default;
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ecommerce_interface.h b/src/core/hle/service/ns/ecommerce_interface.h
new file mode 100644
index 000000000..4352101f4
--- /dev/null
+++ b/src/core/hle/service/ns/ecommerce_interface.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
+public:
+ explicit IECommerceInterface(Core::System& system_);
+ ~IECommerceInterface() override;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/factory_reset_interface.cpp b/src/core/hle/service/ns/factory_reset_interface.cpp
new file mode 100644
index 000000000..fd5cf7e1f
--- /dev/null
+++ b/src/core/hle/service/ns/factory_reset_interface.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/ns/factory_reset_interface.h"
+
+namespace Service::NS {
+
+IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
+ : ServiceFramework{system_, "IFactoryResetInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {100, nullptr, "ResetToFactorySettings"},
+ {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
+ {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
+ {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
+ {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
+ {105, nullptr, "RequestResetToFactorySettingsSecurely"},
+ {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IFactoryResetInterface::~IFactoryResetInterface() = default;
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/factory_reset_interface.h b/src/core/hle/service/ns/factory_reset_interface.h
new file mode 100644
index 000000000..50d125123
--- /dev/null
+++ b/src/core/hle/service/ns/factory_reset_interface.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
+public:
+ explicit IFactoryResetInterface(Core::System& system_);
+ ~IFactoryResetInterface() override;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/iplatform_service_manager.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp
deleted file mode 100644
index 46268be95..000000000
--- a/src/core/hle/service/ns/iplatform_service_manager.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <algorithm>
-#include <cstring>
-#include <vector>
-
-#include "common/assert.h"
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "common/swap.h"
-#include "core/core.h"
-#include "core/file_sys/content_archive.h"
-#include "core/file_sys/nca_metadata.h"
-#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/romfs.h"
-#include "core/file_sys/system_archive/system_archive.h"
-#include "core/hle/kernel/k_shared_memory.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/physical_memory.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ns/iplatform_service_manager.h"
-
-namespace Service::NS {
-
-struct FontRegion {
- u32 offset;
- u32 size;
-};
-
-// The below data is specific to shared font data dumped from Switch on f/w 2.2
-// Virtual address and offsets/sizes likely will vary by dump
-[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
-constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
-constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
-constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
-constexpr FontRegion EMPTY_REGION{0, 0};
-
-enum class LoadState : u32 {
- Loading = 0,
- Done = 1,
-};
-
-static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
- std::size_t& offset) {
- ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
- "Shared fonts exceeds 17mb!");
- ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
-
- const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
- std::vector<u32> transformed_font(input.size());
- // TODO(ogniK): Figure out a better way to do this
- std::transform(input.begin(), input.end(), transformed_font.begin(),
- [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
- transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
- std::memcpy(output.data() + offset, transformed_font.data(),
- transformed_font.size() * sizeof(u32));
- offset += transformed_font.size() * sizeof(u32);
-}
-
-void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
- ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
-
- if (input.size() < 2) {
- LOG_ERROR(Service_NS, "Input font is empty");
- return;
- }
-
- const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
- std::vector<u32> transformed_font(input.size());
- // TODO(ogniK): Figure out a better way to do this
- std::transform(input.begin(), input.end(), transformed_font.begin(),
- [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
- std::memcpy(output.data(), transformed_font.data() + 2,
- (transformed_font.size() - 2) * sizeof(u32));
-}
-
-void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
- std::size_t& offset) {
- ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
- "Shared fonts exceeds 17mb!");
-
- const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC);
- std::vector<u32> transformed_font(input.size() + 2);
- transformed_font[0] = Common::swap32(EXPECTED_MAGIC);
- transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key;
- std::transform(input.begin(), input.end(), transformed_font.begin() + 2,
- [key](u32 in) { return in ^ key; });
- std::memcpy(output.data() + offset, transformed_font.data(),
- transformed_font.size() * sizeof(u32));
- offset += transformed_font.size() * sizeof(u32);
-}
-
-// Helper function to make BuildSharedFontsRawRegions a bit nicer
-static u32 GetU32Swapped(const u8* data) {
- u32 value;
- std::memcpy(&value, data, sizeof(value));
- return Common::swap32(value);
-}
-
-struct IPlatformServiceManager::Impl {
- const FontRegion& GetSharedFontRegion(std::size_t index) const {
- if (index >= shared_font_regions.size() || shared_font_regions.empty()) {
- // No font fallback
- return EMPTY_REGION;
- }
- return shared_font_regions.at(index);
- }
-
- void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) {
- // As we can derive the xor key we can just populate the offsets
- // based on the shared memory dump
- unsigned cur_offset = 0;
-
- for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) {
- // Out of shared fonts/invalid font
- if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) {
- break;
- }
-
- // Derive key within inverse xor
- const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC;
- const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY;
- shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE});
- cur_offset += SIZE + 8;
- }
- }
-
- /// Backing memory for the shared font data
- std::shared_ptr<Kernel::PhysicalMemory> shared_font;
-
- // Automatically populated based on shared_fonts dump or system archives.
- std::vector<FontRegion> shared_font_regions;
-};
-
-IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_)
- : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"},
- {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"},
- {2, &IPlatformServiceManager::GetSize, "GetSize"},
- {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
- {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
- {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
- {6, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriorityForSystem"},
- {100, nullptr, "RequestApplicationFunctionAuthorization"},
- {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
- {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
- {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"},
- {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"},
- {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"},
- {106, nullptr, "GetFunctionBlackListVersion"},
- {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
- {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
- };
- // clang-format on
- RegisterHandlers(functions);
-
- auto& fsc = system.GetFileSystemController();
-
- // Attempt to load shared font data from disk
- const auto* nand = fsc.GetSystemNANDContents();
- std::size_t offset = 0;
- // Rebuild shared fonts from data ncas or synthesize
-
- impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
- for (auto font : SHARED_FONTS) {
- FileSys::VirtualFile romfs;
- const auto nca =
- nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
- if (nca) {
- romfs = nca->GetRomFS();
- }
-
- if (!romfs) {
- romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first));
- }
-
- if (!romfs) {
- LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first);
- continue;
- }
-
- const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
- if (!extracted_romfs) {
- LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first);
- continue;
- }
- const auto font_fp = extracted_romfs->GetFile(font.second);
- if (!font_fp) {
- LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second);
- continue;
- }
- std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
- font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
- // We need to be BigEndian as u32s for the xor encryption
- std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
- Common::swap32);
- // Font offset and size do not account for the header
- const FontRegion region{static_cast<u32>(offset + 8),
- static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)};
- DecryptSharedFont(font_data_u32, *impl->shared_font, offset);
- impl->shared_font_regions.push_back(region);
- }
-}
-
-IPlatformServiceManager::~IPlatformServiceManager() = default;
-
-void IPlatformServiceManager::RequestLoad(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 shared_font_type{rp.Pop<u32>()};
- // Games don't call this so all fonts should be loaded
- LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IPlatformServiceManager::GetLoadState(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 font_id{rp.Pop<u32>()};
- LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(LoadState::Done));
-}
-
-void IPlatformServiceManager::GetSize(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 font_id{rp.Pop<u32>()};
- LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(impl->GetSharedFontRegion(font_id).size);
-}
-
-void IPlatformServiceManager::GetSharedMemoryAddressOffset(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 font_id{rp.Pop<u32>()};
- LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset);
-}
-
-void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
- // Map backing memory for the font data
- LOG_DEBUG(Service_NS, "called");
-
- // Create shared font memory object
- std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(),
- impl->shared_font->size());
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(&kernel.GetFontSharedMem());
-}
-
-void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) {
- // The maximum number of elements that can be returned is 6. Regardless of the available fonts
- // or buffer size.
- constexpr std::size_t MaxElementCount = 6;
- IPC::RequestParser rp{ctx};
- const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
- const std::size_t font_codes_count =
- std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(0));
- const std::size_t font_offsets_count =
- std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(1));
- const std::size_t font_sizes_count =
- std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(2));
- LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
-
- IPC::ResponseBuilder rb{ctx, 4};
- std::vector<u32> font_codes;
- std::vector<u32> font_offsets;
- std::vector<u32> font_sizes;
-
- // TODO(ogniK): Have actual priority order
- for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) {
- font_codes.push_back(static_cast<u32>(i));
- auto region = impl->GetSharedFontRegion(i);
- font_offsets.push_back(region.offset);
- font_sizes.push_back(region.size);
- }
-
- // Resize buffers if game requests smaller size output
- font_codes.resize(std::min(font_codes.size(), font_codes_count));
- font_offsets.resize(std::min(font_offsets.size(), font_offsets_count));
- font_sizes.resize(std::min(font_sizes.size(), font_sizes_count));
-
- ctx.WriteBuffer(font_codes, 0);
- ctx.WriteBuffer(font_offsets, 1);
- ctx.WriteBuffer(font_sizes, 2);
-
- rb.Push(ResultSuccess);
- rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded
- rb.Push<u32>(static_cast<u32>(font_codes.size()));
-}
-
-} // namespace Service::NS
diff --git a/src/core/hle/service/ns/iplatform_service_manager.h b/src/core/hle/service/ns/iplatform_service_manager.h
deleted file mode 100644
index 03071e02b..000000000
--- a/src/core/hle/service/ns/iplatform_service_manager.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-#include <vector>
-#include "core/hle/service/service.h"
-
-namespace Service {
-
-namespace FileSystem {
-class FileSystemController;
-} // namespace FileSystem
-
-namespace NS {
-
-enum class FontArchives : u64 {
- Extension = 0x0100000000000810,
- Standard = 0x0100000000000811,
- Korean = 0x0100000000000812,
- ChineseTraditional = 0x0100000000000813,
- ChineseSimple = 0x0100000000000814,
-};
-
-constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
- std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
- std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
- std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
- std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
- std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
- std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
- std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
-};
-
-void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output);
-void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);
-
-class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> {
-public:
- explicit IPlatformServiceManager(Core::System& system_, const char* service_name_);
- ~IPlatformServiceManager() override;
-
-private:
- void RequestLoad(HLERequestContext& ctx);
- void GetLoadState(HLERequestContext& ctx);
- void GetSize(HLERequestContext& ctx);
- void GetSharedMemoryAddressOffset(HLERequestContext& ctx);
- void GetSharedMemoryNativeHandle(HLERequestContext& ctx);
- void GetSharedFontInOrderOfPriority(HLERequestContext& ctx);
-
- struct Impl;
- std::unique_ptr<Impl> impl;
-};
-
-} // namespace NS
-
-} // namespace Service
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 19c3ff01b..8402e83cb 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -1,893 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "common/logging/log.h"
-#include "common/settings.h"
-#include "core/arm/debug.h"
-#include "core/core.h"
-#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/patch_manager.h"
-#include "core/file_sys/vfs/vfs.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/glue/glue_manager.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ns/errors.h"
-#include "core/hle/service/ns/iplatform_service_manager.h"
-#include "core/hle/service/ns/language.h"
+#include "core/hle/service/ns/develop_interface.h"
#include "core/hle/service/ns/ns.h"
-#include "core/hle/service/ns/pdm_qry.h"
+#include "core/hle/service/ns/platform_service_manager.h"
+#include "core/hle/service/ns/query_service.h"
+#include "core/hle/service/ns/service_getter_interface.h"
+#include "core/hle/service/ns/system_update_interface.h"
+#include "core/hle/service/ns/vulnerability_manager_interface.h"
#include "core/hle/service/server_manager.h"
-#include "core/hle/service/set/settings_server.h"
namespace Service::NS {
-IAccountProxyInterface::IAccountProxyInterface(Core::System& system_)
- : ServiceFramework{system_, "IAccountProxyInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "CreateUserAccount"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IAccountProxyInterface::~IAccountProxyInterface() = default;
-
-IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
- : ServiceFramework{system_, "IApplicationManagerInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "ListApplicationRecord"},
- {1, nullptr, "GenerateApplicationRecordCount"},
- {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
- {3, nullptr, "GetApplicationViewDeprecated"},
- {4, nullptr, "DeleteApplicationEntity"},
- {5, nullptr, "DeleteApplicationCompletely"},
- {6, nullptr, "IsAnyApplicationEntityRedundant"},
- {7, nullptr, "DeleteRedundantApplicationEntity"},
- {8, nullptr, "IsApplicationEntityMovable"},
- {9, nullptr, "MoveApplicationEntity"},
- {11, nullptr, "CalculateApplicationOccupiedSize"},
- {16, nullptr, "PushApplicationRecord"},
- {17, nullptr, "ListApplicationRecordContentMeta"},
- {19, nullptr, "LaunchApplicationOld"},
- {21, nullptr, "GetApplicationContentPath"},
- {22, nullptr, "TerminateApplication"},
- {23, nullptr, "ResolveApplicationContentPath"},
- {26, nullptr, "BeginInstallApplication"},
- {27, nullptr, "DeleteApplicationRecord"},
- {30, nullptr, "RequestApplicationUpdateInfo"},
- {31, nullptr, "Unknown31"},
- {32, nullptr, "CancelApplicationDownload"},
- {33, nullptr, "ResumeApplicationDownload"},
- {35, nullptr, "UpdateVersionList"},
- {36, nullptr, "PushLaunchVersion"},
- {37, nullptr, "ListRequiredVersion"},
- {38, nullptr, "CheckApplicationLaunchVersion"},
- {39, nullptr, "CheckApplicationLaunchRights"},
- {40, nullptr, "GetApplicationLogoData"},
- {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
- {42, nullptr, "CleanupSdCard"},
- {43, nullptr, "CheckSdCardMountStatus"},
- {44, nullptr, "GetSdCardMountStatusChangedEvent"},
- {45, nullptr, "GetGameCardAttachmentEvent"},
- {46, nullptr, "GetGameCardAttachmentInfo"},
- {47, nullptr, "GetTotalSpaceSize"},
- {48, nullptr, "GetFreeSpaceSize"},
- {49, nullptr, "GetSdCardRemovedEvent"},
- {52, nullptr, "GetGameCardUpdateDetectionEvent"},
- {53, nullptr, "DisableApplicationAutoDelete"},
- {54, nullptr, "EnableApplicationAutoDelete"},
- {55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
- {56, nullptr, "SetApplicationTerminateResult"},
- {57, nullptr, "ClearApplicationTerminateResult"},
- {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
- {59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"},
- {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
- {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
- {62, nullptr, "GetGameCardStopper"},
- {63, nullptr, "IsSystemProgramInstalled"},
- {64, nullptr, "StartApplyDeltaTask"},
- {65, nullptr, "GetRequestServerStopper"},
- {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
- {67, nullptr, "CancelApplicationApplyDelta"},
- {68, nullptr, "ResumeApplicationApplyDelta"},
- {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
- {70, nullptr, "ResumeAll"},
- {71, nullptr, "GetStorageSize"},
- {80, nullptr, "RequestDownloadApplication"},
- {81, nullptr, "RequestDownloadAddOnContent"},
- {82, nullptr, "DownloadApplication"},
- {83, nullptr, "CheckApplicationResumeRights"},
- {84, nullptr, "GetDynamicCommitEvent"},
- {85, nullptr, "RequestUpdateApplication2"},
- {86, nullptr, "EnableApplicationCrashReport"},
- {87, nullptr, "IsApplicationCrashReportEnabled"},
- {90, nullptr, "BoostSystemMemoryResourceLimit"},
- {91, nullptr, "DeprecatedLaunchApplication"},
- {92, nullptr, "GetRunningApplicationProgramId"},
- {93, nullptr, "GetMainApplicationProgramIndex"},
- {94, nullptr, "LaunchApplication"},
- {95, nullptr, "GetApplicationLaunchInfo"},
- {96, nullptr, "AcquireApplicationLaunchInfo"},
- {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
- {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
- {99, nullptr, "LaunchDevMenu"},
- {100, nullptr, "ResetToFactorySettings"},
- {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
- {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
- {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
- {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
- {105, nullptr, "RequestResetToFactorySettingsSecurely"},
- {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
- {200, nullptr, "CalculateUserSaveDataStatistics"},
- {201, nullptr, "DeleteUserSaveDataAll"},
- {210, nullptr, "DeleteUserSystemSaveData"},
- {211, nullptr, "DeleteSaveData"},
- {220, nullptr, "UnregisterNetworkServiceAccount"},
- {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
- {300, nullptr, "GetApplicationShellEvent"},
- {301, nullptr, "PopApplicationShellEventInfo"},
- {302, nullptr, "LaunchLibraryApplet"},
- {303, nullptr, "TerminateLibraryApplet"},
- {304, nullptr, "LaunchSystemApplet"},
- {305, nullptr, "TerminateSystemApplet"},
- {306, nullptr, "LaunchOverlayApplet"},
- {307, nullptr, "TerminateOverlayApplet"},
- {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
- {401, nullptr, "InvalidateAllApplicationControlCache"},
- {402, nullptr, "RequestDownloadApplicationControlData"},
- {403, nullptr, "GetMaxApplicationControlCacheCount"},
- {404, nullptr, "InvalidateApplicationControlCache"},
- {405, nullptr, "ListApplicationControlCacheEntryInfo"},
- {406, nullptr, "GetApplicationControlProperty"},
- {407, nullptr, "ListApplicationTitle"},
- {408, nullptr, "ListApplicationIcon"},
- {502, nullptr, "RequestCheckGameCardRegistration"},
- {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
- {504, nullptr, "RequestRegisterGameCard"},
- {505, nullptr, "GetGameCardMountFailureEvent"},
- {506, nullptr, "IsGameCardInserted"},
- {507, nullptr, "EnsureGameCardAccess"},
- {508, nullptr, "GetLastGameCardMountFailureResult"},
- {509, nullptr, "ListApplicationIdOnGameCard"},
- {510, nullptr, "GetGameCardPlatformRegion"},
- {600, nullptr, "CountApplicationContentMeta"},
- {601, nullptr, "ListApplicationContentMetaStatus"},
- {602, nullptr, "ListAvailableAddOnContent"},
- {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
- {604, nullptr, "RegisterContentsExternalKey"},
- {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
- {606, nullptr, "GetContentMetaStorage"},
- {607, nullptr, "ListAvailableAddOnContent"},
- {609, nullptr, "ListAvailabilityAssuredAddOnContent"},
- {610, nullptr, "GetInstalledContentMetaStorage"},
- {611, nullptr, "PrepareAddOnContent"},
- {700, nullptr, "PushDownloadTaskList"},
- {701, nullptr, "ClearTaskStatusList"},
- {702, nullptr, "RequestDownloadTaskList"},
- {703, nullptr, "RequestEnsureDownloadTask"},
- {704, nullptr, "ListDownloadTaskStatus"},
- {705, nullptr, "RequestDownloadTaskListData"},
- {800, nullptr, "RequestVersionList"},
- {801, nullptr, "ListVersionList"},
- {802, nullptr, "RequestVersionListData"},
- {900, nullptr, "GetApplicationRecord"},
- {901, nullptr, "GetApplicationRecordProperty"},
- {902, nullptr, "EnableApplicationAutoUpdate"},
- {903, nullptr, "DisableApplicationAutoUpdate"},
- {904, nullptr, "TouchApplication"},
- {905, nullptr, "RequestApplicationUpdate"},
- {906, nullptr, "IsApplicationUpdateRequested"},
- {907, nullptr, "WithdrawApplicationUpdateRequest"},
- {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
- {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
- {910, nullptr, "HasApplicationRecord"},
- {911, nullptr, "SetPreInstalledApplication"},
- {912, nullptr, "ClearPreInstalledApplicationFlag"},
- {913, nullptr, "ListAllApplicationRecord"},
- {914, nullptr, "HideApplicationRecord"},
- {915, nullptr, "ShowApplicationRecord"},
- {916, nullptr, "IsApplicationAutoDeleteDisabled"},
- {1000, nullptr, "RequestVerifyApplicationDeprecated"},
- {1001, nullptr, "CorruptApplicationForDebug"},
- {1002, nullptr, "RequestVerifyAddOnContentsRights"},
- {1003, nullptr, "RequestVerifyApplication"},
- {1004, nullptr, "CorruptContentForDebug"},
- {1200, nullptr, "NeedsUpdateVulnerability"},
- {1300, nullptr, "IsAnyApplicationEntityInstalled"},
- {1301, nullptr, "DeleteApplicationContentEntities"},
- {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
- {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
- {1304, nullptr, "DeleteApplicationContentEntity"},
- {1305, nullptr, "TryDeleteRunningApplicationEntity"},
- {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
- {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
- {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
- {1309, nullptr, "CleanupUnavailableAddOnContents"},
- {1310, nullptr, "RequestMoveApplicationEntity"},
- {1311, nullptr, "EstimateSizeToMove"},
- {1312, nullptr, "HasMovableEntity"},
- {1313, nullptr, "CleanupOrphanContents"},
- {1314, nullptr, "CheckPreconditionSatisfiedToMove"},
- {1400, nullptr, "PrepareShutdown"},
- {1500, nullptr, "FormatSdCard"},
- {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
- {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
- {1504, nullptr, "InsertSdCard"},
- {1505, nullptr, "RemoveSdCard"},
- {1506, nullptr, "GetSdCardStartupStatus"},
- {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
- {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
- {1700, nullptr, "ListApplicationDownloadingContentMeta"},
- {1701, nullptr, "GetApplicationView"},
- {1702, nullptr, "GetApplicationDownloadTaskStatus"},
- {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
- {1704, nullptr, "GetApplicationViewWithPromotionInfo"},
- {1705, nullptr, "IsPatchAutoDeletableApplication"},
- {1800, nullptr, "IsNotificationSetupCompleted"},
- {1801, nullptr, "GetLastNotificationInfoCount"},
- {1802, nullptr, "ListLastNotificationInfo"},
- {1803, nullptr, "ListNotificationTask"},
- {1900, nullptr, "IsActiveAccount"},
- {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
- {1902, nullptr, "GetApplicationTicketInfo"},
- {1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"},
- {2000, nullptr, "GetSystemDeliveryInfo"},
- {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
- {2002, nullptr, "VerifyDeliveryProtocolVersion"},
- {2003, nullptr, "GetApplicationDeliveryInfo"},
- {2004, nullptr, "HasAllContentsToDeliver"},
- {2005, nullptr, "CompareApplicationDeliveryInfo"},
- {2006, nullptr, "CanDeliverApplication"},
- {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
- {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
- {2009, nullptr, "EstimateRequiredSize"},
- {2010, nullptr, "RequestReceiveApplication"},
- {2011, nullptr, "CommitReceiveApplication"},
- {2012, nullptr, "GetReceiveApplicationProgress"},
- {2013, nullptr, "RequestSendApplication"},
- {2014, nullptr, "GetSendApplicationProgress"},
- {2015, nullptr, "CompareSystemDeliveryInfo"},
- {2016, nullptr, "ListNotCommittedContentMeta"},
- {2017, nullptr, "CreateDownloadTask"},
- {2018, nullptr, "GetApplicationDeliveryInfoHash"},
- {2050, nullptr, "GetApplicationRightsOnClient"},
- {2051, nullptr, "InvalidateRightsIdCache"},
- {2100, nullptr, "GetApplicationTerminateResult"},
- {2101, nullptr, "GetRawApplicationTerminateResult"},
- {2150, nullptr, "CreateRightsEnvironment"},
- {2151, nullptr, "DestroyRightsEnvironment"},
- {2152, nullptr, "ActivateRightsEnvironment"},
- {2153, nullptr, "DeactivateRightsEnvironment"},
- {2154, nullptr, "ForceActivateRightsContextForExit"},
- {2155, nullptr, "UpdateRightsEnvironmentStatus"},
- {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
- {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
- {2161, nullptr, "SetUsersToRightsEnvironment"},
- {2170, nullptr, "GetRightsEnvironmentStatus"},
- {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
- {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
- {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
- {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
- {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
- {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
- {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
- {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
- {2250, nullptr, "RequestReportActiveELicence"},
- {2300, nullptr, "ListEventLog"},
- {2350, nullptr, "PerformAutoUpdateByApplicationId"},
- {2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
- {2352, nullptr, "RequestResolveNoDownloadRightsError"},
- {2353, nullptr, "GetApplicationDownloadTaskInfo"},
- {2354, nullptr, "PrioritizeApplicationBackgroundTask"},
- {2355, nullptr, "PreferStorageEfficientUpdate"},
- {2356, nullptr, "RequestStorageEfficientUpdatePreferable"},
- {2357, nullptr, "EnableMultiCoreDownload"},
- {2358, nullptr, "DisableMultiCoreDownload"},
- {2359, nullptr, "IsMultiCoreDownloadEnabled"},
- {2400, nullptr, "GetPromotionInfo"},
- {2401, nullptr, "CountPromotionInfo"},
- {2402, nullptr, "ListPromotionInfo"},
- {2403, nullptr, "ImportPromotionJsonForDebug"},
- {2404, nullptr, "ClearPromotionInfoForDebug"},
- {2500, nullptr, "ConfirmAvailableTime"},
- {2510, nullptr, "CreateApplicationResource"},
- {2511, nullptr, "GetApplicationResource"},
- {2513, nullptr, "LaunchMicroApplication"},
- {2514, nullptr, "ClearTaskOfAsyncTaskManager"},
- {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
- {2516, nullptr, "EnsureApplicationCertificate"},
- {2517, nullptr, "CreateApplicationInstance"},
- {2518, nullptr, "UpdateQualificationForDebug"},
- {2519, nullptr, "IsQualificationTransitionSupported"},
- {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
- {2521, nullptr, "GetRightsUserChangedEvent"},
- {2522, nullptr, "IsRomRedirectionAvailable"},
- {2800, nullptr, "GetApplicationIdOfPreomia"},
- {3000, nullptr, "RegisterDeviceLockKey"},
- {3001, nullptr, "UnregisterDeviceLockKey"},
- {3002, nullptr, "VerifyDeviceLockKey"},
- {3003, nullptr, "HideApplicationIcon"},
- {3004, nullptr, "ShowApplicationIcon"},
- {3005, nullptr, "HideApplicationTitle"},
- {3006, nullptr, "ShowApplicationTitle"},
- {3007, nullptr, "EnableGameCard"},
- {3008, nullptr, "DisableGameCard"},
- {3009, nullptr, "EnableLocalContentShare"},
- {3010, nullptr, "DisableLocalContentShare"},
- {3011, nullptr, "IsApplicationIconHidden"},
- {3012, nullptr, "IsApplicationTitleHidden"},
- {3013, nullptr, "IsGameCardEnabled"},
- {3014, nullptr, "IsLocalContentShareEnabled"},
- {3050, nullptr, "ListAssignELicenseTaskResult"},
- {9999, nullptr, "GetApplicationCertificate"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IApplicationManagerInterface::~IApplicationManagerInterface() = default;
-
-void IApplicationManagerInterface::GetApplicationControlData(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto flag = rp.PopRaw<u64>();
- LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
-
- const auto title_id = rp.PopRaw<u64>();
-
- const auto size = ctx.GetWriteBufferSize();
-
- const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
- system.GetContentProvider()};
- const auto control = pm.GetControlMetadata();
-
- std::vector<u8> out;
-
- if (control.first != nullptr) {
- if (size < 0x4000) {
- LOG_ERROR(Service_NS,
- "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
- IPC::ResponseBuilder rb{ctx, 2};
- // TODO(DarkLordZach): Find a better error code for this.
- rb.Push(ResultUnknown);
- return;
- }
-
- out.resize(0x4000);
- const auto bytes = control.first->GetRawBytes();
- std::memcpy(out.data(), bytes.data(), bytes.size());
- } else {
- LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
- title_id);
- out.resize(std::min<u64>(0x4000, size));
- }
-
- if (control.second != nullptr) {
- if (size < 0x4000 + control.second->GetSize()) {
- LOG_ERROR(Service_NS,
- "output buffer is too small! (actual={:016X}, expected_min={:016X})", size,
- 0x4000 + control.second->GetSize());
- IPC::ResponseBuilder rb{ctx, 2};
- // TODO(DarkLordZach): Find a better error code for this.
- rb.Push(ResultUnknown);
- return;
- }
-
- out.resize(0x4000 + control.second->GetSize());
- control.second->Read(out.data() + 0x4000, control.second->GetSize());
- } else {
- LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
- title_id);
- }
-
- ctx.WriteBuffer(out);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(out.size()));
-}
-
-void IApplicationManagerInterface::GetApplicationDesiredLanguage(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto supported_languages = rp.Pop<u32>();
-
- u8 desired_language{};
- const auto res = GetApplicationDesiredLanguage(&desired_language, supported_languages);
- if (res == ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(desired_language);
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
- }
-}
-
-Result IApplicationManagerInterface::GetApplicationDesiredLanguage(u8* out_desired_language,
- const u32 supported_languages) {
- LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
-
- // Get language code from settings
- const auto language_code =
- Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
-
- // Convert to application language, get priority list
- const auto application_language = ConvertToApplicationLanguage(language_code);
- if (application_language == std::nullopt) {
- LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
- language_code);
- return Service::NS::ResultApplicationLanguageNotFound;
- }
- const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
- if (!priority_list) {
- LOG_ERROR(Service_NS,
- "Could not find application language priorities! application_language={}",
- *application_language);
- return Service::NS::ResultApplicationLanguageNotFound;
- }
-
- // Try to find a valid language.
- for (const auto lang : *priority_list) {
- const auto supported_flag = GetSupportedLanguageFlag(lang);
- if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
- *out_desired_language = static_cast<u8>(lang);
- return ResultSuccess;
- }
- }
-
- LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
- supported_languages);
- return Service::NS::ResultApplicationLanguageNotFound;
-}
-
-void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
- HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto application_language = rp.Pop<u8>();
-
- u64 language_code{};
- const auto res = ConvertApplicationLanguageToLanguageCode(&language_code, application_language);
- if (res == ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(language_code);
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
- }
-}
-
-Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
- u64* out_language_code, u8 application_language) {
- const auto language_code =
- ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
- if (language_code == std::nullopt) {
- LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language);
- return Service::NS::ResultApplicationLanguageNotFound;
- }
-
- *out_language_code = static_cast<u64>(*language_code);
- return ResultSuccess;
-}
-
-IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_)
- : ServiceFramework{system_, "IApplicationVersionInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetLaunchRequiredVersion"},
- {1, nullptr, "UpgradeLaunchRequiredVersion"},
- {35, nullptr, "UpdateVersionList"},
- {36, nullptr, "PushLaunchVersion"},
- {37, nullptr, "ListRequiredVersion"},
- {800, nullptr, "RequestVersionList"},
- {801, nullptr, "ListVersionList"},
- {802, nullptr, "RequestVersionListData"},
- {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"},
- {901, nullptr, "ListDefaultAutoUpdatePolicy"},
- {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"},
- {1000, nullptr, "PerformAutoUpdate"},
- {1001, nullptr, "ListAutoUpdateSchedule"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IApplicationVersionInterface::~IApplicationVersionInterface() = default;
-
-IContentManagementInterface::IContentManagementInterface(Core::System& system_)
- : ServiceFramework{system_, "IContentManagementInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {11, nullptr, "CalculateApplicationOccupiedSize"},
- {43, nullptr, "CheckSdCardMountStatus"},
- {47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"},
- {48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"},
- {600, nullptr, "CountApplicationContentMeta"},
- {601, nullptr, "ListApplicationContentMetaStatus"},
- {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
- {607, nullptr, "IsAnyApplicationRunning"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IContentManagementInterface::~IContentManagementInterface() = default;
-
-void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto storage{rp.PopEnum<FileSys::StorageId>()};
-
- LOG_INFO(Service_Capture, "called, storage={}", storage);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage));
-}
-
-void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto storage{rp.PopEnum<FileSys::StorageId>()};
-
- LOG_INFO(Service_Capture, "called, storage={}", storage);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage));
-}
-
-IDocumentInterface::IDocumentInterface(Core::System& system_)
- : ServiceFramework{system_, "IDocumentInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {21, nullptr, "GetApplicationContentPath"},
- {23, &IDocumentInterface::ResolveApplicationContentPath, "ResolveApplicationContentPath"},
- {92, &IDocumentInterface::GetRunningApplicationProgramId, "GetRunningApplicationProgramId"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IDocumentInterface::~IDocumentInterface() = default;
-
-void IDocumentInterface::ResolveApplicationContentPath(HLERequestContext& ctx) {
- struct ContentPath {
- u8 file_system_proxy_type;
- u64 program_id;
- };
- static_assert(sizeof(ContentPath) == 0x10, "ContentPath has wrong size");
-
- IPC::RequestParser rp{ctx};
- auto content_path = rp.PopRaw<ContentPath>();
- LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}",
- content_path.file_system_proxy_type, content_path.program_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
-
-void IDocumentInterface::GetRunningApplicationProgramId(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto caller_program_id = rp.PopRaw<u64>();
- LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(system.GetApplicationProcessProgramID());
-}
-
-IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_)
- : ServiceFramework{system_, "IDownloadTaskInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {701, nullptr, "ClearTaskStatusList"},
- {702, nullptr, "RequestDownloadTaskList"},
- {703, nullptr, "RequestEnsureDownloadTask"},
- {704, nullptr, "ListDownloadTaskStatus"},
- {705, nullptr, "RequestDownloadTaskListData"},
- {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
- {707, nullptr, "EnableAutoCommit"},
- {708, nullptr, "DisableAutoCommit"},
- {709, nullptr, "TriggerDynamicCommitEvent"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IDownloadTaskInterface::~IDownloadTaskInterface() = default;
-
-IECommerceInterface::IECommerceInterface(Core::System& system_)
- : ServiceFramework{system_, "IECommerceInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestLinkDevice"},
- {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
- {2, nullptr, "RequestCleanupPreInstalledApplication"},
- {3, nullptr, "RequestSyncRights"},
- {4, nullptr, "RequestUnlinkDevice"},
- {5, nullptr, "RequestRevokeAllELicense"},
- {6, nullptr, "RequestSyncRightsBasedOnAssignedELicenses"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IECommerceInterface::~IECommerceInterface() = default;
-
-IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
- : ServiceFramework{system_, "IFactoryResetInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {100, nullptr, "ResetToFactorySettings"},
- {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
- {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
- {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
- {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
- {105, nullptr, "RequestResetToFactorySettingsSecurely"},
- {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IFactoryResetInterface::~IFactoryResetInterface() = default;
-
-IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_)
- : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} {
- static const FunctionInfo functions[] = {
- {0, &IReadOnlyApplicationRecordInterface::HasApplicationRecord, "HasApplicationRecord"},
- {1, nullptr, "NotifyApplicationFailure"},
- {2, &IReadOnlyApplicationRecordInterface::IsDataCorruptedResult, "IsDataCorruptedResult"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default;
-
-void IReadOnlyApplicationRecordInterface::HasApplicationRecord(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 program_id = rp.PopRaw<u64>();
- LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:X}", program_id);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(1);
-}
-
-void IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto result = rp.PopRaw<Result>();
- LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue());
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
-}
-
-IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
- Core::System& system_)
- : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
- {1, nullptr, "GetApplicationDesiredLanguage"},
- {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
- {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
- {4, nullptr, "SelectApplicationDesiredLanguage"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
-
-void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(HLERequestContext& ctx) {
- enum class ApplicationControlSource : u8 {
- CacheOnly,
- Storage,
- StorageOnly,
- };
-
- struct RequestParameters {
- ApplicationControlSource source;
- u64 application_id;
- };
- static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
-
- IPC::RequestParser rp{ctx};
- std::vector<u8> nacp_data{};
- const auto parameters{rp.PopRaw<RequestParameters>()};
- const auto result =
- system.GetARPManager().GetControlProperty(&nacp_data, parameters.application_id);
-
- if (result == ResultSuccess) {
- ctx.WriteBuffer(nacp_data.data(), nacp_data.size());
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
-}
-
-NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {7988, nullptr, "GetDynamicRightsInterface"},
- {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
- {7991, &NS::PushInterface<IReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
- {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
- {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
- {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
- {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
- {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
- {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
- {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
- {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-NS::~NS() = default;
-
-std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
- return GetInterface<IApplicationManagerInterface>(system);
-}
-
-class NS_DEV final : public ServiceFramework<NS_DEV> {
-public:
- explicit NS_DEV(Core::System& system_) : ServiceFramework{system_, "ns:dev"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "LaunchProgram"},
- {1, nullptr, "TerminateProcess"},
- {2, nullptr, "TerminateProgram"},
- {4, nullptr, "GetShellEvent"},
- {5, nullptr, "GetShellEventInfo"},
- {6, nullptr, "TerminateApplication"},
- {7, nullptr, "PrepareLaunchProgramFromHost"},
- {8, nullptr, "LaunchApplicationFromHost"},
- {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
- {10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
- {11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
- {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"},
- {13, nullptr, "CreateApplicationResourceForDevelop"},
- {14, nullptr, "IsPreomiaForDevelop"},
- {15, nullptr, "GetApplicationProgramIdFromHost"},
- {16, nullptr, "RefreshCachedDebugValues"},
- {17, nullptr, "PrepareLaunchApplicationFromHost"},
- {18, nullptr, "GetLaunchEvent"},
- {19, nullptr, "GetLaunchResult"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> {
-public:
- explicit ISystemUpdateControl(Core::System& system_)
- : ServiceFramework{system_, "ISystemUpdateControl"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "HasDownloaded"},
- {1, nullptr, "RequestCheckLatestUpdate"},
- {2, nullptr, "RequestDownloadLatestUpdate"},
- {3, nullptr, "GetDownloadProgress"},
- {4, nullptr, "ApplyDownloadedUpdate"},
- {5, nullptr, "RequestPrepareCardUpdate"},
- {6, nullptr, "GetPrepareCardUpdateProgress"},
- {7, nullptr, "HasPreparedCardUpdate"},
- {8, nullptr, "ApplyCardUpdate"},
- {9, nullptr, "GetDownloadedEulaDataSize"},
- {10, nullptr, "GetDownloadedEulaData"},
- {11, nullptr, "SetupCardUpdate"},
- {12, nullptr, "GetPreparedCardUpdateEulaDataSize"},
- {13, nullptr, "GetPreparedCardUpdateEulaData"},
- {14, nullptr, "SetupCardUpdateViaSystemUpdater"},
- {15, nullptr, "HasReceived"},
- {16, nullptr, "RequestReceiveSystemUpdate"},
- {17, nullptr, "GetReceiveProgress"},
- {18, nullptr, "ApplyReceivedUpdate"},
- {19, nullptr, "GetReceivedEulaDataSize"},
- {20, nullptr, "GetReceivedEulaData"},
- {21, nullptr, "SetupToReceiveSystemUpdate"},
- {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class NS_SU final : public ServiceFramework<NS_SU> {
-public:
- explicit NS_SU(Core::System& system_) : ServiceFramework{system_, "ns:su"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetBackgroundNetworkUpdateState"},
- {1, &NS_SU::OpenSystemUpdateControl, "OpenSystemUpdateControl"},
- {2, nullptr, "NotifyExFatDriverRequired"},
- {3, nullptr, "ClearExFatDriverStatusForDebug"},
- {4, nullptr, "RequestBackgroundNetworkUpdate"},
- {5, nullptr, "NotifyBackgroundNetworkUpdate"},
- {6, nullptr, "NotifyExFatDriverDownloadedForDebug"},
- {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
- {10, nullptr, "NotifySystemUpdateForContentDelivery"},
- {11, nullptr, "PrepareShutdown"},
- {12, nullptr, "Unknown12"},
- {13, nullptr, "Unknown13"},
- {14, nullptr, "Unknown14"},
- {15, nullptr, "Unknown15"},
- {16, nullptr, "DestroySystemUpdateTask"},
- {17, nullptr, "RequestSendSystemUpdate"},
- {18, nullptr, "GetSendSystemUpdateProgress"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void OpenSystemUpdateControl(HLERequestContext& ctx) {
- LOG_DEBUG(Service_NS, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISystemUpdateControl>(system);
- }
-};
-
-class NS_VM final : public ServiceFramework<NS_VM> {
-public:
- explicit NS_VM(Core::System& system_) : ServiceFramework{system_, "ns:vm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {1200, &NS_VM::NeedsUpdateVulnerability, "NeedsUpdateVulnerability"},
- {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
- {1202, nullptr, "GetSafeSystemVersion"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void NeedsUpdateVulnerability(HLERequestContext& ctx) {
- LOG_WARNING(Service_NS, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(false);
- }
-};
-
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- server_manager->RegisterNamedService("ns:am2", std::make_shared<NS>("ns:am2", system));
- server_manager->RegisterNamedService("ns:ec", std::make_shared<NS>("ns:ec", system));
- server_manager->RegisterNamedService("ns:rid", std::make_shared<NS>("ns:rid", system));
- server_manager->RegisterNamedService("ns:rt", std::make_shared<NS>("ns:rt", system));
- server_manager->RegisterNamedService("ns:web", std::make_shared<NS>("ns:web", system));
- server_manager->RegisterNamedService("ns:ro", std::make_shared<NS>("ns:ro", system));
-
- server_manager->RegisterNamedService("ns:dev", std::make_shared<NS_DEV>(system));
- server_manager->RegisterNamedService("ns:su", std::make_shared<NS_SU>(system));
- server_manager->RegisterNamedService("ns:vm", std::make_shared<NS_VM>(system));
- server_manager->RegisterNamedService("pdm:qry", std::make_shared<PDM_QRY>(system));
+ server_manager->RegisterNamedService(
+ "ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2"));
+ server_manager->RegisterNamedService(
+ "ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec"));
+ server_manager->RegisterNamedService(
+ "ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid"));
+ server_manager->RegisterNamedService(
+ "ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt"));
+ server_manager->RegisterNamedService(
+ "ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web"));
+ server_manager->RegisterNamedService(
+ "ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro"));
+
+ server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system));
+ server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system));
+ server_manager->RegisterNamedService("ns:vm",
+ std::make_shared<IVulnerabilityManagerInterface>(system));
+ server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system));
server_manager->RegisterNamedService("pl:s",
std::make_shared<IPlatformServiceManager>(system, "pl:s"));
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 9ee306ef9..f79b4ae3d 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -3,141 +3,12 @@
#pragma once
-#include "core/hle/service/service.h"
-
namespace Core {
class System;
}
-namespace Service {
-
-namespace FileSystem {
-class FileSystemController;
-} // namespace FileSystem
-
-namespace NS {
-
-class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
-public:
- explicit IAccountProxyInterface(Core::System& system_);
- ~IAccountProxyInterface() override;
-};
-
-class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
-public:
- explicit IApplicationManagerInterface(Core::System& system_);
- ~IApplicationManagerInterface() override;
-
- Result GetApplicationDesiredLanguage(u8* out_desired_language, u32 supported_languages);
- Result ConvertApplicationLanguageToLanguageCode(u64* out_language_code,
- u8 application_language);
-
-private:
- void GetApplicationControlData(HLERequestContext& ctx);
- void GetApplicationDesiredLanguage(HLERequestContext& ctx);
- void ConvertApplicationLanguageToLanguageCode(HLERequestContext& ctx);
-};
-
-class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
-public:
- explicit IApplicationVersionInterface(Core::System& system_);
- ~IApplicationVersionInterface() override;
-};
-
-class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
-public:
- explicit IContentManagementInterface(Core::System& system_);
- ~IContentManagementInterface() override;
-
-private:
- void GetTotalSpaceSize(HLERequestContext& ctx);
- void GetFreeSpaceSize(HLERequestContext& ctx);
-};
-
-class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
-public:
- explicit IDocumentInterface(Core::System& system_);
- ~IDocumentInterface() override;
-
-private:
- void ResolveApplicationContentPath(HLERequestContext& ctx);
- void GetRunningApplicationProgramId(HLERequestContext& ctx);
-};
-
-class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
-public:
- explicit IDownloadTaskInterface(Core::System& system_);
- ~IDownloadTaskInterface() override;
-};
-
-class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
-public:
- explicit IECommerceInterface(Core::System& system_);
- ~IECommerceInterface() override;
-};
-
-class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
-public:
- explicit IFactoryResetInterface(Core::System& system_);
- ~IFactoryResetInterface() override;
-};
-
-class IReadOnlyApplicationRecordInterface final
- : public ServiceFramework<IReadOnlyApplicationRecordInterface> {
-public:
- explicit IReadOnlyApplicationRecordInterface(Core::System& system_);
- ~IReadOnlyApplicationRecordInterface() override;
-
-private:
- void HasApplicationRecord(HLERequestContext& ctx);
- void IsDataCorruptedResult(HLERequestContext& ctx);
-};
-
-class IReadOnlyApplicationControlDataInterface final
- : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
-public:
- explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
- ~IReadOnlyApplicationControlDataInterface() override;
-
-private:
- void GetApplicationControlData(HLERequestContext& ctx);
-};
-
-class NS final : public ServiceFramework<NS> {
-public:
- explicit NS(const char* name, Core::System& system_);
- ~NS() override;
-
- std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
-
-private:
- template <typename T, typename... Args>
- void PushInterface(HLERequestContext& ctx) {
- LOG_DEBUG(Service_NS, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<T>(system);
- }
-
- void PushIApplicationManagerInterface(HLERequestContext& ctx) {
- LOG_DEBUG(Service_NS, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationManagerInterface>(system);
- }
-
- template <typename T, typename... Args>
- std::shared_ptr<T> GetInterface(Args&&... args) const {
- static_assert(std::is_base_of_v<SessionRequestHandler, T>,
- "Not a base of ServiceFrameworkBase");
-
- return std::make_shared<T>(std::forward<Args>(args)...);
- }
-};
+namespace Service::NS {
void LoopProcess(Core::System& system);
-} // namespace NS
-} // namespace Service
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/ns_results.h
index 16d2ea6f7..16d2ea6f7 100644
--- a/src/core/hle/service/ns/errors.h
+++ b/src/core/hle/service/ns/ns_results.h
diff --git a/src/core/hle/service/ns/ns_types.h b/src/core/hle/service/ns/ns_types.h
new file mode 100644
index 000000000..38421b0f4
--- /dev/null
+++ b/src/core/hle/service/ns/ns_types.h
@@ -0,0 +1,111 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/uuid.h"
+#include "core/file_sys/romfs_factory.h"
+
+namespace Service::NS {
+
+enum class ApplicationRecordType : u8 {
+ Installing = 2,
+ Installed = 3,
+ GameCardNotInserted = 5,
+ Archived = 11,
+ GameCard = 16,
+};
+
+enum class ApplicationControlSource : u8 {
+ CacheOnly = 0,
+ Storage = 1,
+ StorageOnly = 2,
+};
+
+enum class BackgroundNetworkUpdateState : u8 {
+ None,
+ InProgress,
+ Ready,
+};
+
+struct ApplicationRecord {
+ u64 application_id;
+ ApplicationRecordType type;
+ u8 unknown;
+ INSERT_PADDING_BYTES_NOINIT(0x6);
+ u8 unknown2;
+ INSERT_PADDING_BYTES_NOINIT(0x7);
+};
+static_assert(sizeof(ApplicationRecord) == 0x18, "ApplicationRecord has incorrect size.");
+
+/// ApplicationView
+struct ApplicationView {
+ u64 application_id; ///< ApplicationId.
+ u32 unk; ///< Unknown.
+ u32 flags; ///< Flags.
+ std::array<u8, 0x10> unk_x10; ///< Unknown.
+ u32 unk_x20; ///< Unknown.
+ u16 unk_x24; ///< Unknown.
+ std::array<u8, 0x2> unk_x26; ///< Unknown.
+ std::array<u8, 0x8> unk_x28; ///< Unknown.
+ std::array<u8, 0x10> unk_x30; ///< Unknown.
+ u32 unk_x40; ///< Unknown.
+ u8 unk_x44; ///< Unknown.
+ std::array<u8, 0xb> unk_x45; ///< Unknown.
+};
+static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size.");
+
+struct ApplicationRightsOnClient {
+ u64 application_id;
+ Common::UUID uid;
+ u8 flags;
+ u8 flags2;
+ INSERT_PADDING_BYTES_NOINIT(0x6);
+};
+static_assert(sizeof(ApplicationRightsOnClient) == 0x20,
+ "ApplicationRightsOnClient has incorrect size.");
+
+/// NsPromotionInfo
+struct PromotionInfo {
+ u64 start_timestamp; ///< POSIX timestamp for the promotion start.
+ u64 end_timestamp; ///< POSIX timestamp for the promotion end.
+ s64 remaining_time; ///< Remaining time until the promotion ends, in nanoseconds
+ ///< ({end_timestamp - current_time} converted to nanoseconds).
+ INSERT_PADDING_BYTES_NOINIT(0x4);
+ u8 flags; ///< Flags. Bit0: whether the PromotionInfo is valid (including bit1). Bit1 clear:
+ ///< remaining_time is set.
+ INSERT_PADDING_BYTES_NOINIT(0x3);
+};
+static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size.");
+
+/// NsApplicationViewWithPromotionInfo
+struct ApplicationViewWithPromotionInfo {
+ ApplicationView view; ///< \ref NsApplicationView
+ PromotionInfo promotion; ///< \ref NsPromotionInfo
+};
+static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70,
+ "ApplicationViewWithPromotionInfo has incorrect size.");
+
+struct ApplicationOccupiedSizeEntity {
+ FileSys::StorageId storage_id;
+ u64 app_size;
+ u64 patch_size;
+ u64 aoc_size;
+};
+static_assert(sizeof(ApplicationOccupiedSizeEntity) == 0x20,
+ "ApplicationOccupiedSizeEntity has incorrect size.");
+
+struct ApplicationOccupiedSize {
+ std::array<ApplicationOccupiedSizeEntity, 4> entities;
+};
+static_assert(sizeof(ApplicationOccupiedSize) == 0x80,
+ "ApplicationOccupiedSize has incorrect size.");
+
+struct ContentPath {
+ u8 file_system_proxy_type;
+ u64 program_id;
+};
+static_assert(sizeof(ContentPath) == 0x10, "ContentPath has incorrect size.");
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
deleted file mode 100644
index ce0ee30e0..000000000
--- a/src/core/hle/service/ns/pdm_qry.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <memory>
-
-#include "common/logging/log.h"
-#include "common/uuid.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ns/pdm_qry.h"
-#include "core/hle/service/service.h"
-
-namespace Service::NS {
-
-PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "QueryAppletEvent"},
- {1, nullptr, "QueryPlayStatistics"},
- {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
- {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
- {4, nullptr, "QueryPlayStatisticsByApplicationId"},
- {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
- {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
- {7, nullptr, "QueryLastPlayTimeV0"},
- {8, nullptr, "QueryPlayEvent"},
- {9, nullptr, "GetAvailablePlayEventRange"},
- {10, nullptr, "QueryAccountEvent"},
- {11, nullptr, "QueryAccountPlayEvent"},
- {12, nullptr, "GetAvailableAccountPlayEventRange"},
- {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
- {14, nullptr, "QueryRecentlyPlayedApplication"},
- {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
- {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
- {17, nullptr, "QueryLastPlayTime"},
- {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
- {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-PDM_QRY::~PDM_QRY() = default;
-
-void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto unknown = rp.Pop<bool>();
- rp.Pop<u8>(); // Padding
- const auto application_id = rp.Pop<u64>();
- const auto user_account_uid = rp.PopRaw<Common::UUID>();
-
- // TODO(German77): Read statistics of the game
- PlayStatistics statistics{
- .application_id = application_id,
- .total_launches = 1,
- };
-
- LOG_WARNING(Service_NS,
- "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
- unknown, application_id, user_account_uid.RawString());
-
- IPC::ResponseBuilder rb{ctx, 12};
- rb.Push(ResultSuccess);
- rb.PushRaw(statistics);
-}
-
-} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h
deleted file mode 100644
index c98e01660..000000000
--- a/src/core/hle/service/ns/pdm_qry.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Service::NS {
-
-struct PlayStatistics {
- u64 application_id{};
- u32 first_entry_index{};
- u32 first_timestamp_user{};
- u32 first_timestamp_network{};
- u32 last_entry_index{};
- u32 last_timestamp_user{};
- u32 last_timestamp_network{};
- u32 play_time_in_minutes{};
- u32 total_launches{};
-};
-static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
-
-class PDM_QRY final : public ServiceFramework<PDM_QRY> {
-public:
- explicit PDM_QRY(Core::System& system_);
- ~PDM_QRY() override;
-
-private:
- void QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx);
-};
-
-} // namespace Service::NS
diff --git a/src/core/hle/service/ns/platform_service_manager.cpp b/src/core/hle/service/ns/platform_service_manager.cpp
new file mode 100644
index 000000000..23cf05005
--- /dev/null
+++ b/src/core/hle/service/ns/platform_service_manager.cpp
@@ -0,0 +1,273 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/swap.h"
+#include "core/core.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_memory.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/ns/platform_service_manager.h"
+
+namespace Service::NS {
+
+struct FontRegion {
+ u32 offset;
+ u32 size;
+};
+
+// The below data is specific to shared font data dumped from Switch on f/w 2.2
+// Virtual address and offsets/sizes likely will vary by dump
+[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
+constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
+constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
+constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
+constexpr FontRegion EMPTY_REGION{0, 0};
+
+static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
+ std::size_t& offset) {
+ ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
+ "Shared fonts exceeds 17mb!");
+ ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
+
+ const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
+ std::vector<u32> transformed_font(input.size());
+ // TODO(ogniK): Figure out a better way to do this
+ std::transform(input.begin(), input.end(), transformed_font.begin(),
+ [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
+ transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
+ std::memcpy(output.data() + offset, transformed_font.data(),
+ transformed_font.size() * sizeof(u32));
+ offset += transformed_font.size() * sizeof(u32);
+}
+
+void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
+ ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
+
+ if (input.size() < 2) {
+ LOG_ERROR(Service_NS, "Input font is empty");
+ return;
+ }
+
+ const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
+ std::vector<u32> transformed_font(input.size());
+ // TODO(ogniK): Figure out a better way to do this
+ std::transform(input.begin(), input.end(), transformed_font.begin(),
+ [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
+ std::memcpy(output.data(), transformed_font.data() + 2,
+ (transformed_font.size() - 2) * sizeof(u32));
+}
+
+void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
+ std::size_t& offset) {
+ ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
+ "Shared fonts exceeds 17mb!");
+
+ const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC);
+ std::vector<u32> transformed_font(input.size() + 2);
+ transformed_font[0] = Common::swap32(EXPECTED_MAGIC);
+ transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key;
+ std::transform(input.begin(), input.end(), transformed_font.begin() + 2,
+ [key](u32 in) { return in ^ key; });
+ std::memcpy(output.data() + offset, transformed_font.data(),
+ transformed_font.size() * sizeof(u32));
+ offset += transformed_font.size() * sizeof(u32);
+}
+
+// Helper function to make BuildSharedFontsRawRegions a bit nicer
+static u32 GetU32Swapped(const u8* data) {
+ u32 value;
+ std::memcpy(&value, data, sizeof(value));
+ return Common::swap32(value);
+}
+
+struct IPlatformServiceManager::Impl {
+ const FontRegion& GetSharedFontRegion(std::size_t index) const {
+ if (index >= shared_font_regions.size() || shared_font_regions.empty()) {
+ // No font fallback
+ return EMPTY_REGION;
+ }
+ return shared_font_regions.at(index);
+ }
+
+ void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) {
+ // As we can derive the xor key we can just populate the offsets
+ // based on the shared memory dump
+ unsigned cur_offset = 0;
+
+ for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) {
+ // Out of shared fonts/invalid font
+ if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) {
+ break;
+ }
+
+ // Derive key within inverse xor
+ const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC;
+ const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY;
+ shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE});
+ cur_offset += SIZE + 8;
+ }
+ }
+
+ /// Backing memory for the shared font data
+ std::shared_ptr<Kernel::PhysicalMemory> shared_font;
+
+ // Automatically populated based on shared_fonts dump or system archives.
+ std::vector<FontRegion> shared_font_regions;
+};
+
+IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_)
+ : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IPlatformServiceManager::RequestLoad>, "RequestLoad"},
+ {1, D<&IPlatformServiceManager::GetLoadState>, "GetLoadState"},
+ {2, D<&IPlatformServiceManager::GetSize>, "GetSize"},
+ {3, D<&IPlatformServiceManager::GetSharedMemoryAddressOffset>, "GetSharedMemoryAddressOffset"},
+ {4, D<&IPlatformServiceManager::GetSharedMemoryNativeHandle>, "GetSharedMemoryNativeHandle"},
+ {5, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriority"},
+ {6, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriorityForSystem"},
+ {100, nullptr, "RequestApplicationFunctionAuthorization"},
+ {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
+ {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
+ {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"},
+ {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"},
+ {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"},
+ {106, nullptr, "GetFunctionBlackListVersion"},
+ {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
+ {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+
+ auto& fsc = system.GetFileSystemController();
+
+ // Attempt to load shared font data from disk
+ const auto* nand = fsc.GetSystemNANDContents();
+ std::size_t offset = 0;
+ // Rebuild shared fonts from data ncas or synthesize
+
+ impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
+ for (auto font : SHARED_FONTS) {
+ FileSys::VirtualFile romfs;
+ const auto nca =
+ nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
+ if (nca) {
+ romfs = nca->GetRomFS();
+ }
+
+ if (!romfs) {
+ romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first));
+ }
+
+ if (!romfs) {
+ LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first);
+ continue;
+ }
+
+ const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
+ if (!extracted_romfs) {
+ LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first);
+ continue;
+ }
+ const auto font_fp = extracted_romfs->GetFile(font.second);
+ if (!font_fp) {
+ LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second);
+ continue;
+ }
+ std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
+ font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
+ // We need to be BigEndian as u32s for the xor encryption
+ std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
+ Common::swap32);
+ // Font offset and size do not account for the header
+ const FontRegion region{static_cast<u32>(offset + 8),
+ static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)};
+ DecryptSharedFont(font_data_u32, *impl->shared_font, offset);
+ impl->shared_font_regions.push_back(region);
+ }
+}
+
+IPlatformServiceManager::~IPlatformServiceManager() = default;
+
+Result IPlatformServiceManager::RequestLoad(SharedFontType type) {
+ // Games don't call this so all fonts should be loaded
+ LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
+ R_SUCCEED();
+}
+
+Result IPlatformServiceManager::GetLoadState(Out<LoadState> out_load_state, SharedFontType type) {
+ LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
+ *out_load_state = LoadState::Loaded;
+ R_SUCCEED();
+}
+
+Result IPlatformServiceManager::GetSize(Out<u32> out_size, SharedFontType type) {
+ LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
+ *out_size = impl->GetSharedFontRegion(static_cast<size_t>(type)).size;
+ R_SUCCEED();
+}
+
+Result IPlatformServiceManager::GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset,
+ SharedFontType type) {
+ LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
+ *out_shared_memory_offset = impl->GetSharedFontRegion(static_cast<size_t>(type)).offset;
+ R_SUCCEED();
+}
+
+Result IPlatformServiceManager::GetSharedMemoryNativeHandle(
+ OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle) {
+ // Map backing memory for the font data
+ LOG_DEBUG(Service_NS, "called");
+
+ // Create shared font memory object
+ std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(),
+ impl->shared_font->size());
+
+ // FIXME: this shouldn't belong to the kernel
+ *out_shared_memory_native_handle = &kernel.GetFontSharedMem();
+ R_SUCCEED();
+}
+
+Result IPlatformServiceManager::GetSharedFontInOrderOfPriority(
+ OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes,
+ OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets,
+ OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes, Out<bool> out_fonts_are_loaded,
+ Out<u32> out_font_count, Set::LanguageCode language_code) {
+ LOG_DEBUG(Service_NS, "called, language_code={:#x}", language_code);
+
+ // The maximum number of elements that can be returned is 6. Regardless of the available fonts
+ // or buffer size.
+ constexpr size_t MaxElementCount = 6;
+
+ // TODO(ogniK): Have actual priority order
+ const auto max_size = std::min({MaxElementCount, out_font_codes.size(), out_font_offsets.size(),
+ out_font_sizes.size(), impl->shared_font_regions.size()});
+
+ for (size_t i = 0; i < max_size; i++) {
+ auto region = impl->GetSharedFontRegion(i);
+
+ out_font_codes[i] = static_cast<u32>(i);
+ out_font_offsets[i] = region.offset;
+ out_font_sizes[i] = region.size;
+ }
+
+ *out_fonts_are_loaded = true;
+ *out_font_count = static_cast<u32>(max_size);
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/platform_service_manager.h b/src/core/hle/service/ns/platform_service_manager.h
new file mode 100644
index 000000000..b82c385a6
--- /dev/null
+++ b/src/core/hle/service/ns/platform_service_manager.h
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/set/settings_types.h"
+
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace NS {
+
+enum class FontArchives : u64 {
+ Extension = 0x0100000000000810,
+ Standard = 0x0100000000000811,
+ Korean = 0x0100000000000812,
+ ChineseTraditional = 0x0100000000000813,
+ ChineseSimple = 0x0100000000000814,
+};
+
+enum class SharedFontType : u32 {
+ JapanUSEuropeStandard = 0,
+ ChineseSimplified = 1,
+ ExtendedChineseSimplified = 2,
+ ChineseTraditional = 3,
+ KoreanHangul = 4,
+ NintendoExtended = 5,
+};
+
+enum class LoadState : u32 {
+ Loading = 0,
+ Loaded = 1,
+};
+
+constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
+ std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
+ std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
+ std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
+ std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
+ std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
+ std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
+ std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
+};
+
+void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output);
+void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);
+
+class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> {
+public:
+ explicit IPlatformServiceManager(Core::System& system_, const char* service_name_);
+ ~IPlatformServiceManager() override;
+
+private:
+ Result RequestLoad(SharedFontType type);
+ Result GetLoadState(Out<LoadState> out_load_state, SharedFontType type);
+ Result GetSize(Out<u32> out_size, SharedFontType type);
+ Result GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset, SharedFontType type);
+ Result GetSharedMemoryNativeHandle(
+ OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle);
+ Result GetSharedFontInOrderOfPriority(OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes,
+ OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets,
+ OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes,
+ Out<bool> out_fonts_are_loaded, Out<u32> out_font_count,
+ Set::LanguageCode language_code);
+
+ struct Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+} // namespace NS
+
+} // namespace Service
diff --git a/src/core/hle/service/ns/query_service.cpp b/src/core/hle/service/ns/query_service.cpp
new file mode 100644
index 000000000..946b7fa23
--- /dev/null
+++ b/src/core/hle/service/ns/query_service.cpp
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "common/uuid.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/query_service.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "QueryAppletEvent"},
+ {1, nullptr, "QueryPlayStatistics"},
+ {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
+ {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
+ {4, nullptr, "QueryPlayStatisticsByApplicationId"},
+ {5, D<&IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId>, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
+ {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
+ {7, nullptr, "QueryLastPlayTimeV0"},
+ {8, nullptr, "QueryPlayEvent"},
+ {9, nullptr, "GetAvailablePlayEventRange"},
+ {10, nullptr, "QueryAccountEvent"},
+ {11, nullptr, "QueryAccountPlayEvent"},
+ {12, nullptr, "GetAvailableAccountPlayEventRange"},
+ {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
+ {14, nullptr, "QueryRecentlyPlayedApplication"},
+ {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
+ {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
+ {17, nullptr, "QueryLastPlayTime"},
+ {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
+ {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IQueryService::~IQueryService() = default;
+
+Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
+ Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
+ u64 application_id) {
+ // TODO(German77): Read statistics of the game
+ *out_play_statistics = {
+ .application_id = application_id,
+ .total_launches = 1,
+ };
+
+ LOG_WARNING(Service_NS, "(STUBBED) called. unknown={}. application_id={:016X}, account_id={}",
+ unknown, application_id, account_id.FormattedString());
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/query_service.h b/src/core/hle/service/ns/query_service.h
new file mode 100644
index 000000000..6cdbfa277
--- /dev/null
+++ b/src/core/hle/service/ns/query_service.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+struct PlayStatistics {
+ u64 application_id{};
+ u32 first_entry_index{};
+ u32 first_timestamp_user{};
+ u32 first_timestamp_network{};
+ u32 last_entry_index{};
+ u32 last_timestamp_user{};
+ u32 last_timestamp_network{};
+ u32 play_time_in_minutes{};
+ u32 total_launches{};
+};
+static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
+
+class IQueryService final : public ServiceFramework<IQueryService> {
+public:
+ explicit IQueryService(Core::System& system_);
+ ~IQueryService() override;
+
+private:
+ Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
+ Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
+ u64 application_id);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp
new file mode 100644
index 000000000..9b2ca94a4
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/language.h"
+#include "core/hle/service/ns/ns_results.h"
+#include "core/hle/service/ns/read_only_application_control_data_interface.h"
+#include "core/hle/service/set/settings_server.h"
+
+namespace Service::NS {
+
+IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
+ Core::System& system_)
+ : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData>, "GetApplicationControlData"},
+ {1, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
+ {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
+ {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
+ {4, nullptr, "SelectApplicationDesiredLanguage"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
+
+Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
+ OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
+ ApplicationControlSource application_control_source, u64 application_id) {
+ LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
+ application_control_source, application_id);
+
+ const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ const auto control = pm.GetControlMetadata();
+ const auto size = out_buffer.size();
+
+ const auto icon_size = control.second ? control.second->GetSize() : 0;
+ const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
+
+ if (size < total_size) {
+ LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
+ size);
+ R_THROW(ResultUnknown);
+ }
+
+ if (control.first != nullptr) {
+ const auto bytes = control.first->GetRawBytes();
+ std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
+ } else {
+ LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
+ application_id);
+ std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
+ }
+
+ if (control.second != nullptr) {
+ control.second->Read(out_buffer.data() + sizeof(FileSys::RawNACP), icon_size);
+ } else {
+ LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}", application_id);
+ }
+
+ *out_actual_size = static_cast<u32>(total_size);
+ R_SUCCEED();
+}
+
+Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
+ Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
+ LOG_INFO(Service_NS, "called with supported_languages={:08X}", supported_languages);
+
+ // Get language code from settings
+ const auto language_code =
+ Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
+
+ // Convert to application language, get priority list
+ const auto application_language = ConvertToApplicationLanguage(language_code);
+ if (application_language == std::nullopt) {
+ LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
+ language_code);
+ R_THROW(Service::NS::ResultApplicationLanguageNotFound);
+ }
+ const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
+ if (!priority_list) {
+ LOG_ERROR(Service_NS,
+ "Could not find application language priorities! application_language={}",
+ *application_language);
+ R_THROW(Service::NS::ResultApplicationLanguageNotFound);
+ }
+
+ // Try to find a valid language.
+ for (const auto lang : *priority_list) {
+ const auto supported_flag = GetSupportedLanguageFlag(lang);
+ if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
+ *out_desired_language = lang;
+ R_SUCCEED();
+ }
+ }
+
+ LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
+ supported_languages);
+ R_THROW(Service::NS::ResultApplicationLanguageNotFound);
+}
+
+Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode(
+ Out<u64> out_language_code, ApplicationLanguage application_language) {
+ const auto language_code = ConvertToLanguageCode(application_language);
+ if (language_code == std::nullopt) {
+ LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language);
+ R_THROW(Service::NS::ResultApplicationLanguageNotFound);
+ }
+
+ *out_language_code = static_cast<u64>(*language_code);
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.h b/src/core/hle/service/ns/read_only_application_control_data_interface.h
new file mode 100644
index 000000000..ac099435a
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_control_data_interface.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/ns/language.h"
+#include "core/hle/service/ns/ns_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IReadOnlyApplicationControlDataInterface final
+ : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
+public:
+ explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
+ ~IReadOnlyApplicationControlDataInterface() override;
+
+public:
+ Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
+ Out<u32> out_actual_size,
+ ApplicationControlSource application_control_source,
+ u64 application_id);
+ Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language,
+ u32 supported_languages);
+ Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
+ ApplicationLanguage application_language);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_record_interface.cpp b/src/core/hle/service/ns/read_only_application_record_interface.cpp
new file mode 100644
index 000000000..816a1e1dc
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_record_interface.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/read_only_application_record_interface.h"
+
+namespace Service::NS {
+
+IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_)
+ : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} {
+ static const FunctionInfo functions[] = {
+ {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
+ {1, nullptr, "NotifyApplicationFailure"},
+ {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
+ "IsDataCorruptedResult"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default;
+
+Result IReadOnlyApplicationRecordInterface::HasApplicationRecord(
+ Out<bool> out_has_application_record, u64 program_id) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:016X}", program_id);
+ *out_has_application_record = true;
+ R_SUCCEED();
+}
+
+Result IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(
+ Out<bool> out_is_data_corrupted_result, Result result) {
+ LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue());
+ *out_is_data_corrupted_result = false;
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_record_interface.h b/src/core/hle/service/ns/read_only_application_record_interface.h
new file mode 100644
index 000000000..d06e8f5e6
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_record_interface.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IReadOnlyApplicationRecordInterface final
+ : public ServiceFramework<IReadOnlyApplicationRecordInterface> {
+public:
+ explicit IReadOnlyApplicationRecordInterface(Core::System& system_);
+ ~IReadOnlyApplicationRecordInterface() override;
+
+private:
+ Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id);
+ Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/service_getter_interface.cpp b/src/core/hle/service/ns/service_getter_interface.cpp
new file mode 100644
index 000000000..1a3dd7166
--- /dev/null
+++ b/src/core/hle/service/ns/service_getter_interface.cpp
@@ -0,0 +1,120 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/account_proxy_interface.h"
+#include "core/hle/service/ns/application_manager_interface.h"
+#include "core/hle/service/ns/application_version_interface.h"
+#include "core/hle/service/ns/content_management_interface.h"
+#include "core/hle/service/ns/document_interface.h"
+#include "core/hle/service/ns/download_task_interface.h"
+#include "core/hle/service/ns/dynamic_rights_interface.h"
+#include "core/hle/service/ns/ecommerce_interface.h"
+#include "core/hle/service/ns/factory_reset_interface.h"
+#include "core/hle/service/ns/read_only_application_control_data_interface.h"
+#include "core/hle/service/ns/read_only_application_record_interface.h"
+#include "core/hle/service/ns/service_getter_interface.h"
+
+namespace Service::NS {
+
+IServiceGetterInterface::IServiceGetterInterface(Core::System& system_, const char* name)
+ : ServiceFramework{system_, name} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {7988, D<&IServiceGetterInterface::GetDynamicRightsInterface>, "GetDynamicRightsInterface"},
+ {7989, D<&IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
+ {7991, D<&IServiceGetterInterface::GetReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
+ {7992, D<&IServiceGetterInterface::GetECommerceInterface>, "GetECommerceInterface"},
+ {7993, D<&IServiceGetterInterface::GetApplicationVersionInterface>, "GetApplicationVersionInterface"},
+ {7994, D<&IServiceGetterInterface::GetFactoryResetInterface>, "GetFactoryResetInterface"},
+ {7995, D<&IServiceGetterInterface::GetAccountProxyInterface>, "GetAccountProxyInterface"},
+ {7996, D<&IServiceGetterInterface::GetApplicationManagerInterface>, "GetApplicationManagerInterface"},
+ {7997, D<&IServiceGetterInterface::GetDownloadTaskInterface>, "GetDownloadTaskInterface"},
+ {7998, D<&IServiceGetterInterface::GetContentManagementInterface>, "GetContentManagementInterface"},
+ {7999, D<&IServiceGetterInterface::GetDocumentInterface>, "GetDocumentInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IServiceGetterInterface::~IServiceGetterInterface() = default;
+
+Result IServiceGetterInterface::GetDynamicRightsInterface(
+ Out<SharedPointer<IDynamicRightsInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IDynamicRightsInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface(
+ Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IReadOnlyApplicationControlDataInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetReadOnlyApplicationRecordInterface(
+ Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IReadOnlyApplicationRecordInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetECommerceInterface(
+ Out<SharedPointer<IECommerceInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IECommerceInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetApplicationVersionInterface(
+ Out<SharedPointer<IApplicationVersionInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IApplicationVersionInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetFactoryResetInterface(
+ Out<SharedPointer<IFactoryResetInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IFactoryResetInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetAccountProxyInterface(
+ Out<SharedPointer<IAccountProxyInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IAccountProxyInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetApplicationManagerInterface(
+ Out<SharedPointer<IApplicationManagerInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IApplicationManagerInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetDownloadTaskInterface(
+ Out<SharedPointer<IDownloadTaskInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IDownloadTaskInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetContentManagementInterface(
+ Out<SharedPointer<IContentManagementInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IContentManagementInterface>(system);
+ R_SUCCEED();
+}
+
+Result IServiceGetterInterface::GetDocumentInterface(
+ Out<SharedPointer<IDocumentInterface>> out_interface) {
+ LOG_DEBUG(Service_NS, "called");
+ *out_interface = std::make_shared<IDocumentInterface>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/service_getter_interface.h b/src/core/hle/service/ns/service_getter_interface.h
new file mode 100644
index 000000000..bbc18d444
--- /dev/null
+++ b/src/core/hle/service/ns/service_getter_interface.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IDynamicRightsInterface;
+class IReadOnlyApplicationControlDataInterface;
+class IReadOnlyApplicationRecordInterface;
+class IECommerceInterface;
+class IApplicationVersionInterface;
+class IFactoryResetInterface;
+class IAccountProxyInterface;
+class IApplicationManagerInterface;
+class IDownloadTaskInterface;
+class IContentManagementInterface;
+class IDocumentInterface;
+
+class IServiceGetterInterface : public ServiceFramework<IServiceGetterInterface> {
+public:
+ explicit IServiceGetterInterface(Core::System& system_, const char* name);
+ ~IServiceGetterInterface() override;
+
+public:
+ Result GetDynamicRightsInterface(Out<SharedPointer<IDynamicRightsInterface>> out_interface);
+ Result GetReadOnlyApplicationControlDataInterface(
+ Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface);
+ Result GetReadOnlyApplicationRecordInterface(
+ Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface);
+ Result GetECommerceInterface(Out<SharedPointer<IECommerceInterface>> out_interface);
+ Result GetApplicationVersionInterface(
+ Out<SharedPointer<IApplicationVersionInterface>> out_interface);
+ Result GetFactoryResetInterface(Out<SharedPointer<IFactoryResetInterface>> out_interface);
+ Result GetAccountProxyInterface(Out<SharedPointer<IAccountProxyInterface>> out_interface);
+ Result GetApplicationManagerInterface(
+ Out<SharedPointer<IApplicationManagerInterface>> out_interface);
+ Result GetDownloadTaskInterface(Out<SharedPointer<IDownloadTaskInterface>> out_interface);
+ Result GetContentManagementInterface(
+ Out<SharedPointer<IContentManagementInterface>> out_interface);
+ Result GetDocumentInterface(Out<SharedPointer<IDocumentInterface>> out_interface);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_control.cpp b/src/core/hle/service/ns/system_update_control.cpp
new file mode 100644
index 000000000..f5f5cfd90
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_control.cpp
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/system_update_control.h"
+
+namespace Service::NS {
+
+ISystemUpdateControl::ISystemUpdateControl(Core::System& system_)
+ : ServiceFramework{system_, "ISystemUpdateControl"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "HasDownloaded"},
+ {1, nullptr, "RequestCheckLatestUpdate"},
+ {2, nullptr, "RequestDownloadLatestUpdate"},
+ {3, nullptr, "GetDownloadProgress"},
+ {4, nullptr, "ApplyDownloadedUpdate"},
+ {5, nullptr, "RequestPrepareCardUpdate"},
+ {6, nullptr, "GetPrepareCardUpdateProgress"},
+ {7, nullptr, "HasPreparedCardUpdate"},
+ {8, nullptr, "ApplyCardUpdate"},
+ {9, nullptr, "GetDownloadedEulaDataSize"},
+ {10, nullptr, "GetDownloadedEulaData"},
+ {11, nullptr, "SetupCardUpdate"},
+ {12, nullptr, "GetPreparedCardUpdateEulaDataSize"},
+ {13, nullptr, "GetPreparedCardUpdateEulaData"},
+ {14, nullptr, "SetupCardUpdateViaSystemUpdater"},
+ {15, nullptr, "HasReceived"},
+ {16, nullptr, "RequestReceiveSystemUpdate"},
+ {17, nullptr, "GetReceiveProgress"},
+ {18, nullptr, "ApplyReceivedUpdate"},
+ {19, nullptr, "GetReceivedEulaDataSize"},
+ {20, nullptr, "GetReceivedEulaData"},
+ {21, nullptr, "SetupToReceiveSystemUpdate"},
+ {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISystemUpdateControl::~ISystemUpdateControl() = default;
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_control.h b/src/core/hle/service/ns/system_update_control.h
new file mode 100644
index 000000000..a30a09000
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_control.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> {
+public:
+ explicit ISystemUpdateControl(Core::System& system_);
+ ~ISystemUpdateControl() override;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_interface.cpp b/src/core/hle/service/ns/system_update_interface.cpp
new file mode 100644
index 000000000..7e22ca3db
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_interface.cpp
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/system_update_control.h"
+#include "core/hle/service/ns/system_update_interface.h"
+
+namespace Service::NS {
+
+ISystemUpdateInterface::ISystemUpdateInterface(Core::System& system_)
+ : ServiceFramework{system_, "ns:su"}, service_context{system_, "ns:su"},
+ update_notification_event{service_context} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, D<&ISystemUpdateInterface::GetBackgroundNetworkUpdateState>, "GetBackgroundNetworkUpdateState"},
+ {1, D<&ISystemUpdateInterface::OpenSystemUpdateControl>, "OpenSystemUpdateControl"},
+ {2, nullptr, "NotifyExFatDriverRequired"},
+ {3, nullptr, "ClearExFatDriverStatusForDebug"},
+ {4, nullptr, "RequestBackgroundNetworkUpdate"},
+ {5, nullptr, "NotifyBackgroundNetworkUpdate"},
+ {6, nullptr, "NotifyExFatDriverDownloadedForDebug"},
+ {9, D<&ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery>, "GetSystemUpdateNotificationEventForContentDelivery"},
+ {10, nullptr, "NotifySystemUpdateForContentDelivery"},
+ {11, nullptr, "PrepareShutdown"},
+ {12, nullptr, "Unknown12"},
+ {13, nullptr, "Unknown13"},
+ {14, nullptr, "Unknown14"},
+ {15, nullptr, "Unknown15"},
+ {16, nullptr, "DestroySystemUpdateTask"},
+ {17, nullptr, "RequestSendSystemUpdate"},
+ {18, nullptr, "GetSendSystemUpdateProgress"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ISystemUpdateInterface::~ISystemUpdateInterface() = default;
+
+Result ISystemUpdateInterface::GetBackgroundNetworkUpdateState(
+ Out<BackgroundNetworkUpdateState> out_background_network_update_state) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ *out_background_network_update_state = BackgroundNetworkUpdateState::None;
+ R_SUCCEED();
+}
+
+Result ISystemUpdateInterface::OpenSystemUpdateControl(
+ Out<SharedPointer<ISystemUpdateControl>> out_system_update_control) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_system_update_control = std::make_shared<ISystemUpdateControl>(system);
+ R_SUCCEED();
+}
+
+Result ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_event = update_notification_event.GetHandle();
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_interface.h b/src/core/hle/service/ns/system_update_interface.h
new file mode 100644
index 000000000..36a2880ec
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_interface.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/ns/ns_types.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::NS {
+
+class ISystemUpdateControl;
+
+class ISystemUpdateInterface final : public ServiceFramework<ISystemUpdateInterface> {
+public:
+ explicit ISystemUpdateInterface(Core::System& system_);
+ ~ISystemUpdateInterface() override;
+
+private:
+ Result GetBackgroundNetworkUpdateState(
+ Out<BackgroundNetworkUpdateState> out_background_network_update_state);
+ Result OpenSystemUpdateControl(
+ Out<SharedPointer<ISystemUpdateControl>> out_system_update_control);
+ Result GetSystemUpdateNotificationEventForContentDelivery(
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+private:
+ KernelHelpers::ServiceContext service_context;
+ Event update_notification_event;
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/vulnerability_manager_interface.cpp b/src/core/hle/service/ns/vulnerability_manager_interface.cpp
new file mode 100644
index 000000000..69c21fb89
--- /dev/null
+++ b/src/core/hle/service/ns/vulnerability_manager_interface.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/ns/vulnerability_manager_interface.h"
+
+namespace Service::NS {
+
+IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_)
+ : ServiceFramework{system_, "ns:vm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
+ {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
+ {1202, nullptr, "GetSafeSystemVersion"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default;
+
+Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability(
+ Out<bool> out_needs_update_vulnerability) {
+ LOG_WARNING(Service_NS, "(STUBBED) called");
+ *out_needs_update_vulnerability = false;
+ R_SUCCEED();
+}
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/ns/vulnerability_manager_interface.h b/src/core/hle/service/ns/vulnerability_manager_interface.h
new file mode 100644
index 000000000..c689cf7ec
--- /dev/null
+++ b/src/core/hle/service/ns/vulnerability_manager_interface.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+class IVulnerabilityManagerInterface final
+ : public ServiceFramework<IVulnerabilityManagerInterface> {
+public:
+ explicit IVulnerabilityManagerInterface(Core::System& system_);
+ ~IVulnerabilityManagerInterface() override;
+
+private:
+ Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability);
+};
+
+} // namespace Service::NS
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp
index e89cca6f2..9edce03f6 100644
--- a/src/core/hle/service/nvdrv/core/container.cpp
+++ b/src/core/hle/service/nvdrv/core/container.cpp
@@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
continue;
}
if (session.process == process) {
+ session.ref_count++;
return session.id;
}
}
@@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
}
auto& session = impl->sessions[new_id];
session.is_active = true;
+ session.ref_count = 1;
// Optimization
if (process->IsApplication()) {
auto& page_table = process->GetPageTable().GetBasePageTable();
@@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard);
- impl->file.UnmapAllHandles(session_id);
auto& session = impl->sessions[session_id.id];
+ if (--session.ref_count > 0) {
+ return;
+ }
+ impl->file.UnmapAllHandles(session_id);
auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) {
const DAddr region_start = session.mapper->GetRegionStart();
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h
index b4d3938a8..f159ced09 100644
--- a/src/core/hle/service/nvdrv/core/container.h
+++ b/src/core/hle/service/nvdrv/core/container.h
@@ -46,6 +46,7 @@ struct Session {
bool has_preallocated_area{};
std::unique_ptr<HeapMapper> mapper{};
bool is_active{};
+ s32 ref_count{};
};
class Container {
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
index bc1c033c6..453cb5831 100644
--- a/src/core/hle/service/nvdrv/core/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
}();
for (auto& [id, handle] : handles_copy) {
- if (handle->session_id.id == session_id.id) {
- FreeHandle(id, false);
+ {
+ std::scoped_lock lk{handle->mutex};
+ if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
+ continue;
+ }
}
+ FreeHandle(id, false);
}
}
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index abe95303e..995646e25 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -15,6 +15,22 @@
namespace Service::Nvidia::Devices {
+namespace {
+
+Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) {
+ switch (blending) {
+ case Service::Nvnflinger::LayerBlending::None:
+ default:
+ return Tegra::BlendMode::Opaque;
+ case Service::Nvnflinger::LayerBlending::Premultiplied:
+ return Tegra::BlendMode::Premultiplied;
+ case Service::Nvnflinger::LayerBlending::Coverage:
+ return Tegra::BlendMode::Coverage;
+ }
+}
+
+} // namespace
+
nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
nvdisp_disp0::~nvdisp_disp0() = default;
@@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
.pixel_format = layer.format,
.transform_flags = layer.transform,
.crop_rect = layer.crop_rect,
+ .blending = ConvertBlending(layer.blending),
});
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 250d01de3..0265d55f2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -92,11 +92,11 @@ NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_a
bool must_unmark_fail = !is_allocation;
const u32 event_id = params.value.raw;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (must_unmark_fail) {
events[event_id].fails = 0;
}
- });
+ };
const u32 fence_id = static_cast<u32>(params.fence.id);
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index cb256e5b4..03eb507b9 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -42,7 +42,7 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) {
module.service_context.CloseEvent(event);
}
-void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
+void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>(system);
const auto NvdrvInterfaceFactoryForApplication = [&, module] {
@@ -62,7 +62,6 @@ void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules);
server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting);
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
- nvnflinger.SetNVDrvInstance(module);
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index c594f0e5e..b76f81e59 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,13 +10,11 @@
#include <span>
#include <string>
#include <unordered_map>
-#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
-#include "core/hle/service/nvnflinger/ui/fence.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -27,10 +25,6 @@ namespace Kernel {
class KEvent;
}
-namespace Service::Nvnflinger {
-class Nvnflinger;
-}
-
namespace Service::Nvidia {
namespace NvCore {
@@ -99,7 +93,6 @@ public:
private:
friend class EventInterface;
- friend class Service::Nvnflinger::Nvnflinger;
/// Manages syncpoints on the host
NvCore::Container container;
@@ -118,6 +111,6 @@ private:
std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
};
-void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system);
+void LoopProcess(Core::System& system);
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index ffe72f281..258970fd5 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -154,10 +154,10 @@ void NVDRV::Close(HLERequestContext& ctx) {
void NVDRV::Initialize(HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
- SCOPE_EXIT({
+ SCOPE_EXIT {
rb.Push(ResultSuccess);
rb.PushEnum(NvResult::Success);
- });
+ };
if (is_initialized) {
// No need to initialize again
@@ -263,8 +263,10 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char*
}
NVDRV::~NVDRV() {
- auto& container = nvdrv->GetContainer();
- container.CloseSession(session_id);
+ if (is_initialized) {
+ auto& container = nvdrv->GetContainer();
+ container.CloseSession(session_id);
+ }
}
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index f2195ae1e..c72f92597 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -16,6 +16,10 @@ public:
explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name);
~NVDRV() override;
+ std::shared_ptr<Module> GetModule() const {
+ return nvdrv;
+ }
+
private:
void Open(HLERequestContext& ctx);
void Ioctl1(HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvnflinger/binder.h b/src/core/hle/service/nvnflinger/binder.h
index aef1477e3..124accb94 100644
--- a/src/core/hle/service/nvnflinger/binder.h
+++ b/src/core/hle/service/nvnflinger/binder.h
@@ -6,6 +6,8 @@
#pragma once
+#include <span>
+
#include "common/common_types.h"
namespace Kernel {
@@ -18,28 +20,12 @@ class HLERequestContext;
namespace Service::android {
-enum class TransactionId {
- RequestBuffer = 1,
- SetBufferCount = 2,
- DequeueBuffer = 3,
- DetachBuffer = 4,
- DetachNextBuffer = 5,
- AttachBuffer = 6,
- QueueBuffer = 7,
- CancelBuffer = 8,
- Query = 9,
- Connect = 10,
- Disconnect = 11,
- AllocateBuffers = 13,
- SetPreallocatedBuffer = 14,
- GetBufferHistory = 17,
-};
-
class IBinder {
public:
virtual ~IBinder() = default;
- virtual void Transact(HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0;
- virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
+ virtual void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
+ u32 flags) = 0;
+ virtual Kernel::KReadableEvent* GetNativeHandle(u32 type_id) = 0;
};
} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp
index cf151ea3a..123507123 100644
--- a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp
@@ -12,7 +12,7 @@
namespace Service::android {
-BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
+BufferItemConsumer::BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer_)
: ConsumerBase{std::move(consumer_)} {}
Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.h b/src/core/hle/service/nvnflinger/buffer_item_consumer.h
index e0c6b3604..9f95c9280 100644
--- a/src/core/hle/service/nvnflinger/buffer_item_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.h
@@ -19,7 +19,7 @@ class BufferItem;
class BufferItemConsumer final : public ConsumerBase {
public:
- explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
+ explicit BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer);
Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
bool wait_for_fence = true);
Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence);
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index bbe8e06d4..3bc23aa97 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -4,12 +4,13 @@
// Parts of this implementation were based on:
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
+#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
+#include "core/hle/service/nvnflinger/parcel.h"
#include "core/hle/service/nvnflinger/producer_listener.h"
-#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
namespace Service::android {
@@ -254,4 +255,77 @@ Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
return Status::NoError;
}
+void BufferQueueConsumer::Transact(u32 code, std::span<const u8> parcel_data,
+ std::span<u8> parcel_reply, u32 flags) {
+ // Values used by BnGraphicBufferConsumer onTransact
+ enum class TransactionId {
+ AcquireBuffer = 1,
+ DetachBuffer = 2,
+ AttachBuffer = 3,
+ ReleaseBuffer = 4,
+ ConsumerConnect = 5,
+ ConsumerDisconnect = 6,
+ GetReleasedBuffers = 7,
+ SetDefaultBufferSize = 8,
+ SetDefaultMaxBufferCount = 9,
+ DisableAsyncBuffer = 10,
+ SetMaxAcquiredBufferCount = 11,
+ SetConsumerName = 12,
+ SetDefaultBufferFormat = 13,
+ SetConsumerUsageBits = 14,
+ SetTransformHint = 15,
+ GetSidebandStream = 16,
+ Unknown18 = 18,
+ Unknown20 = 20,
+ };
+
+ Status status{Status::NoError};
+ InputParcel parcel_in{parcel_data};
+ OutputParcel parcel_out{};
+
+ switch (static_cast<TransactionId>(code)) {
+ case TransactionId::AcquireBuffer: {
+ BufferItem item;
+ const s64 present_when = parcel_in.Read<s64>();
+
+ status = AcquireBuffer(&item, std::chrono::nanoseconds{present_when});
+
+ // TODO: can't write this directly, needs a flattener for the sp<GraphicBuffer>
+ // parcel_out.WriteFlattened(item);
+ UNREACHABLE();
+ }
+ case TransactionId::ReleaseBuffer: {
+ const s32 slot = parcel_in.Read<s32>();
+ const u64 frame_number = parcel_in.Read<u64>();
+ const auto release_fence = parcel_in.ReadFlattened<Fence>();
+
+ status = ReleaseBuffer(slot, frame_number, release_fence);
+
+ break;
+ }
+ case TransactionId::GetReleasedBuffers: {
+ u64 slot_mask = 0;
+
+ status = GetReleasedBuffers(&slot_mask);
+
+ parcel_out.Write(slot_mask);
+ break;
+ }
+ default:
+ ASSERT_MSG(false, "called, code={} flags={}", code, flags);
+ break;
+ }
+
+ parcel_out.Write(status);
+
+ const auto serialized = parcel_out.Serialize();
+ std::memcpy(parcel_reply.data(), serialized.data(),
+ std::min(parcel_reply.size(), serialized.size()));
+}
+
+Kernel::KReadableEvent* BufferQueueConsumer::GetNativeHandle(u32 type_id) {
+ ASSERT_MSG(false, "called, type_id={}", type_id);
+ return nullptr;
+}
+
} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
index 0a61e8dbd..a9226f1c3 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
@@ -10,6 +10,7 @@
#include <memory>
#include "common/common_types.h"
+#include "core/hle/service/nvnflinger/binder.h"
#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
#include "core/hle/service/nvnflinger/status.h"
@@ -19,10 +20,10 @@ class BufferItem;
class BufferQueueCore;
class IConsumerListener;
-class BufferQueueConsumer final {
+class BufferQueueConsumer final : public IBinder {
public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
- ~BufferQueueConsumer();
+ ~BufferQueueConsumer() override;
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
@@ -30,6 +31,11 @@ public:
Status Disconnect();
Status GetReleasedBuffers(u64* out_slot_mask);
+ void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
+ u32 flags) override;
+
+ Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override;
+
private:
std::shared_ptr<BufferQueueCore> core;
BufferQueueDefs::SlotsType& slots;
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index 5d8762d25..9e5091eeb 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -6,12 +6,9 @@
#include "common/assert.h"
#include "common/logging/log.h"
-#include "common/settings.h"
-#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
@@ -19,7 +16,6 @@
#include "core/hle/service/nvnflinger/parcel.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
#include "core/hle/service/nvnflinger/window.h"
-#include "core/hle/service/vi/vi.h"
namespace Service::android {
@@ -807,12 +803,31 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
return Status::NoError;
}
-void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u32 flags) {
+void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
+ std::span<u8> parcel_reply, u32 flags) {
+ // Values used by BnGraphicBufferProducer onTransact
+ enum class TransactionId {
+ RequestBuffer = 1,
+ SetBufferCount = 2,
+ DequeueBuffer = 3,
+ DetachBuffer = 4,
+ DetachNextBuffer = 5,
+ AttachBuffer = 6,
+ QueueBuffer = 7,
+ CancelBuffer = 8,
+ Query = 9,
+ Connect = 10,
+ Disconnect = 11,
+ AllocateBuffers = 13,
+ SetPreallocatedBuffer = 14,
+ GetBufferHistory = 17,
+ };
+
Status status{Status::NoError};
- InputParcel parcel_in{ctx.ReadBuffer()};
+ InputParcel parcel_in{parcel_data};
OutputParcel parcel_out{};
- switch (code) {
+ switch (static_cast<TransactionId>(code)) {
case TransactionId::Connect: {
const auto enable_listener = parcel_in.Read<bool>();
const auto api = parcel_in.Read<NativeWindowApi>();
@@ -917,11 +932,13 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
parcel_out.Write(status);
- ctx.WriteBuffer(parcel_out.Serialize());
+ const auto serialized = parcel_out.Serialize();
+ std::memcpy(parcel_reply.data(), serialized.data(),
+ std::min(parcel_reply.size(), serialized.size()));
}
-Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
- return buffer_wait_event->GetReadableEvent();
+Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) {
+ return &buffer_wait_event->GetReadableEvent();
}
} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index 64c17d56c..048523514 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -45,11 +45,12 @@ public:
explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
Service::Nvidia::NvCore::NvMap& nvmap_);
- ~BufferQueueProducer();
+ ~BufferQueueProducer() override;
- void Transact(HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
+ void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
+ u32 flags) override;
- Kernel::KReadableEvent& GetNativeHandle() override;
+ Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override;
public:
Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp
index 1059e72bf..e360ebfd8 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvnflinger/consumer_base.cpp
@@ -14,7 +14,7 @@
namespace Service::android {
-ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
+ConsumerBase::ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_)
: consumer{std::move(consumer_)} {}
ConsumerBase::~ConsumerBase() {
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h
index ea3e9e97a..b29c16f86 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.h
+++ b/src/core/hle/service/nvnflinger/consumer_base.h
@@ -27,7 +27,7 @@ public:
void Abandon();
protected:
- explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
+ explicit ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_);
~ConsumerBase() override;
void OnFrameAvailable(const BufferItem& item) override;
@@ -54,7 +54,7 @@ protected:
bool is_abandoned{};
- std::unique_ptr<BufferQueueConsumer> consumer;
+ std::shared_ptr<BufferQueueConsumer> consumer;
mutable std::mutex mutex;
};
diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h
new file mode 100644
index 000000000..40aa59787
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/display.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvnflinger/hwc_layer.h"
+
+namespace Service::Nvnflinger {
+
+struct Layer {
+ explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
+ s32 consumer_id_)
+ : buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
+ blending(LayerBlending::None), visible(true) {}
+ ~Layer() {
+ buffer_item_consumer->Abandon();
+ }
+
+ std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer;
+ s32 consumer_id;
+ LayerBlending blending;
+ bool visible;
+};
+
+struct LayerStack {
+ std::vector<std::shared_ptr<Layer>> layers;
+
+ std::shared_ptr<Layer> FindLayer(s32 consumer_id) {
+ for (auto& layer : layers) {
+ if (layer->consumer_id == consumer_id) {
+ return layer;
+ }
+ }
+
+ return nullptr;
+ }
+
+ bool HasLayers() {
+ return !layers.empty();
+ }
+};
+
+struct Display {
+ explicit Display(u64 id_) {
+ id = id_;
+ }
+
+ u64 id;
+ LayerStack stack;
+};
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
deleted file mode 100644
index e71652cdf..000000000
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <random>
-
-#include "core/core.h"
-#include "core/hle/kernel/k_process.h"
-#include "core/hle/kernel/k_system_resource.h"
-#include "core/hle/service/nvdrv/devices/nvmap.h"
-#include "core/hle/service/nvdrv/nvdrv.h"
-#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
-#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
-#include "core/hle/service/nvnflinger/pixel_format.h"
-#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
-#include "core/hle/service/vi/layer/vi_layer.h"
-#include "core/hle/service/vi/vi_results.h"
-
-namespace Service::Nvnflinger {
-
-namespace {
-
-Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
- std::unique_ptr<Kernel::KPageGroup>* out_page_group,
- Core::System& system, u32 size) {
- using Core::Memory::YUZU_PAGESIZE;
-
- // Allocate memory for the system shared buffer.
- // FIXME: Because the gmmu can only point to cpu addresses, we need
- // to map this in the application space to allow it to be used.
- // FIXME: Add proper smmu emulation.
- // FIXME: This memory belongs to vi's .data section.
- auto& kernel = system.Kernel();
- auto* process = system.ApplicationProcess();
- auto& page_table = process->GetPageTable();
-
- // Hold a temporary page group reference while we try to map it.
- auto pg = std::make_unique<Kernel::KPageGroup>(
- kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager()));
-
- // Allocate memory from secure pool.
- R_TRY(kernel.MemoryManager().AllocateAndOpen(
- pg.get(), size / YUZU_PAGESIZE,
- Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
- Kernel::KMemoryManager::Direction::FromBack)));
-
- // Get bounds of where mapping is possible.
- const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
- const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
- const auto state = Kernel::KMemoryState::IoMemory;
- const auto perm = Kernel::KMemoryPermission::UserReadWrite;
- std::mt19937_64 rng{process->GetRandomEntropy(0)};
-
- // Retry up to 64 times to map into alias code range.
- Result res = ResultSuccess;
- int i;
- for (i = 0; i < 64; i++) {
- *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE);
- res = page_table.MapPageGroup(*out_map_address, *pg, state, perm);
- if (R_SUCCEEDED(res)) {
- break;
- }
- }
-
- // Return failure, if necessary
- R_UNLESS(i < 64, res);
-
- // Return the mapped page group.
- *out_page_group = std::move(pg);
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
- // Create a handle.
- Nvidia::Devices::nvmap::IocCreateParams create_params{
- .size = size,
- .handle = 0,
- };
- R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success,
- VI::ResultOperationFailed);
-
- // Assign the output handle.
- *out_nv_map_handle = create_params.handle;
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) {
- // Free the handle.
- Nvidia::Devices::nvmap::IocFreeParams free_params{
- .handle = handle,
- };
- R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success,
- VI::ResultOperationFailed);
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
- u32 size, Nvidia::DeviceFD nvmap_fd) {
- // Assign the allocated memory to the handle.
- Nvidia::Devices::nvmap::IocAllocParams alloc_params{
- .handle = handle,
- .heap_mask = 0,
- .flags = {},
- .align = 0,
- .kind = 0,
- .address = GetInteger(buffer),
- };
- R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success,
- VI::ResultOperationFailed);
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd,
- Common::ProcessAddress buffer, u32 size) {
- // Get the nvmap device.
- auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
- ASSERT(nvmap != nullptr);
-
- // Create a handle.
- R_TRY(CreateNvMapHandle(out_handle, *nvmap, size));
-
- // Ensure we maintain a clean state on failure.
- ON_RESULT_FAILURE {
- R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd));
- };
-
- // Assign the allocated memory to the handle.
- R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
-}
-
-constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
-constexpr u32 SharedBufferBlockLinearBpp = 4;
-
-constexpr u32 SharedBufferBlockLinearWidth = 1280;
-constexpr u32 SharedBufferBlockLinearHeight = 768;
-constexpr u32 SharedBufferBlockLinearStride =
- SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
-constexpr u32 SharedBufferNumSlots = 7;
-
-constexpr u32 SharedBufferWidth = 1280;
-constexpr u32 SharedBufferHeight = 720;
-constexpr u32 SharedBufferAsync = false;
-
-constexpr u32 SharedBufferSlotSize =
- SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp;
-constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots;
-
-constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
- SharedMemoryPoolLayout layout{};
- layout.num_slots = SharedBufferNumSlots;
-
- for (u32 i = 0; i < SharedBufferNumSlots; i++) {
- layout.slots[i].buffer_offset = i * SharedBufferSlotSize;
- layout.slots[i].size = SharedBufferSlotSize;
- layout.slots[i].width = SharedBufferWidth;
- layout.slots[i].height = SharedBufferHeight;
- }
-
- return layout;
-}();
-
-void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
- auto buffer = std::make_shared<android::NvGraphicBuffer>();
- buffer->width = SharedBufferWidth;
- buffer->height = SharedBufferHeight;
- buffer->stride = SharedBufferBlockLinearStride;
- buffer->format = SharedBufferBlockLinearFormat;
- buffer->external_format = SharedBufferBlockLinearFormat;
- buffer->buffer_id = handle;
- buffer->offset = slot * SharedBufferSlotSize;
- ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
-}
-
-} // namespace
-
-FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
- std::shared_ptr<Nvidia::Module> nvdrv)
- : m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {}
-
-FbShareBufferManager::~FbShareBufferManager() = default;
-
-Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) {
- std::scoped_lock lk{m_guard};
-
- // Ensure we have not already created a buffer.
- R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed);
-
- // Allocate memory and space for the shared buffer.
- Common::ProcessAddress map_address;
- R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address),
- std::addressof(m_buffer_page_group), m_system,
- SharedBufferSize));
-
- auto& container = m_nvdrv->GetContainer();
- m_session_id = container.OpenSession(m_system.ApplicationProcess());
- m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id);
-
- // Create an nvmap handle for the buffer and assign the memory to it.
- R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd,
- map_address, SharedBufferSize));
-
- // Record the display id.
- m_display_id = display_id;
-
- // Create and open a layer for the display.
- m_layer_id = m_flinger.CreateLayer(m_display_id).value();
- m_flinger.OpenLayer(m_layer_id);
-
- // Set up the buffer.
- m_buffer_id = m_next_buffer_id++;
-
- // Get the layer.
- VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id);
- ASSERT(layer != nullptr);
-
- // Get the producer and set preallocated buffers.
- auto& producer = layer->GetBufferQueue();
- MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle);
- MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle);
-
- // Assign outputs.
- *out_buffer_id = m_buffer_id;
- *out_layer_id = m_layer_id;
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
- s32* out_nvmap_handle,
- SharedMemoryPoolLayout* out_pool_layout,
- u64 buffer_id,
- u64 applet_resource_user_id) {
- std::scoped_lock lk{m_guard};
-
- R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
- R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
-
- *out_pool_layout = SharedBufferPoolLayout;
- *out_buffer_size = SharedBufferSize;
- *out_nvmap_handle = m_buffer_nvmap_handle;
-
- R_SUCCEED();
-}
-
-Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
- // Ensure the layer id is valid.
- R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound);
-
- // Get the layer.
- VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
- R_UNLESS(layer != nullptr, VI::ResultNotFound);
-
- // We succeeded.
- *out_layer = layer;
- R_SUCCEED();
-}
-
-Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
- std::array<s32, 4>& out_slot_indexes,
- s64* out_target_slot, u64 layer_id) {
- std::scoped_lock lk{m_guard};
-
- // Get the layer.
- VI::Layer* layer;
- R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
-
- // Get the producer.
- auto& producer = layer->GetBufferQueue();
-
- // Get the next buffer from the producer.
- s32 slot;
- R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
- SharedBufferWidth, SharedBufferHeight,
- SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
- VI::ResultOperationFailed);
-
- // Assign remaining outputs.
- *out_target_slot = slot;
- out_slot_indexes = {0, 1, -1, -1};
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
- Common::Rectangle<s32> crop_region,
- u32 transform, s32 swap_interval,
- u64 layer_id, s64 slot) {
- std::scoped_lock lk{m_guard};
-
- // Get the layer.
- VI::Layer* layer;
- R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
-
- // Get the producer.
- auto& producer = layer->GetBufferQueue();
-
- // Request to queue the buffer.
- std::shared_ptr<android::GraphicBuffer> buffer;
- R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
- android::Status::NoError,
- VI::ResultOperationFailed);
-
- // Queue the buffer to the producer.
- android::QueueBufferInput input{};
- android::QueueBufferOutput output{};
- input.crop = crop_region;
- input.fence = fence;
- input.transform = static_cast<android::NativeWindowTransform>(transform);
- input.swap_interval = swap_interval;
- R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
- android::Status::NoError,
- VI::ResultOperationFailed);
-
- // We succeeded.
- R_SUCCEED();
-}
-
-Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
- u64 layer_id) {
- std::scoped_lock lk{m_guard};
-
- // Get the layer.
- VI::Layer* layer;
- R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
-
- // Get the producer.
- auto& producer = layer->GetBufferQueue();
-
- // Set the event.
- *out_event = std::addressof(producer.GetNativeHandle());
-
- // We succeeded.
- R_SUCCEED();
-}
-
-} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
deleted file mode 100644
index 033bf4bbe..000000000
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "common/math_util.h"
-#include "core/hle/service/nvdrv/core/container.h"
-#include "core/hle/service/nvdrv/nvdata.h"
-#include "core/hle/service/nvnflinger/nvnflinger.h"
-#include "core/hle/service/nvnflinger/ui/fence.h"
-
-namespace Kernel {
-class KPageGroup;
-}
-
-namespace Service::Nvnflinger {
-
-struct SharedMemorySlot {
- u64 buffer_offset;
- u64 size;
- s32 width;
- s32 height;
-};
-static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size");
-
-struct SharedMemoryPoolLayout {
- s32 num_slots;
- std::array<SharedMemorySlot, 0x10> slots;
-};
-static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
-
-class FbShareBufferManager final {
-public:
- explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
- std::shared_ptr<Nvidia::Module> nvdrv);
- ~FbShareBufferManager();
-
- Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id);
- Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
- SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
- u64 applet_resource_user_id);
- Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots,
- s64* out_target_slot, u64 layer_id);
- Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
- u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
- Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
-
-private:
- Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
-
-private:
- u64 m_next_buffer_id = 1;
- u64 m_display_id = 0;
- u64 m_buffer_id = 0;
- u64 m_layer_id = 0;
- u32 m_buffer_nvmap_handle = 0;
- SharedMemoryPoolLayout m_pool_layout = {};
- Nvidia::DeviceFD m_nvmap_fd = {};
- Nvidia::NvCore::SessionId m_session_id = {};
- std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
-
- std::mutex m_guard;
- Core::System& m_system;
- Nvnflinger& m_flinger;
- std::shared_ptr<Nvidia::Module> m_nvdrv;
-};
-
-} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp
index ba2b5c28c..f2dfe85a9 100644
--- a/src/core/hle/service/nvnflinger/hardware_composer.cpp
+++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp
@@ -10,8 +10,6 @@
#include "core/hle/service/nvnflinger/hardware_composer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
-#include "core/hle/service/vi/display/vi_display.h"
-#include "core/hle/service/vi/layer/vi_layer.h"
namespace Service::Nvnflinger {
@@ -44,7 +42,7 @@ s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) {
HardwareComposer::HardwareComposer() = default;
HardwareComposer::~HardwareComposer() = default;
-u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
+u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp) {
boost::container::small_vector<HwcLayer, 2> composition_stack;
@@ -56,12 +54,11 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
bool has_acquired_buffer{};
// Acquire all necessary framebuffers.
- for (size_t i = 0; i < display.GetNumLayers(); i++) {
- auto& layer = display.GetLayer(i);
- auto layer_id = layer.GetLayerId();
+ for (auto& layer : display.stack.layers) {
+ auto consumer_id = layer->consumer_id;
// Try to fetch the framebuffer (either new or stale).
- const auto result = this->CacheFramebufferLocked(layer, layer_id);
+ const auto result = this->CacheFramebufferLocked(*layer, consumer_id);
// If we failed, skip this layer.
if (result == CacheStatus::NoBufferAvailable) {
@@ -73,23 +70,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
has_acquired_buffer = true;
}
- const auto& buffer = m_framebuffers[layer_id];
+ const auto& buffer = m_framebuffers[consumer_id];
const auto& item = buffer.item;
const auto& igbp_buffer = *item.graphic_buffer;
// TODO: get proper Z-index from layer
- composition_stack.emplace_back(HwcLayer{
- .buffer_handle = igbp_buffer.BufferId(),
- .offset = igbp_buffer.Offset(),
- .format = igbp_buffer.ExternalFormat(),
- .width = igbp_buffer.Width(),
- .height = igbp_buffer.Height(),
- .stride = igbp_buffer.Stride(),
- .z_index = 0,
- .transform = static_cast<android::BufferTransformFlags>(item.transform),
- .crop_rect = item.crop,
- .acquire_fence = item.fence,
- });
+ if (layer->visible) {
+ composition_stack.emplace_back(HwcLayer{
+ .buffer_handle = igbp_buffer.BufferId(),
+ .offset = igbp_buffer.Offset(),
+ .format = igbp_buffer.ExternalFormat(),
+ .width = igbp_buffer.Width(),
+ .height = igbp_buffer.Height(),
+ .stride = igbp_buffer.Stride(),
+ .z_index = 0,
+ .blending = layer->blending,
+ .transform = static_cast<android::BufferTransformFlags>(item.transform),
+ .crop_rect = item.crop,
+ .acquire_fence = item.fence,
+ });
+ }
// We need to compose again either before this frame is supposed to
// be released, or exactly on the vsync period it should be released.
@@ -134,10 +134,10 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
continue;
}
- if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
+ if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
- layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
+ layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
@@ -145,26 +145,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
return frame_advance;
}
-void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {
- // Check if we are tracking a slot with this layer_id.
- const auto it = m_framebuffers.find(layer_id);
+void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) {
+ // Check if we are tracking a slot with this consumer_id.
+ const auto it = m_framebuffers.find(consumer_id);
if (it == m_framebuffers.end()) {
return;
}
// Try to release the buffer item.
- auto* const layer = display.FindLayer(layer_id);
+ const auto layer = display.stack.FindLayer(consumer_id);
if (layer && it->second.is_acquired) {
- layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence());
+ layer->buffer_item_consumer->ReleaseBuffer(it->second.item, android::Fence::NoFence());
}
// Erase the slot.
m_framebuffers.erase(it);
}
-bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) {
+bool HardwareComposer::TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer) {
// Attempt the update.
- const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false);
+ const auto status = layer.buffer_item_consumer->AcquireBuffer(&framebuffer.item, {}, false);
if (status != android::Status::NoError) {
return false;
}
@@ -177,10 +177,10 @@ bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer
return true;
}
-HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer,
- LayerId layer_id) {
+HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(Layer& layer,
+ ConsumerId consumer_id) {
// Check if this framebuffer is already present.
- const auto it = m_framebuffers.find(layer_id);
+ const auto it = m_framebuffers.find(consumer_id);
if (it != m_framebuffers.end()) {
// If it's currently still acquired, we are done.
if (it->second.is_acquired) {
@@ -202,7 +202,7 @@ HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer
if (this->TryAcquireFramebufferLocked(layer, framebuffer)) {
// Move the buffer item into a new slot.
- m_framebuffers.emplace(layer_id, std::move(framebuffer));
+ m_framebuffers.emplace(consumer_id, std::move(framebuffer));
// We succeeded.
return CacheStatus::BufferAcquired;
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h
index 28392c512..c5b830468 100644
--- a/src/core/hle/service/nvnflinger/hardware_composer.h
+++ b/src/core/hle/service/nvnflinger/hardware_composer.h
@@ -3,35 +3,29 @@
#pragma once
-#include <memory>
#include <boost/container/flat_map.hpp>
#include "core/hle/service/nvnflinger/buffer_item.h"
+#include "core/hle/service/nvnflinger/display.h"
namespace Service::Nvidia::Devices {
class nvdisp_disp0;
}
-namespace Service::VI {
-class Display;
-class Layer;
-} // namespace Service::VI
-
namespace Service::Nvnflinger {
-using LayerId = u64;
+using ConsumerId = s32;
class HardwareComposer {
public:
explicit HardwareComposer();
~HardwareComposer();
- u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
+ u32 ComposeLocked(f32* out_speed_scale, Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp);
- void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
+ void RemoveLayerLocked(Display& display, ConsumerId consumer_id);
private:
- // TODO: do we want to track frame number in vi instead?
u64 m_frame_number{0};
private:
@@ -49,11 +43,11 @@ private:
CachedBufferReused,
};
- boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{};
+ boost::container::flat_map<ConsumerId, Framebuffer> m_framebuffers{};
private:
- bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer);
- CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id);
+ bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer);
+ CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id);
};
} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp
new file mode 100644
index 000000000..8629a2e89
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/nvnflinger/binder.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
+
+namespace Service::Nvnflinger {
+
+IHOSBinderDriver::IHOSBinderDriver(Core::System& system_,
+ std::shared_ptr<HosBinderDriverServer> server,
+ std::shared_ptr<SurfaceFlinger> surface_flinger)
+ : ServiceFramework{system_, "IHOSBinderDriver"}, m_server(server),
+ m_surface_flinger(surface_flinger) {
+ static const FunctionInfo functions[] = {
+ {0, C<&IHOSBinderDriver::TransactParcel>, "TransactParcel"},
+ {1, C<&IHOSBinderDriver::AdjustRefcount>, "AdjustRefcount"},
+ {2, C<&IHOSBinderDriver::GetNativeHandle>, "GetNativeHandle"},
+ {3, C<&IHOSBinderDriver::TransactParcelAuto>, "TransactParcelAuto"},
+ };
+ RegisterHandlers(functions);
+}
+
+IHOSBinderDriver::~IHOSBinderDriver() = default;
+
+Result IHOSBinderDriver::TransactParcel(s32 binder_id, u32 transaction_id,
+ InBuffer<BufferAttr_HipcMapAlias> parcel_data,
+ OutBuffer<BufferAttr_HipcMapAlias> parcel_reply,
+ u32 flags) {
+ LOG_DEBUG(Service_VI, "called. id={} transaction={}, flags={}", binder_id, transaction_id,
+ flags);
+
+ const auto binder = m_server->TryGetBinder(binder_id);
+ R_SUCCEED_IF(binder == nullptr);
+
+ binder->Transact(transaction_id, parcel_data, parcel_reply, flags);
+
+ R_SUCCEED();
+}
+
+Result IHOSBinderDriver::AdjustRefcount(s32 binder_id, s32 addval, s32 type) {
+ LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={}, type={}", binder_id, addval, type);
+ R_SUCCEED();
+}
+
+Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id,
+ OutCopyHandle<Kernel::KReadableEvent> out_handle) {
+ LOG_WARNING(Service_VI, "(STUBBED) called id={}, type_id={}", binder_id, type_id);
+
+ const auto binder = m_server->TryGetBinder(binder_id);
+ R_UNLESS(binder != nullptr, ResultUnknown);
+
+ *out_handle = binder->GetNativeHandle(type_id);
+
+ R_SUCCEED();
+}
+
+Result IHOSBinderDriver::TransactParcelAuto(s32 binder_id, u32 transaction_id,
+ InBuffer<BufferAttr_HipcAutoSelect> parcel_data,
+ OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply,
+ u32 flags) {
+ R_RETURN(this->TransactParcel(binder_id, transaction_id, parcel_data, parcel_reply, flags));
+}
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver.h b/src/core/hle/service/nvnflinger/hos_binder_driver.h
new file mode 100644
index 000000000..b7fb07bd2
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver.h
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::Nvnflinger {
+
+class HosBinderDriverServer;
+class SurfaceFlinger;
+
+class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
+public:
+ explicit IHOSBinderDriver(Core::System& system_, std::shared_ptr<HosBinderDriverServer> server,
+ std::shared_ptr<SurfaceFlinger> surface_flinger);
+ ~IHOSBinderDriver() override;
+
+ std::shared_ptr<SurfaceFlinger> GetSurfaceFlinger() {
+ return m_surface_flinger;
+ }
+
+ std::shared_ptr<HosBinderDriverServer> GetServer() {
+ return m_server;
+ }
+
+private:
+ Result TransactParcel(s32 binder_id, u32 transaction_id,
+ InBuffer<BufferAttr_HipcMapAlias> parcel_data,
+ OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, u32 flags);
+ Result AdjustRefcount(s32 binder_id, s32 addval, s32 type);
+ Result GetNativeHandle(s32 binder_id, u32 type_id,
+ OutCopyHandle<Kernel::KReadableEvent> out_handle);
+ Result TransactParcelAuto(s32 binder_id, u32 transaction_id,
+ InBuffer<BufferAttr_HipcAutoSelect> parcel_data,
+ OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, u32 flags);
+
+private:
+ const std::shared_ptr<HosBinderDriverServer> m_server;
+ const std::shared_ptr<SurfaceFlinger> m_surface_flinger;
+};
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp
index b86a79ec9..29addda44 100644
--- a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp
@@ -8,26 +8,30 @@
namespace Service::Nvnflinger {
-HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
- : service_context(system_, "HosBinderDriverServer") {}
+HosBinderDriverServer::HosBinderDriverServer() = default;
+HosBinderDriverServer::~HosBinderDriverServer() = default;
-HosBinderDriverServer::~HosBinderDriverServer() {}
-
-u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
+s32 HosBinderDriverServer::RegisterBinder(std::shared_ptr<android::IBinder>&& binder) {
std::scoped_lock lk{lock};
last_id++;
- producers[last_id] = std::move(binder);
+ binders[last_id] = std::move(binder);
return last_id;
}
-android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
+void HosBinderDriverServer::UnregisterBinder(s32 binder_id) {
+ std::scoped_lock lk{lock};
+
+ binders.erase(binder_id);
+}
+
+std::shared_ptr<android::IBinder> HosBinderDriverServer::TryGetBinder(s32 id) const {
std::scoped_lock lk{lock};
- if (auto search = producers.find(id); search != producers.end()) {
- return search->second.get();
+ if (auto search = binders.find(id); search != binders.end()) {
+ return search->second;
}
return {};
diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h
index 58bb9469a..d72b50833 100644
--- a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h
@@ -8,7 +8,6 @@
#include <unordered_map>
#include "common/common_types.h"
-#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/binder.h"
namespace Core {
@@ -19,19 +18,18 @@ namespace Service::Nvnflinger {
class HosBinderDriverServer final {
public:
- explicit HosBinderDriverServer(Core::System& system_);
+ explicit HosBinderDriverServer();
~HosBinderDriverServer();
- u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
+ s32 RegisterBinder(std::shared_ptr<android::IBinder>&& binder);
+ void UnregisterBinder(s32 binder_id);
- android::IBinder* TryGetProducer(u64 id);
+ std::shared_ptr<android::IBinder> TryGetBinder(s32 id) const;
private:
- KernelHelpers::ServiceContext service_context;
-
- std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
- std::mutex lock;
- u64 last_id{};
+ std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders;
+ mutable std::mutex lock;
+ s32 last_id{};
};
} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h
index 3af668a25..f71a5d822 100644
--- a/src/core/hle/service/nvnflinger/hwc_layer.h
+++ b/src/core/hle/service/nvnflinger/hwc_layer.h
@@ -11,6 +11,18 @@
namespace Service::Nvnflinger {
+// hwc_layer_t::blending values
+enum class LayerBlending : u32 {
+ // No blending
+ None = 0x100,
+
+ // ONE / ONE_MINUS_SRC_ALPHA
+ Premultiplied = 0x105,
+
+ // SRC_ALPHA / ONE_MINUS_SRC_ALPHA
+ Coverage = 0x405,
+};
+
struct HwcLayer {
u32 buffer_handle;
u32 offset;
@@ -19,6 +31,7 @@ struct HwcLayer {
u32 height;
u32 stride;
s32 z_index;
+ LayerBlending blending;
android::BufferTransformFlags transform;
Common::Rectangle<int> crop_rect;
android::Fence acquire_fence;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index d8ba89d43..9e3b68b8a 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -1,334 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-#include <algorithm>
-#include <optional>
-
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "common/microprofile.h"
-#include "common/scope_exit.h"
-#include "common/settings.h"
-#include "common/thread.h"
#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
-#include "core/hle/service/nvdrv/nvdrv.h"
-#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
-#include "core/hle/service/nvnflinger/buffer_queue_core.h"
-#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
-#include "core/hle/service/nvnflinger/hardware_composer.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
-#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
-#include "core/hle/service/vi/display/vi_display.h"
-#include "core/hle/service/vi/layer/vi_layer.h"
-#include "core/hle/service/vi/vi_results.h"
-#include "video_core/gpu.h"
-#include "video_core/host1x/host1x.h"
-#include "video_core/host1x/syncpoint_manager.h"
+#include "core/hle/service/nvnflinger/surface_flinger.h"
+#include "core/hle/service/server_manager.h"
+#include "core/hle/service/sm/sm.h"
namespace Service::Nvnflinger {
-constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
-
-void Nvnflinger::SplitVSync(std::stop_token stop_token) {
- system.RegisterHostThread();
- std::string name = "VSyncThread";
- MicroProfileOnThreadCreate(name.c_str());
-
- // Cleanup
- SCOPE_EXIT({ MicroProfileOnThreadExit(); });
-
- Common::SetCurrentThreadName(name.c_str());
- Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
-
- while (!stop_token.stop_requested()) {
- vsync_signal.Wait();
-
- const auto lock_guard = Lock();
-
- if (!is_abandoned) {
- Compose();
- }
- }
-}
-
-Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
- : system(system_), service_context(system_, "nvnflinger"),
- hos_binder_driver_server(hos_binder_driver_server_) {
- displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
- displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
- displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
- displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
- displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
- guard = std::make_shared<std::mutex>();
-
- // Schedule the screen composition events
- multi_composition_event = Core::Timing::CreateEvent(
- "ScreenComposition",
- [this](s64 time,
- std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- vsync_signal.Set();
- return std::chrono::nanoseconds(GetNextTicks());
- });
-
- single_composition_event = Core::Timing::CreateEvent(
- "ScreenComposition",
- [this](s64 time,
- std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- const auto lock_guard = Lock();
- Compose();
-
- return std::chrono::nanoseconds(GetNextTicks());
- });
-
- if (system.IsMulticore()) {
- system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event);
- vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
- } else {
- system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event);
- }
-}
-
-Nvnflinger::~Nvnflinger() {
- if (system.IsMulticore()) {
- system.CoreTiming().UnscheduleEvent(multi_composition_event);
- vsync_thread.request_stop();
- vsync_signal.Set();
- } else {
- system.CoreTiming().UnscheduleEvent(single_composition_event);
- }
-
- ShutdownLayers();
-
- if (nvdrv) {
- nvdrv->Close(disp_fd);
- }
-}
-
-void Nvnflinger::ShutdownLayers() {
- // Abandon consumers.
- {
- const auto lock_guard = Lock();
- for (auto& display : displays) {
- display.Abandon();
- }
-
- is_abandoned = true;
- }
-
- // Join the vsync thread, if it exists.
- vsync_thread = {};
-}
-
-void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
- nvdrv = std::move(instance);
- disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
-}
-
-std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) {
- const auto lock_guard = Lock();
-
- LOG_DEBUG(Service_Nvnflinger, "Opening \"{}\" display", name);
-
- const auto itr =
- std::find_if(displays.begin(), displays.end(),
- [&](const VI::Display& display) { return display.GetName() == name; });
-
- if (itr == displays.end()) {
- return std::nullopt;
- }
-
- return itr->GetID();
-}
-
-bool Nvnflinger::CloseDisplay(u64 display_id) {
- const auto lock_guard = Lock();
- auto* const display = FindDisplay(display_id);
-
- if (display == nullptr) {
- return false;
- }
-
- display->Reset();
-
- return true;
-}
-
-std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
- const auto lock_guard = Lock();
- auto* const display = FindDisplay(display_id);
-
- if (display == nullptr) {
- return std::nullopt;
- }
-
- const u64 layer_id = next_layer_id++;
- CreateLayerAtId(*display, layer_id);
- return layer_id;
-}
-
-void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
- const auto buffer_id = next_buffer_queue_id++;
- display.CreateLayer(layer_id, buffer_id, nvdrv->container);
-}
-
-bool Nvnflinger::OpenLayer(u64 layer_id) {
- const auto lock_guard = Lock();
-
- for (auto& display : displays) {
- if (auto* layer = display.FindLayer(layer_id); layer) {
- return layer->Open();
- }
- }
-
- return false;
-}
-
-bool Nvnflinger::CloseLayer(u64 layer_id) {
- const auto lock_guard = Lock();
-
- for (auto& display : displays) {
- if (auto* layer = display.FindLayer(layer_id); layer) {
- return layer->Close();
- }
- }
-
- return false;
-}
-
-void Nvnflinger::SetLayerVisibility(u64 layer_id, bool visible) {
- const auto lock_guard = Lock();
-
- for (auto& display : displays) {
- if (auto* layer = display.FindLayer(layer_id); layer) {
- layer->SetVisibility(visible);
- }
- }
-}
-
-void Nvnflinger::DestroyLayer(u64 layer_id) {
- const auto lock_guard = Lock();
-
- for (auto& display : displays) {
- display.DestroyLayer(layer_id);
- }
-}
-
-std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
- const auto lock_guard = Lock();
- const auto* const layer = FindLayer(display_id, layer_id);
-
- if (layer == nullptr) {
- return std::nullopt;
- }
-
- return layer->GetBinderId();
-}
-
-Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id) {
- const auto lock_guard = Lock();
- auto* const display = FindDisplay(display_id);
-
- if (display == nullptr) {
- return VI::ResultNotFound;
- }
-
- *out_vsync_event = display->GetVSyncEvent();
- return ResultSuccess;
-}
-
-VI::Display* Nvnflinger::FindDisplay(u64 display_id) {
- const auto itr =
- std::find_if(displays.begin(), displays.end(),
- [&](const VI::Display& display) { return display.GetID() == display_id; });
-
- if (itr == displays.end()) {
- return nullptr;
- }
-
- return &*itr;
-}
-
-const VI::Display* Nvnflinger::FindDisplay(u64 display_id) const {
- const auto itr =
- std::find_if(displays.begin(), displays.end(),
- [&](const VI::Display& display) { return display.GetID() == display_id; });
-
- if (itr == displays.end()) {
- return nullptr;
- }
-
- return &*itr;
-}
-
-VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) {
- auto* const display = FindDisplay(display_id);
-
- if (display == nullptr) {
- return nullptr;
- }
-
- return display->FindLayer(layer_id);
-}
-
-void Nvnflinger::Compose() {
- for (auto& display : displays) {
- // Trigger vsync for this display at the end of drawing
- SCOPE_EXIT({ display.SignalVSyncEvent(); });
-
- // Don't do anything for displays without layers.
- if (!display.HasLayers()) {
- continue;
- }
-
- if (!system.IsPoweredOn()) {
- return; // We are likely shutting down
- }
-
- auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
- ASSERT(nvdisp);
-
- swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
- }
-}
-
-s64 Nvnflinger::GetNextTicks() const {
- const auto& settings = Settings::values;
- auto speed_scale = 1.f;
- if (settings.use_multi_core.GetValue()) {
- if (settings.use_speed_limit.GetValue()) {
- // Scales the speed based on speed_limit setting on MC. SC is handled by
- // SpeedLimiter::DoSpeedLimiting.
- speed_scale = 100.f / settings.speed_limit.GetValue();
- } else {
- // Run at unlocked framerate.
- speed_scale = 0.01f;
- }
- }
-
- // Adjust by speed limit determined during composition.
- speed_scale /= compose_speed_scale;
-
- if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
- // Run at intended presentation rate during video playback.
- speed_scale = 1.f;
- }
-
- const f32 effective_fps = 60.f / static_cast<f32>(swap_interval);
- return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
-}
-
-FbShareBufferManager& Nvnflinger::GetSystemBufferManager() {
- const auto lock_guard = Lock();
-
- if (!system_buffer_manager) {
- system_buffer_manager = std::make_unique<FbShareBufferManager>(system, *this, nvdrv);
- }
+void LoopProcess(Core::System& system) {
+ const auto binder_server = std::make_shared<HosBinderDriverServer>();
+ const auto surface_flinger = std::make_shared<SurfaceFlinger>(system, *binder_server);
- return *system_buffer_manager;
+ auto server_manager = std::make_unique<ServerManager>(system);
+ server_manager->RegisterNamedService(
+ "dispdrv", std::make_shared<IHOSBinderDriver>(system, binder_server, surface_flinger));
+ ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index c984d55a0..5c41f3013 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -3,168 +3,12 @@
#pragma once
-#include <list>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-#include <vector>
-
-#include "common/common_types.h"
-#include "common/polyfill_thread.h"
-#include "common/thread.h"
-#include "core/hle/result.h"
-#include "core/hle/service/kernel_helpers.h"
-
-namespace Common {
-class Event;
-} // namespace Common
-
-namespace Core::Timing {
-class CoreTiming;
-struct EventType;
-} // namespace Core::Timing
-
-namespace Kernel {
-class KReadableEvent;
-} // namespace Kernel
-
-namespace Service::Nvidia {
-class Module;
-} // namespace Service::Nvidia
-
-namespace Service::VI {
-class Display;
-class Layer;
-} // namespace Service::VI
-
-namespace Service::android {
-class BufferQueueCore;
-class BufferQueueProducer;
-} // namespace Service::android
+namespace Core {
+class System;
+}
namespace Service::Nvnflinger {
-class FbShareBufferManager;
-class HardwareComposer;
-class HosBinderDriverServer;
-
-class Nvnflinger final {
-public:
- explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
- ~Nvnflinger();
-
- void ShutdownLayers();
-
- /// Sets the NVDrv module instance to use to send buffers to the GPU.
- void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
-
- /// Opens the specified display and returns the ID.
- ///
- /// If an invalid display name is provided, then an empty optional is returned.
- [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
-
- /// Closes the specified display by its ID.
- ///
- /// Returns false if an invalid display ID is provided.
- [[nodiscard]] bool CloseDisplay(u64 display_id);
-
- /// Creates a layer on the specified display and returns the layer ID.
- ///
- /// If an invalid display ID is specified, then an empty optional is returned.
- [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
-
- /// Opens a layer on all displays for the given layer ID.
- bool OpenLayer(u64 layer_id);
-
- /// Closes a layer on all displays for the given layer ID.
- bool CloseLayer(u64 layer_id);
-
- /// Makes a layer visible on all displays for the given layer ID.
- void SetLayerVisibility(u64 layer_id, bool visible);
-
- /// Destroys the given layer ID.
- void DestroyLayer(u64 layer_id);
-
- /// Finds the buffer queue ID of the specified layer in the specified display.
- ///
- /// If an invalid display ID or layer ID is provided, then an empty optional is returned.
- [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id);
-
- /// Gets the vsync event for the specified display.
- ///
- /// If an invalid display ID is provided, then VI::ResultNotFound is returned.
- /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
- [[nodiscard]] Result FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id);
-
- /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
- /// finished.
- void Compose();
-
- [[nodiscard]] s64 GetNextTicks() const;
-
- FbShareBufferManager& GetSystemBufferManager();
-
-private:
- struct Layer {
- std::unique_ptr<android::BufferQueueCore> core;
- std::unique_ptr<android::BufferQueueProducer> producer;
- };
-
- friend class FbShareBufferManager;
-
-private:
- [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
- return std::unique_lock{*guard};
- }
-
- /// Finds the display identified by the specified ID.
- [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
-
- /// Finds the display identified by the specified ID.
- [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
-
- /// Finds the layer identified by the specified ID in the desired display.
- [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
-
- /// Creates a layer with the specified layer ID in the desired display.
- void CreateLayerAtId(VI::Display& display, u64 layer_id);
-
- void SplitVSync(std::stop_token stop_token);
-
- std::shared_ptr<Nvidia::Module> nvdrv;
- s32 disp_fd;
-
- std::list<VI::Display> displays;
-
- /// Id to use for the next layer that is created, this counter is shared among all displays.
- u64 next_layer_id = 1;
- /// Id to use for the next buffer queue that is created, this counter is shared among all
- /// layers.
- u32 next_buffer_queue_id = 1;
-
- s32 swap_interval = 1;
- f32 compose_speed_scale = 1.0f;
-
- bool is_abandoned = false;
-
- /// Event that handles screen composition.
- std::shared_ptr<Core::Timing::EventType> multi_composition_event;
- std::shared_ptr<Core::Timing::EventType> single_composition_event;
-
- std::unique_ptr<FbShareBufferManager> system_buffer_manager;
-
- std::shared_ptr<std::mutex> guard;
-
- Core::System& system;
-
- Common::Event vsync_signal;
-
- std::jthread vsync_thread;
-
- KernelHelpers::ServiceContext service_context;
-
- HosBinderDriverServer& hos_binder_driver_server;
-};
+void LoopProcess(Core::System& system);
} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/surface_flinger.cpp b/src/core/hle/service/nvnflinger/surface_flinger.cpp
new file mode 100644
index 000000000..8362b65e5
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/surface_flinger.cpp
@@ -0,0 +1,139 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
+#include "core/hle/service/nvdrv/nvdrv_interface.h"
+#include "core/hle/service/nvnflinger/display.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
+#include "core/hle/service/nvnflinger/surface_flinger.h"
+#include "core/hle/service/sm/sm.h"
+
+#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvnflinger/buffer_queue_core.h"
+#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
+
+namespace Service::Nvnflinger {
+
+SurfaceFlinger::SurfaceFlinger(Core::System& system, HosBinderDriverServer& server)
+ : m_system(system), m_server(server), m_context(m_system, "SurfaceFlinger") {
+ nvdrv = m_system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
+ disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
+}
+
+SurfaceFlinger::~SurfaceFlinger() {
+ nvdrv->Close(disp_fd);
+}
+
+void SurfaceFlinger::AddDisplay(u64 display_id) {
+ m_displays.emplace_back(display_id);
+}
+
+void SurfaceFlinger::RemoveDisplay(u64 display_id) {
+ std::erase_if(m_displays, [&](auto& display) { return display.id == display_id; });
+}
+
+bool SurfaceFlinger::ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
+ u64 display_id) {
+ auto* const display = this->FindDisplay(display_id);
+ if (!display || !display->stack.HasLayers()) {
+ return false;
+ }
+
+ *out_swap_interval =
+ m_composer.ComposeLocked(out_compose_speed_scale, *display,
+ *nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd));
+ return true;
+}
+
+void SurfaceFlinger::CreateLayer(s32 consumer_binder_id) {
+ auto binder = std::static_pointer_cast<android::BufferQueueConsumer>(
+ m_server.TryGetBinder(consumer_binder_id));
+ if (!binder) {
+ return;
+ }
+
+ auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(binder));
+ buffer_item_consumer->Connect(false);
+
+ m_layers.layers.emplace_back(
+ std::make_shared<Layer>(std::move(buffer_item_consumer), consumer_binder_id));
+}
+
+void SurfaceFlinger::DestroyLayer(s32 consumer_binder_id) {
+ std::erase_if(m_layers.layers,
+ [&](auto& layer) { return layer->consumer_id == consumer_binder_id; });
+}
+
+void SurfaceFlinger::AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id) {
+ auto* const display = this->FindDisplay(display_id);
+ auto layer = this->FindLayer(consumer_binder_id);
+
+ if (!display || !layer) {
+ return;
+ }
+
+ display->stack.layers.emplace_back(std::move(layer));
+}
+
+void SurfaceFlinger::RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id) {
+ auto* const display = this->FindDisplay(display_id);
+ if (!display) {
+ return;
+ }
+
+ m_composer.RemoveLayerLocked(*display, consumer_binder_id);
+ std::erase_if(display->stack.layers,
+ [&](auto& layer) { return layer->consumer_id == consumer_binder_id; });
+}
+
+void SurfaceFlinger::SetLayerVisibility(s32 consumer_binder_id, bool visible) {
+ if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
+ layer->visible = visible;
+ return;
+ }
+}
+
+void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blending) {
+ if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
+ layer->blending = blending;
+ return;
+ }
+}
+
+Display* SurfaceFlinger::FindDisplay(u64 display_id) {
+ for (auto& display : m_displays) {
+ if (display.id == display_id) {
+ return &display;
+ }
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<Layer> SurfaceFlinger::FindLayer(s32 consumer_binder_id) {
+ for (auto& layer : m_layers.layers) {
+ if (layer->consumer_id == consumer_binder_id) {
+ return layer;
+ }
+ }
+
+ return nullptr;
+}
+
+void SurfaceFlinger::CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id) {
+ auto& nvmap = nvdrv->GetContainer().GetNvMapFile();
+ auto core = std::make_shared<android::BufferQueueCore>();
+ auto producer = std::make_shared<android::BufferQueueProducer>(m_context, core, nvmap);
+ auto consumer = std::make_shared<android::BufferQueueConsumer>(core);
+
+ *out_consumer_binder_id = m_server.RegisterBinder(std::move(consumer));
+ *out_producer_binder_id = m_server.RegisterBinder(std::move(producer));
+}
+
+void SurfaceFlinger::DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id) {
+ m_server.UnregisterBinder(producer_binder_id);
+ m_server.UnregisterBinder(consumer_binder_id);
+}
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h
new file mode 100644
index 000000000..406281c83
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/surface_flinger.h
@@ -0,0 +1,69 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <vector>
+
+#include "common/common_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvnflinger/hardware_composer.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Nvidia {
+class Module;
+}
+
+// TODO: ISurfaceComposer
+// TODO: ISurfaceComposerClient
+
+namespace Service::Nvnflinger {
+
+struct Display;
+class HosBinderDriverServer;
+enum class LayerBlending : u32;
+struct Layer;
+
+class SurfaceFlinger {
+public:
+ explicit SurfaceFlinger(Core::System& system, HosBinderDriverServer& server);
+ ~SurfaceFlinger();
+
+ void AddDisplay(u64 display_id);
+ void RemoveDisplay(u64 display_id);
+ bool ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
+
+ void CreateLayer(s32 consumer_binder_id);
+ void DestroyLayer(s32 consumer_binder_id);
+
+ void AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id);
+ void RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id);
+
+ void SetLayerVisibility(s32 consumer_binder_id, bool visible);
+ void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
+
+private:
+ Display* FindDisplay(u64 display_id);
+ std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
+
+public:
+ // TODO: these don't belong here
+ void CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id);
+ void DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id);
+
+private:
+ Core::System& m_system;
+ HosBinderDriverServer& m_server;
+ KernelHelpers::ServiceContext m_context;
+
+ std::vector<Display> m_displays;
+ LayerStack m_layers;
+ std::shared_ptr<Nvidia::Module> nvdrv;
+ s32 disp_fd;
+ HardwareComposer m_composer;
+};
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/omm/omm.cpp b/src/core/hle/service/omm/omm.cpp
new file mode 100644
index 000000000..b95319e26
--- /dev/null
+++ b/src/core/hle/service/omm/omm.cpp
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/omm/omm.h"
+#include "core/hle/service/omm/operation_mode_manager.h"
+#include "core/hle/service/omm/policy_manager_system.h"
+#include "core/hle/service/omm/power_state_interface.h"
+#include "core/hle/service/server_manager.h"
+
+namespace Service::OMM {
+
+void LoopProcess(Core::System& system) {
+ auto server_manager = std::make_unique<ServerManager>(system);
+
+ server_manager->RegisterNamedService("idle:sys",
+ std::make_shared<IPolicyManagerSystem>(system));
+ server_manager->RegisterNamedService("omm", std::make_shared<IOperationModeManager>(system));
+ server_manager->RegisterNamedService("spsm", std::make_shared<IPowerStateInterface>(system));
+ ServerManager::RunServer(std::move(server_manager));
+}
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/omm.h b/src/core/hle/service/omm/omm.h
new file mode 100644
index 000000000..7bf04688a
--- /dev/null
+++ b/src/core/hle/service/omm/omm.h
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::OMM {
+
+void LoopProcess(Core::System& system);
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/operation_mode_manager.cpp b/src/core/hle/service/omm/operation_mode_manager.cpp
new file mode 100644
index 000000000..fe7ed84a7
--- /dev/null
+++ b/src/core/hle/service/omm/operation_mode_manager.cpp
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/omm/operation_mode_manager.h"
+
+namespace Service::OMM {
+
+IOperationModeManager::IOperationModeManager(Core::System& system_)
+ : ServiceFramework{system_, "omm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetOperationMode"},
+ {1, nullptr, "GetOperationModeChangeEvent"},
+ {2, nullptr, "EnableAudioVisual"},
+ {3, nullptr, "DisableAudioVisual"},
+ {4, nullptr, "EnterSleepAndWait"},
+ {5, nullptr, "GetCradleStatus"},
+ {6, nullptr, "FadeInDisplay"},
+ {7, nullptr, "FadeOutDisplay"},
+ {8, nullptr, "GetCradleFwVersion"},
+ {9, nullptr, "NotifyCecSettingsChanged"},
+ {10, nullptr, "SetOperationModePolicy"},
+ {11, nullptr, "GetDefaultDisplayResolution"},
+ {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
+ {13, nullptr, "UpdateDefaultDisplayResolution"},
+ {14, nullptr, "ShouldSleepOnBoot"},
+ {15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
+ {16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
+ {17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
+ {18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
+ {19, nullptr, "GetHdcpAuthenticationFailedEvent"},
+ {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
+ {21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
+ {22, nullptr, "GetHdcpStateChangeEvent"},
+ {23, nullptr, "GetHdcpState"},
+ {24, nullptr, "ShowCardUpdateProcessing"},
+ {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
+ {26, nullptr, "GetOperationModeSystemInfo"},
+ {27, nullptr, "GetAppletFullAwakingSystemEvent"},
+ {28, nullptr, "CreateCradleFirmwareUpdater"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IOperationModeManager::~IOperationModeManager() = default;
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/operation_mode_manager.h b/src/core/hle/service/omm/operation_mode_manager.h
new file mode 100644
index 000000000..32bc7b2f9
--- /dev/null
+++ b/src/core/hle/service/omm/operation_mode_manager.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::OMM {
+
+class IOperationModeManager final : public ServiceFramework<IOperationModeManager> {
+public:
+ explicit IOperationModeManager(Core::System& system_);
+ ~IOperationModeManager() override;
+};
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/policy_manager_system.cpp b/src/core/hle/service/omm/policy_manager_system.cpp
new file mode 100644
index 000000000..1cd6fd807
--- /dev/null
+++ b/src/core/hle/service/omm/policy_manager_system.cpp
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/omm/policy_manager_system.h"
+
+namespace Service::OMM {
+
+IPolicyManagerSystem::IPolicyManagerSystem(Core::System& system_)
+ : ServiceFramework{system_, "idle:sys"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetAutoPowerDownEvent"},
+ {1, nullptr, "IsAutoPowerDownRequested"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "SetHandlingContext"},
+ {4, nullptr, "LoadAndApplySettings"},
+ {5, nullptr, "ReportUserIsActive"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IPolicyManagerSystem::~IPolicyManagerSystem() = default;
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/policy_manager_system.h b/src/core/hle/service/omm/policy_manager_system.h
new file mode 100644
index 000000000..151ca0d2e
--- /dev/null
+++ b/src/core/hle/service/omm/policy_manager_system.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::OMM {
+
+class IPolicyManagerSystem final : public ServiceFramework<IPolicyManagerSystem> {
+public:
+ explicit IPolicyManagerSystem(Core::System& system_);
+ ~IPolicyManagerSystem() override;
+};
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/power_state_interface.cpp b/src/core/hle/service/omm/power_state_interface.cpp
new file mode 100644
index 000000000..22cac8259
--- /dev/null
+++ b/src/core/hle/service/omm/power_state_interface.cpp
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/omm/power_state_interface.h"
+
+namespace Service::OMM {
+
+IPowerStateInterface::IPowerStateInterface(Core::System& system_)
+ : ServiceFramework{system_, "spsm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetState"},
+ {1, nullptr, "EnterSleep"},
+ {2, nullptr, "GetLastWakeReason"},
+ {3, nullptr, "Shutdown"},
+ {4, nullptr, "GetNotificationMessageEventHandle"},
+ {5, nullptr, "ReceiveNotificationMessage"},
+ {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"},
+ {7, nullptr, "ResetEventLog"},
+ {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"},
+ {9, nullptr, "ChangeHomeButtonLongPressingTime"},
+ {10, nullptr, "PutErrorState"},
+ {11, nullptr, "InvalidateCurrentHomeButtonPressing"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IPowerStateInterface::~IPowerStateInterface() = default;
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/omm/power_state_interface.h b/src/core/hle/service/omm/power_state_interface.h
new file mode 100644
index 000000000..825a6512d
--- /dev/null
+++ b/src/core/hle/service/omm/power_state_interface.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::OMM {
+
+class IPowerStateInterface final : public ServiceFramework<IPowerStateInterface> {
+public:
+ explicit IPowerStateInterface(Core::System& system_);
+ ~IPowerStateInterface() override;
+};
+
+} // namespace Service::OMM
diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp
index 24b85cc61..9a0adb295 100644
--- a/src/core/hle/service/psc/time/static.cpp
+++ b/src/core/hle/service/psc/time/static.cpp
@@ -144,7 +144,9 @@ Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) {
Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
Out<bool> out_is_enabled) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled);
+ };
R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
@@ -180,7 +182,9 @@ Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) {
}
Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient);
+ };
*out_is_sufficient = m_network_system_clock.IsAccuracySufficient();
@@ -189,7 +193,9 @@ Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> o
Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
Out<SteadyClockTimePoint> out_time_point) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
+ };
R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
@@ -200,7 +206,9 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
Out<s64> out_time, const SystemClockContext& context) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time);
+ };
R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);
@@ -219,8 +227,9 @@ Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
}
Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type) {
- SCOPE_EXIT(
- { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot);
+ };
SystemClockContext user_context{};
R_TRY(m_user_system_clock.GetContext(user_context));
@@ -234,11 +243,11 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType t
Result StaticService::GetClockSnapshotFromSystemClockContext(
TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context,
const SystemClockContext& network_context) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. type={} user_context={} network_context={} out_snapshot={}", type,
user_context, network_context, *out_snapshot);
- });
+ };
R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
}
@@ -246,9 +255,9 @@ Result StaticService::GetClockSnapshotFromSystemClockContext(
Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
InClockSnapshot a,
InClockSnapshot b) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. a={} b={} out_difference={}", *a, *b, *out_difference);
- });
+ };
auto diff_s =
std::chrono::seconds(b->user_context.offset) - std::chrono::seconds(a->user_context.offset);
@@ -276,7 +285,9 @@ Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64>
Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a,
InClockSnapshot b) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
+ };
s64 time_s{};
auto res =
diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp
index 948610a2b..78dcf532c 100644
--- a/src/core/hle/service/psc/time/steady_clock.cpp
+++ b/src/core/hle/service/psc/time/steady_clock.cpp
@@ -29,7 +29,9 @@ SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> man
}
Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
@@ -38,7 +40,9 @@ Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point
}
Result SteadyClock::GetTestOffset(Out<s64> out_test_offset) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_test_offset={}", *out_test_offset); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_test_offset={}", *out_test_offset);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
@@ -59,7 +63,9 @@ Result SteadyClock::SetTestOffset(s64 test_offset) {
}
Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
@@ -68,7 +74,9 @@ Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) {
}
Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_detected={}", *out_is_detected); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_is_detected={}", *out_is_detected);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
@@ -78,7 +86,9 @@ Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) {
}
Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
@@ -88,8 +98,9 @@ Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
}
Result SteadyClock::GetInternalOffset(Out<s64> out_internal_offset) {
- SCOPE_EXIT(
- { LOG_DEBUG(Service_Time, "called. out_internal_offset={}", *out_internal_offset); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_internal_offset={}", *out_internal_offset);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp
index b4e9264d8..9f841d8e0 100644
--- a/src/core/hle/service/psc/time/system_clock.cpp
+++ b/src/core/hle/service/psc/time/system_clock.cpp
@@ -26,7 +26,9 @@ SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, boo
}
Result SystemClock::GetCurrentTime(Out<s64> out_time) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time={}", *out_time); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_time={}", *out_time);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
@@ -45,7 +47,9 @@ Result SystemClock::SetCurrentTime(s64 time) {
}
Result SystemClock::GetSystemClockContext(Out<SystemClockContext> out_context) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_context={}", *out_context); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_context={}", *out_context);
+ };
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp
index 2f80030a4..9e0674f27 100644
--- a/src/core/hle/service/psc/time/time_zone_service.cpp
+++ b/src/core/hle/service/psc/time/time_zone_service.cpp
@@ -37,7 +37,9 @@ TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore&
}
Result TimeZoneService::GetDeviceLocationName(Out<LocationName> out_location_name) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name);
+ };
R_RETURN(m_time_zone.GetLocationName(*out_location_name));
}
@@ -50,7 +52,9 @@ Result TimeZoneService::SetDeviceLocationName(const LocationName& location_name)
}
Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count);
+ };
R_RETURN(m_time_zone.GetTotalLocationCount(*out_count));
}
@@ -69,17 +73,19 @@ Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, const LocationName& l
}
Result TimeZoneService::GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version) {
- SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); });
+ SCOPE_EXIT {
+ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version);
+ };
R_RETURN(m_time_zone.GetRuleVersion(*out_rule_version));
}
Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
Out<LocationName> out_location_name, Out<SteadyClockTimePoint> out_time_point) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. out_location_name={} out_time_point={}",
*out_location_name, *out_time_point);
- });
+ };
R_TRY(m_time_zone.GetLocationName(*out_location_name));
R_RETURN(m_time_zone.GetTimePoint(*out_time_point));
@@ -116,10 +122,10 @@ Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time,
Out<CalendarAdditionalInfo> out_additional_info, s64 time,
InRule rule) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
*out_calendar_time, *out_additional_info);
- });
+ };
R_RETURN(
m_time_zone.ToCalendarTime(*out_calendar_time, *out_additional_info, time, *rule.Get()));
@@ -128,10 +134,10 @@ Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time,
Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time,
Out<CalendarAdditionalInfo> out_additional_info,
s64 time) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
*out_calendar_time, *out_additional_info);
- });
+ };
R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(*out_calendar_time, *out_additional_info, time));
}
@@ -139,11 +145,11 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_
Result TimeZoneService::ToPosixTime(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const CalendarTime& calendar_time, InRule rule) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times[0], out_times[1]);
- });
+ };
R_RETURN(
m_time_zone.ToPosixTime(*out_count, out_times, out_times.size(), calendar_time, *rule));
@@ -152,11 +158,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
OutArray<s64, BufferAttr_HipcPointer> out_times,
const CalendarTime& calendar_time) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times[0], out_times[1]);
- });
+ };
R_RETURN(
m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, out_times.size(), calendar_time));
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 8c7f94c8c..0b41bbcb9 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -177,10 +177,10 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
Kernel::KPort::Register(m_system.Kernel(), port);
// Ensure that our reference to the port is closed if we fail to register it.
- SCOPE_EXIT({
+ SCOPE_EXIT {
port->GetClientPort().Close();
port->GetServerPort().Close();
- });
+ };
// Register the object name with the kernel.
R_TRY(Kernel::KObjectName::NewFromName(m_system.Kernel(), std::addressof(port->GetClientPort()),
@@ -237,7 +237,9 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
}
Result ServerManager::LoopProcess() {
- SCOPE_EXIT({ m_stopped.Set(); });
+ SCOPE_EXIT {
+ m_stopped.Set();
+ };
R_RETURN(this->LoopProcessImpl());
}
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index f68c3c686..ce5e3b5b4 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,67 +7,10 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/ipc.h"
-#include "core/hle/kernel/k_process.h"
-#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/acc/acc.h"
-#include "core/hle/service/am/am.h"
-#include "core/hle/service/aoc/aoc_u.h"
-#include "core/hle/service/apm/apm.h"
-#include "core/hle/service/audio/audio.h"
-#include "core/hle/service/bcat/bcat.h"
-#include "core/hle/service/bpc/bpc.h"
-#include "core/hle/service/btdrv/btdrv.h"
-#include "core/hle/service/btm/btm.h"
-#include "core/hle/service/caps/caps.h"
-#include "core/hle/service/erpt/erpt.h"
-#include "core/hle/service/es/es.h"
-#include "core/hle/service/eupld/eupld.h"
-#include "core/hle/service/fatal/fatal.h"
-#include "core/hle/service/fgm/fgm.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/friend/friend.h"
-#include "core/hle/service/glue/glue.h"
-#include "core/hle/service/grc/grc.h"
-#include "core/hle/service/hid/hid.h"
#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/jit/jit.h"
-#include "core/hle/service/lbl/lbl.h"
-#include "core/hle/service/ldn/ldn.h"
-#include "core/hle/service/ldr/ldr.h"
-#include "core/hle/service/lm/lm.h"
-#include "core/hle/service/mig/mig.h"
-#include "core/hle/service/mii/mii.h"
-#include "core/hle/service/mm/mm_u.h"
-#include "core/hle/service/mnpp/mnpp_app.h"
-#include "core/hle/service/ncm/ncm.h"
-#include "core/hle/service/nfc/nfc.h"
-#include "core/hle/service/nfp/nfp.h"
-#include "core/hle/service/ngc/ngc.h"
-#include "core/hle/service/nifm/nifm.h"
-#include "core/hle/service/nim/nim.h"
-#include "core/hle/service/npns/npns.h"
-#include "core/hle/service/ns/ns.h"
-#include "core/hle/service/nvdrv/nvdrv.h"
-#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
-#include "core/hle/service/nvnflinger/nvnflinger.h"
-#include "core/hle/service/olsc/olsc.h"
-#include "core/hle/service/pcie/pcie.h"
-#include "core/hle/service/pctl/pctl_module.h"
-#include "core/hle/service/pcv/pcv.h"
-#include "core/hle/service/pm/pm.h"
-#include "core/hle/service/prepo/prepo.h"
-#include "core/hle/service/psc/psc.h"
-#include "core/hle/service/ptm/ptm.h"
-#include "core/hle/service/ro/ro.h"
#include "core/hle/service/service.h"
-#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/sm.h"
-#include "core/hle/service/sockets/sockets.h"
-#include "core/hle/service/spl/spl_module.h"
-#include "core/hle/service/ssl/ssl.h"
-#include "core/hle/service/usb/usb.h"
-#include "core/hle/service/vi/vi.h"
#include "core/reporter.h"
namespace Service {
@@ -208,81 +151,4 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
return result;
}
-/// Initialize Services
-Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
- : hos_binder_driver_server{std::make_unique<Nvnflinger::HosBinderDriverServer>(system)},
- nv_flinger{std::make_unique<Nvnflinger::Nvnflinger>(system, *hos_binder_driver_server)} {
-
- auto& kernel = system.Kernel();
-
- // Nvnflinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
- // here and pass it into the respective InstallInterfaces functions.
- system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
-
- // clang-format off
- kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach();
- kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach();
- kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach();
- kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach();
- kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach();
- kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(*nv_flinger, system); }).detach();
- kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach();
- kernel.RunOnHostCoreProcess("vi", [&] { VI::LoopProcess(system, *nv_flinger, *hos_binder_driver_server); }).detach();
-
- kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(*nv_flinger, system); });
- kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
- // glue depends on settings and psc, so they must come first
- kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
- // clang-format on
-}
-
-Services::~Services() = default;
-
-void Services::KillNVNFlinger() {
- nv_flinger->ShutdownLayers();
-}
-
} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 22d1343d5..36aae1c79 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -28,11 +28,6 @@ namespace FileSystem {
class FileSystemController;
}
-namespace Nvnflinger {
-class HosBinderDriverServer;
-class Nvnflinger;
-} // namespace Nvnflinger
-
namespace SM {
class ServiceManager;
}
@@ -236,20 +231,4 @@ private:
}
};
-/**
- * The purpose of this class is to own any objects that need to be shared across the other service
- * implementations. Will be torn down when the global system instance is shutdown.
- */
-class Services final {
-public:
- explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
- ~Services();
-
- void KillNVNFlinger();
-
-private:
- std::unique_ptr<Nvnflinger::HosBinderDriverServer> hos_binder_driver_server;
- std::unique_ptr<Nvnflinger::Nvnflinger> nv_flinger;
-};
-
} // namespace Service
diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp
new file mode 100644
index 000000000..d6c6eff50
--- /dev/null
+++ b/src/core/hle/service/services.cpp
@@ -0,0 +1,136 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/services.h"
+
+#include "core/hle/service/acc/acc.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/aoc/aoc_u.h"
+#include "core/hle/service/apm/apm.h"
+#include "core/hle/service/audio/audio.h"
+#include "core/hle/service/bcat/bcat.h"
+#include "core/hle/service/bpc/bpc.h"
+#include "core/hle/service/btdrv/btdrv.h"
+#include "core/hle/service/btm/btm.h"
+#include "core/hle/service/caps/caps.h"
+#include "core/hle/service/erpt/erpt.h"
+#include "core/hle/service/es/es.h"
+#include "core/hle/service/eupld/eupld.h"
+#include "core/hle/service/fatal/fatal.h"
+#include "core/hle/service/fgm/fgm.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/friend/friend.h"
+#include "core/hle/service/glue/glue.h"
+#include "core/hle/service/grc/grc.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/jit/jit.h"
+#include "core/hle/service/lbl/lbl.h"
+#include "core/hle/service/ldn/ldn.h"
+#include "core/hle/service/ldr/ldr.h"
+#include "core/hle/service/lm/lm.h"
+#include "core/hle/service/mig/mig.h"
+#include "core/hle/service/mii/mii.h"
+#include "core/hle/service/mm/mm_u.h"
+#include "core/hle/service/mnpp/mnpp_app.h"
+#include "core/hle/service/ncm/ncm.h"
+#include "core/hle/service/nfc/nfc.h"
+#include "core/hle/service/nfp/nfp.h"
+#include "core/hle/service/ngc/ngc.h"
+#include "core/hle/service/nifm/nifm.h"
+#include "core/hle/service/nim/nim.h"
+#include "core/hle/service/npns/npns.h"
+#include "core/hle/service/ns/ns.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
+#include "core/hle/service/nvnflinger/nvnflinger.h"
+#include "core/hle/service/olsc/olsc.h"
+#include "core/hle/service/omm/omm.h"
+#include "core/hle/service/pcie/pcie.h"
+#include "core/hle/service/pctl/pctl_module.h"
+#include "core/hle/service/pcv/pcv.h"
+#include "core/hle/service/pm/pm.h"
+#include "core/hle/service/prepo/prepo.h"
+#include "core/hle/service/psc/psc.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ro/ro.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/set/settings.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/sockets/sockets.h"
+#include "core/hle/service/spl/spl_module.h"
+#include "core/hle/service/ssl/ssl.h"
+#include "core/hle/service/usb/usb.h"
+#include "core/hle/service/vi/vi.h"
+
+namespace Service {
+
+Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
+ std::stop_token token) {
+ auto& kernel = system.Kernel();
+
+ system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
+
+ // clang-format off
+ kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach();
+ kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
+
+ kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("nvnflinger", [&] { Nvnflinger::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("omm", [&] { OMM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
+ // clang-format on
+}
+
+Services::~Services() = default;
+
+} // namespace Service
diff --git a/src/core/hle/service/services.h b/src/core/hle/service/services.h
new file mode 100644
index 000000000..a99fa1e53
--- /dev/null
+++ b/src/core/hle/service/services.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/polyfill_thread.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service {
+
+/**
+ * The purpose of this class is to own any objects that need to be shared across the other service
+ * implementations. Will be torn down when the global system instance is shutdown.
+ */
+class Services final {
+public:
+ explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
+ std::stop_token token);
+ ~Services();
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/set/key_code_map.h b/src/core/hle/service/set/key_code_map.h
new file mode 100644
index 000000000..c76dc5729
--- /dev/null
+++ b/src/core/hle/service/set/key_code_map.h
@@ -0,0 +1,973 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::Set {
+
+// Raw key codes map extracted from the settings sysmodule FW 16.2.0
+// This is nn::kpr::KeyCodeMap
+using KeyCodeMap = std::array<u8, 0x1000>;
+
+constexpr KeyCodeMap KeyCodeMapChineseTraditional = {
+ 0x61, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x07, 0x31, 0x00, 0x00, 0xE5, 0x65, 0x00, 0x00, 0x01, 0x10,
+ 0x62, 0x00, 0x42, 0x00, 0x16, 0x31, 0x00, 0x00, 0x08, 0x67, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00,
+ 0x43, 0x00, 0x0F, 0x31, 0x00, 0x00, 0xD1, 0x91, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00,
+ 0x0E, 0x31, 0x00, 0x00, 0x28, 0x67, 0x00, 0x00, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x0D, 0x31,
+ 0x00, 0x00, 0x34, 0x6C, 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x11, 0x31, 0x00, 0x00,
+ 0x6B, 0x70, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x15, 0x31, 0x00, 0x00, 0x1F, 0x57,
+ 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x18, 0x31, 0x00, 0x00, 0xF9, 0x7A, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x1B, 0x31, 0x00, 0x00, 0x08, 0x62, 0x00, 0x00, 0x01, 0x10,
+ 0x6A, 0x00, 0x4A, 0x00, 0x28, 0x31, 0x00, 0x00, 0x41, 0x53, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00,
+ 0x4B, 0x00, 0x1C, 0x31, 0x00, 0x00, 0x27, 0x59, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00,
+ 0x20, 0x31, 0x00, 0x00, 0x2D, 0x4E, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x29, 0x31,
+ 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x19, 0x31, 0x00, 0x00,
+ 0x13, 0x5F, 0x00, 0x00, 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x1F, 0x31, 0x00, 0x00, 0xBA, 0x4E,
+ 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x23, 0x31, 0x00, 0x00, 0xC3, 0x5F, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x06, 0x31, 0x00, 0x00, 0x4B, 0x62, 0x00, 0x00, 0x01, 0x10,
+ 0x72, 0x00, 0x52, 0x00, 0x10, 0x31, 0x00, 0x00, 0xE3, 0x53, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00,
+ 0x53, 0x00, 0x0B, 0x31, 0x00, 0x00, 0x38, 0x5C, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00,
+ 0x14, 0x31, 0x00, 0x00, 0xFF, 0x5E, 0x00, 0x00, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x27, 0x31,
+ 0x00, 0x00, 0x71, 0x5C, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x12, 0x31, 0x00, 0x00,
+ 0x73, 0x59, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x0A, 0x31, 0x00, 0x00, 0x30, 0x75,
+ 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x0C, 0x31, 0x00, 0x00, 0xE3, 0x96, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x17, 0x31, 0x00, 0x00, 0x5C, 0x53, 0x00, 0x00, 0x01, 0x10,
+ 0x7A, 0x00, 0x5A, 0x00, 0x08, 0x31, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x00,
+ 0x21, 0x00, 0x05, 0x31, 0x00, 0x00, 0x31, 0x00, 0x21, 0x00, 0x00, 0x10, 0x32, 0x00, 0x40, 0x00,
+ 0x09, 0x31, 0x00, 0x00, 0x32, 0x00, 0x40, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0xC7, 0x02,
+ 0x00, 0x00, 0x33, 0x00, 0x23, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xCB, 0x02, 0x00, 0x00,
+ 0x34, 0x00, 0x24, 0x00, 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x13, 0x31, 0x00, 0x00, 0x35, 0x00,
+ 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0xCA, 0x02, 0x00, 0x00, 0x36, 0x00, 0x5E, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0xD9, 0x02, 0x00, 0x00, 0x37, 0x00, 0x26, 0x00, 0x00, 0x10,
+ 0x38, 0x00, 0x2A, 0x00, 0x1A, 0x31, 0x00, 0x00, 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00,
+ 0x28, 0x00, 0x1E, 0x31, 0x00, 0x00, 0x39, 0x00, 0x28, 0x00, 0x00, 0x10, 0x30, 0x00, 0x29, 0x00,
+ 0x22, 0x31, 0x00, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10,
+ 0x2D, 0x00, 0x5F, 0x00, 0x26, 0x31, 0x00, 0x00, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00,
+ 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x00, 0x10, 0x5B, 0x00, 0x7B, 0x00,
+ 0x5B, 0x00, 0x7B, 0x00, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x5D, 0x00,
+ 0x7D, 0x00, 0x5D, 0x00, 0x7D, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00,
+ 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00,
+ 0x7C, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x24, 0x31, 0x00, 0x00, 0x3B, 0x00, 0x3A, 0x00,
+ 0x00, 0x10, 0x27, 0x00, 0x22, 0x00, 0x27, 0x00, 0x22, 0x00, 0x27, 0x00, 0x22, 0x00, 0x00, 0x10,
+ 0x60, 0x00, 0x7E, 0x00, 0x60, 0x00, 0x7E, 0x00, 0x60, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x2C, 0x00,
+ 0x3C, 0x00, 0x1D, 0x31, 0x00, 0x00, 0x2C, 0x00, 0x3C, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3E, 0x00,
+ 0x21, 0x31, 0x00, 0x00, 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x25, 0x31,
+ 0x00, 0x00, 0x2F, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10,
+ 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00,
+ 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00,
+ 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20,
+ 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00,
+ 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00,
+ 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapChineseSimplified = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x01, 0x10, 0x63, 0x00,
+ 0x43, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x01, 0x10,
+ 0x66, 0x00, 0x46, 0x00, 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x01, 0x10, 0x6B, 0x00,
+ 0x4B, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x01, 0x10,
+ 0x6E, 0x00, 0x4E, 0x00, 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x01, 0x10, 0x73, 0x00,
+ 0x53, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x01, 0x10,
+ 0x76, 0x00, 0x56, 0x00, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x10, 0x31, 0x00,
+ 0x21, 0x00, 0x00, 0x10, 0x32, 0x00, 0x40, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x00, 0x10,
+ 0x34, 0x00, 0x24, 0x00, 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00,
+ 0x28, 0x00, 0x00, 0x10, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00,
+ 0x2B, 0x00, 0x00, 0x10, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x00, 0x10,
+ 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00,
+ 0x00, 0x10, 0x27, 0x00, 0x22, 0x00, 0x00, 0x10, 0x60, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x2C, 0x00,
+ 0x3C, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00,
+ 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x36, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapKorean = {
+ 0x11, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x41, 0x31, 0x41, 0x31, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00,
+ 0x60, 0x31, 0x60, 0x31, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x4A, 0x31, 0x4A, 0x31, 0x01, 0x10,
+ 0x64, 0x00, 0x44, 0x00, 0x47, 0x31, 0x47, 0x31, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x37, 0x31,
+ 0x38, 0x31, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x39, 0x31, 0x39, 0x31, 0x01, 0x10, 0x67, 0x00,
+ 0x47, 0x00, 0x4E, 0x31, 0x4E, 0x31, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x57, 0x31, 0x57, 0x31,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x51, 0x31, 0x51, 0x31, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00,
+ 0x53, 0x31, 0x53, 0x31, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x4F, 0x31, 0x4F, 0x31, 0x01, 0x10,
+ 0x6C, 0x00, 0x4C, 0x00, 0x63, 0x31, 0x63, 0x31, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x61, 0x31,
+ 0x61, 0x31, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x5C, 0x31, 0x5C, 0x31, 0x01, 0x10, 0x6F, 0x00,
+ 0x4F, 0x00, 0x50, 0x31, 0x52, 0x31, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x54, 0x31, 0x56, 0x31,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x42, 0x31, 0x43, 0x31, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00,
+ 0x31, 0x31, 0x32, 0x31, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x34, 0x31, 0x34, 0x31, 0x01, 0x10,
+ 0x74, 0x00, 0x54, 0x00, 0x45, 0x31, 0x46, 0x31, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x55, 0x31,
+ 0x55, 0x31, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x4D, 0x31, 0x4D, 0x31, 0x01, 0x10, 0x77, 0x00,
+ 0x57, 0x00, 0x48, 0x31, 0x49, 0x31, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x4C, 0x31, 0x4C, 0x31,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x5B, 0x31, 0x5B, 0x31, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00,
+ 0x4B, 0x31, 0x4B, 0x31, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x00, 0x10,
+ 0x32, 0x00, 0x40, 0x00, 0x32, 0x00, 0x40, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x33, 0x00,
+ 0x23, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x34, 0x00, 0x24, 0x00, 0x00, 0x10, 0x35, 0x00,
+ 0x25, 0x00, 0x35, 0x00, 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0x36, 0x00, 0x5E, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x37, 0x00, 0x26, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00,
+ 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x39, 0x00, 0x28, 0x00, 0x00, 0x10,
+ 0x30, 0x00, 0x29, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00,
+ 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x00, 0x10,
+ 0x5B, 0x00, 0x7B, 0x00, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x5D, 0x00,
+ 0x7D, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x5C, 0x00,
+ 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3A, 0x00,
+ 0x00, 0x10, 0x27, 0x00, 0x22, 0x00, 0x27, 0x00, 0x22, 0x00, 0x00, 0x10, 0x60, 0x00, 0x7E, 0x00,
+ 0x60, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0x2C, 0x00, 0x3C, 0x00, 0x00, 0x10,
+ 0x2E, 0x00, 0x3E, 0x00, 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x2F, 0x00,
+ 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00,
+ 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00,
+ 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00,
+ 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapRussian = {
+ 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x10, 0x61, 0x00, 0x41, 0x00, 0x44, 0x04, 0x24, 0x04, 0x11, 0x10, 0x62, 0x00, 0x42, 0x00,
+ 0x38, 0x04, 0x18, 0x04, 0x11, 0x10, 0x63, 0x00, 0x43, 0x00, 0x41, 0x04, 0x21, 0x04, 0x11, 0x10,
+ 0x64, 0x00, 0x44, 0x00, 0x32, 0x04, 0x12, 0x04, 0x11, 0x10, 0x65, 0x00, 0x45, 0x00, 0x43, 0x04,
+ 0x23, 0x04, 0x11, 0x10, 0x66, 0x00, 0x46, 0x00, 0x30, 0x04, 0x10, 0x04, 0x11, 0x10, 0x67, 0x00,
+ 0x47, 0x00, 0x3F, 0x04, 0x1F, 0x04, 0x11, 0x10, 0x68, 0x00, 0x48, 0x00, 0x40, 0x04, 0x20, 0x04,
+ 0x11, 0x10, 0x69, 0x00, 0x49, 0x00, 0x48, 0x04, 0x28, 0x04, 0x11, 0x10, 0x6A, 0x00, 0x4A, 0x00,
+ 0x3E, 0x04, 0x1E, 0x04, 0x11, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x3B, 0x04, 0x1B, 0x04, 0x11, 0x10,
+ 0x6C, 0x00, 0x4C, 0x00, 0x34, 0x04, 0x14, 0x04, 0x11, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x4C, 0x04,
+ 0x2C, 0x04, 0x11, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x42, 0x04, 0x22, 0x04, 0x11, 0x10, 0x6F, 0x00,
+ 0x4F, 0x00, 0x49, 0x04, 0x29, 0x04, 0x11, 0x10, 0x70, 0x00, 0x50, 0x00, 0x37, 0x04, 0x17, 0x04,
+ 0x11, 0x10, 0x71, 0x00, 0x51, 0x00, 0x39, 0x04, 0x19, 0x04, 0x11, 0x10, 0x72, 0x00, 0x52, 0x00,
+ 0x3A, 0x04, 0x1A, 0x04, 0x11, 0x10, 0x73, 0x00, 0x53, 0x00, 0x4B, 0x04, 0x2B, 0x04, 0x11, 0x10,
+ 0x74, 0x00, 0x54, 0x00, 0x35, 0x04, 0x15, 0x04, 0x11, 0x10, 0x75, 0x00, 0x55, 0x00, 0x33, 0x04,
+ 0x13, 0x04, 0x11, 0x10, 0x76, 0x00, 0x56, 0x00, 0x3C, 0x04, 0x1C, 0x04, 0x11, 0x10, 0x77, 0x00,
+ 0x57, 0x00, 0x46, 0x04, 0x26, 0x04, 0x11, 0x10, 0x78, 0x00, 0x58, 0x00, 0x47, 0x04, 0x27, 0x04,
+ 0x11, 0x10, 0x79, 0x00, 0x59, 0x00, 0x3D, 0x04, 0x1D, 0x04, 0x11, 0x10, 0x7A, 0x00, 0x5A, 0x00,
+ 0x4F, 0x04, 0x2F, 0x04, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x00, 0x10,
+ 0x32, 0x00, 0x40, 0x00, 0x32, 0x00, 0x22, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x33, 0x00,
+ 0x16, 0x21, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x34, 0x00, 0x3B, 0x00, 0x00, 0x10, 0x35, 0x00,
+ 0x25, 0x00, 0x35, 0x00, 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0x36, 0x00, 0x3A, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x37, 0x00, 0x3F, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00,
+ 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x39, 0x00, 0x28, 0x00, 0x00, 0x10,
+ 0x30, 0x00, 0x29, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00,
+ 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x10, 0x10,
+ 0x5B, 0x00, 0x7B, 0x00, 0x45, 0x04, 0x25, 0x04, 0x10, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x4A, 0x04,
+ 0x2A, 0x04, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x5C, 0x00,
+ 0x7C, 0x00, 0x5C, 0x00, 0x2F, 0x00, 0x10, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x36, 0x04, 0x16, 0x04,
+ 0x10, 0x10, 0x27, 0x00, 0x22, 0x00, 0x4D, 0x04, 0x2D, 0x04, 0x10, 0x10, 0x60, 0x00, 0x7E, 0x00,
+ 0x51, 0x04, 0x01, 0x04, 0x10, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0x31, 0x04, 0x11, 0x04, 0x10, 0x10,
+ 0x2E, 0x00, 0x3E, 0x00, 0x4E, 0x04, 0x2E, 0x04, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x2E, 0x00,
+ 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00,
+ 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00,
+ 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x00,
+ 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapPortuguese = {
+ 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x40, 0x00,
+ 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0xA3, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xA7, 0x00,
+ 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, 0x5B, 0x00,
+ 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x00, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x7D, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0xAB, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x08, 0x03,
+ 0x00, 0x10, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x03, 0x03, 0x02, 0x03, 0x00, 0x00,
+ 0x00, 0x10, 0x03, 0x03, 0x02, 0x03, 0x00, 0x00, 0x01, 0x10, 0xE7, 0x00, 0xC7, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0xBA, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapItalian = {
+ 0x01, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
+ 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20,
+ 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00,
+ 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
+ 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6F, 0x00,
+ 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
+ 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00,
+ 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x32, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0x00, 0xA3, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x35, 0x00,
+ 0x25, 0x00, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x30, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xEC, 0x00, 0x5E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0xE8, 0x00, 0xE9, 0x00, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x5D, 0x00,
+ 0x7D, 0x00, 0x00, 0x10, 0xF9, 0x00, 0xA7, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF9, 0x00,
+ 0xA7, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF2, 0x00, 0xE7, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0xE0, 0x00, 0xB0, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00,
+ 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00,
+ 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapGerman = {
+ 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0xB5, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x40, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x10, 0x32, 0x00, 0x22, 0x00, 0xB2, 0x00,
+ 0x01, 0x10, 0x33, 0x00, 0xA7, 0x00, 0xB3, 0x00, 0x01, 0x10, 0x34, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x7B, 0x00, 0x01, 0x10, 0x38, 0x00, 0x28, 0x00, 0x5B, 0x00,
+ 0x01, 0x10, 0x39, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x01, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x7D, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x01, 0x10, 0xDF, 0x00, 0x3F, 0x00, 0x5C, 0x00,
+ 0x00, 0x10, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x01, 0x10, 0xFC, 0x00, 0xDC, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x7E, 0x00, 0x01, 0x10, 0x23, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x23, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x10, 0xF6, 0x00, 0xD6, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0xE4, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x03, 0xB0, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapSpanishLatin = {
+ 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x40, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, 0x5C, 0x00,
+ 0x00, 0x10, 0xBF, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x08, 0x03, 0x00, 0x00,
+ 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x7D, 0x00, 0x5D, 0x00, 0x00, 0x03,
+ 0x00, 0x10, 0x7D, 0x00, 0x5D, 0x00, 0x00, 0x03, 0x01, 0x10, 0xF1, 0x00, 0xD1, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x7B, 0x00, 0x5B, 0x00, 0x02, 0x03, 0x00, 0x10, 0x7C, 0x00, 0xB0, 0x00, 0xAC, 0x00,
+ 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapSpanish = {
+ 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x40, 0x00,
+ 0x00, 0x10, 0x33, 0x00, 0xB7, 0x00, 0x23, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x03, 0x03,
+ 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0xAC, 0x20, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0xAC, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0xA1, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x02, 0x03, 0x5B, 0x00,
+ 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x5D, 0x00, 0x01, 0x10, 0xE7, 0x00, 0xC7, 0x00, 0x7D, 0x00,
+ 0x01, 0x10, 0xE7, 0x00, 0xC7, 0x00, 0x7D, 0x00, 0x01, 0x10, 0xF1, 0x00, 0xD1, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x01, 0x03, 0x08, 0x03, 0x7B, 0x00, 0x00, 0x10, 0xBA, 0x00, 0xAA, 0x00, 0x5C, 0x00,
+ 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapFrenchCa = {
+ 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0xB5, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0xA7, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0xB6, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0xB1, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x40, 0x00,
+ 0x00, 0x10, 0x33, 0x00, 0x2F, 0x00, 0xA3, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xA2, 0x00,
+ 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0xA4, 0x00, 0x00, 0x10, 0x36, 0x00, 0x3F, 0x00, 0xAC, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0xA6, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, 0xB2, 0x00,
+ 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0xB3, 0x00, 0x00, 0x10, 0x30, 0x00, 0x29, 0x00, 0xBC, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0xBD, 0x00,
+ 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0xBE, 0x00, 0x00, 0x10, 0x02, 0x03, 0x02, 0x03, 0x5B, 0x00,
+ 0x00, 0x10, 0x27, 0x03, 0x08, 0x03, 0x5D, 0x00, 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x7D, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x7D, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x7E, 0x00,
+ 0x00, 0x10, 0x00, 0x03, 0x00, 0x03, 0x7B, 0x00, 0x00, 0x10, 0x23, 0x00, 0x7C, 0x00, 0x5C, 0x00,
+ 0x00, 0x10, 0x2C, 0x00, 0x27, 0x00, 0xAF, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x2E, 0x00, 0x2D, 0x00,
+ 0x01, 0x10, 0xE9, 0x00, 0xC9, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0xAB, 0x00, 0xBB, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapFrench = {
+ 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x2C, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x26, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE9, 0x00, 0x32, 0x00, 0x03, 0x03,
+ 0x01, 0x10, 0x22, 0x00, 0x33, 0x00, 0x23, 0x00, 0x01, 0x10, 0x27, 0x00, 0x34, 0x00, 0x7B, 0x00,
+ 0x01, 0x10, 0x28, 0x00, 0x35, 0x00, 0x5B, 0x00, 0x01, 0x10, 0x2D, 0x00, 0x36, 0x00, 0x7C, 0x00,
+ 0x01, 0x10, 0xE8, 0x00, 0x37, 0x00, 0x00, 0x03, 0x01, 0x10, 0x5F, 0x00, 0x38, 0x00, 0x5C, 0x00,
+ 0x01, 0x10, 0xE7, 0x00, 0x39, 0x00, 0x5E, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x30, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x01, 0x10, 0x29, 0x00, 0xB0, 0x00, 0x5D, 0x00,
+ 0x01, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x7D, 0x00, 0x01, 0x10, 0x02, 0x03, 0x08, 0x03, 0x00, 0x00,
+ 0x01, 0x10, 0x24, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0x01, 0x10, 0x2A, 0x00, 0xB5, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x2A, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0xF9, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x3B, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x10, 0x3A, 0x00, 0x2F, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x21, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00,
+ 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapEnglishUk = {
+ 0x01, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x10, 0x61, 0x00, 0x41, 0x00, 0xE1, 0x00, 0xC1, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
+ 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x65, 0x00, 0x45, 0x00, 0xE9, 0x00,
+ 0xC9, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00,
+ 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x10, 0x69, 0x00, 0x49, 0x00, 0xED, 0x00, 0xCD, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
+ 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x6F, 0x00,
+ 0x4F, 0x00, 0xF3, 0x00, 0xD3, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10,
+ 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x75, 0x00, 0x55, 0x00, 0xFA, 0x00,
+ 0xDA, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00,
+ 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x32, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0x00, 0xA3, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x10, 0x35, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x23, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x23, 0x00,
+ 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x27, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0xAC, 0x00,
+ 0xA6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00,
+ 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00,
+ 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00,
+ 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapJapanese = {
+ 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x61, 0x30, 0x61, 0x30, 0xC1, 0x30, 0xC1, 0x30, 0x01, 0x10,
+ 0x62, 0x00, 0x42, 0x00, 0x53, 0x30, 0x53, 0x30, 0xB3, 0x30, 0xB3, 0x30, 0x01, 0x10, 0x63, 0x00,
+ 0x43, 0x00, 0x5D, 0x30, 0x5D, 0x30, 0xBD, 0x30, 0xBD, 0x30, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00,
+ 0x57, 0x30, 0x57, 0x30, 0xB7, 0x30, 0xB7, 0x30, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x44, 0x30,
+ 0x43, 0x30, 0xA4, 0x30, 0xA3, 0x30, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x6F, 0x30, 0x6F, 0x30,
+ 0xCF, 0x30, 0xCF, 0x30, 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x4D, 0x30, 0x4D, 0x30, 0xAD, 0x30,
+ 0xAD, 0x30, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x4F, 0x30, 0x4F, 0x30, 0xAF, 0x30, 0xAF, 0x30,
+ 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x6B, 0x30, 0x6B, 0x30, 0xCB, 0x30, 0xCB, 0x30, 0x01, 0x10,
+ 0x6A, 0x00, 0x4A, 0x00, 0x7E, 0x30, 0x7E, 0x30, 0xDE, 0x30, 0xDE, 0x30, 0x01, 0x10, 0x6B, 0x00,
+ 0x4B, 0x00, 0x6E, 0x30, 0x6E, 0x30, 0xCE, 0x30, 0xCE, 0x30, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00,
+ 0x8A, 0x30, 0x8A, 0x30, 0xEA, 0x30, 0xEA, 0x30, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x82, 0x30,
+ 0x82, 0x30, 0xE2, 0x30, 0xE2, 0x30, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x7F, 0x30, 0x7F, 0x30,
+ 0xDF, 0x30, 0xDF, 0x30, 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x89, 0x30, 0x89, 0x30, 0xE9, 0x30,
+ 0xE9, 0x30, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x5B, 0x30, 0x5B, 0x30, 0xBB, 0x30, 0xBB, 0x30,
+ 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x5F, 0x30, 0x5F, 0x30, 0xBF, 0x30, 0xBF, 0x30, 0x01, 0x10,
+ 0x72, 0x00, 0x52, 0x00, 0x59, 0x30, 0x59, 0x30, 0xB9, 0x30, 0xB9, 0x30, 0x01, 0x10, 0x73, 0x00,
+ 0x53, 0x00, 0x68, 0x30, 0x68, 0x30, 0xC8, 0x30, 0xC8, 0x30, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00,
+ 0x4B, 0x30, 0x4B, 0x30, 0xAB, 0x30, 0xAB, 0x30, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x6A, 0x30,
+ 0x6A, 0x30, 0xCA, 0x30, 0xCA, 0x30, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x72, 0x30, 0x72, 0x30,
+ 0xD2, 0x30, 0xD2, 0x30, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x66, 0x30, 0x66, 0x30, 0xC6, 0x30,
+ 0xC6, 0x30, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x55, 0x30, 0x55, 0x30, 0xB5, 0x30, 0xB5, 0x30,
+ 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x93, 0x30, 0x93, 0x30, 0xF3, 0x30, 0xF3, 0x30, 0x01, 0x10,
+ 0x7A, 0x00, 0x5A, 0x00, 0x64, 0x30, 0x63, 0x30, 0xC4, 0x30, 0xC3, 0x30, 0x00, 0x10, 0x31, 0x00,
+ 0x21, 0x00, 0x6C, 0x30, 0x6C, 0x30, 0xCC, 0x30, 0xCC, 0x30, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00,
+ 0x75, 0x30, 0x75, 0x30, 0xD5, 0x30, 0xD5, 0x30, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x42, 0x30,
+ 0x41, 0x30, 0xA2, 0x30, 0xA1, 0x30, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x46, 0x30, 0x45, 0x30,
+ 0xA6, 0x30, 0xA5, 0x30, 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x48, 0x30, 0x47, 0x30, 0xA8, 0x30,
+ 0xA7, 0x30, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x4A, 0x30, 0x49, 0x30, 0xAA, 0x30, 0xA9, 0x30,
+ 0x00, 0x10, 0x37, 0x00, 0x27, 0x00, 0x84, 0x30, 0x83, 0x30, 0xE4, 0x30, 0xE3, 0x30, 0x00, 0x10,
+ 0x38, 0x00, 0x28, 0x00, 0x86, 0x30, 0x85, 0x30, 0xE6, 0x30, 0xE5, 0x30, 0x00, 0x10, 0x39, 0x00,
+ 0x29, 0x00, 0x88, 0x30, 0x87, 0x30, 0xE8, 0x30, 0xE7, 0x30, 0x00, 0x10, 0x30, 0x00, 0x00, 0x00,
+ 0x8F, 0x30, 0x92, 0x30, 0xEF, 0x30, 0xF2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10,
+ 0x2D, 0x00, 0x3D, 0x00, 0x7B, 0x30, 0x7B, 0x30, 0xDB, 0x30, 0xDB, 0x30, 0x00, 0x10, 0x5E, 0x00,
+ 0x7E, 0x00, 0x78, 0x30, 0x78, 0x30, 0xD8, 0x30, 0xD8, 0x30, 0x00, 0x10, 0x40, 0x00, 0x60, 0x00,
+ 0x9E, 0xFF, 0x9E, 0xFF, 0x9E, 0xFF, 0x9E, 0xFF, 0x00, 0x10, 0x5B, 0x00, 0x7B, 0x00, 0x9F, 0xFF,
+ 0x62, 0xFF, 0x9F, 0xFF, 0x62, 0xFF, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x80, 0x30, 0x63, 0xFF,
+ 0xE0, 0x30, 0x63, 0xFF, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x80, 0x30, 0x63, 0xFF, 0xE0, 0x30,
+ 0x63, 0xFF, 0x00, 0x10, 0x3B, 0x00, 0x2B, 0x00, 0x8C, 0x30, 0x8C, 0x30, 0xEC, 0x30, 0xEC, 0x30,
+ 0x00, 0x10, 0x3A, 0x00, 0x2A, 0x00, 0x51, 0x30, 0x51, 0x30, 0xB1, 0x30, 0xB1, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2C, 0x00,
+ 0x3C, 0x00, 0x6D, 0x30, 0x64, 0xFF, 0xCD, 0x30, 0x64, 0xFF, 0x00, 0x10, 0x2E, 0x00, 0x3E, 0x00,
+ 0x8B, 0x30, 0x61, 0xFF, 0xEB, 0x30, 0x61, 0xFF, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x81, 0x30,
+ 0x65, 0xFF, 0xE1, 0x30, 0x65, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10,
+ 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00,
+ 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00,
+ 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20,
+ 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00,
+ 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x5F, 0x00,
+ 0x8D, 0x30, 0x8D, 0x30, 0xED, 0x30, 0xED, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x70, 0xFF, 0x70, 0xFF,
+ 0x70, 0xFF, 0x70, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+constexpr KeyCodeMap KeyCodeMapEnglishUsInternational = {
+ 0x01, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x10, 0x61, 0x00, 0x41, 0x00, 0xE1, 0x00, 0xC1, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0xA9, 0x00, 0xA2, 0x00, 0x03, 0x10,
+ 0x64, 0x00, 0x44, 0x00, 0xF0, 0x00, 0xD0, 0x00, 0x03, 0x10, 0x65, 0x00, 0x45, 0x00, 0xE9, 0x00,
+ 0xC9, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00,
+ 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x10, 0x69, 0x00, 0x49, 0x00, 0xED, 0x00, 0xCD, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10,
+ 0x6C, 0x00, 0x4C, 0x00, 0xF8, 0x00, 0xD8, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0xB5, 0x00,
+ 0x00, 0x00, 0x03, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0xF1, 0x00, 0xD1, 0x00, 0x03, 0x10, 0x6F, 0x00,
+ 0x4F, 0x00, 0xF3, 0x00, 0xD3, 0x00, 0x03, 0x10, 0x70, 0x00, 0x50, 0x00, 0xF6, 0x00, 0xD6, 0x00,
+ 0x03, 0x10, 0x71, 0x00, 0x51, 0x00, 0xE4, 0x00, 0xC4, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00,
+ 0xAE, 0x00, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0xDF, 0x00, 0xA7, 0x00, 0x03, 0x10,
+ 0x74, 0x00, 0x54, 0x00, 0xFE, 0x00, 0xDE, 0x00, 0x03, 0x10, 0x75, 0x00, 0x55, 0x00, 0xFA, 0x00,
+ 0xDA, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x77, 0x00,
+ 0x57, 0x00, 0xE5, 0x00, 0xC5, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x10, 0x79, 0x00, 0x59, 0x00, 0xFC, 0x00, 0xDC, 0x00, 0x03, 0x10, 0x7A, 0x00, 0x5A, 0x00,
+ 0xE6, 0x00, 0xC6, 0x00, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0xA1, 0x00, 0xB9, 0x00, 0x00, 0x10,
+ 0x32, 0x00, 0x40, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0xB3, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xA4, 0x00, 0xA3, 0x00, 0x00, 0x10, 0x35, 0x00,
+ 0x25, 0x00, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x02, 0x03, 0xBC, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00,
+ 0xBE, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x18, 0x20, 0x00, 0x00, 0x00, 0x10,
+ 0x30, 0x00, 0x29, 0x00, 0x19, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00,
+ 0xA5, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0xD7, 0x00, 0xF7, 0x00, 0x00, 0x10,
+ 0x5B, 0x00, 0x7B, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0xBB, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0xAC, 0x00, 0xA6, 0x00, 0x00, 0x10, 0x5C, 0x00,
+ 0x7C, 0x00, 0xAC, 0x00, 0xA6, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0xB6, 0x00, 0xB0, 0x00,
+ 0x00, 0x10, 0x0D, 0x03, 0x0E, 0x03, 0xB4, 0x00, 0xA8, 0x00, 0x00, 0x10, 0x00, 0x03, 0x03, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0xE7, 0x00, 0xC7, 0x00, 0x00, 0x10,
+ 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0xBF, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00,
+ 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10,
+ 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00,
+ 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20,
+ 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00,
+ 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+} // namespace Service::Set
diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h
deleted file mode 100644
index b02291ce7..000000000
--- a/src/core/hle/service/set/private_settings.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <array>
-
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/uuid.h"
-#include "core/hle/service/psc/time/common.h"
-
-namespace Service::Set {
-
-/// This is nn::settings::system::InitialLaunchFlag
-struct InitialLaunchFlag {
- union {
- u32 raw{};
-
- BitField<0, 1, u32> InitialLaunchCompletionFlag;
- BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
- BitField<16, 1, u32> InitialLaunchTimestampFlag;
- };
-};
-static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
-
-/// This is nn::settings::system::InitialLaunchSettings
-struct InitialLaunchSettings {
- InitialLaunchFlag flags;
- INSERT_PADDING_BYTES(0x4);
- Service::PSC::Time::SteadyClockTimePoint timestamp;
-};
-static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
-
-#pragma pack(push, 4)
-struct InitialLaunchSettingsPacked {
- InitialLaunchFlag flags;
- Service::PSC::Time::SteadyClockTimePoint timestamp;
-};
-#pragma pack(pop)
-static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
- "InitialLaunchSettingsPacked is incorrect size");
-
-struct PrivateSettings {
- std::array<u8, 0x10> reserved_00;
-
- // nn::settings::system::InitialLaunchSettings
- InitialLaunchSettings initial_launch_settings;
-
- std::array<u8, 0x20> reserved_30;
-
- Common::UUID external_clock_source_id;
- s64 shutdown_rtc_value;
- s64 external_steady_clock_internal_offset;
-
- std::array<u8, 0x60> reserved_70;
-
- // nn::settings::system::PlatformRegion
- std::array<u8, 0x4> platform_region;
-
- std::array<u8, 0x4> reserved_D4;
-};
-static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10);
-static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50);
-static_assert(offsetof(PrivateSettings, reserved_70) == 0x70);
-static_assert(offsetof(PrivateSettings, platform_region) == 0xD0);
-static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!");
-
-PrivateSettings DefaultPrivateSettings();
-
-} // namespace Service::Set
diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h
index 40230182a..a5b1552a5 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.h
+++ b/src/core/hle/service/set/setting_formats/system_settings.h
@@ -244,7 +244,7 @@ struct SystemSettings {
INSERT_PADDING_BYTES(0x60); // Reserved
// nn::settings::system::AccountNotificationSettings
- u32 account_notification_settings_count;
+ s32 account_notification_settings_count;
INSERT_PADDING_BYTES(0xC); // Reserved
std::array<AccountNotificationSettings, 8> account_notification_settings;
INSERT_PADDING_BYTES(0x140); // Reserved
@@ -308,7 +308,7 @@ struct SystemSettings {
INSERT_PADDING_BYTES(0x34); // Reserved
// nn::settings::system::EulaVersion
- u32 eula_version_count;
+ s32 eula_version_count;
INSERT_PADDING_BYTES(0xC); // Reserved
std::array<EulaVersion, 32> eula_versions;
INSERT_PADDING_BYTES(0x200); // Reserved
diff --git a/src/core/hle/service/set/settings_server.cpp b/src/core/hle/service/set/settings_server.cpp
index b2caa00ff..a9321b98d 100644
--- a/src/core/hle/service/set/settings_server.cpp
+++ b/src/core/hle/service/set/settings_server.cpp
@@ -6,7 +6,9 @@
#include <chrono>
#include "common/logging/log.h"
#include "common/settings.h"
+#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/set/key_code_map.h"
#include "core/hle/service/set/settings_server.h"
namespace Service::Set {
@@ -15,43 +17,69 @@ constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
constexpr Result ResultInvalidLanguage{ErrorModule::Settings, 625};
-
-void PushResponseLanguageCode(HLERequestContext& ctx, std::size_t num_language_codes) {
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(num_language_codes));
-}
-
-void GetAvailableLanguageCodesImpl(HLERequestContext& ctx, std::size_t max_entries) {
- const std::size_t requested_amount = ctx.GetWriteBufferNumElements<LanguageCode>();
- const std::size_t max_amount = std::min(requested_amount, max_entries);
- const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
- const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
-
- ctx.WriteBuffer(available_language_codes.data(), copy_size);
- PushResponseLanguageCode(ctx, copy_amount);
-}
-
-void GetKeyCodeMapImpl(HLERequestContext& ctx) {
- const auto language_code =
- available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
- const auto key_code =
- std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
- [=](const auto& element) { return element.first == language_code; });
- KeyboardLayout layout = KeyboardLayout::EnglishUs;
- if (key_code == language_to_layout.cend()) {
- LOG_ERROR(Service_SET,
- "Could not find keyboard layout for language index {}, defaulting to English us",
- Settings::values.language_index.GetValue());
- } else {
- layout = key_code->second;
+constexpr Result ResultNullPointer{ErrorModule::Settings, 1261};
+
+Result GetKeyCodeMapImpl(KeyCodeMap& out_key_code_map, KeyboardLayout keyboard_layout,
+ LanguageCode language_code) {
+ switch (keyboard_layout) {
+ case KeyboardLayout::Japanese:
+ out_key_code_map = KeyCodeMapJapanese;
+ R_SUCCEED();
+ case KeyboardLayout::EnglishUs:
+ out_key_code_map = KeyCodeMapEnglishUsInternational;
+ if (language_code == LanguageCode::KO) {
+ out_key_code_map = KeyCodeMapKorean;
+ }
+ if (language_code == LanguageCode::ZH_HANS) {
+ out_key_code_map = KeyCodeMapChineseSimplified;
+ }
+ if (language_code == LanguageCode::ZH_HANT) {
+ out_key_code_map = KeyCodeMapChineseTraditional;
+ }
+ R_SUCCEED();
+ case KeyboardLayout::EnglishUk:
+ out_key_code_map = KeyCodeMapEnglishUk;
+ R_SUCCEED();
+ case KeyboardLayout::French:
+ out_key_code_map = KeyCodeMapFrench;
+ R_SUCCEED();
+ case KeyboardLayout::FrenchCa:
+ out_key_code_map = KeyCodeMapFrenchCa;
+ R_SUCCEED();
+ case KeyboardLayout::Spanish:
+ out_key_code_map = KeyCodeMapSpanish;
+ R_SUCCEED();
+ case KeyboardLayout::SpanishLatin:
+ out_key_code_map = KeyCodeMapSpanishLatin;
+ R_SUCCEED();
+ case KeyboardLayout::German:
+ out_key_code_map = KeyCodeMapGerman;
+ R_SUCCEED();
+ case KeyboardLayout::Italian:
+ out_key_code_map = KeyCodeMapItalian;
+ R_SUCCEED();
+ case KeyboardLayout::Portuguese:
+ out_key_code_map = KeyCodeMapPortuguese;
+ R_SUCCEED();
+ case KeyboardLayout::Russian:
+ out_key_code_map = KeyCodeMapRussian;
+ R_SUCCEED();
+ case KeyboardLayout::Korean:
+ out_key_code_map = KeyCodeMapKorean;
+ R_SUCCEED();
+ case KeyboardLayout::ChineseSimplified:
+ out_key_code_map = KeyCodeMapChineseSimplified;
+ R_SUCCEED();
+ case KeyboardLayout::ChineseTraditional:
+ out_key_code_map = KeyCodeMapChineseTraditional;
+ R_SUCCEED();
+ default:
+ case KeyboardLayout::EnglishUsInternational:
+ out_key_code_map = KeyCodeMapEnglishUsInternational;
+ R_SUCCEED();
}
-
- ctx.WriteBuffer(layout);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
}
+
} // Anonymous namespace
LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
@@ -61,18 +89,18 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
ISettingsServer::ISettingsServer(Core::System& system_) : ServiceFramework{system_, "set"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &ISettingsServer::GetLanguageCode, "GetLanguageCode"},
- {1, &ISettingsServer::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
- {2, &ISettingsServer::MakeLanguageCode, "MakeLanguageCode"},
- {3, &ISettingsServer::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
- {4, &ISettingsServer::GetRegionCode, "GetRegionCode"},
- {5, &ISettingsServer::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
- {6, &ISettingsServer::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
- {7, &ISettingsServer::GetKeyCodeMap, "GetKeyCodeMap"},
- {8, &ISettingsServer::GetQuestFlag, "GetQuestFlag"},
- {9, &ISettingsServer::GetKeyCodeMap2, "GetKeyCodeMap2"},
+ {0, C<&ISettingsServer::GetLanguageCode>, "GetLanguageCode"},
+ {1, C<&ISettingsServer::GetAvailableLanguageCodes>, "GetAvailableLanguageCodes"},
+ {2, C<&ISettingsServer::MakeLanguageCode>, "MakeLanguageCode"},
+ {3, C<&ISettingsServer::GetAvailableLanguageCodeCount>, "GetAvailableLanguageCodeCount"},
+ {4, C<&ISettingsServer::GetRegionCode>, "GetRegionCode"},
+ {5, C<&ISettingsServer::GetAvailableLanguageCodes2>, "GetAvailableLanguageCodes2"},
+ {6, C<&ISettingsServer::GetAvailableLanguageCodeCount2>, "GetAvailableLanguageCodeCount2"},
+ {7, C<&ISettingsServer::GetKeyCodeMap>, "GetKeyCodeMap"},
+ {8, C<&ISettingsServer::GetQuestFlag>, "GetQuestFlag"},
+ {9, C<&ISettingsServer::GetKeyCodeMap2>, "GetKeyCodeMap2"},
{10, nullptr, "GetFirmwareVersionForDebug"},
- {11, &ISettingsServer::GetDeviceNickName, "GetDeviceNickName"},
+ {11, C<&ISettingsServer::GetDeviceNickName>, "GetDeviceNickName"},
};
// clang-format on
@@ -81,86 +109,134 @@ ISettingsServer::ISettingsServer(Core::System& system_) : ServiceFramework{syste
ISettingsServer::~ISettingsServer() = default;
-void ISettingsServer::GetAvailableLanguageCodes(HLERequestContext& ctx) {
+Result ISettingsServer::GetLanguageCode(Out<LanguageCode> out_language_code) {
+ LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
+
+ *out_language_code = available_language_codes[static_cast<std::size_t>(
+ Settings::values.language_index.GetValue())];
+ R_SUCCEED();
+}
+
+Result ISettingsServer::GetAvailableLanguageCodes(
+ Out<s32> out_count, OutArray<LanguageCode, BufferAttr_HipcPointer> out_language_codes) {
LOG_DEBUG(Service_SET, "called");
- GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
+ const std::size_t max_amount = std::min(PRE_4_0_0_MAX_ENTRIES, out_language_codes.size());
+ *out_count = static_cast<s32>(std::min(available_language_codes.size(), max_amount));
+
+ memcpy(out_language_codes.data(), available_language_codes.data(),
+ static_cast<std::size_t>(*out_count) * sizeof(LanguageCode));
+
+ R_SUCCEED();
}
-void ISettingsServer::MakeLanguageCode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto index = rp.Pop<u32>();
+Result ISettingsServer::MakeLanguageCode(Out<LanguageCode> out_language_code, Language language) {
+ LOG_DEBUG(Service_SET, "called, language={}", language);
- if (index >= available_language_codes.size()) {
- LOG_ERROR(Service_SET, "Invalid language code index! index={}", index);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(Set::ResultInvalidLanguage);
- return;
- }
+ const auto index = static_cast<std::size_t>(language);
+ R_UNLESS(index < available_language_codes.size(), Set::ResultInvalidLanguage);
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushEnum(available_language_codes[index]);
+ *out_language_code = available_language_codes[index];
+ R_SUCCEED();
}
-void ISettingsServer::GetAvailableLanguageCodes2(HLERequestContext& ctx) {
+Result ISettingsServer::GetAvailableLanguageCodeCount(Out<s32> out_count) {
LOG_DEBUG(Service_SET, "called");
- GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
+ *out_count = PRE_4_0_0_MAX_ENTRIES;
+ R_SUCCEED();
}
-void ISettingsServer::GetAvailableLanguageCodeCount(HLERequestContext& ctx) {
+Result ISettingsServer::GetRegionCode(Out<SystemRegionCode> out_region_code) {
LOG_DEBUG(Service_SET, "called");
- PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
+ *out_region_code = static_cast<SystemRegionCode>(Settings::values.region_index.GetValue());
+ R_SUCCEED();
}
-void ISettingsServer::GetAvailableLanguageCodeCount2(HLERequestContext& ctx) {
+Result ISettingsServer::GetAvailableLanguageCodes2(
+ Out<s32> out_count, OutArray<LanguageCode, BufferAttr_HipcMapAlias> language_codes) {
LOG_DEBUG(Service_SET, "called");
- PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
+ const std::size_t max_amount = std::min(POST_4_0_0_MAX_ENTRIES, language_codes.size());
+ *out_count = static_cast<s32>(std::min(available_language_codes.size(), max_amount));
+
+ memcpy(language_codes.data(), available_language_codes.data(),
+ static_cast<std::size_t>(*out_count) * sizeof(LanguageCode));
+
+ R_SUCCEED();
}
-void ISettingsServer::GetQuestFlag(HLERequestContext& ctx) {
+Result ISettingsServer::GetAvailableLanguageCodeCount2(Out<s32> out_count) {
LOG_DEBUG(Service_SET, "called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<s32>(Settings::values.quest_flag.GetValue()));
+ *out_count = POST_4_0_0_MAX_ENTRIES;
+ R_SUCCEED();
}
-void ISettingsServer::GetLanguageCode(HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
+Result ISettingsServer::GetKeyCodeMap(
+ OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map) {
+ LOG_DEBUG(Service_SET, "called");
+
+ R_UNLESS(out_key_code_map != nullptr, ResultNullPointer);
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushEnum(
- available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]);
+ const auto language_code =
+ available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
+ const auto key_code =
+ std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
+ [=](const auto& element) { return element.first == language_code; });
+
+ if (key_code == language_to_layout.cend()) {
+ LOG_ERROR(Service_SET,
+ "Could not find keyboard layout for language index {}, defaulting to English us",
+ Settings::values.language_index.GetValue());
+ *out_key_code_map = KeyCodeMapEnglishUsInternational;
+ R_SUCCEED();
+ }
+
+ R_RETURN(GetKeyCodeMapImpl(*out_key_code_map, key_code->second, key_code->first));
}
-void ISettingsServer::GetRegionCode(HLERequestContext& ctx) {
+Result ISettingsServer::GetQuestFlag(Out<bool> out_quest_flag) {
LOG_DEBUG(Service_SET, "called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(Settings::values.region_index.GetValue()));
+ *out_quest_flag = Settings::values.quest_flag.GetValue();
+ R_SUCCEED();
}
-void ISettingsServer::GetKeyCodeMap(HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
- GetKeyCodeMapImpl(ctx);
-}
+Result ISettingsServer::GetKeyCodeMap2(
+ OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map) {
+ LOG_DEBUG(Service_SET, "called");
+
+ R_UNLESS(out_key_code_map != nullptr, ResultNullPointer);
-void ISettingsServer::GetKeyCodeMap2(HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
- GetKeyCodeMapImpl(ctx);
+ const auto language_code =
+ available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
+ const auto key_code =
+ std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
+ [=](const auto& element) { return element.first == language_code; });
+
+ if (key_code == language_to_layout.cend()) {
+ LOG_ERROR(Service_SET,
+ "Could not find keyboard layout for language index {}, defaulting to English us",
+ Settings::values.language_index.GetValue());
+ *out_key_code_map = KeyCodeMapEnglishUsInternational;
+ R_SUCCEED();
+ }
+
+ R_RETURN(GetKeyCodeMapImpl(*out_key_code_map, key_code->second, key_code->first));
}
-void ISettingsServer::GetDeviceNickName(HLERequestContext& ctx) {
+Result ISettingsServer::GetDeviceNickName(
+ OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name) {
LOG_DEBUG(Service_SET, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- ctx.WriteBuffer(Settings::values.device_name.GetValue());
+
+ const std::size_t string_size =
+ std::min(Settings::values.device_name.GetValue().size(), out_device_name->size());
+
+ *out_device_name = {};
+ memcpy(out_device_name->data(), Settings::values.device_name.GetValue().data(), string_size);
+ R_SUCCEED();
}
} // namespace Service::Set
diff --git a/src/core/hle/service/set/settings_server.h b/src/core/hle/service/set/settings_server.h
index 8304e8424..a39971fe9 100644
--- a/src/core/hle/service/set/settings_server.h
+++ b/src/core/hle/service/set/settings_server.h
@@ -3,6 +3,7 @@
#pragma once
+#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings_types.h"
@@ -11,6 +12,7 @@ class System;
}
namespace Service::Set {
+using KeyCodeMap = std::array<u8, 0x1000>;
LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
@@ -20,17 +22,30 @@ public:
~ISettingsServer() override;
private:
- void GetLanguageCode(HLERequestContext& ctx);
- void GetAvailableLanguageCodes(HLERequestContext& ctx);
- void MakeLanguageCode(HLERequestContext& ctx);
- void GetAvailableLanguageCodes2(HLERequestContext& ctx);
- void GetAvailableLanguageCodeCount(HLERequestContext& ctx);
- void GetAvailableLanguageCodeCount2(HLERequestContext& ctx);
- void GetQuestFlag(HLERequestContext& ctx);
- void GetRegionCode(HLERequestContext& ctx);
- void GetKeyCodeMap(HLERequestContext& ctx);
- void GetKeyCodeMap2(HLERequestContext& ctx);
- void GetDeviceNickName(HLERequestContext& ctx);
+ Result GetLanguageCode(Out<LanguageCode> out_language_code);
+
+ Result GetAvailableLanguageCodes(Out<s32> out_count,
+ OutArray<LanguageCode, BufferAttr_HipcPointer> language_codes);
+
+ Result MakeLanguageCode(Out<LanguageCode> out_language_code, Language language);
+
+ Result GetAvailableLanguageCodeCount(Out<s32> out_count);
+
+ Result GetRegionCode(Out<SystemRegionCode> out_region_code);
+
+ Result GetAvailableLanguageCodes2(
+ Out<s32> out_count, OutArray<LanguageCode, BufferAttr_HipcMapAlias> language_codes);
+
+ Result GetAvailableLanguageCodeCount2(Out<s32> out_count);
+
+ Result GetKeyCodeMap(OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map);
+
+ Result GetQuestFlag(Out<bool> out_quest_flag);
+
+ Result GetKeyCodeMap2(OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map);
+
+ Result GetDeviceNickName(
+ OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name);
};
} // namespace Service::Set
diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h
index ceb85b82a..29664e88c 100644
--- a/src/core/hle/service/set/settings_types.h
+++ b/src/core/hle/service/set/settings_types.h
@@ -12,6 +12,7 @@
#include "core/hle/service/psc/time/common.h"
namespace Service::Set {
+using SettingItemName = std::array<u8, 0x48>;
/// This is nn::settings::system::AudioOutputMode
enum class AudioOutputMode : u32 {
@@ -148,6 +149,28 @@ enum class KeyboardLayout : u32 {
ChineseTraditional = 14,
};
+// This is nn::settings::Language
+enum class Language : u32 {
+ Japanese,
+ AmericanEnglish,
+ French,
+ German,
+ Italian,
+ Spanish,
+ Chinese,
+ Korean,
+ Dutch,
+ Portiguesue,
+ Russian,
+ Taiwanese,
+ BritishEnglish,
+ CanadianFrench,
+ LatinAmericanSpanish,
+ SimplifiedCHhinese,
+ TraditionalChinese,
+ BrazilianPortuguese,
+};
+
/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
enum class LanguageCode : u64 {
JA = 0x000000000000616A,
@@ -391,16 +414,18 @@ struct FirmwareVersionFormat {
u8 major;
u8 minor;
u8 micro;
- INSERT_PADDING_BYTES(1);
+ INSERT_PADDING_BYTES_NOINIT(1);
u8 revision_major;
u8 revision_minor;
- INSERT_PADDING_BYTES(2);
+ INSERT_PADDING_BYTES_NOINIT(2);
std::array<char, 0x20> platform;
std::array<u8, 0x40> version_hash;
std::array<char, 0x18> display_version;
std::array<char, 0x80> display_title;
};
static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
+static_assert(std::is_trivial_v<FirmwareVersionFormat>,
+ "FirmwareVersionFormat type must be trivially copyable.");
/// This is nn::settings::system::HomeMenuScheme
struct HomeMenuScheme {
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index 7ef4a0ded..93925f783 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -17,6 +17,7 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
+#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/set/settings_server.h"
@@ -91,83 +92,83 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
: ServiceFramework{system_, "set:sys"}, m_system{system} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &ISystemSettingsServer::SetLanguageCode, "SetLanguageCode"},
+ {0, C<&ISystemSettingsServer::SetLanguageCode>, "SetLanguageCode"},
{1, nullptr, "SetNetworkSettings"},
{2, nullptr, "GetNetworkSettings"},
- {3, &ISystemSettingsServer::GetFirmwareVersion, "GetFirmwareVersion"},
- {4, &ISystemSettingsServer::GetFirmwareVersion2, "GetFirmwareVersion2"},
+ {3, C<&ISystemSettingsServer::GetFirmwareVersion>, "GetFirmwareVersion"},
+ {4, C<&ISystemSettingsServer::GetFirmwareVersion2>, "GetFirmwareVersion2"},
{5, nullptr, "GetFirmwareVersionDigest"},
- {7, &ISystemSettingsServer::GetLockScreenFlag, "GetLockScreenFlag"},
- {8, &ISystemSettingsServer::SetLockScreenFlag, "SetLockScreenFlag"},
+ {7, C<&ISystemSettingsServer::GetLockScreenFlag>, "GetLockScreenFlag"},
+ {8, C<&ISystemSettingsServer::SetLockScreenFlag>, "SetLockScreenFlag"},
{9, nullptr, "GetBacklightSettings"},
{10, nullptr, "SetBacklightSettings"},
{11, nullptr, "SetBluetoothDevicesSettings"},
{12, nullptr, "GetBluetoothDevicesSettings"},
- {13, &ISystemSettingsServer::GetExternalSteadyClockSourceId, "GetExternalSteadyClockSourceId"},
- {14, &ISystemSettingsServer::SetExternalSteadyClockSourceId, "SetExternalSteadyClockSourceId"},
- {15, &ISystemSettingsServer::GetUserSystemClockContext, "GetUserSystemClockContext"},
- {16, &ISystemSettingsServer::SetUserSystemClockContext, "SetUserSystemClockContext"},
- {17, &ISystemSettingsServer::GetAccountSettings, "GetAccountSettings"},
- {18, &ISystemSettingsServer::SetAccountSettings, "SetAccountSettings"},
+ {13, C<&ISystemSettingsServer::GetExternalSteadyClockSourceId>, "GetExternalSteadyClockSourceId"},
+ {14, C<&ISystemSettingsServer::SetExternalSteadyClockSourceId>, "SetExternalSteadyClockSourceId"},
+ {15, C<&ISystemSettingsServer::GetUserSystemClockContext>, "GetUserSystemClockContext"},
+ {16, C<&ISystemSettingsServer::SetUserSystemClockContext>, "SetUserSystemClockContext"},
+ {17, C<&ISystemSettingsServer::GetAccountSettings>, "GetAccountSettings"},
+ {18, C<&ISystemSettingsServer::SetAccountSettings>, "SetAccountSettings"},
{19, nullptr, "GetAudioVolume"},
{20, nullptr, "SetAudioVolume"},
- {21, &ISystemSettingsServer::GetEulaVersions, "GetEulaVersions"},
- {22, &ISystemSettingsServer::SetEulaVersions, "SetEulaVersions"},
- {23, &ISystemSettingsServer::GetColorSetId, "GetColorSetId"},
- {24, &ISystemSettingsServer::SetColorSetId, "SetColorSetId"},
+ {21, C<&ISystemSettingsServer::GetEulaVersions>, "GetEulaVersions"},
+ {22, C<&ISystemSettingsServer::SetEulaVersions>, "SetEulaVersions"},
+ {23, C<&ISystemSettingsServer::GetColorSetId>, "GetColorSetId"},
+ {24, C<&ISystemSettingsServer::SetColorSetId>, "SetColorSetId"},
{25, nullptr, "GetConsoleInformationUploadFlag"},
{26, nullptr, "SetConsoleInformationUploadFlag"},
{27, nullptr, "GetAutomaticApplicationDownloadFlag"},
{28, nullptr, "SetAutomaticApplicationDownloadFlag"},
- {29, &ISystemSettingsServer::GetNotificationSettings, "GetNotificationSettings"},
- {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"},
- {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"},
- {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"},
- {35, &ISystemSettingsServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
- {36, &ISystemSettingsServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
- {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
- {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"},
- {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"},
- {40, &ISystemSettingsServer::SetTvSettings, "SetTvSettings"},
+ {29, C<&ISystemSettingsServer::GetNotificationSettings>, "GetNotificationSettings"},
+ {30, C<&ISystemSettingsServer::SetNotificationSettings>, "SetNotificationSettings"},
+ {31, C<&ISystemSettingsServer::GetAccountNotificationSettings>, "GetAccountNotificationSettings"},
+ {32, C<&ISystemSettingsServer::SetAccountNotificationSettings>, "SetAccountNotificationSettings"},
+ {35, C<&ISystemSettingsServer::GetVibrationMasterVolume>, "GetVibrationMasterVolume"},
+ {36, C<&ISystemSettingsServer::SetVibrationMasterVolume>, "SetVibrationMasterVolume"},
+ {37, C<&ISystemSettingsServer::GetSettingsItemValueSize>, "GetSettingsItemValueSize"},
+ {38, C<&ISystemSettingsServer::GetSettingsItemValue>, "GetSettingsItemValue"},
+ {39, C<&ISystemSettingsServer::GetTvSettings>, "GetTvSettings"},
+ {40, C<&ISystemSettingsServer::SetTvSettings>, "SetTvSettings"},
{41, nullptr, "GetEdid"},
{42, nullptr, "SetEdid"},
- {43, &ISystemSettingsServer::GetAudioOutputMode, "GetAudioOutputMode"},
- {44, &ISystemSettingsServer::SetAudioOutputMode, "SetAudioOutputMode"},
- {45, &ISystemSettingsServer::GetSpeakerAutoMuteFlag , "GetSpeakerAutoMuteFlag"},
- {46, &ISystemSettingsServer::SetSpeakerAutoMuteFlag , "SetSpeakerAutoMuteFlag"},
- {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"},
- {48, &ISystemSettingsServer::SetQuestFlag, "SetQuestFlag"},
+ {43, C<&ISystemSettingsServer::GetAudioOutputMode>, "GetAudioOutputMode"},
+ {44, C<&ISystemSettingsServer::SetAudioOutputMode>, "SetAudioOutputMode"},
+ {45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag> , "GetSpeakerAutoMuteFlag"},
+ {46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag> , "SetSpeakerAutoMuteFlag"},
+ {47, C<&ISystemSettingsServer::GetQuestFlag>, "GetQuestFlag"},
+ {48, C<&ISystemSettingsServer::SetQuestFlag>, "SetQuestFlag"},
{49, nullptr, "GetDataDeletionSettings"},
{50, nullptr, "SetDataDeletionSettings"},
{51, nullptr, "GetInitialSystemAppletProgramId"},
{52, nullptr, "GetOverlayDispProgramId"},
- {53, &ISystemSettingsServer::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"},
- {54, &ISystemSettingsServer::SetDeviceTimeZoneLocationName, "SetDeviceTimeZoneLocationName"},
+ {53, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationName>, "GetDeviceTimeZoneLocationName"},
+ {54, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationName>, "SetDeviceTimeZoneLocationName"},
{55, nullptr, "GetWirelessCertificationFileSize"},
{56, nullptr, "GetWirelessCertificationFile"},
- {57, &ISystemSettingsServer::SetRegionCode, "SetRegionCode"},
- {58, &ISystemSettingsServer::GetNetworkSystemClockContext, "GetNetworkSystemClockContext"},
- {59, &ISystemSettingsServer::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
- {60, &ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
- {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
- {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"},
- {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
- {64, &ISystemSettingsServer::SetPrimaryAlbumStorage, "SetPrimaryAlbumStorage"},
+ {57, C<&ISystemSettingsServer::SetRegionCode>, "SetRegionCode"},
+ {58, C<&ISystemSettingsServer::GetNetworkSystemClockContext>, "GetNetworkSystemClockContext"},
+ {59, C<&ISystemSettingsServer::SetNetworkSystemClockContext>, "SetNetworkSystemClockContext"},
+ {60, C<&ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled>, "IsUserSystemClockAutomaticCorrectionEnabled"},
+ {61, C<&ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled>, "SetUserSystemClockAutomaticCorrectionEnabled"},
+ {62, C<&ISystemSettingsServer::GetDebugModeFlag>, "GetDebugModeFlag"},
+ {63, C<&ISystemSettingsServer::GetPrimaryAlbumStorage>, "GetPrimaryAlbumStorage"},
+ {64, C<&ISystemSettingsServer::SetPrimaryAlbumStorage>, "SetPrimaryAlbumStorage"},
{65, nullptr, "GetUsb30EnableFlag"},
{66, nullptr, "SetUsb30EnableFlag"},
- {67, &ISystemSettingsServer::GetBatteryLot, "GetBatteryLot"},
- {68, &ISystemSettingsServer::GetSerialNumber, "GetSerialNumber"},
- {69, &ISystemSettingsServer::GetNfcEnableFlag, "GetNfcEnableFlag"},
- {70, &ISystemSettingsServer::SetNfcEnableFlag, "SetNfcEnableFlag"},
- {71, &ISystemSettingsServer::GetSleepSettings, "GetSleepSettings"},
- {72, &ISystemSettingsServer::SetSleepSettings, "SetSleepSettings"},
- {73, &ISystemSettingsServer::GetWirelessLanEnableFlag, "GetWirelessLanEnableFlag"},
- {74, &ISystemSettingsServer::SetWirelessLanEnableFlag, "SetWirelessLanEnableFlag"},
- {75, &ISystemSettingsServer::GetInitialLaunchSettings, "GetInitialLaunchSettings"},
- {76, &ISystemSettingsServer::SetInitialLaunchSettings, "SetInitialLaunchSettings"},
- {77, &ISystemSettingsServer::GetDeviceNickName, "GetDeviceNickName"},
- {78, &ISystemSettingsServer::SetDeviceNickName, "SetDeviceNickName"},
- {79, &ISystemSettingsServer::GetProductModel, "GetProductModel"},
+ {67, C<&ISystemSettingsServer::GetBatteryLot>, "GetBatteryLot"},
+ {68, C<&ISystemSettingsServer::GetSerialNumber>, "GetSerialNumber"},
+ {69, C<&ISystemSettingsServer::GetNfcEnableFlag>, "GetNfcEnableFlag"},
+ {70, C<&ISystemSettingsServer::SetNfcEnableFlag>, "SetNfcEnableFlag"},
+ {71, C<&ISystemSettingsServer::GetSleepSettings>, "GetSleepSettings"},
+ {72, C<&ISystemSettingsServer::SetSleepSettings>, "SetSleepSettings"},
+ {73, C<&ISystemSettingsServer::GetWirelessLanEnableFlag>, "GetWirelessLanEnableFlag"},
+ {74, C<&ISystemSettingsServer::SetWirelessLanEnableFlag>, "SetWirelessLanEnableFlag"},
+ {75, C<&ISystemSettingsServer::GetInitialLaunchSettings>, "GetInitialLaunchSettings"},
+ {76, C<&ISystemSettingsServer::SetInitialLaunchSettings>, "SetInitialLaunchSettings"},
+ {77, C<&ISystemSettingsServer::GetDeviceNickName>, "GetDeviceNickName"},
+ {78, C<&ISystemSettingsServer::SetDeviceNickName>, "SetDeviceNickName"},
+ {79, C<&ISystemSettingsServer::GetProductModel>, "GetProductModel"},
{80, nullptr, "GetLdnChannel"},
{81, nullptr, "SetLdnChannel"},
{82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"},
@@ -176,25 +177,25 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{85, nullptr, "SetPtmBatteryLot"},
{86, nullptr, "GetPtmFuelGaugeParameter"},
{87, nullptr, "SetPtmFuelGaugeParameter"},
- {88, &ISystemSettingsServer::GetBluetoothEnableFlag, "GetBluetoothEnableFlag"},
- {89, &ISystemSettingsServer::SetBluetoothEnableFlag, "SetBluetoothEnableFlag"},
- {90, &ISystemSettingsServer::GetMiiAuthorId, "GetMiiAuthorId"},
+ {88, C<&ISystemSettingsServer::GetBluetoothEnableFlag>, "GetBluetoothEnableFlag"},
+ {89, C<&ISystemSettingsServer::SetBluetoothEnableFlag>, "SetBluetoothEnableFlag"},
+ {90, C<&ISystemSettingsServer::GetMiiAuthorId>, "GetMiiAuthorId"},
{91, nullptr, "SetShutdownRtcValue"},
{92, nullptr, "GetShutdownRtcValue"},
{93, nullptr, "AcquireFatalDirtyFlagEventHandle"},
{94, nullptr, "GetFatalDirtyFlags"},
- {95, &ISystemSettingsServer::GetAutoUpdateEnableFlag, "GetAutoUpdateEnableFlag"},
- {96, &ISystemSettingsServer::SetAutoUpdateEnableFlag, "SetAutoUpdateEnableFlag"},
+ {95, C<&ISystemSettingsServer::GetAutoUpdateEnableFlag>, "GetAutoUpdateEnableFlag"},
+ {96, C<&ISystemSettingsServer::SetAutoUpdateEnableFlag>, "SetAutoUpdateEnableFlag"},
{97, nullptr, "GetNxControllerSettings"},
{98, nullptr, "SetNxControllerSettings"},
- {99, &ISystemSettingsServer::GetBatteryPercentageFlag, "GetBatteryPercentageFlag"},
- {100, &ISystemSettingsServer::SetBatteryPercentageFlag, "SetBatteryPercentageFlag"},
+ {99, C<&ISystemSettingsServer::GetBatteryPercentageFlag>, "GetBatteryPercentageFlag"},
+ {100, C<&ISystemSettingsServer::SetBatteryPercentageFlag>, "SetBatteryPercentageFlag"},
{101, nullptr, "GetExternalRtcResetFlag"},
{102, nullptr, "SetExternalRtcResetFlag"},
{103, nullptr, "GetUsbFullKeyEnableFlag"},
{104, nullptr, "SetUsbFullKeyEnableFlag"},
- {105, &ISystemSettingsServer::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"},
- {106, &ISystemSettingsServer::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"},
+ {105, C<&ISystemSettingsServer::SetExternalSteadyClockInternalOffset>, "SetExternalSteadyClockInternalOffset"},
+ {106, C<&ISystemSettingsServer::GetExternalSteadyClockInternalOffset>, "GetExternalSteadyClockInternalOffset"},
{107, nullptr, "GetBacklightSettingsEx"},
{108, nullptr, "SetBacklightSettingsEx"},
{109, nullptr, "GetHeadphoneVolumeWarningCount"},
@@ -208,14 +209,14 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{117, nullptr, "GetHeadphoneVolumeUpdateFlag"},
{118, nullptr, "SetHeadphoneVolumeUpdateFlag"},
{119, nullptr, "NeedsToUpdateHeadphoneVolume"},
- {120, &ISystemSettingsServer::GetPushNotificationActivityModeOnSleep, "GetPushNotificationActivityModeOnSleep"},
- {121, &ISystemSettingsServer::SetPushNotificationActivityModeOnSleep, "SetPushNotificationActivityModeOnSleep"},
+ {120, C<&ISystemSettingsServer::GetPushNotificationActivityModeOnSleep>, "GetPushNotificationActivityModeOnSleep"},
+ {121, C<&ISystemSettingsServer::SetPushNotificationActivityModeOnSleep>, "SetPushNotificationActivityModeOnSleep"},
{122, nullptr, "GetServiceDiscoveryControlSettings"},
{123, nullptr, "SetServiceDiscoveryControlSettings"},
- {124, &ISystemSettingsServer::GetErrorReportSharePermission, "GetErrorReportSharePermission"},
- {125, &ISystemSettingsServer::SetErrorReportSharePermission, "SetErrorReportSharePermission"},
- {126, &ISystemSettingsServer::GetAppletLaunchFlags, "GetAppletLaunchFlags"},
- {127, &ISystemSettingsServer::SetAppletLaunchFlags, "SetAppletLaunchFlags"},
+ {124, C<&ISystemSettingsServer::GetErrorReportSharePermission>, "GetErrorReportSharePermission"},
+ {125, C<&ISystemSettingsServer::SetErrorReportSharePermission>, "SetErrorReportSharePermission"},
+ {126, C<&ISystemSettingsServer::GetAppletLaunchFlags>, "GetAppletLaunchFlags"},
+ {127, C<&ISystemSettingsServer::SetAppletLaunchFlags>, "SetAppletLaunchFlags"},
{128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"},
{129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"},
{130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"},
@@ -224,8 +225,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"},
{134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"},
{135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"},
- {136, &ISystemSettingsServer::GetKeyboardLayout, "GetKeyboardLayout"},
- {137, &ISystemSettingsServer::SetKeyboardLayout, "SetKeyboardLayout"},
+ {136, C<&ISystemSettingsServer::GetKeyboardLayout>, "GetKeyboardLayout"},
+ {137, C<&ISystemSettingsServer::SetKeyboardLayout>, "SetKeyboardLayout"},
{138, nullptr, "GetWebInspectorFlag"},
{139, nullptr, "GetAllowedSslHosts"},
{140, nullptr, "GetHostFsMountPoint"},
@@ -238,10 +239,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
{148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
{149, nullptr, "GetRebootlessSystemUpdateVersion"},
- {150, &ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime, "GetDeviceTimeZoneLocationUpdatedTime"},
- {151, &ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime, "SetDeviceTimeZoneLocationUpdatedTime"},
- {152, &ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
- {153, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime, "SetUserSystemClockAutomaticCorrectionUpdatedTime"},
+ {150, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime>, "GetDeviceTimeZoneLocationUpdatedTime"},
+ {151, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime>, "SetDeviceTimeZoneLocationUpdatedTime"},
+ {152, C<&ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime>, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
+ {153, C<&ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime>, "SetUserSystemClockAutomaticCorrectionUpdatedTime"},
{154, nullptr, "GetAccountOnlineStorageSettings"},
{155, nullptr, "SetAccountOnlineStorageSettings"},
{156, nullptr, "GetPctlReadyFlag"},
@@ -258,11 +259,11 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{167, nullptr, "SetUsb30DeviceEnableFlag"},
{168, nullptr, "GetThemeId"},
{169, nullptr, "SetThemeId"},
- {170, &ISystemSettingsServer::GetChineseTraditionalInputMethod, "GetChineseTraditionalInputMethod"},
+ {170, C<&ISystemSettingsServer::GetChineseTraditionalInputMethod>, "GetChineseTraditionalInputMethod"},
{171, nullptr, "SetChineseTraditionalInputMethod"},
{172, nullptr, "GetPtmCycleCountReliability"},
{173, nullptr, "SetPtmCycleCountReliability"},
- {174, &ISystemSettingsServer::GetHomeMenuScheme, "GetHomeMenuScheme"},
+ {174, C<&ISystemSettingsServer::GetHomeMenuScheme>, "GetHomeMenuScheme"},
{175, nullptr, "GetThemeSettings"},
{176, nullptr, "SetThemeSettings"},
{177, nullptr, "GetThemeKey"},
@@ -273,10 +274,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{182, nullptr, "SetT"},
{183, nullptr, "GetPlatformRegion"},
{184, nullptr, "SetPlatformRegion"},
- {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
+ {185, C<&ISystemSettingsServer::GetHomeMenuSchemeModel>, "GetHomeMenuSchemeModel"},
{186, nullptr, "GetMemoryUsageRateFlag"},
- {187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"},
- {188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"},
+ {187, C<&ISystemSettingsServer::GetTouchScreenMode>, "GetTouchScreenMode"},
+ {188, C<&ISystemSettingsServer::SetTouchScreenMode>, "SetTouchScreenMode"},
{189, nullptr, "GetButtonConfigSettingsFull"},
{190, nullptr, "SetButtonConfigSettingsFull"},
{191, nullptr, "GetButtonConfigSettingsEmbedded"},
@@ -289,10 +290,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
{199, nullptr, "GetButtonConfigRegisteredSettings"},
{200, nullptr, "SetButtonConfigRegisteredSettings"},
- {201, &ISystemSettingsServer::GetFieldTestingFlag, "GetFieldTestingFlag"},
+ {201, C<&ISystemSettingsServer::GetFieldTestingFlag>, "GetFieldTestingFlag"},
{202, nullptr, "SetFieldTestingFlag"},
- {203, &ISystemSettingsServer::GetPanelCrcMode, "GetPanelCrcMode"},
- {204, &ISystemSettingsServer::SetPanelCrcMode, "SetPanelCrcMode"},
+ {203, C<&ISystemSettingsServer::GetPanelCrcMode>, "GetPanelCrcMode"},
+ {204, C<&ISystemSettingsServer::SetPanelCrcMode>, "SetPanelCrcMode"},
{205, nullptr, "GetNxControllerSettingsEx"},
{206, nullptr, "SetNxControllerSettingsEx"},
{207, nullptr, "GetHearingProtectionSafeguardFlag"},
@@ -422,178 +423,134 @@ bool ISystemSettingsServer::StoreSettingsFile(std::filesystem::path& path, auto&
return true;
}
-void ISystemSettingsServer::SetLanguageCode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.language_code = rp.PopEnum<LanguageCode>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, language_code={}", m_system_settings.language_code);
+Result ISystemSettingsServer::SetLanguageCode(LanguageCode language_code) {
+ LOG_INFO(Service_SET, "called, language_code={}", language_code);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.language_code = language_code;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetFirmwareVersion(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetFirmwareVersion(
+ OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data) {
LOG_DEBUG(Service_SET, "called");
- FirmwareVersionFormat firmware_data{};
- const auto result =
- GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1);
-
- if (result.IsSuccess()) {
- ctx.WriteBuffer(firmware_data);
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ R_RETURN(GetFirmwareVersionImpl(*out_firmware_data, system, GetFirmwareVersionType::Version1));
}
-void ISystemSettingsServer::GetFirmwareVersion2(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetFirmwareVersion2(
+ OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data) {
LOG_DEBUG(Service_SET, "called");
- FirmwareVersionFormat firmware_data{};
- const auto result =
- GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2);
-
- if (result.IsSuccess()) {
- ctx.WriteBuffer(firmware_data);
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ R_RETURN(GetFirmwareVersionImpl(*out_firmware_data, system, GetFirmwareVersionType::Version2));
}
-void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called");
-
- Common::UUID id{};
- const auto res = GetExternalSteadyClockSourceId(id);
+Result ISystemSettingsServer::GetLockScreenFlag(Out<bool> out_lock_screen_flag) {
+ LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag);
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)};
- rb.Push(res);
- rb.PushRaw(id);
+ *out_lock_screen_flag = m_system_settings.lock_screen_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called");
+Result ISystemSettingsServer::SetLockScreenFlag(bool lock_screen_flag) {
+ LOG_INFO(Service_SET, "called, lock_screen_flag={}", lock_screen_flag);
- IPC::RequestParser rp{ctx};
- const auto id{rp.PopRaw<Common::UUID>()};
+ m_system_settings.lock_screen_flag = lock_screen_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
+}
- const auto res = SetExternalSteadyClockSourceId(id);
+Result ISystemSettingsServer::GetExternalSteadyClockSourceId(
+ Out<Common::UUID> out_clock_source_id) {
+ LOG_INFO(Service_SET, "called, clock_source_id={}",
+ m_private_settings.external_clock_source_id.FormattedString());
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ *out_clock_source_id = m_private_settings.external_clock_source_id;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called");
-
- Service::PSC::Time::SystemClockContext context{};
- const auto res = GetUserSystemClockContext(context);
+Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& clock_source_id) {
+ LOG_INFO(Service_SET, "called, clock_source_id={}", clock_source_id.FormattedString());
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
- rb.Push(res);
- rb.PushRaw(context);
+ m_private_settings.external_clock_source_id = clock_source_id;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetUserSystemClockContext(
+ Out<Service::PSC::Time::SystemClockContext> out_clock_context) {
LOG_INFO(Service_SET, "called");
- IPC::RequestParser rp{ctx};
- const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
-
- const auto res = SetUserSystemClockContext(context);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ *out_clock_context = m_system_settings.user_system_clock_context;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetLockScreenFlag(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.lock_screen_flag);
-}
+Result ISystemSettingsServer::SetUserSystemClockContext(
+ const Service::PSC::Time::SystemClockContext& clock_context) {
+ LOG_INFO(Service_SET, "called");
-void ISystemSettingsServer::SetLockScreenFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.lock_screen_flag = rp.Pop<bool>();
+ m_system_settings.user_system_clock_context = clock_context;
SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetAccountSettings(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called");
+Result ISystemSettingsServer::GetAccountSettings(Out<AccountSettings> out_account_settings) {
+ LOG_INFO(Service_SET, "called, account_settings_flags={}",
+ m_system_settings.account_settings.flags);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.account_settings);
+ *out_account_settings = m_system_settings.account_settings;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetAccountSettings(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.account_settings = rp.PopRaw<AccountSettings>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, account_settings_flags={}",
- m_system_settings.account_settings.flags);
+Result ISystemSettingsServer::SetAccountSettings(AccountSettings account_settings) {
+ LOG_INFO(Service_SET, "called, account_settings_flags={}", account_settings.flags);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.account_settings = account_settings;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetEulaVersions(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetEulaVersions(
+ Out<s32> out_count, OutArray<EulaVersion, BufferAttr_HipcMapAlias> out_eula_versions) {
LOG_INFO(Service_SET, "called, elements={}", m_system_settings.eula_version_count);
- ctx.WriteBuffer(m_system_settings.eula_versions);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.eula_version_count);
+ *out_count =
+ std::min(m_system_settings.eula_version_count, static_cast<s32>(out_eula_versions.size()));
+ memcpy(out_eula_versions.data(), m_system_settings.eula_versions.data(),
+ static_cast<std::size_t>(*out_count) * sizeof(EulaVersion));
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetEulaVersions(HLERequestContext& ctx) {
- const auto elements = ctx.GetReadBufferNumElements<EulaVersion>();
- const auto buffer_data = ctx.ReadBuffer();
+Result ISystemSettingsServer::SetEulaVersions(
+ InArray<EulaVersion, BufferAttr_HipcMapAlias> eula_versions) {
+ LOG_INFO(Service_SET, "called, elements={}", eula_versions.size());
- LOG_INFO(Service_SET, "called, elements={}", elements);
- ASSERT(elements <= m_system_settings.eula_versions.size());
+ ASSERT(eula_versions.size() <= m_system_settings.eula_versions.size());
- m_system_settings.eula_version_count = static_cast<u32>(elements);
- std::memcpy(&m_system_settings.eula_versions, buffer_data.data(),
- sizeof(EulaVersion) * elements);
+ m_system_settings.eula_version_count = static_cast<s32>(eula_versions.size());
+ std::memcpy(m_system_settings.eula_versions.data(), eula_versions.data(),
+ eula_versions.size() * sizeof(EulaVersion));
SetSaveNeeded();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetColorSetId(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetColorSetId(Out<ColorSet> out_color_set_id) {
LOG_DEBUG(Service_SET, "called, color_set=", m_system_settings.color_set_id);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(m_system_settings.color_set_id);
+ *out_color_set_id = m_system_settings.color_set_id;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetColorSetId(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.color_set_id = rp.PopEnum<ColorSet>();
- SetSaveNeeded();
-
- LOG_DEBUG(Service_SET, "called, color_set={}", m_system_settings.color_set_id);
+Result ISystemSettingsServer::SetColorSetId(ColorSet color_set_id) {
+ LOG_DEBUG(Service_SET, "called, color_set={}", color_set_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.color_set_id = color_set_id;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetNotificationSettings(
+ Out<NotificationSettings> out_notification_settings) {
LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}",
m_system_settings.notification_settings.flags.raw,
m_system_settings.notification_settings.volume,
@@ -602,77 +559,67 @@ void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) {
m_system_settings.notification_settings.stop_time.hour,
m_system_settings.notification_settings.stop_time.minute);
- IPC::ResponseBuilder rb{ctx, 8};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.notification_settings);
+ *out_notification_settings = m_system_settings.notification_settings;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetNotificationSettings(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.notification_settings = rp.PopRaw<NotificationSettings>();
- SetSaveNeeded();
-
+Result ISystemSettingsServer::SetNotificationSettings(
+ const NotificationSettings& notification_settings) {
LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}",
- m_system_settings.notification_settings.flags.raw,
- m_system_settings.notification_settings.volume,
- m_system_settings.notification_settings.start_time.hour,
- m_system_settings.notification_settings.start_time.minute,
- m_system_settings.notification_settings.stop_time.hour,
- m_system_settings.notification_settings.stop_time.minute);
+ notification_settings.flags.raw, notification_settings.volume,
+ notification_settings.start_time.hour, notification_settings.start_time.minute,
+ notification_settings.stop_time.hour, notification_settings.stop_time.minute);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.notification_settings = notification_settings;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetAccountNotificationSettings(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetAccountNotificationSettings(
+ Out<s32> out_count, OutArray<AccountNotificationSettings, BufferAttr_HipcMapAlias>
+ out_account_notification_settings) {
LOG_INFO(Service_SET, "called, elements={}",
m_system_settings.account_notification_settings_count);
- ctx.WriteBuffer(m_system_settings.account_notification_settings);
+ *out_count = std::min(m_system_settings.account_notification_settings_count,
+ static_cast<s32>(out_account_notification_settings.size()));
+ memcpy(out_account_notification_settings.data(),
+ m_system_settings.account_notification_settings.data(),
+ static_cast<std::size_t>(*out_count) * sizeof(AccountNotificationSettings));
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.account_notification_settings_count);
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ctx) {
- const auto elements = ctx.GetReadBufferNumElements<AccountNotificationSettings>();
- const auto buffer_data = ctx.ReadBuffer();
-
- LOG_INFO(Service_SET, "called, elements={}", elements);
+Result ISystemSettingsServer::SetAccountNotificationSettings(
+ InArray<AccountNotificationSettings, BufferAttr_HipcMapAlias> account_notification_settings) {
+ LOG_INFO(Service_SET, "called, elements={}", account_notification_settings.size());
- ASSERT(elements <= m_system_settings.account_notification_settings.size());
+ ASSERT(account_notification_settings.size() <=
+ m_system_settings.account_notification_settings.size());
- m_system_settings.account_notification_settings_count = static_cast<u32>(elements);
- std::memcpy(&m_system_settings.account_notification_settings, buffer_data.data(),
- elements * sizeof(AccountNotificationSettings));
+ m_system_settings.account_notification_settings_count =
+ static_cast<s32>(account_notification_settings.size());
+ std::memcpy(m_system_settings.account_notification_settings.data(),
+ account_notification_settings.data(),
+ account_notification_settings.size() * sizeof(AccountNotificationSettings));
SetSaveNeeded();
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
- f32 vibration_master_volume = {};
- const auto result = GetVibrationMasterVolume(vibration_master_volume);
+Result ISystemSettingsServer::GetVibrationMasterVolume(Out<f32> vibration_master_volume) {
+ LOG_INFO(Service_SET, "called, vibration_master_volume={}",
+ m_system_settings.vibration_master_volume);
- LOG_INFO(Service_SET, "called, master_volume={}", vibration_master_volume);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(result);
- rb.Push(vibration_master_volume);
+ *vibration_master_volume = m_system_settings.vibration_master_volume;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto vibration_master_volume = rp.PopRaw<f32>();
-
- LOG_INFO(Service_SET, "called, elements={}", m_system_settings.vibration_master_volume);
+Result ISystemSettingsServer::SetVibrationMasterVolume(f32 vibration_master_volume) {
+ LOG_INFO(Service_SET, "called, vibration_master_volume={}", vibration_master_volume);
- const auto result = SetVibrationMasterVolume(vibration_master_volume);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ m_system_settings.vibration_master_volume = vibration_master_volume;
+ SetSaveNeeded();
+ R_SUCCEED();
}
// FIXME: implement support for the real system_settings.ini
@@ -734,55 +681,38 @@ static Settings GetSettings() {
return ret;
}
-void ISystemSettingsServer::GetSettingsItemValueSize(HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "called");
+Result ISystemSettingsServer::GetSettingsItemValueSize(
+ Out<u64> out_size, InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer) {
+ const std::string setting_category{Common::StringFromBuffer(*setting_category_buffer)};
+ const std::string setting_name{Common::StringFromBuffer(*setting_name_buffer)};
- // The category of the setting. This corresponds to the top-level keys of
- // system_settings.ini.
- const auto setting_category_buf{ctx.ReadBuffer(0)};
- const std::string setting_category{Common::StringFromBuffer(setting_category_buf)};
+ LOG_DEBUG(Service_SET, "called, category={}, name={}", setting_category, setting_name);
- // The name of the setting. This corresponds to the second-level keys of
- // system_settings.ini.
- const auto setting_name_buf{ctx.ReadBuffer(1)};
- const std::string setting_name{Common::StringFromBuffer(setting_name_buf)};
+ *out_size = 0;
auto settings{GetSettings()};
- u64 response_size{0};
-
if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
- response_size = settings[setting_category][setting_name].size();
+ *out_size = settings[setting_category][setting_name].size();
}
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess);
- rb.Push(response_size);
+ R_UNLESS(*out_size != 0, ResultUnknown);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetSettingsItemValue(HLERequestContext& ctx) {
- // The category of the setting. This corresponds to the top-level keys of
- // system_settings.ini.
- const auto setting_category_buf{ctx.ReadBuffer(0)};
- const std::string setting_category{Common::StringFromBuffer(setting_category_buf)};
-
- // The name of the setting. This corresponds to the second-level keys of
- // system_settings.ini.
- const auto setting_name_buf{ctx.ReadBuffer(1)};
- const std::string setting_name{Common::StringFromBuffer(setting_name_buf)};
+Result ISystemSettingsServer::GetSettingsItemValue(
+ OutBuffer<BufferAttr_HipcMapAlias> out_data,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer) {
+ const std::string setting_category{Common::StringFromBuffer(*setting_category_buffer)};
+ const std::string setting_name{Common::StringFromBuffer(*setting_name_buffer)};
- std::vector<u8> value;
- auto response = GetSettingsItemValue(value, setting_category, setting_name);
+ LOG_INFO(Service_SET, "called, category={}, name={}", setting_category, setting_name);
- LOG_INFO(Service_SET, "called. category={}, name={} -- res=0x{:X}", setting_category,
- setting_name, response.raw);
-
- ctx.WriteBuffer(value.data(), value.size());
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(response);
+ R_RETURN(GetSettingsItemValueImpl(out_data, setting_category, setting_name));
}
-void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetTvSettings(Out<TvSettings> out_tv_settings) {
LOG_INFO(Service_SET,
"called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, "
"rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}",
@@ -793,371 +723,335 @@ void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) {
m_system_settings.tv_settings.tv_resolution,
m_system_settings.tv_settings.tv_underscan);
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.tv_settings);
+ *out_tv_settings = m_system_settings.tv_settings;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.tv_settings = rp.PopRaw<TvSettings>();
- SetSaveNeeded();
+Result ISystemSettingsServer::SetTvSettings(TvSettings tv_settings) {
LOG_INFO(Service_SET,
"called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, "
"rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}",
- m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode,
- m_system_settings.tv_settings.contrast_ratio,
- m_system_settings.tv_settings.hdmi_content_type,
- m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama,
- m_system_settings.tv_settings.tv_resolution,
- m_system_settings.tv_settings.tv_underscan);
+ tv_settings.flags.raw, tv_settings.cmu_mode, tv_settings.contrast_ratio,
+ tv_settings.hdmi_content_type, tv_settings.rgb_range, tv_settings.tv_gama,
+ tv_settings.tv_resolution, tv_settings.tv_underscan);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.tv_settings = tv_settings;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetAudioOutputMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto target{rp.PopEnum<AudioOutputModeTarget>()};
-
- AudioOutputMode output_mode{};
- const auto result = GetAudioOutputMode(output_mode, target);
-
- LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
+Result ISystemSettingsServer::GetAudioOutputMode(Out<AudioOutputMode> out_output_mode,
+ AudioOutputModeTarget target) {
+ switch (target) {
+ case AudioOutputModeTarget::Hdmi:
+ *out_output_mode = m_system_settings.audio_output_mode_hdmi;
+ break;
+ case AudioOutputModeTarget::Speaker:
+ *out_output_mode = m_system_settings.audio_output_mode_speaker;
+ break;
+ case AudioOutputModeTarget::Headphone:
+ *out_output_mode = m_system_settings.audio_output_mode_headphone;
+ break;
+ case AudioOutputModeTarget::Type3:
+ *out_output_mode = m_system_settings.audio_output_mode_type3;
+ break;
+ case AudioOutputModeTarget::Type4:
+ *out_output_mode = m_system_settings.audio_output_mode_type4;
+ break;
+ default:
+ LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target);
+ }
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(result);
- rb.PushEnum(output_mode);
+ LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, *out_output_mode);
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetAudioOutputMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto target{rp.PopEnum<AudioOutputModeTarget>()};
- const auto output_mode{rp.PopEnum<AudioOutputMode>()};
-
- const auto result = SetAudioOutputMode(target, output_mode);
-
+Result ISystemSettingsServer::SetAudioOutputMode(AudioOutputModeTarget target,
+ AudioOutputMode output_mode) {
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ switch (target) {
+ case AudioOutputModeTarget::Hdmi:
+ m_system_settings.audio_output_mode_hdmi = output_mode;
+ break;
+ case AudioOutputModeTarget::Speaker:
+ m_system_settings.audio_output_mode_speaker = output_mode;
+ break;
+ case AudioOutputModeTarget::Headphone:
+ m_system_settings.audio_output_mode_headphone = output_mode;
+ break;
+ case AudioOutputModeTarget::Type3:
+ m_system_settings.audio_output_mode_type3 = output_mode;
+ break;
+ case AudioOutputModeTarget::Type4:
+ m_system_settings.audio_output_mode_type4 = output_mode;
+ break;
+ default:
+ LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target);
+ }
+
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetSpeakerAutoMuteFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetSpeakerAutoMuteFlag(
+ Out<bool> out_force_mute_on_headphone_removed) {
LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}",
m_system_settings.force_mute_on_headphone_removed);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.force_mute_on_headphone_removed);
+ *out_force_mute_on_headphone_removed = m_system_settings.force_mute_on_headphone_removed;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetSpeakerAutoMuteFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.force_mute_on_headphone_removed = rp.PopRaw<bool>();
- SetSaveNeeded();
-
+Result ISystemSettingsServer::SetSpeakerAutoMuteFlag(bool force_mute_on_headphone_removed) {
LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}",
- m_system_settings.force_mute_on_headphone_removed);
+ force_mute_on_headphone_removed);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.force_mute_on_headphone_removed = force_mute_on_headphone_removed;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetQuestFlag(Out<QuestFlag> out_quest_flag) {
LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(m_system_settings.quest_flag);
+ *out_quest_flag = m_system_settings.quest_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.quest_flag = rp.PopEnum<QuestFlag>();
- SetSaveNeeded();
+Result ISystemSettingsServer::SetQuestFlag(QuestFlag quest_flag) {
+ LOG_INFO(Service_SET, "called, quest_flag={}", quest_flag);
- LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.quest_flag = quest_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
+ Out<Service::PSC::Time::LocationName> out_name) {
LOG_INFO(Service_SET, "called");
- Service::PSC::Time::LocationName name{};
- const auto res = GetDeviceTimeZoneLocationName(name);
-
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
- rb.Push(res);
- rb.PushRaw<Service::PSC::Time::LocationName>(name);
+ *out_name = m_system_settings.device_time_zone_location_name;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
+Result ISystemSettingsServer::SetDeviceTimeZoneLocationName(
+ const Service::PSC::Time::LocationName& name) {
LOG_INFO(Service_SET, "called");
- IPC::RequestParser rp{ctx};
- auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
-
- const auto res = SetDeviceTimeZoneLocationName(name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
-}
-
-void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.region_code = rp.PopEnum<SystemRegionCode>();
+ m_system_settings.device_time_zone_location_name = name;
SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called");
+Result ISystemSettingsServer::SetRegionCode(SystemRegionCode region_code) {
+ LOG_INFO(Service_SET, "called, region_code={}", region_code);
- Service::PSC::Time::SystemClockContext context{};
- const auto res = GetNetworkSystemClockContext(context);
-
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
- rb.Push(res);
- rb.PushRaw(context);
+ m_system_settings.region_code = region_code;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetNetworkSystemClockContext(
+ Out<Service::PSC::Time::SystemClockContext> out_context) {
LOG_INFO(Service_SET, "called");
- IPC::RequestParser rp{ctx};
- const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
-
- const auto res = SetNetworkSystemClockContext(context);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ *out_context = m_system_settings.network_system_clock_context;
+ R_SUCCEED();
}
-void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) {
+Result ISystemSettingsServer::SetNetworkSystemClockContext(
+ const Service::PSC::Time::SystemClockContext& context) {
LOG_INFO(Service_SET, "called");
- bool enabled{};
- const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(res);
- rb.PushRaw(enabled);
+ m_system_settings.network_system_clock_context = context;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) {
- LOG_INFO(Service_SET, "called");
+Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(
+ Out<bool> out_automatic_correction_enabled) {
+ LOG_INFO(Service_SET, "called, out_automatic_correction_enabled={}",
+ m_system_settings.user_system_clock_automatic_correction_enabled);
- IPC::RequestParser rp{ctx};
- auto enabled{rp.Pop<bool>()};
+ *out_automatic_correction_enabled =
+ m_system_settings.user_system_clock_automatic_correction_enabled;
+ R_SUCCEED();
+}
- const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled);
+Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(
+ bool automatic_correction_enabled) {
+ LOG_INFO(Service_SET, "called, out_automatic_correction_enabled={}",
+ automatic_correction_enabled);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ m_system_settings.user_system_clock_automatic_correction_enabled = automatic_correction_enabled;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) {
- bool is_debug_mode_enabled = false;
- GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled");
+Result ISystemSettingsServer::GetDebugModeFlag(Out<bool> is_debug_mode_enabled) {
+ const auto result = GetSettingsItemValueImpl<bool>(*is_debug_mode_enabled, "settings_debug",
+ "is_debug_mode_enabled");
- LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(is_debug_mode_enabled);
+ LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", *is_debug_mode_enabled);
+ R_RETURN(result);
}
-void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetPrimaryAlbumStorage(
+ Out<PrimaryAlbumStorage> out_primary_album_storage) {
LOG_INFO(Service_SET, "called, primary_album_storage={}",
m_system_settings.primary_album_storage);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(m_system_settings.primary_album_storage);
+ *out_primary_album_storage = m_system_settings.primary_album_storage;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.primary_album_storage = rp.PopEnum<PrimaryAlbumStorage>();
- SetSaveNeeded();
+Result ISystemSettingsServer::SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage) {
+ LOG_INFO(Service_SET, "called, primary_album_storage={}", primary_album_storage);
- LOG_INFO(Service_SET, "called, primary_album_storage={}",
- m_system_settings.primary_album_storage);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.primary_album_storage = primary_album_storage;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetBatteryLot(HLERequestContext& ctx) {
- BatteryLot battery_lot = {"YUZUEMULATOR123456789"};
-
+Result ISystemSettingsServer::GetBatteryLot(Out<BatteryLot> out_battery_lot) {
LOG_INFO(Service_SET, "called");
- IPC::ResponseBuilder rb{ctx, 8};
- rb.Push(ResultSuccess);
- rb.PushRaw(battery_lot);
+ *out_battery_lot = {"YUZU0EMULATOR14022024"};
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetSerialNumber(HLERequestContext& ctx) {
- SerialNumber console_serial = {"YUZ10012345678"};
-
+Result ISystemSettingsServer::GetSerialNumber(Out<SerialNumber> out_console_serial) {
LOG_INFO(Service_SET, "called");
- IPC::ResponseBuilder rb{ctx, 8};
- rb.Push(ResultSuccess);
- rb.PushRaw(console_serial);
+ *out_console_serial = {"YUZ10000000001"};
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetNfcEnableFlag(Out<bool> out_nfc_enable_flag) {
LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(m_system_settings.nfc_enable_flag);
+ *out_nfc_enable_flag = m_system_settings.nfc_enable_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetNfcEnableFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.nfc_enable_flag = rp.Pop<bool>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag);
+Result ISystemSettingsServer::SetNfcEnableFlag(bool nfc_enable_flag) {
+ LOG_INFO(Service_SET, "called, nfc_enable_flag={}", nfc_enable_flag);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.nfc_enable_flag = nfc_enable_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetSleepSettings(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetSleepSettings(Out<SleepSettings> out_sleep_settings) {
LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}",
m_system_settings.sleep_settings.flags.raw,
m_system_settings.sleep_settings.handheld_sleep_plan,
m_system_settings.sleep_settings.console_sleep_plan);
- IPC::ResponseBuilder rb{ctx, 5};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.sleep_settings);
+ *out_sleep_settings = m_system_settings.sleep_settings;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetSleepSettings(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.sleep_settings = rp.PopRaw<SleepSettings>();
- SetSaveNeeded();
-
+Result ISystemSettingsServer::SetSleepSettings(SleepSettings sleep_settings) {
LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}",
- m_system_settings.sleep_settings.flags.raw,
- m_system_settings.sleep_settings.handheld_sleep_plan,
- m_system_settings.sleep_settings.console_sleep_plan);
+ sleep_settings.flags.raw, sleep_settings.handheld_sleep_plan,
+ sleep_settings.console_sleep_plan);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.sleep_settings = sleep_settings;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetWirelessLanEnableFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetWirelessLanEnableFlag(Out<bool> out_wireless_lan_enable_flag) {
LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}",
m_system_settings.wireless_lan_enable_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.wireless_lan_enable_flag);
+ *out_wireless_lan_enable_flag = m_system_settings.wireless_lan_enable_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetWirelessLanEnableFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.wireless_lan_enable_flag = rp.Pop<bool>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}",
- m_system_settings.wireless_lan_enable_flag);
+Result ISystemSettingsServer::SetWirelessLanEnableFlag(bool wireless_lan_enable_flag) {
+ LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}", wireless_lan_enable_flag);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.wireless_lan_enable_flag = wireless_lan_enable_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetInitialLaunchSettings(
+ Out<InitialLaunchSettings> out_initial_launch_settings) {
LOG_INFO(Service_SET, "called, flags={}, timestamp={}",
m_system_settings.initial_launch_settings_packed.flags.raw,
m_system_settings.initial_launch_settings_packed.timestamp.time_point);
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.initial_launch_settings_packed);
+ *out_initial_launch_settings = {
+ .flags = m_system_settings.initial_launch_settings_packed.flags,
+ .timestamp = m_system_settings.initial_launch_settings_packed.timestamp,
+ };
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto initial_launch_settings = rp.PopRaw<InitialLaunchSettings>();
+Result ISystemSettingsServer::SetInitialLaunchSettings(
+ InitialLaunchSettings initial_launch_settings) {
+ LOG_INFO(Service_SET, "called, flags={}, timestamp={}", initial_launch_settings.flags.raw,
+ initial_launch_settings.timestamp.time_point);
m_system_settings.initial_launch_settings_packed.flags = initial_launch_settings.flags;
m_system_settings.initial_launch_settings_packed.timestamp = initial_launch_settings.timestamp;
SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, flags={}, timestamp={}",
- m_system_settings.initial_launch_settings_packed.flags.raw,
- m_system_settings.initial_launch_settings_packed.timestamp.time_point);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetDeviceNickName(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetDeviceNickName(
+ OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name) {
LOG_DEBUG(Service_SET, "called");
- ctx.WriteBuffer(::Settings::values.device_name.GetValue());
+ *out_device_name = {};
+ const auto device_name_buffer = ::Settings::values.device_name.GetValue().c_str();
+ memcpy(out_device_name->data(), device_name_buffer,
+ ::Settings::values.device_name.GetValue().size());
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetDeviceNickName(HLERequestContext& ctx) {
- const std::string device_name = Common::StringFromBuffer(ctx.ReadBuffer());
+Result ISystemSettingsServer::SetDeviceNickName(
+ InLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> device_name_buffer) {
+ const std::string device_name = Common::StringFromBuffer(*device_name_buffer);
LOG_INFO(Service_SET, "called, device_name={}", device_name);
::Settings::values.device_name = device_name;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetProductModel(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetProductModel(Out<u32> out_product_model) {
const u32 product_model = 1;
LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(product_model);
+
+ *out_product_model = product_model;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetBluetoothEnableFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetBluetoothEnableFlag(Out<bool> out_bluetooth_enable_flag) {
LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}",
m_system_settings.bluetooth_enable_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(m_system_settings.bluetooth_enable_flag);
+ *out_bluetooth_enable_flag = m_system_settings.bluetooth_enable_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetBluetoothEnableFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.bluetooth_enable_flag = rp.Pop<bool>();
- SetSaveNeeded();
+Result ISystemSettingsServer::SetBluetoothEnableFlag(bool bluetooth_enable_flag) {
+ LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}", bluetooth_enable_flag);
- LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}",
- m_system_settings.bluetooth_enable_flag);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.bluetooth_enable_flag = bluetooth_enable_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetMiiAuthorId(Out<Common::UUID> out_mii_author_id) {
if (m_system_settings.mii_author_id.IsInvalid()) {
m_system_settings.mii_author_id = Common::UUID::MakeDefault();
SetSaveNeeded();
@@ -1166,282 +1060,224 @@ void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) {
LOG_INFO(Service_SET, "called, author_id={}",
m_system_settings.mii_author_id.FormattedString());
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.PushRaw(m_system_settings.mii_author_id);
+ *out_mii_author_id = m_system_settings.mii_author_id;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetAutoUpdateEnableFlag(Out<bool> out_auto_update_enable_flag) {
LOG_INFO(Service_SET, "called, auto_update_flag={}", m_system_settings.auto_update_enable_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.auto_update_enable_flag);
+ *out_auto_update_enable_flag = m_system_settings.auto_update_enable_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetAutoUpdateEnableFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.auto_update_enable_flag = rp.Pop<bool>();
- SetSaveNeeded();
+Result ISystemSettingsServer::SetAutoUpdateEnableFlag(bool auto_update_enable_flag) {
+ LOG_INFO(Service_SET, "called, auto_update_flag={}", auto_update_enable_flag);
- LOG_INFO(Service_SET, "called, auto_update_flag={}", m_system_settings.auto_update_enable_flag);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.auto_update_enable_flag = auto_update_enable_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetBatteryPercentageFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetBatteryPercentageFlag(Out<bool> out_battery_percentage_flag) {
LOG_DEBUG(Service_SET, "called, battery_percentage_flag={}",
m_system_settings.battery_percentage_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.battery_percentage_flag);
+ *out_battery_percentage_flag = m_system_settings.battery_percentage_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetBatteryPercentageFlag(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.battery_percentage_flag = rp.Pop<bool>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, battery_percentage_flag={}",
- m_system_settings.battery_percentage_flag);
+Result ISystemSettingsServer::SetBatteryPercentageFlag(bool battery_percentage_flag) {
+ LOG_INFO(Service_SET, "called, battery_percentage_flag={}", battery_percentage_flag);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.battery_percentage_flag = battery_percentage_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "called.");
-
- IPC::RequestParser rp{ctx};
- auto offset{rp.Pop<s64>()};
-
- const auto res = SetExternalSteadyClockInternalOffset(offset);
+Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) {
+ LOG_DEBUG(Service_SET, "called, external_steady_clock_internal_offset={}", offset);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ m_private_settings.external_steady_clock_internal_offset = offset;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "called.");
+Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(Out<s64> out_offset) {
+ LOG_DEBUG(Service_SET, "called, external_steady_clock_internal_offset={}",
+ m_private_settings.external_steady_clock_internal_offset);
- s64 offset{};
- const auto res = GetExternalSteadyClockInternalOffset(offset);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.Push(offset);
+ *out_offset = m_private_settings.external_steady_clock_internal_offset;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetPushNotificationActivityModeOnSleep(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetPushNotificationActivityModeOnSleep(
+ Out<s32> out_push_notification_activity_mode_on_sleep) {
LOG_INFO(Service_SET, "called, push_notification_activity_mode_on_sleep={}",
m_system_settings.push_notification_activity_mode_on_sleep);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.push_notification_activity_mode_on_sleep);
+ *out_push_notification_activity_mode_on_sleep =
+ m_system_settings.push_notification_activity_mode_on_sleep;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetPushNotificationActivityModeOnSleep(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.push_notification_activity_mode_on_sleep = rp.Pop<s32>();
- SetSaveNeeded();
-
+Result ISystemSettingsServer::SetPushNotificationActivityModeOnSleep(
+ s32 push_notification_activity_mode_on_sleep) {
LOG_INFO(Service_SET, "called, push_notification_activity_mode_on_sleep={}",
- m_system_settings.push_notification_activity_mode_on_sleep);
+ push_notification_activity_mode_on_sleep);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.push_notification_activity_mode_on_sleep =
+ push_notification_activity_mode_on_sleep;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetErrorReportSharePermission(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetErrorReportSharePermission(
+ Out<ErrorReportSharePermission> out_error_report_share_permission) {
LOG_INFO(Service_SET, "called, error_report_share_permission={}",
m_system_settings.error_report_share_permission);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(m_system_settings.error_report_share_permission);
+ *out_error_report_share_permission = m_system_settings.error_report_share_permission;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetErrorReportSharePermission(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.error_report_share_permission = rp.PopEnum<ErrorReportSharePermission>();
- SetSaveNeeded();
-
+Result ISystemSettingsServer::SetErrorReportSharePermission(
+ ErrorReportSharePermission error_report_share_permission) {
LOG_INFO(Service_SET, "called, error_report_share_permission={}",
- m_system_settings.error_report_share_permission);
+ error_report_share_permission);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.error_report_share_permission = error_report_share_permission;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetAppletLaunchFlags(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetAppletLaunchFlags(Out<u32> out_applet_launch_flag) {
LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.applet_launch_flag);
+ *out_applet_launch_flag = m_system_settings.applet_launch_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetAppletLaunchFlags(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.applet_launch_flag = rp.Pop<u32>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag);
+Result ISystemSettingsServer::SetAppletLaunchFlags(u32 applet_launch_flag) {
+ LOG_INFO(Service_SET, "called, applet_launch_flag={}", applet_launch_flag);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.applet_launch_flag = applet_launch_flag;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetKeyboardLayout(Out<KeyboardLayout> out_keyboard_layout) {
LOG_INFO(Service_SET, "called, keyboard_layout={}", m_system_settings.keyboard_layout);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(m_system_settings.keyboard_layout));
+ *out_keyboard_layout = m_system_settings.keyboard_layout;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetKeyboardLayout(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.keyboard_layout = rp.PopRaw<KeyboardLayout>();
- SetSaveNeeded();
-
- LOG_INFO(Service_SET, "called, keyboard_layout={}", m_system_settings.keyboard_layout);
+Result ISystemSettingsServer::SetKeyboardLayout(KeyboardLayout keyboard_layout) {
+ LOG_INFO(Service_SET, "called, keyboard_layout={}", keyboard_layout);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.keyboard_layout = keyboard_layout;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
+ Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
LOG_INFO(Service_SET, "called");
- Service::PSC::Time::SteadyClockTimePoint time_point{};
- const auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
+ *out_time_point = m_system_settings.device_time_zone_location_updated_time;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
+Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(
+ const Service::PSC::Time::SteadyClockTimePoint& time_point) {
LOG_INFO(Service_SET, "called");
- IPC::RequestParser rp{ctx};
- auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
-
- const auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ m_system_settings.device_time_zone_location_updated_time = time_point;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
- HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
+ Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
LOG_INFO(Service_SET, "called");
- Service::PSC::Time::SteadyClockTimePoint time_point{};
- const auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(res);
- rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
+ *out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
- HLERequestContext& ctx) {
+Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
+ const Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
LOG_INFO(Service_SET, "called");
- IPC::RequestParser rp{ctx};
- const auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
-
- const auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetChineseTraditionalInputMethod(
+ Out<ChineseTraditionalInputMethod> out_chinese_traditional_input_method) {
LOG_INFO(Service_SET, "called, chinese_traditional_input_method={}",
m_system_settings.chinese_traditional_input_method);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(m_system_settings.chinese_traditional_input_method);
+ *out_chinese_traditional_input_method = m_system_settings.chinese_traditional_input_method;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetHomeMenuScheme(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetHomeMenuScheme(Out<HomeMenuScheme> out_home_menu_scheme) {
LOG_DEBUG(Service_SET, "(STUBBED) called");
- const HomeMenuScheme default_color = {
+ *out_home_menu_scheme = {
.main = 0xFF323232,
.back = 0xFF323232,
.sub = 0xFFFFFFFF,
.bezel = 0xFFFFFFFF,
.extra = 0xFF000000,
};
-
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(HomeMenuScheme) / sizeof(u32)};
- rb.Push(ResultSuccess);
- rb.PushRaw(default_color);
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetHomeMenuSchemeModel(Out<u32> out_home_menu_scheme_model) {
LOG_WARNING(Service_SET, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(0);
+ *out_home_menu_scheme_model = 0;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) {
- TouchScreenMode touch_screen_mode{};
- auto res = GetTouchScreenMode(touch_screen_mode);
+Result ISystemSettingsServer::GetTouchScreenMode(Out<TouchScreenMode> out_touch_screen_mode) {
+ LOG_INFO(Service_SET, "called, touch_screen_mode={}", m_system_settings.touch_screen_mode);
- LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(res);
- rb.PushEnum(touch_screen_mode);
+ *out_touch_screen_mode = m_system_settings.touch_screen_mode;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>();
- auto res = SetTouchScreenMode(touch_screen_mode);
-
+Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) {
LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(res);
+ m_system_settings.touch_screen_mode = touch_screen_mode;
+ SetSaveNeeded();
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetFieldTestingFlag(Out<bool> out_field_testing_flag) {
LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.field_testing_flag);
+ *out_field_testing_flag = m_system_settings.field_testing_flag;
+ R_SUCCEED();
}
-void ISystemSettingsServer::GetPanelCrcMode(HLERequestContext& ctx) {
+Result ISystemSettingsServer::GetPanelCrcMode(Out<s32> out_panel_crc_mode) {
LOG_INFO(Service_SET, "called, panel_crc_mode={}", m_system_settings.panel_crc_mode);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(m_system_settings.panel_crc_mode);
+ *out_panel_crc_mode = m_system_settings.panel_crc_mode;
+ R_SUCCEED();
}
-void ISystemSettingsServer::SetPanelCrcMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- m_system_settings.panel_crc_mode = rp.PopRaw<s32>();
- SetSaveNeeded();
+Result ISystemSettingsServer::SetPanelCrcMode(s32 panel_crc_mode) {
+ LOG_INFO(Service_SET, "called, panel_crc_mode={}", panel_crc_mode);
- LOG_INFO(Service_SET, "called, panel_crc_mode={}", m_system_settings.panel_crc_mode);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ m_system_settings.panel_crc_mode = panel_crc_mode;
+ SetSaveNeeded();
+ R_SUCCEED();
}
void ISystemSettingsServer::SetupSettings() {
@@ -1513,9 +1349,9 @@ void ISystemSettingsServer::SetSaveNeeded() {
m_save_needed = true;
}
-Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value,
- const std::string& category,
- const std::string& name) {
+Result ISystemSettingsServer::GetSettingsItemValueImpl(std::vector<u8>& out_value,
+ const std::string& category,
+ const std::string& name) {
auto settings{GetSettings()};
R_UNLESS(settings.contains(category) && settings[category].contains(name), ResultUnknown);
@@ -1523,184 +1359,4 @@ Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value,
R_SUCCEED();
}
-Result ISystemSettingsServer::GetVibrationMasterVolume(f32& out_volume) const {
- out_volume = m_system_settings.vibration_master_volume;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetVibrationMasterVolume(f32 volume) {
- m_system_settings.vibration_master_volume = volume;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetAudioOutputMode(AudioOutputMode& out_output_mode,
- AudioOutputModeTarget target) const {
- switch (target) {
- case AudioOutputModeTarget::Hdmi:
- out_output_mode = m_system_settings.audio_output_mode_hdmi;
- break;
- case AudioOutputModeTarget::Speaker:
- out_output_mode = m_system_settings.audio_output_mode_speaker;
- break;
- case AudioOutputModeTarget::Headphone:
- out_output_mode = m_system_settings.audio_output_mode_headphone;
- break;
- case AudioOutputModeTarget::Type3:
- out_output_mode = m_system_settings.audio_output_mode_type3;
- break;
- case AudioOutputModeTarget::Type4:
- out_output_mode = m_system_settings.audio_output_mode_type4;
- break;
- default:
- LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target);
- }
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetAudioOutputMode(AudioOutputModeTarget target,
- AudioOutputMode output_mode) {
- switch (target) {
- case AudioOutputModeTarget::Hdmi:
- m_system_settings.audio_output_mode_hdmi = output_mode;
- break;
- case AudioOutputModeTarget::Speaker:
- m_system_settings.audio_output_mode_speaker = output_mode;
- break;
- case AudioOutputModeTarget::Headphone:
- m_system_settings.audio_output_mode_headphone = output_mode;
- break;
- case AudioOutputModeTarget::Type3:
- m_system_settings.audio_output_mode_type3 = output_mode;
- break;
- case AudioOutputModeTarget::Type4:
- m_system_settings.audio_output_mode_type4 = output_mode;
- break;
- default:
- LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target);
- }
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetSpeakerAutoMuteFlag(bool& is_auto_mute) const {
- is_auto_mute = m_system_settings.force_mute_on_headphone_removed;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetSpeakerAutoMuteFlag(bool is_auto_mute) {
- m_system_settings.force_mute_on_headphone_removed = is_auto_mute;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) const {
- out_id = m_private_settings.external_clock_source_id;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& id) {
- m_private_settings.external_clock_source_id = id;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetUserSystemClockContext(
- Service::PSC::Time::SystemClockContext& out_context) const {
- out_context = m_system_settings.user_system_clock_context;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetUserSystemClockContext(
- const Service::PSC::Time::SystemClockContext& context) {
- m_system_settings.user_system_clock_context = context;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
- Service::PSC::Time::LocationName& out_name) const {
- out_name = m_system_settings.device_time_zone_location_name;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetDeviceTimeZoneLocationName(
- const Service::PSC::Time::LocationName& name) {
- m_system_settings.device_time_zone_location_name = name;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetNetworkSystemClockContext(
- Service::PSC::Time::SystemClockContext& out_context) const {
- out_context = m_system_settings.network_system_clock_context;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetNetworkSystemClockContext(
- const Service::PSC::Time::SystemClockContext& context) {
- m_system_settings.network_system_clock_context = context;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const {
- out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
- m_system_settings.user_system_clock_automatic_correction_enabled = enabled;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) {
- m_private_settings.external_steady_clock_internal_offset = offset;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) const {
- out_offset = m_private_settings.external_steady_clock_internal_offset;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point) const {
- out_time_point = m_system_settings.device_time_zone_location_updated_time;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(
- const Service::PSC::Time::SteadyClockTimePoint& time_point) {
- m_system_settings.device_time_zone_location_updated_time = time_point;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point) const {
- out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
- const Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
- m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const {
- touch_screen_mode = m_system_settings.touch_screen_mode;
- R_SUCCEED();
-}
-
-Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) {
- m_system_settings.touch_screen_mode = touch_screen_mode;
- SetSaveNeeded();
- R_SUCCEED();
-}
-
} // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index 9a3b36f0c..46e06c8ea 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -11,6 +11,7 @@
#include "common/polyfill_thread.h"
#include "common/uuid.h"
#include "core/hle/result.h"
+#include "core/hle/service/cmif_types.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/setting_formats/appln_settings.h"
@@ -33,13 +34,14 @@ public:
explicit ISystemSettingsServer(Core::System& system_);
~ISystemSettingsServer() override;
- Result GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category,
- const std::string& name);
+ Result GetSettingsItemValueImpl(std::vector<u8>& out_value, const std::string& category,
+ const std::string& name);
template <typename T>
- Result GetSettingsItemValue(T& value, const std::string& category, const std::string& name) {
+ Result GetSettingsItemValueImpl(T& value, const std::string& category,
+ const std::string& name) {
std::vector<u8> data;
- const auto result = GetSettingsItemValue(data, category, name);
+ const auto result = GetSettingsItemValueImpl(data, category, name);
if (result.IsError()) {
return result;
}
@@ -48,120 +50,114 @@ public:
return result;
}
- Result GetVibrationMasterVolume(f32& out_volume) const;
- Result SetVibrationMasterVolume(f32 volume);
- Result GetAudioOutputMode(AudioOutputMode& out_output_mode, AudioOutputModeTarget target) const;
+public:
+ Result SetLanguageCode(LanguageCode language_code);
+ Result GetFirmwareVersion(
+ OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data);
+ Result GetFirmwareVersion2(
+ OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data);
+ Result GetLockScreenFlag(Out<bool> out_lock_screen_flag);
+ Result SetLockScreenFlag(bool lock_screen_flag);
+ Result GetExternalSteadyClockSourceId(Out<Common::UUID> out_clock_source_id);
+ Result SetExternalSteadyClockSourceId(const Common::UUID& clock_source_id);
+ Result GetUserSystemClockContext(Out<Service::PSC::Time::SystemClockContext> out_clock_context);
+ Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& clock_context);
+ Result GetAccountSettings(Out<AccountSettings> out_account_settings);
+ Result SetAccountSettings(AccountSettings account_settings);
+ Result GetEulaVersions(Out<s32> out_count,
+ OutArray<EulaVersion, BufferAttr_HipcMapAlias> out_eula_versions);
+ Result SetEulaVersions(InArray<EulaVersion, BufferAttr_HipcMapAlias> eula_versions);
+ Result GetColorSetId(Out<ColorSet> out_color_set_id);
+ Result SetColorSetId(ColorSet color_set_id);
+ Result GetNotificationSettings(Out<NotificationSettings> out_notification_settings);
+ Result SetNotificationSettings(const NotificationSettings& notification_settings);
+ Result GetAccountNotificationSettings(
+ Out<s32> out_count, OutArray<AccountNotificationSettings, BufferAttr_HipcMapAlias>
+ out_account_notification_settings);
+ Result SetAccountNotificationSettings(
+ InArray<AccountNotificationSettings, BufferAttr_HipcMapAlias>
+ account_notification_settings);
+ Result GetVibrationMasterVolume(Out<f32> vibration_master_volume);
+ Result SetVibrationMasterVolume(f32 vibration_master_volume);
+ Result GetSettingsItemValueSize(
+ Out<u64> out_size,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buf);
+ Result GetSettingsItemValue(
+ OutBuffer<BufferAttr_HipcMapAlias> out_data,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
+ InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer);
+ Result GetTvSettings(Out<TvSettings> out_tv_settings);
+ Result SetTvSettings(TvSettings tv_settings);
+ Result GetAudioOutputMode(Out<AudioOutputMode> out_output_mode, AudioOutputModeTarget target);
Result SetAudioOutputMode(AudioOutputModeTarget target, AudioOutputMode output_mode);
- Result GetSpeakerAutoMuteFlag(bool& is_auto_mute) const;
- Result SetSpeakerAutoMuteFlag(bool auto_mute);
- Result GetExternalSteadyClockSourceId(Common::UUID& out_id) const;
- Result SetExternalSteadyClockSourceId(const Common::UUID& id);
- Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const;
- Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& context);
- Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name) const;
+ Result GetSpeakerAutoMuteFlag(Out<bool> out_force_mute_on_headphone_removed);
+ Result SetSpeakerAutoMuteFlag(bool force_mute_on_headphone_removed);
+ Result GetQuestFlag(Out<QuestFlag> out_quest_flag);
+ Result SetQuestFlag(QuestFlag quest_flag);
+ Result GetDeviceTimeZoneLocationName(Out<Service::PSC::Time::LocationName> out_name);
Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name);
- Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const;
+ Result SetRegionCode(SystemRegionCode region_code);
+ Result GetNetworkSystemClockContext(Out<Service::PSC::Time::SystemClockContext> out_context);
Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context);
- Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const;
- Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled);
+ Result IsUserSystemClockAutomaticCorrectionEnabled(Out<bool> out_automatic_correction_enabled);
+ Result SetUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction_enabled);
+ Result GetDebugModeFlag(Out<bool> is_debug_mode_enabled);
+ Result GetPrimaryAlbumStorage(Out<PrimaryAlbumStorage> out_primary_album_storage);
+ Result SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage);
+ Result GetBatteryLot(Out<BatteryLot> out_battery_lot);
+ Result GetSerialNumber(Out<SerialNumber> out_console_serial);
+ Result GetNfcEnableFlag(Out<bool> out_nfc_enable_flag);
+ Result SetNfcEnableFlag(bool nfc_enable_flag);
+ Result GetSleepSettings(Out<SleepSettings> out_sleep_settings);
+ Result SetSleepSettings(SleepSettings sleep_settings);
+ Result GetWirelessLanEnableFlag(Out<bool> out_wireless_lan_enable_flag);
+ Result SetWirelessLanEnableFlag(bool wireless_lan_enable_flag);
+ Result GetInitialLaunchSettings(Out<InitialLaunchSettings> out_initial_launch_settings);
+ Result SetInitialLaunchSettings(InitialLaunchSettings initial_launch_settings);
+ Result GetDeviceNickName(
+ OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name);
+ Result SetDeviceNickName(
+ InLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> device_name_buffer);
+ Result GetProductModel(Out<u32> out_product_model);
+ Result GetBluetoothEnableFlag(Out<bool> out_bluetooth_enable_flag);
+ Result SetBluetoothEnableFlag(bool bluetooth_enable_flag);
+ Result GetMiiAuthorId(Out<Common::UUID> out_mii_author_id);
+ Result GetAutoUpdateEnableFlag(Out<bool> out_auto_update_enable_flag);
+ Result SetAutoUpdateEnableFlag(bool auto_update_enable_flag);
+ Result GetBatteryPercentageFlag(Out<bool> out_battery_percentage_flag);
+ Result SetBatteryPercentageFlag(bool battery_percentage_flag);
Result SetExternalSteadyClockInternalOffset(s64 offset);
- Result GetExternalSteadyClockInternalOffset(s64& out_offset) const;
+ Result GetExternalSteadyClockInternalOffset(Out<s64> out_offset);
+ Result GetPushNotificationActivityModeOnSleep(
+ Out<s32> out_push_notification_activity_mode_on_sleep);
+ Result SetPushNotificationActivityModeOnSleep(s32 push_notification_activity_mode_on_sleep);
+ Result GetErrorReportSharePermission(
+ Out<ErrorReportSharePermission> out_error_report_share_permission);
+ Result SetErrorReportSharePermission(ErrorReportSharePermission error_report_share_permission);
+ Result GetAppletLaunchFlags(Out<u32> out_applet_launch_flag);
+ Result SetAppletLaunchFlags(u32 applet_launch_flag);
+ Result GetKeyboardLayout(Out<KeyboardLayout> out_keyboard_layout);
+ Result SetKeyboardLayout(KeyboardLayout keyboard_layout);
Result GetDeviceTimeZoneLocationUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
+ Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
Result SetDeviceTimeZoneLocationUpdatedTime(
const Service::PSC::Time::SteadyClockTimePoint& time_point);
Result GetUserSystemClockAutomaticCorrectionUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
+ Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
- const Service::PSC::Time::SteadyClockTimePoint& time_point);
- Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const;
+ const Service::PSC::Time::SteadyClockTimePoint& out_time_point);
+ Result GetChineseTraditionalInputMethod(
+ Out<ChineseTraditionalInputMethod> out_chinese_traditional_input_method);
+ Result GetHomeMenuScheme(Out<HomeMenuScheme> out_home_menu_scheme);
+ Result GetHomeMenuSchemeModel(Out<u32> out_home_menu_scheme_model);
+ Result GetTouchScreenMode(Out<TouchScreenMode> out_touch_screen_mode);
Result SetTouchScreenMode(TouchScreenMode touch_screen_mode);
+ Result GetFieldTestingFlag(Out<bool> out_field_testing_flag);
+ Result GetPanelCrcMode(Out<s32> out_panel_crc_mode);
+ Result SetPanelCrcMode(s32 panel_crc_mode);
private:
- void SetLanguageCode(HLERequestContext& ctx);
- void GetFirmwareVersion(HLERequestContext& ctx);
- void GetFirmwareVersion2(HLERequestContext& ctx);
- void GetLockScreenFlag(HLERequestContext& ctx);
- void SetLockScreenFlag(HLERequestContext& ctx);
- void GetExternalSteadyClockSourceId(HLERequestContext& ctx);
- void SetExternalSteadyClockSourceId(HLERequestContext& ctx);
- void GetUserSystemClockContext(HLERequestContext& ctx);
- void SetUserSystemClockContext(HLERequestContext& ctx);
- void GetAccountSettings(HLERequestContext& ctx);
- void SetAccountSettings(HLERequestContext& ctx);
- void GetEulaVersions(HLERequestContext& ctx);
- void SetEulaVersions(HLERequestContext& ctx);
- void GetColorSetId(HLERequestContext& ctx);
- void SetColorSetId(HLERequestContext& ctx);
- void GetNotificationSettings(HLERequestContext& ctx);
- void SetNotificationSettings(HLERequestContext& ctx);
- void GetAccountNotificationSettings(HLERequestContext& ctx);
- void SetAccountNotificationSettings(HLERequestContext& ctx);
- void GetVibrationMasterVolume(HLERequestContext& ctx);
- void SetVibrationMasterVolume(HLERequestContext& ctx);
- void GetSettingsItemValueSize(HLERequestContext& ctx);
- void GetSettingsItemValue(HLERequestContext& ctx);
- void GetTvSettings(HLERequestContext& ctx);
- void SetTvSettings(HLERequestContext& ctx);
- void GetAudioOutputMode(HLERequestContext& ctx);
- void SetAudioOutputMode(HLERequestContext& ctx);
- void GetSpeakerAutoMuteFlag(HLERequestContext& ctx);
- void SetSpeakerAutoMuteFlag(HLERequestContext& ctx);
- void GetDebugModeFlag(HLERequestContext& ctx);
- void GetQuestFlag(HLERequestContext& ctx);
- void SetQuestFlag(HLERequestContext& ctx);
- void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
- void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
- void SetRegionCode(HLERequestContext& ctx);
- void GetNetworkSystemClockContext(HLERequestContext& ctx);
- void SetNetworkSystemClockContext(HLERequestContext& ctx);
- void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
- void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
- void GetPrimaryAlbumStorage(HLERequestContext& ctx);
- void SetPrimaryAlbumStorage(HLERequestContext& ctx);
- void GetBatteryLot(HLERequestContext& ctx);
- void GetSerialNumber(HLERequestContext& ctx);
- void GetNfcEnableFlag(HLERequestContext& ctx);
- void SetNfcEnableFlag(HLERequestContext& ctx);
- void GetSleepSettings(HLERequestContext& ctx);
- void SetSleepSettings(HLERequestContext& ctx);
- void GetWirelessLanEnableFlag(HLERequestContext& ctx);
- void SetWirelessLanEnableFlag(HLERequestContext& ctx);
- void GetInitialLaunchSettings(HLERequestContext& ctx);
- void SetInitialLaunchSettings(HLERequestContext& ctx);
- void GetDeviceNickName(HLERequestContext& ctx);
- void SetDeviceNickName(HLERequestContext& ctx);
- void GetProductModel(HLERequestContext& ctx);
- void GetBluetoothEnableFlag(HLERequestContext& ctx);
- void SetBluetoothEnableFlag(HLERequestContext& ctx);
- void GetMiiAuthorId(HLERequestContext& ctx);
- void GetAutoUpdateEnableFlag(HLERequestContext& ctx);
- void SetAutoUpdateEnableFlag(HLERequestContext& ctx);
- void GetBatteryPercentageFlag(HLERequestContext& ctx);
- void SetBatteryPercentageFlag(HLERequestContext& ctx);
- void SetExternalSteadyClockInternalOffset(HLERequestContext& ctx);
- void GetExternalSteadyClockInternalOffset(HLERequestContext& ctx);
- void GetPushNotificationActivityModeOnSleep(HLERequestContext& ctx);
- void SetPushNotificationActivityModeOnSleep(HLERequestContext& ctx);
- void GetErrorReportSharePermission(HLERequestContext& ctx);
- void SetErrorReportSharePermission(HLERequestContext& ctx);
- void GetAppletLaunchFlags(HLERequestContext& ctx);
- void SetAppletLaunchFlags(HLERequestContext& ctx);
- void GetKeyboardLayout(HLERequestContext& ctx);
- void SetKeyboardLayout(HLERequestContext& ctx);
- void GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx);
- void SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx);
- void GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
- void SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
- void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
- void GetHomeMenuScheme(HLERequestContext& ctx);
- void GetHomeMenuSchemeModel(HLERequestContext& ctx);
- void GetTouchScreenMode(HLERequestContext& ctx);
- void SetTouchScreenMode(HLERequestContext& ctx);
- void GetFieldTestingFlag(HLERequestContext& ctx);
- void GetPanelCrcMode(HLERequestContext& ctx);
- void SetPanelCrcMode(HLERequestContext& ctx);
-
bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func);
bool StoreSettingsFile(std::filesystem::path& path, auto& settings);
void SetupSettings();
diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp
new file mode 100644
index 000000000..6b0bcb536
--- /dev/null
+++ b/src/core/hle/service/vi/application_display_service.cpp
@@ -0,0 +1,302 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver.h"
+#include "core/hle/service/nvnflinger/parcel.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/vi/application_display_service.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/manager_display_service.h"
+#include "core/hle/service/vi/system_display_service.h"
+#include "core/hle/service/vi/vi_results.h"
+
+namespace Service::VI {
+
+IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
+ std::shared_ptr<Container> container)
+ : ServiceFramework{system_, "IApplicationDisplayService"},
+ m_container{std::move(container)}, m_context{system, "IApplicationDisplayService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {100, C<&IApplicationDisplayService::GetRelayService>, "GetRelayService"},
+ {101, C<&IApplicationDisplayService::GetSystemDisplayService>, "GetSystemDisplayService"},
+ {102, C<&IApplicationDisplayService::GetManagerDisplayService>, "GetManagerDisplayService"},
+ {103, C<&IApplicationDisplayService::GetIndirectDisplayTransactionService>, "GetIndirectDisplayTransactionService"},
+ {1000, C<&IApplicationDisplayService::ListDisplays>, "ListDisplays"},
+ {1010, C<&IApplicationDisplayService::OpenDisplay>, "OpenDisplay"},
+ {1011, C<&IApplicationDisplayService::OpenDefaultDisplay>, "OpenDefaultDisplay"},
+ {1020, C<&IApplicationDisplayService::CloseDisplay>, "CloseDisplay"},
+ {1101, C<&IApplicationDisplayService::SetDisplayEnabled>, "SetDisplayEnabled"},
+ {1102, C<&IApplicationDisplayService::GetDisplayResolution>, "GetDisplayResolution"},
+ {2020, C<&IApplicationDisplayService::OpenLayer>, "OpenLayer"},
+ {2021, C<&IApplicationDisplayService::CloseLayer>, "CloseLayer"},
+ {2030, C<&IApplicationDisplayService::CreateStrayLayer>, "CreateStrayLayer"},
+ {2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
+ {2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
+ {2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
+ {2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
+ {2451, nullptr, "GetIndirectLayerImageCropMap"},
+ {2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
+ {5202, C<&IApplicationDisplayService::GetDisplayVsyncEvent>, "GetDisplayVsyncEvent"},
+ {5203, nullptr, "GetDisplayVsyncEventForDebug"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IApplicationDisplayService::~IApplicationDisplayService() {
+ for (auto& [display_id, event] : m_display_vsync_events) {
+ m_container->UnlinkVsyncEvent(display_id, &event);
+ }
+ for (const auto layer_id : m_open_layer_ids) {
+ m_container->CloseLayer(layer_id);
+ }
+ for (const auto layer_id : m_stray_layer_ids) {
+ m_container->DestroyStrayLayer(layer_id);
+ }
+}
+
+Result IApplicationDisplayService::GetRelayService(
+ Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+ R_RETURN(m_container->GetBinderDriver(out_relay_service));
+}
+
+Result IApplicationDisplayService::GetSystemDisplayService(
+ Out<SharedPointer<ISystemDisplayService>> out_system_display_service) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+ *out_system_display_service = std::make_shared<ISystemDisplayService>(system, m_container);
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::GetManagerDisplayService(
+ Out<SharedPointer<IManagerDisplayService>> out_manager_display_service) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+ *out_manager_display_service = std::make_shared<IManagerDisplayService>(system, m_container);
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
+ Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+ R_RETURN(m_container->GetBinderDriver(out_indirect_display_transaction_service));
+}
+
+Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+
+ display_name[display_name.size() - 1] = '\0';
+ ASSERT_MSG(strcmp(display_name.data(), "Default") == 0,
+ "Non-default displays aren't supported yet");
+
+ R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
+}
+
+Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(this->OpenDisplay(out_display_id, DisplayName{"Default"}));
+}
+
+Result IApplicationDisplayService::CloseDisplay(u64 display_id) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(m_container->CloseDisplay(display_id));
+}
+
+Result IApplicationDisplayService::SetDisplayEnabled(u32 state, u64 display_id) {
+ LOG_DEBUG(Service_VI, "called");
+
+ // This literally does nothing internally in the actual service itself,
+ // and just returns a successful result code regardless of the input.
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::GetDisplayResolution(Out<s64> out_width, Out<s64> out_height,
+ u64 display_id) {
+ LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
+
+ // This only returns the fixed values of 1280x720 and makes no distinguishing
+ // between docked and undocked dimensions.
+ *out_width = static_cast<s64>(DisplayResolution::UndockedWidth);
+ *out_height = static_cast<s64>(DisplayResolution::UndockedHeight);
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::SetLayerScalingMode(NintendoScaleMode scale_mode, u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called. scale_mode={}, unknown=0x{:016X}", scale_mode, layer_id);
+
+ if (scale_mode > NintendoScaleMode::PreserveAspectRatio) {
+ LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
+ R_THROW(VI::ResultOperationFailed);
+ }
+
+ if (scale_mode != NintendoScaleMode::ScaleToWindow &&
+ scale_mode != NintendoScaleMode::PreserveAspectRatio) {
+ LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
+ R_THROW(VI::ResultNotSupported);
+ }
+
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::ListDisplays(
+ Out<u64> out_count, OutArray<DisplayInfo, BufferAttr_HipcMapAlias> out_displays) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+
+ if (out_displays.size() > 0) {
+ out_displays[0] = DisplayInfo{};
+ *out_count = 1;
+ } else {
+ *out_count = 0;
+ }
+
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::OpenLayer(Out<u64> out_size,
+ OutBuffer<BufferAttr_HipcMapAlias> out_native_window,
+ DisplayName display_name, u64 layer_id,
+ ClientAppletResourceUserId aruid) {
+ display_name[display_name.size() - 1] = '\0';
+
+ LOG_DEBUG(Service_VI, "called. layer_id={}, aruid={:#x}", layer_id, aruid.pid);
+
+ u64 display_id;
+ R_TRY(m_container->OpenDisplay(&display_id, display_name));
+
+ s32 producer_binder_id;
+ R_TRY(m_container->OpenLayer(&producer_binder_id, layer_id, aruid.pid));
+
+ {
+ std::scoped_lock lk{m_lock};
+ m_open_layer_ids.insert(layer_id);
+ }
+
+ android::OutputParcel parcel;
+ parcel.WriteInterface(NativeWindow{producer_binder_id});
+
+ const auto buffer = parcel.Serialize();
+ std::memcpy(out_native_window.data(), buffer.data(),
+ std::min(out_native_window.size(), buffer.size()));
+ *out_size = buffer.size();
+
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::CloseLayer(u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
+
+ {
+ std::scoped_lock lk{m_lock};
+ R_UNLESS(m_open_layer_ids.contains(layer_id), VI::ResultNotFound);
+ m_open_layer_ids.erase(layer_id);
+ }
+
+ R_RETURN(m_container->CloseLayer(layer_id));
+}
+
+Result IApplicationDisplayService::CreateStrayLayer(
+ Out<u64> out_layer_id, Out<u64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_native_window,
+ u32 flags, u64 display_id) {
+ LOG_DEBUG(Service_VI, "called. flags={}, display_id={}", flags, display_id);
+
+ s32 producer_binder_id;
+ R_TRY(m_container->CreateStrayLayer(&producer_binder_id, out_layer_id, display_id));
+
+ std::scoped_lock lk{m_lock};
+ m_stray_layer_ids.insert(*out_layer_id);
+
+ android::OutputParcel parcel;
+ parcel.WriteInterface(NativeWindow{producer_binder_id});
+
+ const auto buffer = parcel.Serialize();
+ std::memcpy(out_native_window.data(), buffer.data(),
+ std::min(out_native_window.size(), buffer.size()));
+
+ *out_size = buffer.size();
+
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::DestroyStrayLayer(u64 layer_id) {
+ LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
+
+ {
+ std::scoped_lock lk{m_lock};
+ R_UNLESS(m_stray_layer_ids.contains(layer_id), VI::ResultNotFound);
+ m_stray_layer_ids.erase(layer_id);
+ }
+
+ R_RETURN(m_container->DestroyStrayLayer(layer_id));
+}
+
+Result IApplicationDisplayService::GetDisplayVsyncEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, u64 display_id) {
+ LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
+
+ std::scoped_lock lk{m_lock};
+
+ auto [it, created] = m_display_vsync_events.emplace(display_id, m_context);
+ R_UNLESS(created, VI::ResultPermissionDenied);
+
+ m_container->LinkVsyncEvent(display_id, &it->second);
+ *out_vsync_event = it->second.GetHandle();
+
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode,
+ NintendoScaleMode mode) {
+ LOG_DEBUG(Service_VI, "called mode={}", mode);
+
+ switch (mode) {
+ case NintendoScaleMode::None:
+ *out_scaling_mode = ConvertedScaleMode::None;
+ R_SUCCEED();
+ case NintendoScaleMode::Freeze:
+ *out_scaling_mode = ConvertedScaleMode::Freeze;
+ R_SUCCEED();
+ case NintendoScaleMode::ScaleToWindow:
+ *out_scaling_mode = ConvertedScaleMode::ScaleToWindow;
+ R_SUCCEED();
+ case NintendoScaleMode::ScaleAndCrop:
+ *out_scaling_mode = ConvertedScaleMode::ScaleAndCrop;
+ R_SUCCEED();
+ case NintendoScaleMode::PreserveAspectRatio:
+ *out_scaling_mode = ConvertedScaleMode::PreserveAspectRatio;
+ R_SUCCEED();
+ default:
+ LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
+ R_THROW(VI::ResultOperationFailed);
+ }
+}
+
+Result IApplicationDisplayService::GetIndirectLayerImageMap(
+ Out<u64> out_size, Out<u64> out_stride,
+ OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
+ s64 width, s64 height, u64 indirect_layer_consumer_handle, ClientAppletResourceUserId aruid) {
+ LOG_WARNING(
+ Service_VI,
+ "(STUBBED) called, width={}, height={}, indirect_layer_consumer_handle={}, aruid={:#x}",
+ width, height, indirect_layer_consumer_handle, aruid.pid);
+ *out_size = 0;
+ *out_stride = 0;
+ R_SUCCEED();
+}
+
+Result IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo(Out<s64> out_size,
+ Out<s64> out_alignment,
+ s64 width, s64 height) {
+ LOG_DEBUG(Service_VI, "called width={}, height={}", width, height);
+
+ constexpr u64 base_size = 0x20000;
+ const auto texture_size = width * height * 4;
+
+ *out_alignment = 0x1000;
+ *out_size = (texture_size + base_size - 1) / base_size * base_size;
+
+ R_SUCCEED();
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/application_display_service.h b/src/core/hle/service/vi/application_display_service.h
new file mode 100644
index 000000000..1bdeb8f84
--- /dev/null
+++ b/src/core/hle/service/vi/application_display_service.h
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <map>
+#include <set>
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::Nvnflinger {
+class IHOSBinderDriver;
+}
+
+namespace Service::VI {
+
+class Container;
+class IManagerDisplayService;
+class ISystemDisplayService;
+
+class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
+public:
+ IApplicationDisplayService(Core::System& system_, std::shared_ptr<Container> container);
+ ~IApplicationDisplayService() override;
+
+ std::shared_ptr<Container> GetContainer() const {
+ return m_container;
+ }
+
+public:
+ Result GetRelayService(Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service);
+ Result GetSystemDisplayService(
+ Out<SharedPointer<ISystemDisplayService>> out_system_display_service);
+ Result GetManagerDisplayService(
+ Out<SharedPointer<IManagerDisplayService>> out_manager_display_service);
+ Result GetIndirectDisplayTransactionService(
+ Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service);
+ Result OpenDisplay(Out<u64> out_display_id, DisplayName display_name);
+ Result OpenDefaultDisplay(Out<u64> out_display_id);
+ Result CloseDisplay(u64 display_id);
+ Result SetDisplayEnabled(u32 state, u64 display_id);
+ Result GetDisplayResolution(Out<s64> out_width, Out<s64> out_height, u64 display_id);
+ Result SetLayerScalingMode(NintendoScaleMode scale_mode, u64 layer_id);
+ Result ListDisplays(Out<u64> out_count,
+ OutArray<DisplayInfo, BufferAttr_HipcMapAlias> out_displays);
+ Result OpenLayer(Out<u64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_native_window,
+ DisplayName display_name, u64 layer_id, ClientAppletResourceUserId aruid);
+ Result CloseLayer(u64 layer_id);
+ Result CreateStrayLayer(Out<u64> out_layer_id, Out<u64> out_size,
+ OutBuffer<BufferAttr_HipcMapAlias> out_native_window, u32 flags,
+ u64 display_id);
+ Result DestroyStrayLayer(u64 layer_id);
+ Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
+ u64 display_id);
+ Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
+ Result GetIndirectLayerImageMap(
+ Out<u64> out_size, Out<u64> out_stride,
+ OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
+ s64 width, s64 height, u64 indirect_layer_consumer_handle,
+ ClientAppletResourceUserId aruid);
+ Result GetIndirectLayerImageRequiredMemoryInfo(Out<s64> out_size, Out<s64> out_alignment,
+ s64 width, s64 height);
+
+private:
+ const std::shared_ptr<Container> m_container;
+
+ KernelHelpers::ServiceContext m_context;
+ std::mutex m_lock{};
+ std::set<u64> m_open_layer_ids{};
+ std::set<u64> m_stray_layer_ids{};
+ std::map<u64, Event> m_display_vsync_events{};
+ bool m_vsync_event_fetched{false};
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/application_root_service.cpp b/src/core/hle/service/vi/application_root_service.cpp
new file mode 100644
index 000000000..7f35a048d
--- /dev/null
+++ b/src/core/hle/service/vi/application_root_service.cpp
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/vi/application_display_service.h"
+#include "core/hle/service/vi/application_root_service.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/service_creator.h"
+#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::VI {
+
+IApplicationRootService::IApplicationRootService(Core::System& system_,
+ std::shared_ptr<Container> container)
+ : ServiceFramework{system_, "vi:u"}, m_container{std::move(container)} {
+ static const FunctionInfo functions[] = {
+ {0, C<&IApplicationRootService::GetDisplayService>, "GetDisplayService"},
+ {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
+ };
+ RegisterHandlers(functions);
+}
+
+IApplicationRootService::~IApplicationRootService() = default;
+
+Result IApplicationRootService::GetDisplayService(
+ Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
+ Permission::User, policy));
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/application_root_service.h b/src/core/hle/service/vi/application_root_service.h
new file mode 100644
index 000000000..15aa4483d
--- /dev/null
+++ b/src/core/hle/service/vi/application_root_service.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::VI {
+
+class Container;
+class IApplicationDisplayService;
+enum class Policy : u32;
+
+class IApplicationRootService final : public ServiceFramework<IApplicationRootService> {
+public:
+ explicit IApplicationRootService(Core::System& system_, std::shared_ptr<Container> container);
+ ~IApplicationRootService() override;
+
+private:
+ Result GetDisplayService(
+ Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
+ Policy policy);
+
+private:
+ const std::shared_ptr<Container> m_container;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/conductor.cpp b/src/core/hle/service/vi/conductor.cpp
new file mode 100644
index 000000000..c8ce4fca0
--- /dev/null
+++ b/src/core/hle/service/vi/conductor.cpp
@@ -0,0 +1,114 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/service/vi/conductor.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/display_list.h"
+#include "core/hle/service/vi/vsync_manager.h"
+
+constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60};
+
+namespace Service::VI {
+
+Conductor::Conductor(Core::System& system, Container& container, DisplayList& displays)
+ : m_system(system), m_container(container) {
+ displays.ForEachDisplay([&](Display& display) {
+ m_vsync_managers.insert({display.GetId(), VsyncManager{}});
+ });
+
+ if (system.IsMulticore()) {
+ m_event = Core::Timing::CreateEvent(
+ "ScreenComposition",
+ [this](s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ m_signal.Set();
+ return std::chrono::nanoseconds(this->GetNextTicks());
+ });
+
+ system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
+ m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
+ } else {
+ m_event = Core::Timing::CreateEvent(
+ "ScreenComposition",
+ [this](s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ this->ProcessVsync();
+ return std::chrono::nanoseconds(this->GetNextTicks());
+ });
+
+ system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
+ }
+}
+
+Conductor::~Conductor() {
+ m_system.CoreTiming().UnscheduleEvent(m_event);
+
+ if (m_system.IsMulticore()) {
+ m_thread.request_stop();
+ m_signal.Set();
+ }
+}
+
+void Conductor::LinkVsyncEvent(u64 display_id, Event* event) {
+ if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
+ it->second.LinkVsyncEvent(event);
+ }
+}
+
+void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) {
+ if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
+ it->second.UnlinkVsyncEvent(event);
+ }
+}
+
+void Conductor::ProcessVsync() {
+ for (auto& [display_id, manager] : m_vsync_managers) {
+ m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id);
+ manager.SignalVsync();
+ }
+}
+
+void Conductor::VsyncThread(std::stop_token token) {
+ Common::SetCurrentThreadName("VSyncThread");
+
+ while (!token.stop_requested()) {
+ m_signal.Wait();
+
+ if (m_system.IsShuttingDown()) {
+ return;
+ }
+
+ this->ProcessVsync();
+ }
+}
+
+s64 Conductor::GetNextTicks() const {
+ const auto& settings = Settings::values;
+ auto speed_scale = 1.f;
+ if (settings.use_multi_core.GetValue()) {
+ if (settings.use_speed_limit.GetValue()) {
+ // Scales the speed based on speed_limit setting on MC. SC is handled by
+ // SpeedLimiter::DoSpeedLimiting.
+ speed_scale = 100.f / settings.speed_limit.GetValue();
+ } else {
+ // Run at unlocked framerate.
+ speed_scale = 0.01f;
+ }
+ }
+
+ // Adjust by speed limit determined during composition.
+ speed_scale /= m_compose_speed_scale;
+
+ if (m_system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
+ // Run at intended presentation rate during video playback.
+ speed_scale = 1.f;
+ }
+
+ const f32 effective_fps = 60.f / static_cast<f32>(m_swap_interval);
+ return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/conductor.h b/src/core/hle/service/vi/conductor.h
new file mode 100644
index 000000000..52e3595d2
--- /dev/null
+++ b/src/core/hle/service/vi/conductor.h
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/polyfill_thread.h"
+#include "common/thread.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+struct EventType;
+}
+
+namespace Service {
+class Event;
+}
+
+namespace Service::VI {
+
+class Container;
+class DisplayList;
+class VsyncManager;
+
+class Conductor {
+public:
+ explicit Conductor(Core::System& system, Container& container, DisplayList& displays);
+ ~Conductor();
+
+ void LinkVsyncEvent(u64 display_id, Event* event);
+ void UnlinkVsyncEvent(u64 display_id, Event* event);
+
+private:
+ void ProcessVsync();
+ void VsyncThread(std::stop_token token);
+ s64 GetNextTicks() const;
+
+private:
+ Core::System& m_system;
+ Container& m_container;
+ std::unordered_map<u64, VsyncManager> m_vsync_managers;
+ std::shared_ptr<Core::Timing::EventType> m_event;
+ Common::Event m_signal;
+ std::jthread m_thread;
+
+private:
+ s32 m_swap_interval = 1;
+ f32 m_compose_speed_scale = 1.0f;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp
new file mode 100644
index 000000000..9074f4ae0
--- /dev/null
+++ b/src/core/hle/service/vi/container.cpp
@@ -0,0 +1,226 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/nvdrv/nvdrv_interface.h"
+#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
+#include "core/hle/service/nvnflinger/surface_flinger.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/vi_results.h"
+
+namespace Service::VI {
+
+Container::Container(Core::System& system) {
+ m_displays.CreateDisplay(DisplayName{"Default"});
+ m_displays.CreateDisplay(DisplayName{"External"});
+ m_displays.CreateDisplay(DisplayName{"Edid"});
+ m_displays.CreateDisplay(DisplayName{"Internal"});
+ m_displays.CreateDisplay(DisplayName{"Null"});
+
+ m_binder_driver =
+ system.ServiceManager().GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true);
+ m_surface_flinger = m_binder_driver->GetSurfaceFlinger();
+
+ const auto nvdrv =
+ system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
+ m_shared_buffer_manager.emplace(system, *this, nvdrv);
+
+ m_displays.ForEachDisplay(
+ [&](auto& display) { m_surface_flinger->AddDisplay(display.GetId()); });
+
+ m_conductor.emplace(system, *this, m_displays);
+}
+
+Container::~Container() {
+ this->OnTerminate();
+}
+
+void Container::OnTerminate() {
+ std::scoped_lock lk{m_lock};
+
+ m_is_shut_down = true;
+
+ m_layers.ForEachLayer([&](auto& layer) { this->DestroyLayerLocked(layer.GetId()); });
+
+ m_displays.ForEachDisplay(
+ [&](auto& display) { m_surface_flinger->RemoveDisplay(display.GetId()); });
+}
+
+SharedBufferManager* Container::GetSharedBufferManager() {
+ return std::addressof(*m_shared_buffer_manager);
+}
+
+Result Container::GetBinderDriver(
+ std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver) {
+ *out_binder_driver = m_binder_driver;
+ R_SUCCEED();
+}
+
+Result Container::GetLayerProducerHandle(
+ std::shared_ptr<android::BufferQueueProducer>* out_producer, u64 layer_id) {
+ std::scoped_lock lk{m_lock};
+
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ const auto binder = m_binder_driver->GetServer()->TryGetBinder(layer->GetProducerBinderId());
+ R_UNLESS(binder != nullptr, VI::ResultNotFound);
+
+ *out_producer = std::static_pointer_cast<android::BufferQueueProducer>(binder);
+ R_SUCCEED();
+}
+
+Result Container::OpenDisplay(u64* out_display_id, const DisplayName& display_name) {
+ auto* const display = m_displays.GetDisplayByName(display_name);
+ R_UNLESS(display != nullptr, VI::ResultNotFound);
+
+ *out_display_id = display->GetId();
+ R_SUCCEED();
+}
+
+Result Container::CloseDisplay(u64 display_id) {
+ R_SUCCEED();
+}
+
+Result Container::CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
+ std::scoped_lock lk{m_lock};
+ R_RETURN(this->CreateLayerLocked(out_layer_id, display_id, owner_aruid));
+}
+
+Result Container::DestroyManagedLayer(u64 layer_id) {
+ std::scoped_lock lk{m_lock};
+
+ // Try to close, if open, but don't fail if not.
+ this->CloseLayerLocked(layer_id);
+
+ R_RETURN(this->DestroyLayerLocked(layer_id));
+}
+
+Result Container::OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
+ std::scoped_lock lk{m_lock};
+ R_RETURN(this->OpenLayerLocked(out_producer_binder_id, layer_id, aruid));
+}
+
+Result Container::CloseLayer(u64 layer_id) {
+ std::scoped_lock lk{m_lock};
+ R_RETURN(this->CloseLayerLocked(layer_id));
+}
+
+Result Container::SetLayerVisibility(u64 layer_id, bool visible) {
+ std::scoped_lock lk{m_lock};
+
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ m_surface_flinger->SetLayerVisibility(layer->GetConsumerBinderId(), visible);
+ R_SUCCEED();
+}
+
+Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
+ std::scoped_lock lk{m_lock};
+
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ m_surface_flinger->SetLayerBlending(layer->GetConsumerBinderId(),
+ enabled ? Nvnflinger::LayerBlending::Coverage
+ : Nvnflinger::LayerBlending::None);
+ R_SUCCEED();
+}
+
+void Container::LinkVsyncEvent(u64 display_id, Event* event) {
+ std::scoped_lock lk{m_lock};
+ m_conductor->LinkVsyncEvent(display_id, event);
+}
+
+void Container::UnlinkVsyncEvent(u64 display_id, Event* event) {
+ std::scoped_lock lk{m_lock};
+ m_conductor->UnlinkVsyncEvent(display_id, event);
+}
+
+Result Container::CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id) {
+ std::scoped_lock lk{m_lock};
+ R_TRY(this->CreateLayerLocked(out_layer_id, display_id, {}));
+ R_RETURN(this->OpenLayerLocked(out_producer_binder_id, *out_layer_id, {}));
+}
+
+Result Container::DestroyStrayLayer(u64 layer_id) {
+ std::scoped_lock lk{m_lock};
+ R_TRY(this->CloseLayerLocked(layer_id));
+ R_RETURN(this->DestroyLayerLocked(layer_id));
+}
+
+Result Container::CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
+ auto* const display = m_displays.GetDisplayById(display_id);
+ R_UNLESS(display != nullptr, VI::ResultNotFound);
+
+ s32 consumer_binder_id, producer_binder_id;
+ m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id);
+
+ auto* const layer =
+ m_layers.CreateLayer(owner_aruid, display, consumer_binder_id, producer_binder_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ m_surface_flinger->CreateLayer(consumer_binder_id);
+
+ *out_layer_id = layer->GetId();
+ R_SUCCEED();
+}
+
+Result Container::DestroyLayerLocked(u64 layer_id) {
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ m_surface_flinger->DestroyLayer(layer->GetConsumerBinderId());
+ m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(),
+ layer->GetProducerBinderId());
+ m_layers.DestroyLayer(layer_id);
+
+ R_SUCCEED();
+}
+
+Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
+ R_UNLESS(!m_is_shut_down, VI::ResultOperationFailed);
+
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+ R_UNLESS(!layer->IsOpen(), VI::ResultOperationFailed);
+ R_UNLESS(layer->GetOwnerAruid() == aruid, VI::ResultPermissionDenied);
+
+ layer->Open();
+
+ if (auto* display = layer->GetDisplay(); display != nullptr) {
+ m_surface_flinger->AddLayerToDisplayStack(display->GetId(), layer->GetConsumerBinderId());
+ }
+
+ *out_producer_binder_id = layer->GetProducerBinderId();
+
+ R_SUCCEED();
+}
+
+Result Container::CloseLayerLocked(u64 layer_id) {
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+ R_UNLESS(layer->IsOpen(), VI::ResultOperationFailed);
+
+ if (auto* display = layer->GetDisplay(); display != nullptr) {
+ m_surface_flinger->RemoveLayerFromDisplayStack(display->GetId(),
+ layer->GetConsumerBinderId());
+ }
+
+ layer->Close();
+
+ R_SUCCEED();
+}
+
+bool Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
+ u64 display_id) {
+ std::scoped_lock lk{m_lock};
+ return m_surface_flinger->ComposeDisplay(out_swap_interval, out_compose_speed_scale,
+ display_id);
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h
new file mode 100644
index 000000000..5eac4d77d
--- /dev/null
+++ b/src/core/hle/service/vi/container.h
@@ -0,0 +1,89 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <optional>
+
+#include "core/hle/service/vi/conductor.h"
+#include "core/hle/service/vi/display_list.h"
+#include "core/hle/service/vi/layer_list.h"
+#include "core/hle/service/vi/shared_buffer_manager.h"
+
+union Result;
+
+namespace Service::android {
+class BufferQueueProducer;
+}
+
+namespace Service::Nvnflinger {
+class IHOSBinderDriver;
+class SurfaceFlinger;
+} // namespace Service::Nvnflinger
+
+namespace Service {
+class Event;
+}
+
+namespace Service::VI {
+
+class SharedBufferManager;
+
+class Container {
+public:
+ explicit Container(Core::System& system);
+ ~Container();
+
+ void OnTerminate();
+
+ SharedBufferManager* GetSharedBufferManager();
+
+ Result GetBinderDriver(std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver);
+ Result GetLayerProducerHandle(std::shared_ptr<android::BufferQueueProducer>* out_producer,
+ u64 layer_id);
+
+ Result OpenDisplay(u64* out_display_id, const DisplayName& display_name);
+ Result CloseDisplay(u64 display_id);
+
+ // Managed layers are created by the interaction between am and ommdisp
+ // on behalf of an applet. Their lifetime ends with the lifetime of the
+ // applet's ISelfController.
+ Result CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid);
+ Result DestroyManagedLayer(u64 layer_id);
+ Result OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
+ Result CloseLayer(u64 layer_id);
+
+ // Stray layers are created by non-applet sysmodules. Their lifetime ends
+ // with the lifetime of the IApplicationDisplayService which created them.
+ Result CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id);
+ Result DestroyStrayLayer(u64 layer_id);
+
+ Result SetLayerVisibility(u64 layer_id, bool visible);
+ Result SetLayerBlending(u64 layer_id, bool enabled);
+
+ void LinkVsyncEvent(u64 display_id, Event* event);
+ void UnlinkVsyncEvent(u64 display_id, Event* event);
+
+private:
+ Result CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid);
+ Result DestroyLayerLocked(u64 layer_id);
+ Result OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
+ Result CloseLayerLocked(u64 layer_id);
+
+public:
+ bool ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
+
+private:
+ std::mutex m_lock{};
+ DisplayList m_displays{};
+ LayerList m_layers{};
+ std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_driver{};
+ std::shared_ptr<Nvnflinger::SurfaceFlinger> m_surface_flinger{};
+ std::optional<SharedBufferManager> m_shared_buffer_manager{};
+ std::optional<Conductor> m_conductor{};
+ bool m_is_shut_down{};
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display.h b/src/core/hle/service/vi/display.h
new file mode 100644
index 000000000..fceda75e3
--- /dev/null
+++ b/src/core/hle/service/vi/display.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::VI {
+
+class Display {
+public:
+ constexpr Display() = default;
+
+ void Initialize(u64 id, const DisplayName& display_name) {
+ m_id = id;
+ m_display_name = display_name;
+ m_is_initialized = true;
+ }
+
+ void Finalize() {
+ m_id = {};
+ m_display_name = {};
+ m_is_initialized = {};
+ }
+
+ u64 GetId() const {
+ return m_id;
+ }
+
+ const DisplayName& GetDisplayName() const {
+ return m_display_name;
+ }
+
+ bool IsInitialized() const {
+ return m_is_initialized;
+ }
+
+private:
+ u64 m_id{};
+ DisplayName m_display_name{};
+ bool m_is_initialized{};
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
deleted file mode 100644
index 7f2af9acc..000000000
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <algorithm>
-#include <utility>
-
-#include <fmt/format.h>
-
-#include "common/assert.h"
-#include "core/core.h"
-#include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/nvdrv/core/container.h"
-#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
-#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
-#include "core/hle/service/nvnflinger/buffer_queue_core.h"
-#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
-#include "core/hle/service/nvnflinger/hardware_composer.h"
-#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
-#include "core/hle/service/vi/display/vi_display.h"
-#include "core/hle/service/vi/layer/vi_layer.h"
-#include "core/hle/service/vi/vi_results.h"
-
-namespace Service::VI {
-
-struct BufferQueue {
- std::shared_ptr<android::BufferQueueCore> core;
- std::unique_ptr<android::BufferQueueProducer> producer;
- std::unique_ptr<android::BufferQueueConsumer> consumer;
-};
-
-static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context,
- Service::Nvidia::NvCore::NvMap& nvmap) {
- auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
- return {
- buffer_queue_core,
- std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
- std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
-}
-
-Display::Display(u64 id, std::string name_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_,
- KernelHelpers::ServiceContext& service_context_, Core::System& system_)
- : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
- service_context{service_context_} {
- hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>();
- vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
-}
-
-Display::~Display() {
- service_context.CloseEvent(vsync_event);
-}
-
-Layer& Display::GetLayer(std::size_t index) {
- size_t i = 0;
- for (auto& layer : layers) {
- if (!layer->IsOpen() || !layer->IsVisible()) {
- continue;
- }
-
- if (i == index) {
- return *layer;
- }
-
- i++;
- }
-
- UNREACHABLE();
-}
-
-size_t Display::GetNumLayers() const {
- return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen() && l->IsVisible(); });
-}
-
-Kernel::KReadableEvent* Display::GetVSyncEvent() {
- return &vsync_event->GetReadableEvent();
-}
-
-void Display::SignalVSyncEvent() {
- vsync_event->Signal();
-}
-
-void Display::CreateLayer(u64 layer_id, u32 binder_id,
- Service::Nvidia::NvCore::Container& nv_core) {
- auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
-
- auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
- buffer_item_consumer->Connect(false);
-
- layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
- std::move(buffer_item_consumer)));
-
- if (is_abandoned) {
- this->FindLayer(layer_id)->GetConsumer().Abandon();
- }
-
- hos_binder_driver_server.RegisterProducer(std::move(producer));
-}
-
-void Display::DestroyLayer(u64 layer_id) {
- if (auto* layer = this->FindLayer(layer_id); layer != nullptr) {
- layer->GetConsumer().Abandon();
- }
-
- std::erase_if(layers,
- [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
-}
-
-void Display::Abandon() {
- for (auto& layer : layers) {
- layer->GetConsumer().Abandon();
- }
- is_abandoned = true;
-}
-
-Layer* Display::FindLayer(u64 layer_id) {
- const auto itr =
- std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
- return layer->GetLayerId() == layer_id;
- });
-
- if (itr == layers.end()) {
- return nullptr;
- }
-
- return itr->get();
-}
-
-const Layer* Display::FindLayer(u64 layer_id) const {
- const auto itr =
- std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
- return layer->GetLayerId() == layer_id;
- });
-
- if (itr == layers.end()) {
- return nullptr;
- }
-
- return itr->get();
-}
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
deleted file mode 100644
index 220292cff..000000000
--- a/src/core/hle/service/vi/display/vi_display.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "core/hle/result.h"
-
-namespace Core {
-class System;
-}
-
-namespace Kernel {
-class KEvent;
-class KReadableEvent;
-} // namespace Kernel
-
-namespace Service::android {
-class BufferQueueProducer;
-}
-
-namespace Service::KernelHelpers {
-class ServiceContext;
-}
-
-namespace Service::Nvnflinger {
-class HardwareComposer;
-class HosBinderDriverServer;
-} // namespace Service::Nvnflinger
-
-namespace Service::Nvidia::NvCore {
-class Container;
-class NvMap;
-} // namespace Service::Nvidia::NvCore
-
-namespace Service::VI {
-
-class Layer;
-
-/// Represents a single display type
-class Display {
-public:
- YUZU_NON_COPYABLE(Display);
- YUZU_NON_MOVEABLE(Display);
-
- /// Constructs a display with a given unique ID and name.
- ///
- /// @param id The unique ID for this display.
- /// @param hos_binder_driver_server_ Nvnflinger HOSBinderDriver server instance.
- /// @param service_context_ The ServiceContext for the owning service.
- /// @param name_ The name for this display.
- /// @param system_ The global system instance.
- ///
- Display(u64 id, std::string name_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_,
- KernelHelpers::ServiceContext& service_context_, Core::System& system_);
- ~Display();
-
- /// Gets the unique ID assigned to this display.
- u64 GetID() const {
- return display_id;
- }
-
- /// Gets the name of this display
- const std::string& GetName() const {
- return name;
- }
-
- /// Whether or not this display has any layers added to it.
- bool HasLayers() const {
- return GetNumLayers() > 0;
- }
-
- /// Gets a layer for this display based off an index.
- Layer& GetLayer(std::size_t index);
-
- std::size_t GetNumLayers() const;
-
- /// Gets the internal vsync event.
- Kernel::KReadableEvent* GetVSyncEvent();
-
- /// Signals the internal vsync event.
- void SignalVSyncEvent();
-
- /// Creates and adds a layer to this display with the given ID.
- ///
- /// @param layer_id The ID to assign to the created layer.
- /// @param binder_id The ID assigned to the buffer queue.
- ///
- void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
-
- /// Removes a layer from this display with the given ID.
- ///
- /// @param layer_id The ID assigned to the layer to destroy.
- ///
- void DestroyLayer(u64 layer_id);
-
- /// Resets the display for a new connection.
- void Reset() {
- layers.clear();
- }
-
- void Abandon();
-
- /// Attempts to find a layer with the given ID.
- ///
- /// @param layer_id The layer ID.
- ///
- /// @returns If found, the Layer instance with the given ID.
- /// If not found, then nullptr is returned.
- ///
- Layer* FindLayer(u64 layer_id);
-
- /// Attempts to find a layer with the given ID.
- ///
- /// @param layer_id The layer ID.
- ///
- /// @returns If found, the Layer instance with the given ID.
- /// If not found, then nullptr is returned.
- ///
- const Layer* FindLayer(u64 layer_id) const;
-
- Nvnflinger::HardwareComposer& GetComposer() const {
- return *hardware_composer;
- }
-
-private:
- u64 display_id;
- std::string name;
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
- KernelHelpers::ServiceContext& service_context;
-
- std::vector<std::unique_ptr<Layer>> layers;
- std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer;
- Kernel::KEvent* vsync_event{};
- bool is_abandoned{};
-};
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display_list.h b/src/core/hle/service/vi/display_list.h
new file mode 100644
index 000000000..f710ac472
--- /dev/null
+++ b/src/core/hle/service/vi/display_list.h
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <cstring>
+
+#include "core/hle/service/vi/display.h"
+
+namespace Service::VI {
+
+class DisplayList {
+public:
+ constexpr DisplayList() = default;
+
+ bool CreateDisplay(const DisplayName& name) {
+ Display* const display = this->GetFreeDisplay();
+ if (!display) {
+ return false;
+ }
+
+ display->Initialize(m_next_id++, name);
+ return true;
+ }
+
+ bool DestroyDisplay(u64 display_id) {
+ Display* display = this->GetDisplayById(display_id);
+ if (!display) {
+ return false;
+ }
+
+ display->Finalize();
+ return true;
+ }
+
+ Display* GetDisplayByName(const DisplayName& name) {
+ for (auto& display : m_displays) {
+ if (display.IsInitialized() &&
+ std::strncmp(name.data(), display.GetDisplayName().data(), sizeof(DisplayName)) ==
+ 0) {
+ return &display;
+ }
+ }
+
+ return nullptr;
+ }
+
+ Display* GetDisplayById(u64 display_id) {
+ for (auto& display : m_displays) {
+ if (display.IsInitialized() && display.GetId() == display_id) {
+ return &display;
+ }
+ }
+
+ return nullptr;
+ }
+
+ template <typename F>
+ void ForEachDisplay(F&& cb) {
+ for (auto& display : m_displays) {
+ if (display.IsInitialized()) {
+ cb(display);
+ }
+ }
+ }
+
+private:
+ Display* GetFreeDisplay() {
+ for (auto& display : m_displays) {
+ if (!display.IsInitialized()) {
+ return &display;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+ std::array<Display, 8> m_displays{};
+ u64 m_next_id{};
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer.h b/src/core/hle/service/vi/layer.h
new file mode 100644
index 000000000..e4c9c9864
--- /dev/null
+++ b/src/core/hle/service/vi/layer.h
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Service::VI {
+
+class Display;
+
+class Layer {
+public:
+ constexpr Layer() = default;
+
+ void Initialize(u64 id, u64 owner_aruid, Display* display, s32 consumer_binder_id,
+ s32 producer_binder_id) {
+ m_id = id;
+ m_owner_aruid = owner_aruid;
+ m_display = display;
+ m_consumer_binder_id = consumer_binder_id;
+ m_producer_binder_id = producer_binder_id;
+ m_is_initialized = true;
+ }
+
+ void Finalize() {
+ m_id = {};
+ m_owner_aruid = {};
+ m_display = {};
+ m_consumer_binder_id = {};
+ m_producer_binder_id = {};
+ m_is_initialized = {};
+ }
+
+ void Open() {
+ m_is_open = true;
+ }
+
+ void Close() {
+ m_is_open = false;
+ }
+
+ u64 GetId() const {
+ return m_id;
+ }
+
+ u64 GetOwnerAruid() const {
+ return m_owner_aruid;
+ }
+
+ Display* GetDisplay() const {
+ return m_display;
+ }
+
+ s32 GetConsumerBinderId() const {
+ return m_consumer_binder_id;
+ }
+
+ s32 GetProducerBinderId() const {
+ return m_producer_binder_id;
+ }
+
+ bool IsInitialized() const {
+ return m_is_initialized;
+ }
+
+ bool IsOpen() const {
+ return m_is_open;
+ }
+
+private:
+ u64 m_id{};
+ u64 m_owner_aruid{};
+ Display* m_display{};
+ s32 m_consumer_binder_id{};
+ s32 m_producer_binder_id{};
+ bool m_is_initialized{};
+ bool m_is_open{};
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
deleted file mode 100644
index 493bd6e9e..000000000
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/vi/layer/vi_layer.h"
-
-namespace Service::VI {
-
-Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
- android::BufferQueueProducer& binder_,
- std::shared_ptr<android::BufferItemConsumer>&& consumer_)
- : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_},
- consumer{std::move(consumer_)}, open{false}, visible{true} {}
-
-Layer::~Layer() = default;
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
deleted file mode 100644
index b4b031ee7..000000000
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-#include <utility>
-
-#include "common/common_types.h"
-
-namespace Service::android {
-class BufferItemConsumer;
-class BufferQueueCore;
-class BufferQueueProducer;
-} // namespace Service::android
-
-namespace Service::VI {
-
-/// Represents a single display layer.
-class Layer {
-public:
- /// Constructs a layer with a given ID and buffer queue.
- ///
- /// @param layer_id_ The ID to assign to this layer.
- /// @param binder_id_ The binder ID to assign to this layer.
- /// @param binder_ The buffer producer queue for this layer to use.
- ///
- Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
- android::BufferQueueProducer& binder_,
- std::shared_ptr<android::BufferItemConsumer>&& consumer_);
- ~Layer();
-
- Layer(const Layer&) = delete;
- Layer& operator=(const Layer&) = delete;
-
- Layer(Layer&&) = default;
- Layer& operator=(Layer&&) = delete;
-
- /// Gets the ID for this layer.
- u64 GetLayerId() const {
- return layer_id;
- }
-
- /// Gets the binder ID for this layer.
- u32 GetBinderId() const {
- return binder_id;
- }
-
- /// Gets a reference to the buffer queue this layer is using.
- android::BufferQueueProducer& GetBufferQueue() {
- return binder;
- }
-
- /// Gets a const reference to the buffer queue this layer is using.
- const android::BufferQueueProducer& GetBufferQueue() const {
- return binder;
- }
-
- android::BufferItemConsumer& GetConsumer() {
- return *consumer;
- }
-
- const android::BufferItemConsumer& GetConsumer() const {
- return *consumer;
- }
-
- android::BufferQueueCore& Core() {
- return core;
- }
-
- const android::BufferQueueCore& Core() const {
- return core;
- }
-
- bool IsVisible() const {
- return visible;
- }
-
- void SetVisibility(bool v) {
- visible = v;
- }
-
- bool IsOpen() const {
- return open;
- }
-
- bool Close() {
- return std::exchange(open, false);
- }
-
- bool Open() {
- return !std::exchange(open, true);
- }
-
-private:
- const u64 layer_id;
- const u32 binder_id;
- android::BufferQueueCore& core;
- android::BufferQueueProducer& binder;
- std::shared_ptr<android::BufferItemConsumer> consumer;
- bool open;
- bool visible;
-};
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer_list.h b/src/core/hle/service/vi/layer_list.h
new file mode 100644
index 000000000..4afca6f40
--- /dev/null
+++ b/src/core/hle/service/vi/layer_list.h
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/vi/layer.h"
+
+namespace Service::VI {
+
+class LayerList {
+public:
+ constexpr LayerList() = default;
+
+ Layer* CreateLayer(u64 owner_aruid, Display* display, s32 consumer_binder_id,
+ s32 producer_binder_id) {
+ Layer* const layer = GetFreeLayer();
+ if (!layer) {
+ return nullptr;
+ }
+
+ layer->Initialize(++m_next_id, owner_aruid, display, consumer_binder_id,
+ producer_binder_id);
+ return layer;
+ }
+
+ bool DestroyLayer(u64 layer_id) {
+ Layer* const layer = GetLayerById(layer_id);
+ if (!layer) {
+ return false;
+ }
+
+ layer->Finalize();
+ return true;
+ }
+
+ Layer* GetLayerById(u64 layer_id) {
+ for (auto& layer : m_layers) {
+ if (layer.IsInitialized() && layer.GetId() == layer_id) {
+ return &layer;
+ }
+ }
+
+ return nullptr;
+ }
+
+ template <typename F>
+ void ForEachLayer(F&& cb) {
+ for (auto& layer : m_layers) {
+ if (layer.IsInitialized()) {
+ cb(layer);
+ }
+ }
+ }
+
+private:
+ Layer* GetFreeLayer() {
+ for (auto& layer : m_layers) {
+ if (!layer.IsInitialized()) {
+ return &layer;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+ std::array<Layer, 8> m_layers{};
+ u64 m_next_id{};
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_display_service.cpp b/src/core/hle/service/vi/manager_display_service.cpp
new file mode 100644
index 000000000..9f856282e
--- /dev/null
+++ b/src/core/hle/service/vi/manager_display_service.cpp
@@ -0,0 +1,140 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/manager_display_service.h"
+
+namespace Service::VI {
+
+IManagerDisplayService::IManagerDisplayService(Core::System& system_,
+ std::shared_ptr<Container> container)
+ : ServiceFramework{system_, "IManagerDisplayService"}, m_container{std::move(container)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {200, nullptr, "AllocateProcessHeapBlock"},
+ {201, nullptr, "FreeProcessHeapBlock"},
+ {1102, nullptr, "GetDisplayResolution"},
+ {2010, C<&IManagerDisplayService::CreateManagedLayer>, "CreateManagedLayer"},
+ {2011, C<&IManagerDisplayService::DestroyManagedLayer>, "DestroyManagedLayer"},
+ {2012, nullptr, "CreateStrayLayer"},
+ {2050, nullptr, "CreateIndirectLayer"},
+ {2051, nullptr, "DestroyIndirectLayer"},
+ {2052, nullptr, "CreateIndirectProducerEndPoint"},
+ {2053, nullptr, "DestroyIndirectProducerEndPoint"},
+ {2054, nullptr, "CreateIndirectConsumerEndPoint"},
+ {2055, nullptr, "DestroyIndirectConsumerEndPoint"},
+ {2060, nullptr, "CreateWatermarkCompositor"},
+ {2062, nullptr, "SetWatermarkText"},
+ {2063, nullptr, "SetWatermarkLayerStacks"},
+ {2300, nullptr, "AcquireLayerTexturePresentingEvent"},
+ {2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
+ {2302, nullptr, "GetDisplayHotplugEvent"},
+ {2303, nullptr, "GetDisplayModeChangedEvent"},
+ {2402, nullptr, "GetDisplayHotplugState"},
+ {2501, nullptr, "GetCompositorErrorInfo"},
+ {2601, nullptr, "GetDisplayErrorEvent"},
+ {2701, nullptr, "GetDisplayFatalErrorEvent"},
+ {4201, nullptr, "SetDisplayAlpha"},
+ {4203, nullptr, "SetDisplayLayerStack"},
+ {4205, nullptr, "SetDisplayPowerState"},
+ {4206, nullptr, "SetDefaultDisplay"},
+ {4207, nullptr, "ResetDisplayPanel"},
+ {4208, nullptr, "SetDisplayFatalErrorEnabled"},
+ {4209, nullptr, "IsDisplayPanelOn"},
+ {4300, nullptr, "GetInternalPanelId"},
+ {6000, C<&IManagerDisplayService::AddToLayerStack>, "AddToLayerStack"},
+ {6001, nullptr, "RemoveFromLayerStack"},
+ {6002, C<&IManagerDisplayService::SetLayerVisibility>, "SetLayerVisibility"},
+ {6003, nullptr, "SetLayerConfig"},
+ {6004, nullptr, "AttachLayerPresentationTracer"},
+ {6005, nullptr, "DetachLayerPresentationTracer"},
+ {6006, nullptr, "StartLayerPresentationRecording"},
+ {6007, nullptr, "StopLayerPresentationRecording"},
+ {6008, nullptr, "StartLayerPresentationFenceWait"},
+ {6009, nullptr, "StopLayerPresentationFenceWait"},
+ {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"},
+ {6011, nullptr, "EnableLayerAutoClearTransitionBuffer"},
+ {6012, nullptr, "DisableLayerAutoClearTransitionBuffer"},
+ {6013, nullptr, "SetLayerOpacity"},
+ {6014, nullptr, "AttachLayerWatermarkCompositor"},
+ {6015, nullptr, "DetachLayerWatermarkCompositor"},
+ {7000, nullptr, "SetContentVisibility"},
+ {8000, nullptr, "SetConductorLayer"},
+ {8001, nullptr, "SetTimestampTracking"},
+ {8100, nullptr, "SetIndirectProducerFlipOffset"},
+ {8200, nullptr, "CreateSharedBufferStaticStorage"},
+ {8201, nullptr, "CreateSharedBufferTransferMemory"},
+ {8202, nullptr, "DestroySharedBuffer"},
+ {8203, nullptr, "BindSharedLowLevelLayerToManagedLayer"},
+ {8204, nullptr, "BindSharedLowLevelLayerToIndirectLayer"},
+ {8207, nullptr, "UnbindSharedLowLevelLayer"},
+ {8208, nullptr, "ConnectSharedLowLevelLayerToSharedBuffer"},
+ {8209, nullptr, "DisconnectSharedLowLevelLayerFromSharedBuffer"},
+ {8210, nullptr, "CreateSharedLayer"},
+ {8211, nullptr, "DestroySharedLayer"},
+ {8216, nullptr, "AttachSharedLayerToLowLevelLayer"},
+ {8217, nullptr, "ForceDetachSharedLayerFromLowLevelLayer"},
+ {8218, nullptr, "StartDetachSharedLayerFromLowLevelLayer"},
+ {8219, nullptr, "FinishDetachSharedLayerFromLowLevelLayer"},
+ {8220, nullptr, "GetSharedLayerDetachReadyEvent"},
+ {8221, nullptr, "GetSharedLowLevelLayerSynchronizedEvent"},
+ {8222, nullptr, "CheckSharedLowLevelLayerSynchronized"},
+ {8223, nullptr, "RegisterSharedBufferImporterAruid"},
+ {8224, nullptr, "UnregisterSharedBufferImporterAruid"},
+ {8227, nullptr, "CreateSharedBufferProcessHeap"},
+ {8228, nullptr, "GetSharedLayerLayerStacks"},
+ {8229, nullptr, "SetSharedLayerLayerStacks"},
+ {8291, nullptr, "PresentDetachedSharedFrameBufferToLowLevelLayer"},
+ {8292, nullptr, "FillDetachedSharedFrameBufferColor"},
+ {8293, nullptr, "GetDetachedSharedFrameBufferImage"},
+ {8294, nullptr, "SetDetachedSharedFrameBufferImage"},
+ {8295, nullptr, "CopyDetachedSharedFrameBufferImage"},
+ {8296, nullptr, "SetDetachedSharedFrameBufferSubImage"},
+ {8297, nullptr, "GetSharedFrameBufferContentParameter"},
+ {8298, nullptr, "ExpandStartupLogoOnSharedFrameBuffer"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IManagerDisplayService::~IManagerDisplayService() = default;
+
+Result IManagerDisplayService::CreateSharedLayerSession(Kernel::KProcess* owner_process,
+ u64* out_buffer_id, u64* out_layer_handle,
+ u64 display_id, bool enable_blending) {
+ R_RETURN(m_container->GetSharedBufferManager()->CreateSession(
+ owner_process, out_buffer_id, out_layer_handle, display_id, enable_blending));
+}
+
+void IManagerDisplayService::DestroySharedLayerSession(Kernel::KProcess* owner_process) {
+ m_container->GetSharedBufferManager()->DestroySession(owner_process);
+}
+
+Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
+ R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
+}
+
+Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
+ AppletResourceUserId aruid) {
+ LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);
+ R_RETURN(m_container->CreateManagedLayer(out_layer_id, display_id, aruid.pid));
+}
+
+Result IManagerDisplayService::DestroyManagedLayer(u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
+ R_RETURN(m_container->DestroyManagedLayer(layer_id));
+}
+
+Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) {
+ LOG_WARNING(Service_VI, "(STUBBED) called. stack_id={}, layer_id={}", stack_id, layer_id);
+ R_SUCCEED();
+}
+
+Result IManagerDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
+ R_RETURN(m_container->SetLayerVisibility(layer_id, visible));
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_display_service.h b/src/core/hle/service/vi/manager_display_service.h
new file mode 100644
index 000000000..b1bdf7f41
--- /dev/null
+++ b/src/core/hle/service/vi/manager_display_service.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KProcess;
+}
+
+namespace Service::VI {
+
+class Container;
+
+class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
+public:
+ explicit IManagerDisplayService(Core::System& system_, std::shared_ptr<Container> container);
+ ~IManagerDisplayService() override;
+
+ Result CreateSharedLayerSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
+ u64* out_layer_handle, u64 display_id, bool enable_blending);
+ void DestroySharedLayerSession(Kernel::KProcess* owner_process);
+
+ Result SetLayerBlending(bool enabled, u64 layer_id);
+
+public:
+ Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
+ AppletResourceUserId aruid);
+ Result DestroyManagedLayer(u64 layer_id);
+ Result AddToLayerStack(u32 stack_id, u64 layer_id);
+ Result SetLayerVisibility(bool visible, u64 layer_id);
+
+private:
+ const std::shared_ptr<Container> m_container;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_root_service.cpp b/src/core/hle/service/vi/manager_root_service.cpp
new file mode 100644
index 000000000..0f16a15b4
--- /dev/null
+++ b/src/core/hle/service/vi/manager_root_service.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/nvnflinger/hos_binder_driver.h"
+#include "core/hle/service/vi/application_display_service.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/manager_root_service.h"
+#include "core/hle/service/vi/service_creator.h"
+#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::VI {
+
+IManagerRootService::IManagerRootService(Core::System& system_,
+ std::shared_ptr<Container> container)
+ : ServiceFramework{system_, "vi:m"}, m_container{std::move(container)} {
+ static const FunctionInfo functions[] = {
+ {2, C<&IManagerRootService::GetDisplayService>, "GetDisplayService"},
+ {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
+ {100, nullptr, "PrepareFatal"},
+ {101, nullptr, "ShowFatal"},
+ {102, nullptr, "DrawFatalRectangle"},
+ {103, nullptr, "DrawFatalText32"},
+ };
+ RegisterHandlers(functions);
+}
+
+IManagerRootService::~IManagerRootService() = default;
+
+Result IManagerRootService::GetDisplayService(
+ Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
+ Permission::Manager, policy));
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_root_service.h b/src/core/hle/service/vi/manager_root_service.h
new file mode 100644
index 000000000..77cd32869
--- /dev/null
+++ b/src/core/hle/service/vi/manager_root_service.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::VI {
+
+class Container;
+class IApplicationDisplayService;
+enum class Policy : u32;
+
+class IManagerRootService final : public ServiceFramework<IManagerRootService> {
+public:
+ explicit IManagerRootService(Core::System& system_, std::shared_ptr<Container> container);
+ ~IManagerRootService() override;
+
+ Result GetDisplayService(
+ Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
+ Policy policy);
+
+private:
+ const std::shared_ptr<Container> m_container;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/service_creator.cpp b/src/core/hle/service/vi/service_creator.cpp
new file mode 100644
index 000000000..2b8e5f957
--- /dev/null
+++ b/src/core/hle/service/vi/service_creator.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/vi/application_display_service.h"
+#include "core/hle/service/vi/service_creator.h"
+#include "core/hle/service/vi/vi_results.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::VI {
+
+static bool IsValidServiceAccess(Permission permission, Policy policy) {
+ if (permission == Permission::User) {
+ return policy == Policy::User;
+ }
+
+ if (permission == Permission::System || permission == Permission::Manager) {
+ return policy == Policy::User || policy == Policy::Compositor;
+ }
+
+ return false;
+}
+
+Result GetApplicationDisplayService(
+ std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
+ Core::System& system, std::shared_ptr<Container> container, Permission permission,
+ Policy policy) {
+
+ if (!IsValidServiceAccess(permission, policy)) {
+ LOG_ERROR(Service_VI, "Permission denied for policy {}", policy);
+ R_THROW(ResultPermissionDenied);
+ }
+
+ *out_application_display_service =
+ std::make_shared<IApplicationDisplayService>(system, std::move(container));
+ R_SUCCEED();
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/service_creator.h b/src/core/hle/service/vi/service_creator.h
new file mode 100644
index 000000000..c6ba1797d
--- /dev/null
+++ b/src/core/hle/service/vi/service_creator.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+
+namespace Core {
+class System;
+}
+
+union Result;
+
+namespace Service::VI {
+
+class Container;
+class IApplicationDisplayService;
+enum class Permission;
+enum class Policy : u32;
+
+Result GetApplicationDisplayService(
+ std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
+ Core::System& system, std::shared_ptr<Container> container, Permission permission,
+ Policy policy);
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/shared_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp
new file mode 100644
index 000000000..12cba16fa
--- /dev/null
+++ b/src/core/hle/service/vi/shared_buffer_manager.cpp
@@ -0,0 +1,431 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <random>
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_system_resource.h"
+#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
+#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvnflinger/pixel_format.h"
+#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/shared_buffer_manager.h"
+#include "core/hle/service/vi/vi_results.h"
+#include "video_core/gpu.h"
+#include "video_core/host1x/host1x.h"
+
+namespace Service::VI {
+
+namespace {
+
+Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
+ Core::System& system, u32 size) {
+ using Core::Memory::YUZU_PAGESIZE;
+
+ // Allocate memory for the system shared buffer.
+ auto& kernel = system.Kernel();
+
+ // Hold a temporary page group reference while we try to map it.
+ auto pg = std::make_unique<Kernel::KPageGroup>(
+ kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager()));
+
+ // Allocate memory from secure pool.
+ R_TRY(kernel.MemoryManager().AllocateAndOpen(
+ pg.get(), size / YUZU_PAGESIZE,
+ Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
+ Kernel::KMemoryManager::Direction::FromBack)));
+
+ // Fill the output data with red.
+ for (auto& block : *pg) {
+ u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
+ u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
+
+ for (; start < end; start++) {
+ *start = 0xFF0000FF;
+ }
+ }
+
+ // Return the mapped page group.
+ *out_page_group = std::move(pg);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address,
+ std::unique_ptr<Kernel::KPageGroup>& pg,
+ Kernel::KProcess* process, Core::System& system) {
+ using Core::Memory::YUZU_PAGESIZE;
+
+ auto& page_table = process->GetPageTable();
+
+ // Get bounds of where mapping is possible.
+ const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
+ const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
+ const auto state = Kernel::KMemoryState::IoMemory;
+ const auto perm = Kernel::KMemoryPermission::UserReadWrite;
+ std::mt19937_64 rng{process->GetRandomEntropy(0)};
+
+ // Retry up to 64 times to map into alias code range.
+ Result res = ResultSuccess;
+ int i;
+ for (i = 0; i < 64; i++) {
+ *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE);
+ res = page_table.MapPageGroup(*out_map_address, *pg, state, perm);
+ if (R_SUCCEEDED(res)) {
+ break;
+ }
+ }
+
+ // Return failure, if necessary
+ R_UNLESS(i < 64, res);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
+ // Create a handle.
+ Nvidia::Devices::nvmap::IocCreateParams create_params{
+ .size = size,
+ .handle = 0,
+ };
+ R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success,
+ VI::ResultOperationFailed);
+
+ // Assign the output handle.
+ *out_nv_map_handle = create_params.handle;
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) {
+ // Free the handle.
+ Nvidia::Devices::nvmap::IocFreeParams free_params{
+ .handle = handle,
+ };
+ R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success,
+ VI::ResultOperationFailed);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
+ u32 size, Nvidia::DeviceFD nvmap_fd) {
+ // Assign the allocated memory to the handle.
+ Nvidia::Devices::nvmap::IocAllocParams alloc_params{
+ .handle = handle,
+ .heap_mask = 0,
+ .flags = {},
+ .align = 0,
+ .kind = 0,
+ .address = GetInteger(buffer),
+ };
+ R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success,
+ VI::ResultOperationFailed);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd,
+ Common::ProcessAddress buffer, u32 size) {
+ // Get the nvmap device.
+ auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
+ ASSERT(nvmap != nullptr);
+
+ // Create a handle.
+ R_TRY(CreateNvMapHandle(out_handle, *nvmap, size));
+
+ // Ensure we maintain a clean state on failure.
+ ON_RESULT_FAILURE {
+ R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd));
+ };
+
+ // Assign the allocated memory to the handle.
+ R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
+}
+
+void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) {
+ auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
+ ASSERT(nvmap != nullptr);
+
+ R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd));
+}
+
+constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
+constexpr u32 SharedBufferBlockLinearBpp = 4;
+
+constexpr u32 SharedBufferBlockLinearWidth = 1280;
+constexpr u32 SharedBufferBlockLinearHeight = 768;
+constexpr u32 SharedBufferBlockLinearStride =
+ SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
+constexpr u32 SharedBufferNumSlots = 7;
+
+constexpr u32 SharedBufferWidth = 1280;
+constexpr u32 SharedBufferHeight = 720;
+constexpr u32 SharedBufferAsync = false;
+
+constexpr u32 SharedBufferSlotSize =
+ SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp;
+constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots;
+
+constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
+ SharedMemoryPoolLayout layout{};
+ layout.num_slots = SharedBufferNumSlots;
+
+ for (u32 i = 0; i < SharedBufferNumSlots; i++) {
+ layout.slots[i].buffer_offset = i * SharedBufferSlotSize;
+ layout.slots[i].size = SharedBufferSlotSize;
+ layout.slots[i].width = SharedBufferWidth;
+ layout.slots[i].height = SharedBufferHeight;
+ }
+
+ return layout;
+}();
+
+void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
+ auto buffer = std::make_shared<android::NvGraphicBuffer>();
+ buffer->width = SharedBufferWidth;
+ buffer->height = SharedBufferHeight;
+ buffer->stride = SharedBufferBlockLinearStride;
+ buffer->format = SharedBufferBlockLinearFormat;
+ buffer->external_format = SharedBufferBlockLinearFormat;
+ buffer->buffer_id = handle;
+ buffer->offset = slot * SharedBufferSlotSize;
+ ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
+}
+
+} // namespace
+
+SharedBufferManager::SharedBufferManager(Core::System& system, Container& container,
+ std::shared_ptr<Nvidia::Module> nvdrv)
+ : m_system(system), m_container(container), m_nvdrv(std::move(nvdrv)) {}
+
+SharedBufferManager::~SharedBufferManager() = default;
+
+Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
+ u64* out_layer_handle, u64 display_id,
+ bool enable_blending) {
+ std::scoped_lock lk{m_guard};
+
+ // Ensure we haven't already created.
+ const u64 aruid = owner_process->GetProcessId();
+ R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
+
+ // Allocate memory for the shared buffer if needed.
+ if (!m_buffer_page_group) {
+ R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
+ SharedBufferSize));
+
+ // Record buffer id.
+ m_buffer_id = m_next_buffer_id++;
+
+ // Record display id.
+ m_display_id = display_id;
+ }
+
+ // Map into process.
+ Common::ProcessAddress map_address{};
+ R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
+ owner_process, m_system));
+
+ // Create new session.
+ auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
+ auto& session = it->second;
+
+ auto& container = m_nvdrv->GetContainer();
+ session.session_id = container.OpenSession(owner_process);
+ session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
+
+ // Create an nvmap handle for the buffer and assign the memory to it.
+ R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
+ session.nvmap_fd, map_address, SharedBufferSize));
+
+ // Create and open a layer for the display.
+ s32 producer_binder_id;
+ R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
+ std::addressof(session.layer_id), display_id));
+
+ // Configure blending.
+ R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
+
+ // Get the producer and set preallocated buffers.
+ std::shared_ptr<android::BufferQueueProducer> producer;
+ R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
+ MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle);
+ MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle);
+
+ // Assign outputs.
+ *out_buffer_id = m_buffer_id;
+ *out_layer_handle = session.layer_id;
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {
+ std::scoped_lock lk{m_guard};
+
+ if (m_buffer_id == 0) {
+ return;
+ }
+
+ const u64 aruid = owner_process->GetProcessId();
+ const auto it = m_sessions.find(aruid);
+ if (it == m_sessions.end()) {
+ return;
+ }
+
+ auto& session = it->second;
+
+ // Destroy the layer.
+ m_container.DestroyStrayLayer(session.layer_id);
+
+ // Close nvmap handle.
+ FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
+
+ // Close nvmap device.
+ m_nvdrv->Close(session.nvmap_fd);
+
+ // Close session.
+ auto& container = m_nvdrv->GetContainer();
+ container.CloseSession(session.session_id);
+
+ // Erase.
+ m_sessions.erase(it);
+}
+
+Result SharedBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
+ s32* out_nvmap_handle,
+ SharedMemoryPoolLayout* out_pool_layout,
+ u64 buffer_id,
+ u64 applet_resource_user_id) {
+ std::scoped_lock lk{m_guard};
+
+ R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
+ R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
+ R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound);
+
+ *out_pool_layout = SharedBufferPoolLayout;
+ *out_buffer_size = SharedBufferSize;
+ *out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle;
+
+ R_SUCCEED();
+}
+
+Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
+ std::array<s32, 4>& out_slot_indexes,
+ s64* out_target_slot, u64 layer_id) {
+ // Get the producer.
+ std::shared_ptr<android::BufferQueueProducer> producer;
+ R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
+
+ // Get the next buffer from the producer.
+ s32 slot;
+ R_UNLESS(producer->DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
+ SharedBufferWidth, SharedBufferHeight,
+ SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
+ VI::ResultOperationFailed);
+
+ // Assign remaining outputs.
+ *out_target_slot = slot;
+ out_slot_indexes = {0, 1, -1, -1};
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
+ Common::Rectangle<s32> crop_region,
+ u32 transform, s32 swap_interval, u64 layer_id,
+ s64 slot) {
+ // Get the producer.
+ std::shared_ptr<android::BufferQueueProducer> producer;
+ R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
+
+ // Request to queue the buffer.
+ std::shared_ptr<android::GraphicBuffer> buffer;
+ R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
+ android::Status::NoError,
+ VI::ResultOperationFailed);
+
+ ON_RESULT_FAILURE {
+ producer->CancelBuffer(static_cast<s32>(slot), fence);
+ };
+
+ // Queue the buffer to the producer.
+ android::QueueBufferInput input{};
+ android::QueueBufferOutput output{};
+ input.crop = crop_region;
+ input.fence = fence;
+ input.transform = static_cast<android::NativeWindowTransform>(transform);
+ input.swap_interval = swap_interval;
+ R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
+ android::Status::NoError,
+ VI::ResultOperationFailed);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
+ // Get the producer.
+ std::shared_ptr<android::BufferQueueProducer> producer;
+ R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
+
+ // Cancel.
+ producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence());
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
+ u64 layer_id) {
+ // Get the producer.
+ std::shared_ptr<android::BufferQueueProducer> producer;
+ R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
+
+ // Set the event.
+ *out_event = producer->GetNativeHandle({});
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
+ std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
+ Common::ScratchBuffer<u32> scratch;
+
+ // TODO: this could be optimized
+ s64 e = -1280 * 768 * 4;
+ for (auto& block : *m_buffer_page_group) {
+ u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
+ u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
+
+ for (; start < end; start++) {
+ *start = 0;
+
+ if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
+ *start = capture_buffer[e];
+ }
+ e++;
+ }
+
+ m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
+ m_system.GPU().InvalidateRegion(addr, end - start);
+ });
+ }
+
+ *out_was_written = true;
+ *out_layer_index = 1;
+ R_SUCCEED();
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/shared_buffer_manager.h b/src/core/hle/service/vi/shared_buffer_manager.h
new file mode 100644
index 000000000..7c9bb7199
--- /dev/null
+++ b/src/core/hle/service/vi/shared_buffer_manager.h
@@ -0,0 +1,92 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+
+#include "common/math_util.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/nvnflinger/nvnflinger.h"
+#include "core/hle/service/nvnflinger/ui/fence.h"
+
+namespace Kernel {
+class KPageGroup;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::android {
+class BufferQueueProducer;
+}
+
+namespace Service::Nvidia {
+class Module;
+}
+
+union Result;
+
+namespace Service::VI {
+
+class Container;
+
+struct SharedMemorySlot {
+ u64 buffer_offset;
+ u64 size;
+ s32 width;
+ s32 height;
+};
+static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size");
+
+struct SharedMemoryPoolLayout {
+ s32 num_slots;
+ std::array<SharedMemorySlot, 0x10> slots;
+};
+static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
+
+struct SharedBufferSession;
+
+class SharedBufferManager final {
+public:
+ explicit SharedBufferManager(Core::System& system, Container& container,
+ std::shared_ptr<Nvidia::Module> nvdrv);
+ ~SharedBufferManager();
+
+ Result CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
+ u64 display_id, bool enable_blending);
+ void DestroySession(Kernel::KProcess* owner_process);
+
+ Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
+ SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
+ u64 applet_resource_user_id);
+ Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots,
+ s64* out_target_slot, u64 layer_id);
+ Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
+ u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
+ Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
+ Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
+
+ Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
+
+private:
+ u64 m_next_buffer_id = 1;
+ u64 m_display_id = 0;
+ u64 m_buffer_id = 0;
+ SharedMemoryPoolLayout m_pool_layout = {};
+ std::map<u64, SharedBufferSession> m_sessions;
+ std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
+
+ std::mutex m_guard;
+ Core::System& m_system;
+ Container& m_container;
+ const std::shared_ptr<Nvidia::Module> m_nvdrv;
+};
+
+struct SharedBufferSession {
+ Nvidia::DeviceFD nvmap_fd = {};
+ Nvidia::NvCore::SessionId session_id = {};
+ u64 layer_id = {};
+ u32 buffer_nvmap_handle = 0;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_display_service.cpp b/src/core/hle/service/vi/system_display_service.cpp
new file mode 100644
index 000000000..c3c50b07b
--- /dev/null
+++ b/src/core/hle/service/vi/system_display_service.cpp
@@ -0,0 +1,169 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/system_display_service.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::VI {
+
+ISystemDisplayService::ISystemDisplayService(Core::System& system_,
+ std::shared_ptr<Container> container)
+ : ServiceFramework{system_, "ISystemDisplayService"}, m_container{std::move(container)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1200, nullptr, "GetZOrderCountMin"},
+ {1202, nullptr, "GetZOrderCountMax"},
+ {1203, nullptr, "GetDisplayLogicalResolution"},
+ {1204, nullptr, "SetDisplayMagnification"},
+ {2201, nullptr, "SetLayerPosition"},
+ {2203, nullptr, "SetLayerSize"},
+ {2204, nullptr, "GetLayerZ"},
+ {2205, C<&ISystemDisplayService::SetLayerZ>, "SetLayerZ"},
+ {2207, C<&ISystemDisplayService::SetLayerVisibility>, "SetLayerVisibility"},
+ {2209, nullptr, "SetLayerAlpha"},
+ {2210, nullptr, "SetLayerPositionAndSize"},
+ {2312, nullptr, "CreateStrayLayer"},
+ {2400, nullptr, "OpenIndirectLayer"},
+ {2401, nullptr, "CloseIndirectLayer"},
+ {2402, nullptr, "FlipIndirectLayer"},
+ {3000, C<&ISystemDisplayService::ListDisplayModes>, "ListDisplayModes"},
+ {3001, nullptr, "ListDisplayRgbRanges"},
+ {3002, nullptr, "ListDisplayContentTypes"},
+ {3200, C<&ISystemDisplayService::GetDisplayMode>, "GetDisplayMode"},
+ {3201, nullptr, "SetDisplayMode"},
+ {3202, nullptr, "GetDisplayUnderscan"},
+ {3203, nullptr, "SetDisplayUnderscan"},
+ {3204, nullptr, "GetDisplayContentType"},
+ {3205, nullptr, "SetDisplayContentType"},
+ {3206, nullptr, "GetDisplayRgbRange"},
+ {3207, nullptr, "SetDisplayRgbRange"},
+ {3208, nullptr, "GetDisplayCmuMode"},
+ {3209, nullptr, "SetDisplayCmuMode"},
+ {3210, nullptr, "GetDisplayContrastRatio"},
+ {3211, nullptr, "SetDisplayContrastRatio"},
+ {3214, nullptr, "GetDisplayGamma"},
+ {3215, nullptr, "SetDisplayGamma"},
+ {3216, nullptr, "GetDisplayCmuLuma"},
+ {3217, nullptr, "SetDisplayCmuLuma"},
+ {3218, nullptr, "SetDisplayCrcMode"},
+ {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
+ {8225, C<&ISystemDisplayService::GetSharedBufferMemoryHandleId>, "GetSharedBufferMemoryHandleId"},
+ {8250, C<&ISystemDisplayService::OpenSharedLayer>, "OpenSharedLayer"},
+ {8251, nullptr, "CloseSharedLayer"},
+ {8252, C<&ISystemDisplayService::ConnectSharedLayer>, "ConnectSharedLayer"},
+ {8253, nullptr, "DisconnectSharedLayer"},
+ {8254, C<&ISystemDisplayService::AcquireSharedFrameBuffer>, "AcquireSharedFrameBuffer"},
+ {8255, C<&ISystemDisplayService::PresentSharedFrameBuffer>, "PresentSharedFrameBuffer"},
+ {8256, C<&ISystemDisplayService::GetSharedFrameBufferAcquirableEvent>, "GetSharedFrameBufferAcquirableEvent"},
+ {8257, nullptr, "FillSharedFrameBufferColor"},
+ {8258, C<&ISystemDisplayService::CancelSharedFrameBuffer>, "CancelSharedFrameBuffer"},
+ {9000, nullptr, "GetDp2hdmiController"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+}
+
+ISystemDisplayService::~ISystemDisplayService() = default;
+
+Result ISystemDisplayService::SetLayerZ(u32 z_value, u64 layer_id) {
+ LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}, z_value={}", layer_id, z_value);
+ R_SUCCEED();
+}
+
+// This function currently does nothing but return a success error code in
+// the vi library itself, so do the same thing, but log out the passed in values.
+Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
+ R_SUCCEED();
+}
+
+Result ISystemDisplayService::ListDisplayModes(
+ Out<u64> out_count, u64 display_id,
+ OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes) {
+ LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id);
+
+ if (!out_display_modes.empty()) {
+ out_display_modes[0] = {
+ .width = 1920,
+ .height = 1080,
+ .refresh_rate = 60.f,
+ .unknown = {},
+ };
+ *out_count = 1;
+ } else {
+ *out_count = 0;
+ }
+
+ R_SUCCEED();
+}
+
+Result ISystemDisplayService::GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id) {
+ LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id);
+
+ if (Settings::IsDockedMode()) {
+ out_display_mode->width = static_cast<u32>(DisplayResolution::DockedWidth);
+ out_display_mode->height = static_cast<u32>(DisplayResolution::DockedHeight);
+ } else {
+ out_display_mode->width = static_cast<u32>(DisplayResolution::UndockedWidth);
+ out_display_mode->height = static_cast<u32>(DisplayResolution::UndockedHeight);
+ }
+
+ out_display_mode->refresh_rate = 60.f; // This wouldn't seem to be correct for 30 fps games.
+ out_display_mode->unknown = 0;
+
+ R_SUCCEED();
+}
+
+Result ISystemDisplayService::GetSharedBufferMemoryHandleId(
+ Out<s32> out_nvmap_handle, Out<u64> out_size,
+ OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, u64 buffer_id,
+ ClientAppletResourceUserId aruid) {
+ LOG_INFO(Service_VI, "called. buffer_id={}, aruid={:#x}", buffer_id, aruid.pid);
+
+ R_RETURN(m_container->GetSharedBufferManager()->GetSharedBufferMemoryHandleId(
+ out_size, out_nvmap_handle, out_pool_layout, buffer_id, aruid.pid));
+}
+
+Result ISystemDisplayService::OpenSharedLayer(u64 layer_id) {
+ LOG_INFO(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
+ R_SUCCEED();
+}
+
+Result ISystemDisplayService::ConnectSharedLayer(u64 layer_id) {
+ LOG_INFO(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
+ R_SUCCEED();
+}
+
+Result ISystemDisplayService::AcquireSharedFrameBuffer(Out<android::Fence> out_fence,
+ Out<std::array<s32, 4>> out_slots,
+ Out<s64> out_target_slot, u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(m_container->GetSharedBufferManager()->AcquireSharedFrameBuffer(
+ out_fence, *out_slots, out_target_slot, layer_id));
+}
+
+Result ISystemDisplayService::PresentSharedFrameBuffer(android::Fence fence,
+ Common::Rectangle<s32> crop_region,
+ u32 window_transform, s32 swap_interval,
+ u64 layer_id, s64 surface_id) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(m_container->GetSharedBufferManager()->PresentSharedFrameBuffer(
+ fence, crop_region, window_transform, swap_interval, layer_id, surface_id));
+}
+
+Result ISystemDisplayService::GetSharedFrameBufferAcquirableEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event, u64 layer_id) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(m_container->GetSharedBufferManager()->GetSharedFrameBufferAcquirableEvent(out_event,
+ layer_id));
+}
+
+Result ISystemDisplayService::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(m_container->GetSharedBufferManager()->CancelSharedFrameBuffer(layer_id, slot));
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_display_service.h b/src/core/hle/service/vi/system_display_service.h
new file mode 100644
index 000000000..7228d826e
--- /dev/null
+++ b/src/core/hle/service/vi/system_display_service.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/math_util.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/nvnflinger/ui/fence.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/vi/shared_buffer_manager.h"
+
+namespace Service::VI {
+struct DisplayMode;
+
+class Container;
+
+class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
+public:
+ explicit ISystemDisplayService(Core::System& system_, std::shared_ptr<Container> container);
+ ~ISystemDisplayService() override;
+
+private:
+ Result SetLayerZ(u32 z_value, u64 layer_id);
+ Result SetLayerVisibility(bool visible, u64 layer_id);
+ Result ListDisplayModes(Out<u64> out_count, u64 display_id,
+ OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes);
+ Result GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id);
+
+ Result GetSharedBufferMemoryHandleId(
+ Out<s32> out_nvmap_handle, Out<u64> out_size,
+ OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout,
+ u64 buffer_id, ClientAppletResourceUserId aruid);
+ Result OpenSharedLayer(u64 layer_id);
+ Result ConnectSharedLayer(u64 layer_id);
+ Result GetSharedFrameBufferAcquirableEvent(OutCopyHandle<Kernel::KReadableEvent> out_event,
+ u64 layer_id);
+ Result AcquireSharedFrameBuffer(Out<android::Fence> out_fence,
+ Out<std::array<s32, 4>> out_slots, Out<s64> out_target_slot,
+ u64 layer_id);
+ Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
+ u32 window_transform, s32 swap_interval, u64 layer_id,
+ s64 surface_id);
+ Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
+
+private:
+ const std::shared_ptr<Container> m_container;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_root_service.cpp b/src/core/hle/service/vi/system_root_service.cpp
new file mode 100644
index 000000000..3489727d8
--- /dev/null
+++ b/src/core/hle/service/vi/system_root_service.cpp
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/vi/application_display_service.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/service_creator.h"
+#include "core/hle/service/vi/system_root_service.h"
+#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/vi/vi_types.h"
+
+namespace Service::VI {
+
+ISystemRootService::ISystemRootService(Core::System& system_, std::shared_ptr<Container> container)
+ : ServiceFramework{system_, "vi:s"}, m_container{std::move(container)} {
+ static const FunctionInfo functions[] = {
+ {1, C<&ISystemRootService::GetDisplayService>, "GetDisplayService"},
+ {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
+ };
+ RegisterHandlers(functions);
+}
+
+ISystemRootService::~ISystemRootService() = default;
+
+Result ISystemRootService::GetDisplayService(
+ Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
+ LOG_DEBUG(Service_VI, "called");
+ R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
+ Permission::System, policy));
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_root_service.h b/src/core/hle/service/vi/system_root_service.h
new file mode 100644
index 000000000..9d5aa53d3
--- /dev/null
+++ b/src/core/hle/service/vi/system_root_service.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::VI {
+
+class Container;
+class IApplicationDisplayService;
+enum class Policy : u32;
+
+class ISystemRootService final : public ServiceFramework<ISystemRootService> {
+public:
+ explicit ISystemRootService(Core::System& system_, std::shared_ptr<Container> container);
+ ~ISystemRootService() override;
+
+private:
+ Result GetDisplayService(
+ Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
+ Policy policy);
+
+ const std::shared_ptr<Container> m_container;
+};
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d508ed28c..b388efaf6 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1,974 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <algorithm>
-#include <array>
-#include <cstring>
-#include <memory>
-#include <optional>
-#include <type_traits>
-#include <utility>
-
-#include "common/alignment.h"
-#include "common/assert.h"
-#include "common/common_funcs.h"
-#include "common/logging/log.h"
-#include "common/math_util.h"
-#include "common/settings.h"
-#include "common/string_util.h"
-#include "common/swap.h"
-#include "core/core_timing.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/nvdrv/devices/nvmap.h"
-#include "core/hle/service/nvdrv/nvdata.h"
-#include "core/hle/service/nvdrv/nvdrv.h"
-#include "core/hle/service/nvnflinger/binder.h"
-#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
-#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
-#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
-#include "core/hle/service/nvnflinger/nvnflinger.h"
-#include "core/hle/service/nvnflinger/parcel.h"
+#include "core/core.h"
#include "core/hle/service/server_manager.h"
-#include "core/hle/service/service.h"
+#include "core/hle/service/vi/application_root_service.h"
+#include "core/hle/service/vi/container.h"
+#include "core/hle/service/vi/manager_root_service.h"
+#include "core/hle/service/vi/system_root_service.h"
#include "core/hle/service/vi/vi.h"
-#include "core/hle/service/vi/vi_m.h"
-#include "core/hle/service/vi/vi_results.h"
-#include "core/hle/service/vi/vi_s.h"
-#include "core/hle/service/vi/vi_u.h"
namespace Service::VI {
-struct DisplayInfo {
- /// The name of this particular display.
- char display_name[0x40]{"Default"};
-
- /// Whether or not the display has a limited number of layers.
- u8 has_limited_layers{1};
- INSERT_PADDING_BYTES(7);
-
- /// Indicates the total amount of layers supported by the display.
- /// @note This is only valid if has_limited_layers is set.
- u64 max_layers{1};
-
- /// Maximum width in pixels.
- u64 width{1920};
-
- /// Maximum height in pixels.
- u64 height{1080};
-};
-static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
-
-class NativeWindow final {
-public:
- constexpr explicit NativeWindow(u32 id_) : id{id_} {}
- constexpr explicit NativeWindow(const NativeWindow& other) = default;
-
-private:
- const u32 magic = 2;
- const u32 process_id = 1;
- const u64 id;
- INSERT_PADDING_WORDS(2);
- std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
- INSERT_PADDING_WORDS(2);
-};
-static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
-
-class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
-public:
- explicit IHOSBinderDriver(Core::System& system_, Nvnflinger::HosBinderDriverServer& server_)
- : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) {
- static const FunctionInfo functions[] = {
- {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
- {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
- {2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"},
- {3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"},
- };
- RegisterHandlers(functions);
- }
-
-private:
- void TransactParcel(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 id = rp.Pop<u32>();
- const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
- const u32 flags = rp.Pop<u32>();
-
- LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
- transaction, flags);
-
- server.TryGetProducer(id)->Transact(ctx, transaction, flags);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void AdjustRefcount(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 id = rp.Pop<u32>();
- const s32 addval = rp.PopRaw<s32>();
- const u32 type = rp.Pop<u32>();
-
- LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval,
- type);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetNativeHandle(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 id = rp.Pop<u32>();
- const u32 unknown = rp.Pop<u32>();
-
- LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
- }
-
-private:
- Nvnflinger::HosBinderDriverServer& server;
-};
-
-class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
-public:
- explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
- : ServiceFramework{system_, "ISystemDisplayService"}, nvnflinger{nvnflinger_} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {1200, nullptr, "GetZOrderCountMin"},
- {1202, nullptr, "GetZOrderCountMax"},
- {1203, nullptr, "GetDisplayLogicalResolution"},
- {1204, nullptr, "SetDisplayMagnification"},
- {2201, nullptr, "SetLayerPosition"},
- {2203, nullptr, "SetLayerSize"},
- {2204, nullptr, "GetLayerZ"},
- {2205, &ISystemDisplayService::SetLayerZ, "SetLayerZ"},
- {2207, &ISystemDisplayService::SetLayerVisibility, "SetLayerVisibility"},
- {2209, nullptr, "SetLayerAlpha"},
- {2210, nullptr, "SetLayerPositionAndSize"},
- {2312, nullptr, "CreateStrayLayer"},
- {2400, nullptr, "OpenIndirectLayer"},
- {2401, nullptr, "CloseIndirectLayer"},
- {2402, nullptr, "FlipIndirectLayer"},
- {3000, nullptr, "ListDisplayModes"},
- {3001, nullptr, "ListDisplayRgbRanges"},
- {3002, nullptr, "ListDisplayContentTypes"},
- {3200, &ISystemDisplayService::GetDisplayMode, "GetDisplayMode"},
- {3201, nullptr, "SetDisplayMode"},
- {3202, nullptr, "GetDisplayUnderscan"},
- {3203, nullptr, "SetDisplayUnderscan"},
- {3204, nullptr, "GetDisplayContentType"},
- {3205, nullptr, "SetDisplayContentType"},
- {3206, nullptr, "GetDisplayRgbRange"},
- {3207, nullptr, "SetDisplayRgbRange"},
- {3208, nullptr, "GetDisplayCmuMode"},
- {3209, nullptr, "SetDisplayCmuMode"},
- {3210, nullptr, "GetDisplayContrastRatio"},
- {3211, nullptr, "SetDisplayContrastRatio"},
- {3214, nullptr, "GetDisplayGamma"},
- {3215, nullptr, "SetDisplayGamma"},
- {3216, nullptr, "GetDisplayCmuLuma"},
- {3217, nullptr, "SetDisplayCmuLuma"},
- {3218, nullptr, "SetDisplayCrcMode"},
- {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
- {8225, &ISystemDisplayService::GetSharedBufferMemoryHandleId, "GetSharedBufferMemoryHandleId"},
- {8250, &ISystemDisplayService::OpenSharedLayer, "OpenSharedLayer"},
- {8251, nullptr, "CloseSharedLayer"},
- {8252, &ISystemDisplayService::ConnectSharedLayer, "ConnectSharedLayer"},
- {8253, nullptr, "DisconnectSharedLayer"},
- {8254, &ISystemDisplayService::AcquireSharedFrameBuffer, "AcquireSharedFrameBuffer"},
- {8255, &ISystemDisplayService::PresentSharedFrameBuffer, "PresentSharedFrameBuffer"},
- {8256, &ISystemDisplayService::GetSharedFrameBufferAcquirableEvent, "GetSharedFrameBufferAcquirableEvent"},
- {8257, nullptr, "FillSharedFrameBufferColor"},
- {8258, nullptr, "CancelSharedFrameBuffer"},
- {9000, nullptr, "GetDp2hdmiController"},
- };
- // clang-format on
- RegisterHandlers(functions);
- }
-
-private:
- void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 buffer_id = rp.PopRaw<u64>();
- const u64 aruid = ctx.GetPID();
-
- LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid);
-
- struct OutputParameters {
- s32 nvmap_handle;
- u64 size;
- };
-
- OutputParameters out{};
- Nvnflinger::SharedMemoryPoolLayout layout{};
- const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId(
- &out.size, &out.nvmap_handle, &layout, buffer_id, aruid);
-
- ctx.WriteBuffer(&layout, sizeof(layout));
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(result);
- rb.PushRaw(out);
- }
-
- void OpenSharedLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.PopRaw<u64>();
-
- LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void ConnectSharedLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.PopRaw<u64>();
-
- LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetSharedFrameBufferAcquirableEvent(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.PopRaw<u64>();
-
- Kernel::KReadableEvent* event{};
- const auto result = nvnflinger.GetSystemBufferManager().GetSharedFrameBufferAcquirableEvent(
- &event, layer_id);
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(result);
- rb.PushCopyObjects(event);
- }
-
- void AcquireSharedFrameBuffer(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.PopRaw<u64>();
-
- struct OutputParameters {
- android::Fence fence;
- std::array<s32, 4> slots;
- s64 target_slot;
- };
- static_assert(sizeof(OutputParameters) == 0x40, "OutputParameters has wrong size");
-
- OutputParameters out{};
- const auto result = nvnflinger.GetSystemBufferManager().AcquireSharedFrameBuffer(
- &out.fence, out.slots, &out.target_slot, layer_id);
-
- IPC::ResponseBuilder rb{ctx, 18};
- rb.Push(result);
- rb.PushRaw(out);
- }
-
- void PresentSharedFrameBuffer(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- struct InputParameters {
- android::Fence fence;
- Common::Rectangle<s32> crop_region;
- u32 window_transform;
- s32 swap_interval;
- u64 layer_id;
- s64 surface_id;
- };
- static_assert(sizeof(InputParameters) == 0x50, "InputParameters has wrong size");
-
- IPC::RequestParser rp{ctx};
- auto input = rp.PopRaw<InputParameters>();
-
- const auto result = nvnflinger.GetSystemBufferManager().PresentSharedFrameBuffer(
- input.fence, input.crop_region, input.window_transform, input.swap_interval,
- input.layer_id, input.surface_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- }
-
- void SetLayerZ(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.Pop<u64>();
- const u64 z_value = rp.Pop<u64>();
-
- LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id,
- z_value);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- // This function currently does nothing but return a success error code in
- // the vi library itself, so do the same thing, but log out the passed in values.
- void SetLayerVisibility(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.Pop<u64>();
- const bool visibility = rp.Pop<bool>();
-
- LOG_DEBUG(Service_VI, "called, layer_id=0x{:08X}, visibility={}", layer_id, visibility);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetDisplayMode(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
-
- if (Settings::IsDockedMode()) {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
- } else {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
- }
-
- rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
- rb.Push<u32>(0);
- }
-
-private:
- Nvnflinger::Nvnflinger& nvnflinger;
-};
-
-class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
-public:
- explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
- : ServiceFramework{system_, "IManagerDisplayService"}, nvnflinger{nvnflinger_} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {200, nullptr, "AllocateProcessHeapBlock"},
- {201, nullptr, "FreeProcessHeapBlock"},
- {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"},
- {1102, nullptr, "GetDisplayResolution"},
- {2010, &IManagerDisplayService::CreateManagedLayer, "CreateManagedLayer"},
- {2011, nullptr, "DestroyManagedLayer"},
- {2012, nullptr, "CreateStrayLayer"},
- {2050, nullptr, "CreateIndirectLayer"},
- {2051, nullptr, "DestroyIndirectLayer"},
- {2052, nullptr, "CreateIndirectProducerEndPoint"},
- {2053, nullptr, "DestroyIndirectProducerEndPoint"},
- {2054, nullptr, "CreateIndirectConsumerEndPoint"},
- {2055, nullptr, "DestroyIndirectConsumerEndPoint"},
- {2060, nullptr, "CreateWatermarkCompositor"},
- {2062, nullptr, "SetWatermarkText"},
- {2063, nullptr, "SetWatermarkLayerStacks"},
- {2300, nullptr, "AcquireLayerTexturePresentingEvent"},
- {2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
- {2302, nullptr, "GetDisplayHotplugEvent"},
- {2303, nullptr, "GetDisplayModeChangedEvent"},
- {2402, nullptr, "GetDisplayHotplugState"},
- {2501, nullptr, "GetCompositorErrorInfo"},
- {2601, nullptr, "GetDisplayErrorEvent"},
- {2701, nullptr, "GetDisplayFatalErrorEvent"},
- {4201, nullptr, "SetDisplayAlpha"},
- {4203, nullptr, "SetDisplayLayerStack"},
- {4205, nullptr, "SetDisplayPowerState"},
- {4206, nullptr, "SetDefaultDisplay"},
- {4207, nullptr, "ResetDisplayPanel"},
- {4208, nullptr, "SetDisplayFatalErrorEnabled"},
- {4209, nullptr, "IsDisplayPanelOn"},
- {4300, nullptr, "GetInternalPanelId"},
- {6000, &IManagerDisplayService::AddToLayerStack, "AddToLayerStack"},
- {6001, nullptr, "RemoveFromLayerStack"},
- {6002, &IManagerDisplayService::SetLayerVisibility, "SetLayerVisibility"},
- {6003, nullptr, "SetLayerConfig"},
- {6004, nullptr, "AttachLayerPresentationTracer"},
- {6005, nullptr, "DetachLayerPresentationTracer"},
- {6006, nullptr, "StartLayerPresentationRecording"},
- {6007, nullptr, "StopLayerPresentationRecording"},
- {6008, nullptr, "StartLayerPresentationFenceWait"},
- {6009, nullptr, "StopLayerPresentationFenceWait"},
- {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"},
- {6011, nullptr, "EnableLayerAutoClearTransitionBuffer"},
- {6012, nullptr, "DisableLayerAutoClearTransitionBuffer"},
- {6013, nullptr, "SetLayerOpacity"},
- {6014, nullptr, "AttachLayerWatermarkCompositor"},
- {6015, nullptr, "DetachLayerWatermarkCompositor"},
- {7000, nullptr, "SetContentVisibility"},
- {8000, nullptr, "SetConductorLayer"},
- {8001, nullptr, "SetTimestampTracking"},
- {8100, nullptr, "SetIndirectProducerFlipOffset"},
- {8200, nullptr, "CreateSharedBufferStaticStorage"},
- {8201, nullptr, "CreateSharedBufferTransferMemory"},
- {8202, nullptr, "DestroySharedBuffer"},
- {8203, nullptr, "BindSharedLowLevelLayerToManagedLayer"},
- {8204, nullptr, "BindSharedLowLevelLayerToIndirectLayer"},
- {8207, nullptr, "UnbindSharedLowLevelLayer"},
- {8208, nullptr, "ConnectSharedLowLevelLayerToSharedBuffer"},
- {8209, nullptr, "DisconnectSharedLowLevelLayerFromSharedBuffer"},
- {8210, nullptr, "CreateSharedLayer"},
- {8211, nullptr, "DestroySharedLayer"},
- {8216, nullptr, "AttachSharedLayerToLowLevelLayer"},
- {8217, nullptr, "ForceDetachSharedLayerFromLowLevelLayer"},
- {8218, nullptr, "StartDetachSharedLayerFromLowLevelLayer"},
- {8219, nullptr, "FinishDetachSharedLayerFromLowLevelLayer"},
- {8220, nullptr, "GetSharedLayerDetachReadyEvent"},
- {8221, nullptr, "GetSharedLowLevelLayerSynchronizedEvent"},
- {8222, nullptr, "CheckSharedLowLevelLayerSynchronized"},
- {8223, nullptr, "RegisterSharedBufferImporterAruid"},
- {8224, nullptr, "UnregisterSharedBufferImporterAruid"},
- {8227, nullptr, "CreateSharedBufferProcessHeap"},
- {8228, nullptr, "GetSharedLayerLayerStacks"},
- {8229, nullptr, "SetSharedLayerLayerStacks"},
- {8291, nullptr, "PresentDetachedSharedFrameBufferToLowLevelLayer"},
- {8292, nullptr, "FillDetachedSharedFrameBufferColor"},
- {8293, nullptr, "GetDetachedSharedFrameBufferImage"},
- {8294, nullptr, "SetDetachedSharedFrameBufferImage"},
- {8295, nullptr, "CopyDetachedSharedFrameBufferImage"},
- {8296, nullptr, "SetDetachedSharedFrameBufferSubImage"},
- {8297, nullptr, "GetSharedFrameBufferContentParameter"},
- {8298, nullptr, "ExpandStartupLogoOnSharedFrameBuffer"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void CloseDisplay(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 display = rp.Pop<u64>();
-
- const Result rc = nvnflinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- }
-
- void CreateManagedLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 unknown = rp.Pop<u32>();
- rp.Skip(1, false);
- const u64 display = rp.Pop<u64>();
- const u64 aruid = rp.Pop<u64>();
-
- LOG_WARNING(Service_VI,
- "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
- unknown, display, aruid);
-
- const auto layer_id = nvnflinger.CreateLayer(display);
- if (!layer_id) {
- LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNotFound);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(*layer_id);
- }
-
- void AddToLayerStack(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 stack = rp.Pop<u32>();
- const u64 layer_id = rp.Pop<u64>();
-
- LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack,
- layer_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void SetLayerVisibility(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.Pop<u64>();
- const bool visibility = rp.Pop<bool>();
-
- LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
- visibility);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- Nvnflinger::Nvnflinger& nvnflinger;
-};
-
-class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
-public:
- IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_)
- : ServiceFramework{system_, "IApplicationDisplayService"}, nvnflinger{nvnflinger_},
- hos_binder_driver_server{hos_binder_driver_server_} {
-
- static const FunctionInfo functions[] = {
- {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
- {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
- {102, &IApplicationDisplayService::GetManagerDisplayService,
- "GetManagerDisplayService"},
- {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
- "GetIndirectDisplayTransactionService"},
- {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
- {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
- {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
- {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
- {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
- {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
- {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
- {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
- {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
- {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
- {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
- {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
- {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
- "GetIndirectLayerImageMap"},
- {2451, nullptr, "GetIndirectLayerImageCropMap"},
- {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
- "GetIndirectLayerImageRequiredMemoryInfo"},
- {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
- {5203, nullptr, "GetDisplayVsyncEventForDebug"},
- };
- RegisterHandlers(functions);
- }
-
- ~IApplicationDisplayService() {
- for (const auto layer_id : stray_layer_ids) {
- nvnflinger.DestroyLayer(layer_id);
- }
- }
-
-private:
- enum class ConvertedScaleMode : u64 {
- Freeze = 0,
- ScaleToWindow = 1,
- ScaleAndCrop = 2,
- None = 3,
- PreserveAspectRatio = 4,
- };
-
- enum class NintendoScaleMode : u32 {
- None = 0,
- Freeze = 1,
- ScaleToWindow = 2,
- ScaleAndCrop = 3,
- PreserveAspectRatio = 4,
- };
-
- void GetRelayService(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
- }
-
- void GetSystemDisplayService(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISystemDisplayService>(system, nvnflinger);
- }
-
- void GetManagerDisplayService(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IManagerDisplayService>(system, nvnflinger);
- }
-
- void GetIndirectDisplayTransactionService(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
+void LoopProcess(Core::System& system, std::stop_token token) {
+ const auto container = std::make_shared<Container>(system);
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
- }
-
- void OpenDisplay(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
- const auto name_buf = rp.PopRaw<std::array<char, 0x40>>();
-
- OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()});
- }
-
- void OpenDefaultDisplay(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- OpenDisplayImpl(ctx, "Default");
- }
-
- void OpenDisplayImpl(HLERequestContext& ctx, std::string_view name) {
- const auto trim_pos = name.find('\0');
-
- if (trim_pos != std::string_view::npos) {
- name.remove_suffix(name.size() - trim_pos);
- }
-
- ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
-
- const auto display_id = nvnflinger.OpenDisplay(name);
- if (!display_id) {
- LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNotFound);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(*display_id);
- }
-
- void CloseDisplay(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 display_id = rp.Pop<u64>();
-
- const Result rc = nvnflinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(rc);
- }
-
- // This literally does nothing internally in the actual service itself,
- // and just returns a successful result code regardless of the input.
- void SetDisplayEnabled(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called.");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetDisplayResolution(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 display_id = rp.Pop<u64>();
-
- LOG_DEBUG(Service_VI, "called. display_id=0x{:016X}", display_id);
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
-
- // This only returns the fixed values of 1280x720 and makes no distinguishing
- // between docked and undocked dimensions. We take the liberty of applying
- // the resolution scaling factor here.
- rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth));
- rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight));
- }
-
- void SetLayerScalingMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto scaling_mode = rp.PopEnum<NintendoScaleMode>();
- const u64 unknown = rp.Pop<u64>();
-
- LOG_DEBUG(Service_VI, "called. scaling_mode=0x{:08X}, unknown=0x{:016X}", scaling_mode,
- unknown);
-
- IPC::ResponseBuilder rb{ctx, 2};
-
- if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) {
- LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
- rb.Push(ResultOperationFailed);
- return;
- }
-
- if (scaling_mode != NintendoScaleMode::ScaleToWindow &&
- scaling_mode != NintendoScaleMode::PreserveAspectRatio) {
- LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
- rb.Push(ResultNotSupported);
- return;
- }
-
- rb.Push(ResultSuccess);
- }
-
- void ListDisplays(HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
- const DisplayInfo display_info;
- ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(1);
- }
-
- void OpenLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
- const std::string display_name(Common::StringFromBuffer(name_buf));
-
- const u64 layer_id = rp.Pop<u64>();
- const u64 aruid = rp.Pop<u64>();
-
- LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
-
- const auto display_id = nvnflinger.OpenDisplay(display_name);
- if (!display_id) {
- LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNotFound);
- return;
- }
-
- const auto buffer_queue_id = nvnflinger.FindBufferQueueId(*display_id, layer_id);
- if (!buffer_queue_id) {
- LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNotFound);
- return;
- }
-
- if (!nvnflinger.OpenLayer(layer_id)) {
- LOG_WARNING(Service_VI, "Tried to open layer which was already open");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultOperationFailed);
- return;
- }
-
- android::OutputParcel parcel;
- parcel.WriteInterface(NativeWindow{*buffer_queue_id});
-
- const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(buffer_size);
- }
-
- void CloseLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto layer_id{rp.Pop<u64>()};
-
- LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
-
- if (!nvnflinger.CloseLayer(layer_id)) {
- LOG_WARNING(Service_VI, "Tried to close layer which was not open");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultOperationFailed);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void CreateStrayLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 flags = rp.Pop<u32>();
- rp.Pop<u32>(); // padding
- const u64 display_id = rp.Pop<u64>();
-
- LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id);
-
- // TODO(Subv): What's the difference between a Stray and a Managed layer?
-
- const auto layer_id = nvnflinger.CreateLayer(display_id);
- if (!layer_id) {
- LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNotFound);
- return;
- }
-
- stray_layer_ids.push_back(*layer_id);
- const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id);
- if (!buffer_queue_id) {
- LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultNotFound);
- return;
- }
-
- android::OutputParcel parcel;
- parcel.WriteInterface(NativeWindow{*buffer_queue_id});
-
- const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.Push(*layer_id);
- rb.Push<u64>(buffer_size);
- }
-
- void DestroyStrayLayer(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 layer_id = rp.Pop<u64>();
-
- LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
- nvnflinger.DestroyLayer(layer_id);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetDisplayVsyncEvent(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 display_id = rp.Pop<u64>();
-
- LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
-
- Kernel::KReadableEvent* vsync_event{};
- const auto result = nvnflinger.FindVsyncEvent(&vsync_event, display_id);
- if (result != ResultSuccess) {
- if (result == ResultNotFound) {
- LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
- }
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- return;
- }
- if (vsync_event_fetched) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(VI::ResultPermissionDenied);
- return;
- }
- vsync_event_fetched = true;
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(vsync_event);
- }
-
- void ConvertScalingMode(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto mode = rp.PopEnum<NintendoScaleMode>();
- LOG_DEBUG(Service_VI, "called mode={}", mode);
-
- ConvertedScaleMode converted_mode{};
- const auto result = ConvertScalingModeImpl(&converted_mode, mode);
-
- if (result == ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushEnum(converted_mode);
- } else {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- }
- }
-
- void GetIndirectLayerImageMap(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto width = rp.Pop<s64>();
- const auto height = rp.Pop<s64>();
- const auto indirect_layer_consumer_handle = rp.Pop<u64>();
- const auto applet_resource_user_id = rp.Pop<u64>();
-
- LOG_WARNING(Service_VI,
- "(STUBBED) called, width={}, height={}, indirect_layer_consumer_handle={}, "
- "applet_resource_user_id={}",
- width, height, indirect_layer_consumer_handle, applet_resource_user_id);
-
- std::vector<u8> out_buffer(0x46);
- ctx.WriteBuffer(out_buffer);
-
- // TODO: Figure out what these are
-
- constexpr s64 unknown_result_1 = 0;
- constexpr s64 unknown_result_2 = 0;
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(unknown_result_1);
- rb.Push(unknown_result_2);
- rb.Push(ResultSuccess);
- }
-
- void GetIndirectLayerImageRequiredMemoryInfo(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto width = rp.Pop<u64>();
- const auto height = rp.Pop<u64>();
- LOG_DEBUG(Service_VI, "called width={}, height={}", width, height);
-
- constexpr u64 base_size = 0x20000;
- constexpr u64 alignment = 0x1000;
- const auto texture_size = width * height * 4;
- const auto out_size = (texture_size + base_size - 1) / base_size * base_size;
-
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(ResultSuccess);
- rb.Push(out_size);
- rb.Push(alignment);
- }
-
- static Result ConvertScalingModeImpl(ConvertedScaleMode* out_scaling_mode,
- NintendoScaleMode mode) {
- switch (mode) {
- case NintendoScaleMode::None:
- *out_scaling_mode = ConvertedScaleMode::None;
- return ResultSuccess;
- case NintendoScaleMode::Freeze:
- *out_scaling_mode = ConvertedScaleMode::Freeze;
- return ResultSuccess;
- case NintendoScaleMode::ScaleToWindow:
- *out_scaling_mode = ConvertedScaleMode::ScaleToWindow;
- return ResultSuccess;
- case NintendoScaleMode::ScaleAndCrop:
- *out_scaling_mode = ConvertedScaleMode::ScaleAndCrop;
- return ResultSuccess;
- case NintendoScaleMode::PreserveAspectRatio:
- *out_scaling_mode = ConvertedScaleMode::PreserveAspectRatio;
- return ResultSuccess;
- default:
- LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
- return ResultOperationFailed;
- }
- }
-
- Nvnflinger::Nvnflinger& nvnflinger;
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
- std::vector<u64> stray_layer_ids;
- bool vsync_event_fetched{false};
-};
-
-static bool IsValidServiceAccess(Permission permission, Policy policy) {
- if (permission == Permission::User) {
- return policy == Policy::User;
- }
-
- if (permission == Permission::System || permission == Permission::Manager) {
- return policy == Policy::User || policy == Policy::Compositor;
- }
-
- return false;
-}
-
-void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
- Nvnflinger::Nvnflinger& nvnflinger,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server,
- Permission permission) {
- IPC::RequestParser rp{ctx};
- const auto policy = rp.PopEnum<Policy>();
-
- if (!IsValidServiceAccess(permission, policy)) {
- LOG_ERROR(Service_VI, "Permission denied for policy {}", policy);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultPermissionDenied);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationDisplayService>(system, nvnflinger, hos_binder_driver_server);
-}
-
-void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) {
auto server_manager = std::make_unique<ServerManager>(system);
+ server_manager->RegisterNamedService("vi:m",
+ std::make_shared<IManagerRootService>(system, container));
+ server_manager->RegisterNamedService("vi:s",
+ std::make_shared<ISystemRootService>(system, container));
server_manager->RegisterNamedService(
- "vi:m", std::make_shared<VI_M>(system, nvnflinger, hos_binder_driver_server));
- server_manager->RegisterNamedService(
- "vi:s", std::make_shared<VI_S>(system, nvnflinger, hos_binder_driver_server));
- server_manager->RegisterNamedService(
- "vi:u", std::make_shared<VI_U>(system, nvnflinger, hos_binder_driver_server));
+ "vi:u", std::make_shared<IApplicationRootService>(system, container));
+
+ std::stop_callback cb(token, [=] { container->OnTerminate(); });
+
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index ee4bcbcfa..7c1f350d8 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -3,52 +3,14 @@
#pragma once
-#include "common/common_types.h"
+#include "common/polyfill_thread.h"
namespace Core {
class System;
}
-namespace Service {
-class HLERequestContext;
-}
-
-namespace Service::Nvnflinger {
-class HosBinderDriverServer;
-class Nvnflinger;
-} // namespace Service::Nvnflinger
-
namespace Service::VI {
-enum class DisplayResolution : u32 {
- DockedWidth = 1920,
- DockedHeight = 1080,
- UndockedWidth = 1280,
- UndockedHeight = 720,
-};
-
-/// Permission level for a particular VI service instance
-enum class Permission {
- User,
- System,
- Manager,
-};
-
-/// A policy type that may be requested via GetDisplayService and
-/// GetDisplayServiceWithProxyNameExchange
-enum class Policy {
- User,
- Compositor,
-};
-
-namespace detail {
-void GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
- Nvnflinger::Nvnflinger& nv_flinger,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server,
- Permission permission);
-} // namespace detail
-
-void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
+void LoopProcess(Core::System& system, std::stop_token token);
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
deleted file mode 100644
index 0f06dc2f3..000000000
--- a/src/core/hle/service/vi/vi_m.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/logging/log.h"
-#include "core/hle/service/vi/vi.h"
-#include "core/hle/service/vi/vi_m.h"
-
-namespace Service::VI {
-
-VI_M::VI_M(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_)
- : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
- hos_binder_driver_server_} {
- static const FunctionInfo functions[] = {
- {2, &VI_M::GetDisplayService, "GetDisplayService"},
- {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
- {100, nullptr, "PrepareFatal"},
- {101, nullptr, "ShowFatal"},
- {102, nullptr, "DrawFatalRectangle"},
- {103, nullptr, "DrawFatalText32"},
- };
- RegisterHandlers(functions);
-}
-
-VI_M::~VI_M() = default;
-
-void VI_M::GetDisplayService(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
- Permission::Manager);
-}
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
deleted file mode 100644
index 9ca6f3905..000000000
--- a/src/core/hle/service/vi/vi_m.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Nvnflinger {
-class HosBinderDriverServer;
-class Nvnflinger;
-} // namespace Service::Nvnflinger
-
-namespace Service::VI {
-
-class VI_M final : public ServiceFramework<VI_M> {
-public:
- explicit VI_M(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_);
- ~VI_M() override;
-
-private:
- void GetDisplayService(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nv_flinger;
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
-};
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
deleted file mode 100644
index 77f7a88ff..000000000
--- a/src/core/hle/service/vi/vi_s.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/logging/log.h"
-#include "core/hle/service/vi/vi.h"
-#include "core/hle/service/vi/vi_s.h"
-
-namespace Service::VI {
-
-VI_S::VI_S(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_)
- : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
- hos_binder_driver_server_} {
- static const FunctionInfo functions[] = {
- {1, &VI_S::GetDisplayService, "GetDisplayService"},
- {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
- };
- RegisterHandlers(functions);
-}
-
-VI_S::~VI_S() = default;
-
-void VI_S::GetDisplayService(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
- Permission::System);
-}
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
deleted file mode 100644
index 157839c91..000000000
--- a/src/core/hle/service/vi/vi_s.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Nvnflinger {
-class HosBinderDriverServer;
-class Nvnflinger;
-} // namespace Service::Nvnflinger
-
-namespace Service::VI {
-
-class VI_S final : public ServiceFramework<VI_S> {
-public:
- explicit VI_S(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_);
- ~VI_S() override;
-
-private:
- void GetDisplayService(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nv_flinger;
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
-};
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_types.h b/src/core/hle/service/vi/vi_types.h
new file mode 100644
index 000000000..95ff66358
--- /dev/null
+++ b/src/core/hle/service/vi/vi_types.h
@@ -0,0 +1,92 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+
+namespace Service::VI {
+
+enum class DisplayResolution : u32 {
+ DockedWidth = 1920,
+ DockedHeight = 1080,
+ UndockedWidth = 1280,
+ UndockedHeight = 720,
+};
+
+/// Permission level for a particular VI service instance
+enum class Permission {
+ User,
+ System,
+ Manager,
+};
+
+/// A policy type that may be requested via GetDisplayService and
+/// GetDisplayServiceWithProxyNameExchange
+enum class Policy : u32 {
+ User,
+ Compositor,
+};
+
+enum class ConvertedScaleMode : u64 {
+ Freeze = 0,
+ ScaleToWindow = 1,
+ ScaleAndCrop = 2,
+ None = 3,
+ PreserveAspectRatio = 4,
+};
+
+enum class NintendoScaleMode : u32 {
+ None = 0,
+ Freeze = 1,
+ ScaleToWindow = 2,
+ ScaleAndCrop = 3,
+ PreserveAspectRatio = 4,
+};
+
+using DisplayName = std::array<char, 0x40>;
+
+struct DisplayInfo {
+ /// The name of this particular display.
+ DisplayName display_name{"Default"};
+
+ /// Whether or not the display has a limited number of layers.
+ u8 has_limited_layers{1};
+ INSERT_PADDING_BYTES(7);
+
+ /// Indicates the total amount of layers supported by the display.
+ /// @note This is only valid if has_limited_layers is set.
+ u64 max_layers{1};
+
+ /// Maximum width in pixels.
+ u64 width{1920};
+
+ /// Maximum height in pixels.
+ u64 height{1080};
+};
+static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
+
+struct DisplayMode {
+ u32 width;
+ u32 height;
+ f32 refresh_rate;
+ u32 unknown;
+};
+static_assert(sizeof(DisplayMode) == 0x10, "DisplayMode has wrong size");
+
+class NativeWindow final {
+public:
+ constexpr explicit NativeWindow(s32 id_) : id{static_cast<u64>(id_)} {}
+ constexpr explicit NativeWindow(const NativeWindow& other) = default;
+
+private:
+ const u32 magic = 2;
+ const u32 process_id = 1;
+ const u64 id;
+ INSERT_PADDING_WORDS(2);
+ std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
+ INSERT_PADDING_WORDS(2);
+};
+static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
deleted file mode 100644
index 59e13c86b..000000000
--- a/src/core/hle/service/vi/vi_u.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/logging/log.h"
-#include "core/hle/service/vi/vi.h"
-#include "core/hle/service/vi/vi_u.h"
-
-namespace Service::VI {
-
-VI_U::VI_U(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_)
- : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
- hos_binder_driver_server_} {
- static const FunctionInfo functions[] = {
- {0, &VI_U::GetDisplayService, "GetDisplayService"},
- {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
- };
- RegisterHandlers(functions);
-}
-
-VI_U::~VI_U() = default;
-
-void VI_U::GetDisplayService(HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
- Permission::User);
-}
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
deleted file mode 100644
index 5d9ca54c6..000000000
--- a/src/core/hle/service/vi/vi_u.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Nvnflinger {
-class HosBinderDriverServer;
-class Nvnflinger;
-} // namespace Service::Nvnflinger
-
-namespace Service::VI {
-
-class VI_U final : public ServiceFramework<VI_U> {
-public:
- explicit VI_U(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_);
- ~VI_U() override;
-
-private:
- void GetDisplayService(HLERequestContext& ctx);
-
- Nvnflinger::Nvnflinger& nv_flinger;
- Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
-};
-
-} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vsync_manager.cpp b/src/core/hle/service/vi/vsync_manager.cpp
new file mode 100644
index 000000000..bdc4dfa96
--- /dev/null
+++ b/src/core/hle/service/vi/vsync_manager.cpp
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/os/event.h"
+#include "core/hle/service/vi/vsync_manager.h"
+
+namespace Service::VI {
+
+VsyncManager::VsyncManager() = default;
+VsyncManager::~VsyncManager() = default;
+
+void VsyncManager::SignalVsync() {
+ for (auto* event : m_vsync_events) {
+ event->Signal();
+ }
+}
+
+void VsyncManager::LinkVsyncEvent(Event* event) {
+ m_vsync_events.insert(event);
+}
+
+void VsyncManager::UnlinkVsyncEvent(Event* event) {
+ m_vsync_events.erase(event);
+}
+
+} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vsync_manager.h b/src/core/hle/service/vi/vsync_manager.h
new file mode 100644
index 000000000..5d45bb5ee
--- /dev/null
+++ b/src/core/hle/service/vi/vsync_manager.h
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <set>
+
+namespace Service {
+class Event;
+}
+
+namespace Service::VI {
+
+class DisplayList;
+
+class VsyncManager {
+public:
+ explicit VsyncManager();
+ ~VsyncManager();
+
+ void SignalVsync();
+ void LinkVsyncEvent(Event* event);
+ void UnlinkVsyncEvent(Event* event);
+
+private:
+ std::set<Event*> m_vsync_events;
+};
+
+} // namespace Service::VI
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 2a32b1276..de27ec49e 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -118,7 +118,9 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)>
mbedtls_sha256_starts_ret(&ctx, 0);
// Ensure we maintain a clean state on exit.
- SCOPE_EXIT({ mbedtls_sha256_free(&ctx); });
+ SCOPE_EXIT {
+ mbedtls_sha256_free(&ctx);
+ };
// Declare counters.
const size_t total_size = file->GetSize();
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index e10a4601e..8775369a4 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -831,11 +831,11 @@ struct Memory::Impl {
if (core == sys_core) [[unlikely]] {
sys_core_guard.lock();
}
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (core == sys_core) [[unlikely]] {
sys_core_guard.unlock();
}
- });
+ };
gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) {
auto& current_area = rasterizer_write_areas[core];
PAddr subaddress = address >> YUZU_PAGEBITS;
@@ -866,11 +866,11 @@ struct Memory::Impl {
if (core == sys_core) [[unlikely]] {
sys_core_guard.lock();
}
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (core == sys_core) [[unlikely]] {
sys_core_guard.unlock();
}
- });
+ };
auto& gpu = system.GPU();
gpu_device_memory->ApplyOpOnPointer(
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index b84b57d92..d8921e565 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -117,9 +117,9 @@ bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
(in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
(in < metadata.alias_extents.base ||
- in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
+ in >= metadata.alias_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
- in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
+ in >= metadata.aslr_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, "
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index f7097d01d..caceeec4f 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -224,12 +224,12 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// If we've ever seen a decode failure, return false.
bool valid = decode_success;
CheatVmOpcode opcode = {};
- SCOPE_EXIT({
+ SCOPE_EXIT {
decode_success &= valid;
if (valid) {
out = opcode;
}
- });
+ };
// Helper function for getting instruction dwords.
const auto GetNextDword = [&] {
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index af6b10db6..95f8c8c36 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -138,6 +138,7 @@ void Config::ReadPlayerValues(const std::size_t player_index) {
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true)[player_index];
+ player.profile_name = "";
return;
}
player.profile_name = profile_name;
@@ -401,6 +402,14 @@ void Config::ReadNetworkValues() {
EndGroup();
}
+void Config::ReadLibraryAppletValues() {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
+
+ ReadCategory(Settings::Category::LibraryApplet);
+
+ EndGroup();
+}
+
void Config::ReadValues() {
if (global) {
ReadDataStorageValues();
@@ -410,6 +419,7 @@ void Config::ReadValues() {
ReadServiceValues();
ReadWebServiceValues();
ReadMiscellaneousValues();
+ ReadLibraryAppletValues();
}
ReadControlValues();
ReadCoreValues();
@@ -511,6 +521,7 @@ void Config::SaveValues() {
SaveNetworkValues();
SaveWebServiceValues();
SaveMiscellaneousValues();
+ SaveLibraryAppletValues();
} else {
LOG_DEBUG(Config, "Saving only generic configuration values");
}
@@ -691,6 +702,14 @@ void Config::SaveWebServiceValues() {
EndGroup();
}
+void Config::SaveLibraryAppletValues() {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
+
+ WriteCategory(Settings::Category::LibraryApplet);
+
+ EndGroup();
+}
+
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) {
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
index 4ecb97044..8b0599cc3 100644
--- a/src/frontend_common/config.h
+++ b/src/frontend_common/config.h
@@ -88,6 +88,7 @@ protected:
void ReadSystemValues();
void ReadWebServiceValues();
void ReadNetworkValues();
+ void ReadLibraryAppletValues();
// Read platform specific sections
virtual void ReadHidbusValues() = 0;
@@ -121,6 +122,7 @@ protected:
void SaveScreenshotValues();
void SaveSystemValues();
void SaveWebServiceValues();
+ void SaveLibraryAppletValues();
// Save platform specific sections
virtual void SaveHidbusValues() = 0;
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
index f3efe3465..c4e97a47b 100644
--- a/src/frontend_common/content_manager.h
+++ b/src/frontend_common/content_manager.h
@@ -251,11 +251,12 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
* \param callback Callback to report the progress of the installation. The first size_t
* parameter is the total size of the installed contents and the second is the current progress. If
* you return true to the callback, it will cancel the installation as soon as possible.
+ * \param firmware_only Set to true to only scan system nand NCAs (firmware), post firmware install.
* \return A list of entries that failed to install. Returns an empty vector if successful.
*/
inline std::vector<std::string> VerifyInstalledContents(
Core::System& system, FileSys::ManualContentProvider& provider,
- const std::function<bool(size_t, size_t)>& callback) {
+ const std::function<bool(size_t, size_t)>& callback, bool firmware_only = false) {
// Get content registries.
auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
auto user_contents = system.GetFileSystemController().GetUserNANDContents();
@@ -264,7 +265,7 @@ inline std::vector<std::string> VerifyInstalledContents(
if (bis_contents) {
content_providers.push_back(bis_contents);
}
- if (user_contents) {
+ if (user_contents && !firmware_only) {
content_providers.push_back(user_contents);
}
diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp
index 819460eb5..5cd26819c 100644
--- a/src/hid_core/frontend/emulated_controller.cpp
+++ b/src/hid_core/frontend/emulated_controller.cpp
@@ -174,18 +174,25 @@ void EmulatedController::LoadDevices() {
// Only map virtual devices to the first controller
if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
- ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+#ifdef HAVE_LIBUSB
+ ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
+#endif
+#ifdef ANDROID
+ android_params = Common::ParamPackage{"engine:android,port:100"};
+#endif
}
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
output_params[2] = camera_params[1];
output_params[3] = nfc_params[0];
+ output_params[4] = android_params;
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
output_params[2].Set("output", true);
output_params[3].Set("output", true);
+ output_params[4].Set("output", true);
LoadTASParams();
LoadVirtualGamepadParams();
@@ -578,6 +585,9 @@ void EmulatedController::DisableConfiguration() {
// Get Joycon colors before turning on the controller
for (const auto& color_device : color_devices) {
+ if (color_device == nullptr) {
+ continue;
+ }
color_device->ForceUpdate();
}
@@ -923,8 +933,9 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
- auto trigger_guard =
- SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
+ auto trigger_guard = SCOPE_GUARD {
+ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring);
+ };
std::scoped_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
@@ -979,8 +990,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
- auto trigger_guard =
- SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
+ auto trigger_guard = SCOPE_GUARD {
+ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring);
+ };
std::scoped_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
@@ -1026,7 +1038,9 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
- SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
+ SCOPE_EXIT {
+ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring);
+ };
std::scoped_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -1060,8 +1074,9 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
if (index >= controller.color_values.size()) {
return;
}
- auto trigger_guard =
- SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
+ auto trigger_guard = SCOPE_GUARD {
+ TriggerOnChange(ControllerTriggerType::Color, !is_configuring);
+ };
std::scoped_lock lock{mutex};
controller.color_values[index] = TransformToColor(callback);
@@ -1110,7 +1125,9 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
- SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
+ SCOPE_EXIT {
+ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring);
+ };
std::scoped_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
@@ -1173,7 +1190,9 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
}
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
- SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
+ SCOPE_EXIT {
+ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring);
+ };
std::scoped_lock lock{mutex};
controller.camera_values = TransformToCamera(callback);
@@ -1188,7 +1207,9 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
}
void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
- SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
+ SCOPE_EXIT {
+ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring);
+ };
std::scoped_lock lock{mutex};
const auto force_value = TransformToStick(callback);
@@ -1202,7 +1223,9 @@ void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& call
}
void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
- SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
+ SCOPE_EXIT {
+ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring);
+ };
std::scoped_lock lock{mutex};
controller.nfc_values = TransformToNfc(callback);
@@ -1277,6 +1300,10 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
.high_frequency = vibration.high_frequency,
.type = type,
};
+
+ // Send vibrations to Android's input overlay
+ output_devices[4]->SetVibration(status);
+
return output_devices[index]->SetVibration(status) == Common::Input::DriverResult::Success;
}
@@ -1671,8 +1698,9 @@ void EmulatedController::Connect(bool use_temporary_value) {
return;
}
- auto trigger_guard =
- SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
+ auto trigger_guard = SCOPE_GUARD {
+ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring);
+ };
std::scoped_lock lock{connect_mutex, mutex};
if (is_configuring) {
tmp_is_connected = true;
@@ -1687,8 +1715,9 @@ void EmulatedController::Connect(bool use_temporary_value) {
}
void EmulatedController::Disconnect() {
- auto trigger_guard =
- SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
+ auto trigger_guard = SCOPE_GUARD {
+ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring);
+ };
std::scoped_lock lock{connect_mutex, mutex};
if (is_configuring) {
tmp_is_connected = false;
@@ -1724,8 +1753,9 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
- auto trigger_guard =
- SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
+ auto trigger_guard = SCOPE_GUARD {
+ TriggerOnChange(ControllerTriggerType::Type, !is_configuring);
+ };
std::scoped_lock lock{mutex, npad_mutex};
if (is_configuring) {
diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h
index 701b38300..ab3c6fcd3 100644
--- a/src/hid_core/frontend/emulated_controller.h
+++ b/src/hid_core/frontend/emulated_controller.h
@@ -21,7 +21,7 @@
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
-const std::size_t output_devices_size = 4;
+const std::size_t output_devices_size = 5;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
@@ -597,6 +597,7 @@ private:
CameraParams camera_params;
RingAnalogParams ring_params;
NfcParams nfc_params;
+ Common::ParamPackage android_params;
OutputParams output_params;
ButtonDevices button_devices;
diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp
index b32c0660a..c0a76d0eb 100644
--- a/src/hid_core/resources/hid_firmware_settings.cpp
+++ b/src/hid_core/resources/hid_firmware_settings.cpp
@@ -22,29 +22,30 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) {
return;
}
- m_set_sys->GetSettingsItemValue<bool>(is_debug_pad_enabled, "hid_debug", "enables_debugpad");
- m_set_sys->GetSettingsItemValue<bool>(is_device_managed, "hid_debug", "manages_devices");
- m_set_sys->GetSettingsItemValue<bool>(is_touch_i2c_managed, "hid_debug",
- "manages_touch_ic_i2c");
- m_set_sys->GetSettingsItemValue<bool>(is_future_devices_emulated, "hid_debug",
- "emulate_future_device");
- m_set_sys->GetSettingsItemValue<bool>(is_mcu_hardware_error_emulated, "hid_debug",
- "emulate_mcu_hardware_error");
- m_set_sys->GetSettingsItemValue<bool>(is_rail_enabled, "hid_debug", "enables_rail");
- m_set_sys->GetSettingsItemValue<bool>(is_firmware_update_failure_emulated, "hid_debug",
- "emulate_firmware_update_failure");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_debug_pad_enabled, "hid_debug",
+ "enables_debugpad");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_device_managed, "hid_debug", "manages_devices");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_touch_i2c_managed, "hid_debug",
+ "manages_touch_ic_i2c");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_future_devices_emulated, "hid_debug",
+ "emulate_future_device");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_mcu_hardware_error_emulated, "hid_debug",
+ "emulate_mcu_hardware_error");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_rail_enabled, "hid_debug", "enables_rail");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_firmware_update_failure_emulated, "hid_debug",
+ "emulate_firmware_update_failure");
is_firmware_update_failure = {};
- m_set_sys->GetSettingsItemValue<bool>(is_ble_disabled, "hid_debug", "ble_disabled");
- m_set_sys->GetSettingsItemValue<bool>(is_dscale_disabled, "hid_debug", "dscale_disabled");
- m_set_sys->GetSettingsItemValue<bool>(is_handheld_forced, "hid_debug", "force_handheld");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_ble_disabled, "hid_debug", "ble_disabled");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_dscale_disabled, "hid_debug", "dscale_disabled");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_handheld_forced, "hid_debug", "force_handheld");
features_per_id_disabled = {};
- m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug",
- "touch_firmware_auto_update_disabled");
+ m_set_sys->GetSettingsItemValueImpl<bool>(is_touch_firmware_auto_update_disabled, "hid_debug",
+ "touch_firmware_auto_update_disabled");
bool has_rail_interface{};
bool has_sio_mcu{};
- m_set_sys->GetSettingsItemValue<bool>(has_rail_interface, "hid", "has_rail_interface");
- m_set_sys->GetSettingsItemValue<bool>(has_sio_mcu, "hid", "has_sio_mcu");
+ m_set_sys->GetSettingsItemValueImpl<bool>(has_rail_interface, "hid", "has_rail_interface");
+ m_set_sys->GetSettingsItemValueImpl<bool>(has_sio_mcu, "hid", "has_sio_mcu");
platform_config.has_rail_interface.Assign(has_rail_interface);
platform_config.has_sio_mcu.Assign(has_sio_mcu);
diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp
index 02b1f0290..4c103889a 100644
--- a/src/hid_core/resources/npad/npad_vibration.cpp
+++ b/src/hid_core/resources/npad/npad_vibration.cpp
@@ -15,7 +15,7 @@ Result NpadVibration::Activate() {
std::scoped_lock lock{mutex};
f32 master_volume = 1.0f;
- m_set_sys->GetVibrationMasterVolume(master_volume);
+ m_set_sys->GetVibrationMasterVolume(&master_volume);
if (master_volume < 0.0f || master_volume > 1.0f) {
return ResultVibrationStrengthOutOfRange;
}
@@ -57,7 +57,7 @@ Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const {
std::scoped_lock lock{mutex};
f32 master_volume = 1.0f;
- m_set_sys->GetVibrationMasterVolume(master_volume);
+ m_set_sys->GetVibrationMasterVolume(&master_volume);
if (master_volume < 0.0f || master_volume > 1.0f) {
return ResultVibrationStrengthOutOfRange;
}
@@ -77,7 +77,7 @@ Result NpadVibration::EndPermitVibrationSession() {
std::scoped_lock lock{mutex};
f32 master_volume = 1.0f;
- m_set_sys->GetVibrationMasterVolume(master_volume);
+ m_set_sys->GetVibrationMasterVolume(&master_volume);
if (master_volume < 0.0f || master_volume > 1.0f) {
return ResultVibrationStrengthOutOfRange;
}
diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
index c39321915..79ddaa4df 100644
--- a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
+++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
@@ -48,7 +48,7 @@ Result TouchResource::ActivateTouch() {
}
Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard};
- m_set_sys->GetTouchScreenMode(touch_mode);
+ m_set_sys->GetTouchScreenMode(&touch_mode);
default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode);
global_ref_counter++;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index d0a71a15b..d455323e0 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -2,8 +2,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(input_common STATIC
- drivers/android.cpp
- drivers/android.h
drivers/camera.cpp
drivers/camera.h
drivers/keyboard.cpp
@@ -94,3 +92,11 @@ target_link_libraries(input_common PUBLIC hid_core PRIVATE common Boost::headers
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(input_common PRIVATE precompiled_headers.h)
endif()
+
+if (ANDROID)
+ target_sources(input_common PRIVATE
+ drivers/android.cpp
+ drivers/android.h
+ )
+ target_link_libraries(input_common PRIVATE android)
+endif()
diff --git a/src/input_common/drivers/android.cpp b/src/input_common/drivers/android.cpp
index b6a03fdc0..e859cc538 100644
--- a/src/input_common/drivers/android.cpp
+++ b/src/input_common/drivers/android.cpp
@@ -1,30 +1,47 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include <set>
+#include <common/settings_input.h>
+#include <jni.h>
+#include "common/android/android_common.h"
+#include "common/android/id_cache.h"
#include "input_common/drivers/android.h"
namespace InputCommon {
Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
-void Android::RegisterController(std::size_t controller_number) {
- PreSetController(GetIdentifier(controller_number));
+void Android::RegisterController(jobject j_input_device) {
+ auto env = Common::Android::GetEnvForThread();
+ const std::string guid = Common::Android::GetJString(
+ env, static_cast<jstring>(
+ env->CallObjectMethod(j_input_device, Common::Android::GetYuzuDeviceGetGUID())));
+ const s32 port = env->CallIntMethod(j_input_device, Common::Android::GetYuzuDeviceGetPort());
+ const auto identifier = GetIdentifier(guid, static_cast<size_t>(port));
+ PreSetController(identifier);
+
+ if (input_devices.find(identifier) != input_devices.end()) {
+ env->DeleteGlobalRef(input_devices[identifier]);
+ }
+ auto new_device = env->NewGlobalRef(j_input_device);
+ input_devices[identifier] = new_device;
}
-void Android::SetButtonState(std::size_t controller_number, int button_id, bool value) {
- const auto identifier = GetIdentifier(controller_number);
+void Android::SetButtonState(std::string guid, size_t port, int button_id, bool value) {
+ const auto identifier = GetIdentifier(guid, port);
SetButton(identifier, button_id, value);
}
-void Android::SetAxisState(std::size_t controller_number, int axis_id, float value) {
- const auto identifier = GetIdentifier(controller_number);
+void Android::SetAxisPosition(std::string guid, size_t port, int axis_id, float value) {
+ const auto identifier = GetIdentifier(guid, port);
SetAxis(identifier, axis_id, value);
}
-void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x,
+void Android::SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x,
float gyro_y, float gyro_z, float accel_x, float accel_y,
float accel_z) {
- const auto identifier = GetIdentifier(controller_number);
+ const auto identifier = GetIdentifier(guid, port);
const BasicMotion motion_data{
.gyro_x = gyro_x,
.gyro_y = gyro_y,
@@ -37,10 +54,295 @@ void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp,
SetMotion(identifier, 0, motion_data);
}
-PadIdentifier Android::GetIdentifier(std::size_t controller_number) const {
+Common::Input::DriverResult Android::SetVibration(
+ [[maybe_unused]] const PadIdentifier& identifier,
+ [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
+ auto device = input_devices.find(identifier);
+ if (device != input_devices.end()) {
+ Common::Android::RunJNIOnFiber<void>([&](JNIEnv* env) {
+ float average_intensity =
+ static_cast<float>((vibration.high_amplitude + vibration.low_amplitude) / 2.0);
+ env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(),
+ average_intensity);
+ });
+ return Common::Input::DriverResult::Success;
+ }
+ return Common::Input::DriverResult::NotSupported;
+}
+
+bool Android::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
+ auto device = input_devices.find(identifier);
+ if (device != input_devices.end()) {
+ return Common::Android::RunJNIOnFiber<bool>([&](JNIEnv* env) {
+ return static_cast<bool>(env->CallBooleanMethod(
+ device->second, Common::Android::GetYuzuDeviceGetSupportsVibration()));
+ });
+ }
+ return false;
+}
+
+std::vector<Common::ParamPackage> Android::GetInputDevices() const {
+ std::vector<Common::ParamPackage> devices;
+ auto env = Common::Android::GetEnvForThread();
+ for (const auto& [key, value] : input_devices) {
+ auto name_object = static_cast<jstring>(
+ env->CallObjectMethod(value, Common::Android::GetYuzuDeviceGetName()));
+ const std::string name =
+ fmt::format("{} {}", Common::Android::GetJString(env, name_object), key.port);
+ devices.emplace_back(Common::ParamPackage{
+ {"engine", GetEngineName()},
+ {"display", std::move(name)},
+ {"guid", key.guid.RawString()},
+ {"port", std::to_string(key.port)},
+ });
+ }
+ return devices;
+}
+
+std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const {
+ auto j_axes = static_cast<jobjectArray>(
+ env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceGetAxes()));
+ std::set<s32> axes;
+ for (int i = 0; i < env->GetArrayLength(j_axes); ++i) {
+ jobject axis = env->GetObjectArrayElement(j_axes, i);
+ axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField()));
+ }
+ return axes;
+}
+
+Common::ParamPackage Android::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
+ int axis_y) const {
+ Common::ParamPackage params;
+ params.Set("engine", GetEngineName());
+ params.Set("port", static_cast<int>(identifier.port));
+ params.Set("guid", identifier.guid.RawString());
+ params.Set("axis_x", axis_x);
+ params.Set("axis_y", axis_y);
+ params.Set("offset_x", 0);
+ params.Set("offset_y", 0);
+ params.Set("invert_x", "+");
+
+ // Invert Y-Axis by default
+ params.Set("invert_y", "-");
+ return params;
+}
+
+Common::ParamPackage Android::BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis,
+ bool invert) const {
+ Common::ParamPackage params{};
+ params.Set("engine", GetEngineName());
+ params.Set("port", static_cast<int>(identifier.port));
+ params.Set("guid", identifier.guid.RawString());
+ params.Set("axis", axis);
+ params.Set("threshold", "0.5");
+ params.Set("invert", invert ? "-" : "+");
+ return params;
+}
+
+Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier identifier,
+ s32 button) const {
+ Common::ParamPackage params{};
+ params.Set("engine", GetEngineName());
+ params.Set("port", static_cast<int>(identifier.port));
+ params.Set("guid", identifier.guid.RawString());
+ params.Set("button", button);
+ return params;
+}
+
+bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const {
+ for (size_t i = 0; i < vids.size(); ++i) {
+ auto fucker = device.RawString();
+ if (fucker.find(vids[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+AnalogMapping Android::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+
+ auto identifier =
+ GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0)));
+ auto& j_device = input_devices[identifier];
+ if (j_device == nullptr) {
+ return {};
+ }
+
+ auto env = Common::Android::GetEnvForThread();
+ std::set<s32> axes = GetDeviceAxes(env, j_device);
+ if (axes.size() == 0) {
+ return {};
+ }
+
+ AnalogMapping mapping = {};
+ if (axes.find(AXIS_X) != axes.end() && axes.find(AXIS_Y) != axes.end()) {
+ mapping.insert_or_assign(Settings::NativeAnalog::LStick,
+ BuildParamPackageForAnalog(identifier, AXIS_X, AXIS_Y));
+ }
+
+ if (axes.find(AXIS_RX) != axes.end() && axes.find(AXIS_RY) != axes.end()) {
+ mapping.insert_or_assign(Settings::NativeAnalog::RStick,
+ BuildParamPackageForAnalog(identifier, AXIS_RX, AXIS_RY));
+ } else if (axes.find(AXIS_Z) != axes.end() && axes.find(AXIS_RZ) != axes.end()) {
+ mapping.insert_or_assign(Settings::NativeAnalog::RStick,
+ BuildParamPackageForAnalog(identifier, AXIS_Z, AXIS_RZ));
+ }
+ return mapping;
+}
+
+ButtonMapping Android::GetButtonMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+
+ auto identifier =
+ GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0)));
+ auto& j_device = input_devices[identifier];
+ if (j_device == nullptr) {
+ return {};
+ }
+
+ auto env = Common::Android::GetEnvForThread();
+ jintArray j_keys = env->NewIntArray(static_cast<int>(keycode_ids.size()));
+ env->SetIntArrayRegion(j_keys, 0, static_cast<int>(keycode_ids.size()), keycode_ids.data());
+ auto j_has_keys_object = static_cast<jbooleanArray>(
+ env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceHasKeys(), j_keys));
+ jboolean isCopy = false;
+ jboolean* j_has_keys = env->GetBooleanArrayElements(j_has_keys_object, &isCopy);
+
+ std::set<s32> available_keys;
+ for (size_t i = 0; i < keycode_ids.size(); ++i) {
+ if (j_has_keys[i]) {
+ available_keys.insert(keycode_ids[i]);
+ }
+ }
+
+ // Some devices use axes instead of buttons for certain controls so we need all the axes here
+ std::set<s32> axes = GetDeviceAxes(env, j_device);
+
+ ButtonMapping mapping = {};
+ if (axes.find(AXIS_HAT_X) != axes.end() && axes.find(AXIS_HAT_Y) != axes.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::DUp,
+ BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, true));
+ mapping.insert_or_assign(Settings::NativeButton::DDown,
+ BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, false));
+ mapping.insert_or_assign(Settings::NativeButton::DLeft,
+ BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, true));
+ mapping.insert_or_assign(Settings::NativeButton::DRight,
+ BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, false));
+ } else if (available_keys.find(KEYCODE_DPAD_UP) != available_keys.end() &&
+ available_keys.find(KEYCODE_DPAD_DOWN) != available_keys.end() &&
+ available_keys.find(KEYCODE_DPAD_LEFT) != available_keys.end() &&
+ available_keys.find(KEYCODE_DPAD_RIGHT) != available_keys.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::DUp,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_UP));
+ mapping.insert_or_assign(Settings::NativeButton::DDown,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_DOWN));
+ mapping.insert_or_assign(Settings::NativeButton::DLeft,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_LEFT));
+ mapping.insert_or_assign(Settings::NativeButton::DRight,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_RIGHT));
+ }
+
+ if (axes.find(AXIS_LTRIGGER) != axes.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::ZL, BuildAnalogParamPackageForButton(
+ identifier, AXIS_LTRIGGER, false));
+ } else if (available_keys.find(KEYCODE_BUTTON_L2) != available_keys.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::ZL,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L2));
+ }
+
+ if (axes.find(AXIS_RTRIGGER) != axes.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::ZR, BuildAnalogParamPackageForButton(
+ identifier, AXIS_RTRIGGER, false));
+ } else if (available_keys.find(KEYCODE_BUTTON_R2) != available_keys.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::ZR,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R2));
+ }
+
+ if (available_keys.find(KEYCODE_BUTTON_A) != available_keys.end()) {
+ if (MatchVID(identifier.guid, flipped_ab_vids)) {
+ mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_A));
+ } else {
+ mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_A));
+ }
+ }
+ if (available_keys.find(KEYCODE_BUTTON_B) != available_keys.end()) {
+ if (MatchVID(identifier.guid, flipped_ab_vids)) {
+ mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_B));
+ } else {
+ mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_B));
+ }
+ }
+ if (available_keys.find(KEYCODE_BUTTON_X) != available_keys.end()) {
+ if (MatchVID(identifier.guid, flipped_xy_vids)) {
+ mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_X));
+ } else {
+ mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_X));
+ }
+ }
+ if (available_keys.find(KEYCODE_BUTTON_Y) != available_keys.end()) {
+ if (MatchVID(identifier.guid, flipped_xy_vids)) {
+ mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_Y));
+ } else {
+ mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton(
+ identifier, KEYCODE_BUTTON_Y));
+ }
+ }
+
+ if (available_keys.find(KEYCODE_BUTTON_L1) != available_keys.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::L,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L1));
+ }
+ if (available_keys.find(KEYCODE_BUTTON_R1) != available_keys.end()) {
+ mapping.insert_or_assign(Settings::NativeButton::R,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R1));
+ }
+
+ if (available_keys.find(KEYCODE_BUTTON_THUMBL) != available_keys.end()) {
+ mapping.insert_or_assign(
+ Settings::NativeButton::LStick,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBL));
+ }
+ if (available_keys.find(KEYCODE_BUTTON_THUMBR) != available_keys.end()) {
+ mapping.insert_or_assign(
+ Settings::NativeButton::RStick,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBR));
+ }
+
+ if (available_keys.find(KEYCODE_BUTTON_START) != available_keys.end()) {
+ mapping.insert_or_assign(
+ Settings::NativeButton::Plus,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_START));
+ }
+ if (available_keys.find(KEYCODE_BUTTON_SELECT) != available_keys.end()) {
+ mapping.insert_or_assign(
+ Settings::NativeButton::Minus,
+ BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_SELECT));
+ }
+
+ return mapping;
+}
+
+Common::Input::ButtonNames Android::GetUIName(
+ [[maybe_unused]] const Common::ParamPackage& params) const {
+ return Common::Input::ButtonNames::Value;
+}
+
+PadIdentifier Android::GetIdentifier(const std::string& guid, size_t port) const {
return {
- .guid = Common::UUID{},
- .port = controller_number,
+ .guid = Common::UUID{guid},
+ .port = port,
.pad = 0,
};
}
diff --git a/src/input_common/drivers/android.h b/src/input_common/drivers/android.h
index 3f01817f6..8a386c1b1 100644
--- a/src/input_common/drivers/android.h
+++ b/src/input_common/drivers/android.h
@@ -3,6 +3,8 @@
#pragma once
+#include <set>
+#include <jni.h>
#include "input_common/input_engine.h"
namespace InputCommon {
@@ -15,40 +17,122 @@ public:
explicit Android(std::string input_engine_);
/**
- * Registers controller number to accept new inputs
- * @param controller_number the controller number that will take this action
+ * Registers controller number to accept new inputs.
+ * @param j_input_device YuzuInputDevice object from the Android frontend to register.
*/
- void RegisterController(std::size_t controller_number);
+ void RegisterController(jobject j_input_device);
/**
- * Sets the status of all buttons bound with the key to pressed
- * @param controller_number the controller number that will take this action
- * @param button_id the id of the button
- * @param value indicates if the button is pressed or not
+ * Sets the status of a button on a specific controller.
+ * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
+ * @param port Port determined by controller connection order.
+ * @param button_id The Android Keycode corresponding to this event.
+ * @param value Whether the button is pressed or not.
*/
- void SetButtonState(std::size_t controller_number, int button_id, bool value);
+ void SetButtonState(std::string guid, size_t port, int button_id, bool value);
/**
- * Sets the status of a analog input to a specific player index
- * @param controller_number the controller number that will take this action
- * @param axis_id the id of the axis to move
- * @param value the analog position of the axis
+ * Sets the status of an axis on a specific controller.
+ * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
+ * @param port Port determined by controller connection order.
+ * @param axis_id The Android axis ID corresponding to this event.
+ * @param value Value along the given axis.
*/
- void SetAxisState(std::size_t controller_number, int axis_id, float value);
+ void SetAxisPosition(std::string guid, size_t port, int axis_id, float value);
/**
- * Sets the status of the motion sensor to a specific player index
- * @param controller_number the controller number that will take this action
- * @param delta_timestamp time passed since last reading
- * @param gyro_x,gyro_y,gyro_z the gyro sensor readings
- * @param accel_x,accel_y,accel_z the accelerometer reading
+ * Sets the status of the motion sensor on a specific controller
+ * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
+ * @param port Port determined by controller connection order.
+ * @param delta_timestamp Time passed since the last read.
+ * @param gyro_x,gyro_y,gyro_z Gyro sensor readings.
+ * @param accel_x,accel_y,accel_z Accelerometer sensor readings.
*/
- void SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x,
+ void SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x,
float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z);
+ Common::Input::DriverResult SetVibration(
+ const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
+
+ bool IsVibrationEnabled(const PadIdentifier& identifier) override;
+
+ std::vector<Common::ParamPackage> GetInputDevices() const override;
+
+ /**
+ * Gets the axes reported by the YuzuInputDevice.
+ * @param env JNI environment pointer.
+ * @param j_device YuzuInputDevice from the Android frontend.
+ * @return Set of the axes reported by the underlying Android InputDevice
+ */
+ std::set<s32> GetDeviceAxes(JNIEnv* env, jobject& j_device) const;
+
+ Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
+ int axis_y) const;
+
+ Common::ParamPackage BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis,
+ bool invert) const;
+
+ Common::ParamPackage BuildButtonParamPackageForButton(PadIdentifier identifier,
+ s32 button) const;
+
+ bool MatchVID(Common::UUID device, const std::vector<std::string>& vids) const;
+
+ AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
+
+ ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
+
+ Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
+
private:
+ std::unordered_map<PadIdentifier, jobject> input_devices;
+
/// Returns the correct identifier corresponding to the player index
- PadIdentifier GetIdentifier(std::size_t controller_number) const;
+ PadIdentifier GetIdentifier(const std::string& guid, size_t port) const;
+
+ static constexpr s32 AXIS_X = 0;
+ static constexpr s32 AXIS_Y = 1;
+ static constexpr s32 AXIS_Z = 11;
+ static constexpr s32 AXIS_RX = 12;
+ static constexpr s32 AXIS_RY = 13;
+ static constexpr s32 AXIS_RZ = 14;
+ static constexpr s32 AXIS_HAT_X = 15;
+ static constexpr s32 AXIS_HAT_Y = 16;
+ static constexpr s32 AXIS_LTRIGGER = 17;
+ static constexpr s32 AXIS_RTRIGGER = 18;
+
+ static constexpr s32 KEYCODE_DPAD_UP = 19;
+ static constexpr s32 KEYCODE_DPAD_DOWN = 20;
+ static constexpr s32 KEYCODE_DPAD_LEFT = 21;
+ static constexpr s32 KEYCODE_DPAD_RIGHT = 22;
+ static constexpr s32 KEYCODE_BUTTON_A = 96;
+ static constexpr s32 KEYCODE_BUTTON_B = 97;
+ static constexpr s32 KEYCODE_BUTTON_X = 99;
+ static constexpr s32 KEYCODE_BUTTON_Y = 100;
+ static constexpr s32 KEYCODE_BUTTON_L1 = 102;
+ static constexpr s32 KEYCODE_BUTTON_R1 = 103;
+ static constexpr s32 KEYCODE_BUTTON_L2 = 104;
+ static constexpr s32 KEYCODE_BUTTON_R2 = 105;
+ static constexpr s32 KEYCODE_BUTTON_THUMBL = 106;
+ static constexpr s32 KEYCODE_BUTTON_THUMBR = 107;
+ static constexpr s32 KEYCODE_BUTTON_START = 108;
+ static constexpr s32 KEYCODE_BUTTON_SELECT = 109;
+ const std::vector<s32> keycode_ids{
+ KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT,
+ KEYCODE_BUTTON_A, KEYCODE_BUTTON_B, KEYCODE_BUTTON_X, KEYCODE_BUTTON_Y,
+ KEYCODE_BUTTON_L1, KEYCODE_BUTTON_R1, KEYCODE_BUTTON_L2, KEYCODE_BUTTON_R2,
+ KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR, KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT,
+ };
+
+ const std::string sony_vid{"054c"};
+ const std::string nintendo_vid{"057e"};
+ const std::string razer_vid{"1532"};
+ const std::string redmagic_vid{"3537"};
+ const std::string backbone_labs_vid{"358a"};
+ const std::string xbox_vid{"045e"};
+ const std::vector<std::string> flipped_ab_vids{sony_vid, nintendo_vid, razer_vid,
+ redmagic_vid, backbone_labs_vid, xbox_vid};
+ const std::vector<std::string> flipped_xy_vids{sony_vid, razer_vid, redmagic_vid,
+ backbone_labs_vid, xbox_vid};
};
} // namespace InputCommon
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index c9f903213..0dd1c958a 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -268,7 +268,9 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
}
Common::Input::DriverResult JoyconDriver::SetPollingMode() {
- SCOPE_EXIT({ disable_input_thread = false; });
+ SCOPE_EXIT {
+ disable_input_thread = false;
+ };
disable_input_thread = true;
rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index f8749ebbf..62a7ae40f 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,7 +4,6 @@
#include <memory>
#include "common/input.h"
#include "common/param_package.h"
-#include "input_common/drivers/android.h"
#include "input_common/drivers/camera.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
@@ -28,6 +27,10 @@
#include "input_common/drivers/sdl_driver.h"
#endif
+#ifdef ANDROID
+#include "input_common/drivers/android.h"
+#endif
+
namespace InputCommon {
/// Dummy engine to get periodic updates
@@ -79,7 +82,9 @@ struct InputSubsystem::Impl {
RegisterEngine("cemuhookudp", udp_client);
RegisterEngine("tas", tas_input);
RegisterEngine("camera", camera);
+#ifdef ANDROID
RegisterEngine("android", android);
+#endif
RegisterEngine("virtual_amiibo", virtual_amiibo);
RegisterEngine("virtual_gamepad", virtual_gamepad);
#ifdef HAVE_SDL2
@@ -111,7 +116,9 @@ struct InputSubsystem::Impl {
UnregisterEngine(udp_client);
UnregisterEngine(tas_input);
UnregisterEngine(camera);
+#ifdef ANDROID
UnregisterEngine(android);
+#endif
UnregisterEngine(virtual_amiibo);
UnregisterEngine(virtual_gamepad);
#ifdef HAVE_SDL2
@@ -128,12 +135,16 @@ struct InputSubsystem::Impl {
Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
};
+#ifndef ANDROID
auto keyboard_devices = keyboard->GetInputDevices();
devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
auto mouse_devices = mouse->GetInputDevices();
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
+#endif
+#ifdef ANDROID
auto android_devices = android->GetInputDevices();
devices.insert(devices.end(), android_devices.begin(), android_devices.end());
+#endif
#ifdef HAVE_LIBUSB
auto gcadapter_devices = gcadapter->GetInputDevices();
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
@@ -162,9 +173,11 @@ struct InputSubsystem::Impl {
if (engine == mouse->GetEngineName()) {
return mouse;
}
+#ifdef ANDROID
if (engine == android->GetEngineName()) {
return android;
}
+#endif
#ifdef HAVE_LIBUSB
if (engine == gcadapter->GetEngineName()) {
return gcadapter;
@@ -245,9 +258,11 @@ struct InputSubsystem::Impl {
if (engine == mouse->GetEngineName()) {
return true;
}
+#ifdef ANDROID
if (engine == android->GetEngineName()) {
return true;
}
+#endif
#ifdef HAVE_LIBUSB
if (engine == gcadapter->GetEngineName()) {
return true;
@@ -276,7 +291,9 @@ struct InputSubsystem::Impl {
void BeginConfiguration() {
keyboard->BeginConfiguration();
mouse->BeginConfiguration();
+#ifdef ANDROID
android->BeginConfiguration();
+#endif
#ifdef HAVE_LIBUSB
gcadapter->BeginConfiguration();
#endif
@@ -290,7 +307,9 @@ struct InputSubsystem::Impl {
void EndConfiguration() {
keyboard->EndConfiguration();
mouse->EndConfiguration();
+#ifdef ANDROID
android->EndConfiguration();
+#endif
#ifdef HAVE_LIBUSB
gcadapter->EndConfiguration();
#endif
@@ -321,7 +340,6 @@ struct InputSubsystem::Impl {
std::shared_ptr<TasInput::Tas> tas_input;
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
std::shared_ptr<Camera> camera;
- std::shared_ptr<Android> android;
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
std::shared_ptr<VirtualGamepad> virtual_gamepad;
@@ -333,6 +351,10 @@ struct InputSubsystem::Impl {
std::shared_ptr<SDLDriver> sdl;
std::shared_ptr<Joycons> joycon;
#endif
+
+#ifdef ANDROID
+ std::shared_ptr<Android> android;
+#endif
};
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -387,6 +409,7 @@ const Camera* InputSubsystem::GetCamera() const {
return impl->camera.get();
}
+#ifdef ANDROID
Android* InputSubsystem::GetAndroid() {
return impl->android.get();
}
@@ -394,6 +417,7 @@ Android* InputSubsystem::GetAndroid() {
const Android* InputSubsystem::GetAndroid() const {
return impl->android.get();
}
+#endif
VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
return impl->virtual_amiibo.get();
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 44281e407..945cdb42b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -60,11 +60,10 @@ public:
Add(spv::ImageOperandsMask::ConstOffsets, offsets);
}
- explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) {
+ explicit ImageOperands(Id lod, Id ms) {
if (Sirit::ValidId(lod)) {
Add(spv::ImageOperandsMask::Lod, lod);
}
- AddOffset(ctx, offset, ImageFetchOffsetAllowed);
if (Sirit::ValidId(ms)) {
Add(spv::ImageOperandsMask::Sample, ms);
}
@@ -312,6 +311,43 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
return coords;
}
}
+
+void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
+ Id offset) {
+ if (!Sirit::ValidId(offset)) {
+ return;
+ }
+
+ Id result_type{};
+ switch (info.type) {
+ case TextureType::Buffer:
+ case TextureType::Color1D: {
+ result_type = ctx.U32[1];
+ break;
+ }
+ case TextureType::ColorArray1D:
+ offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value);
+ [[fallthrough]];
+ case TextureType::Color2D:
+ case TextureType::Color2DRect: {
+ result_type = ctx.U32[2];
+ break;
+ }
+ case TextureType::ColorArray2D:
+ offset = ctx.OpCompositeConstruct(ctx.U32[3], ctx.OpCompositeExtract(ctx.U32[1], coords, 0),
+ ctx.OpCompositeExtract(ctx.U32[1], coords, 1),
+ ctx.u32_zero_value);
+ [[fallthrough]];
+ case TextureType::Color3D: {
+ result_type = ctx.U32[3];
+ break;
+ }
+ case TextureType::ColorCube:
+ case TextureType::ColorArrayCube:
+ return;
+ }
+ coords = ctx.OpIAdd(result_type, coords, offset);
+}
} // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -494,9 +530,10 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
operands.Span());
}
-Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
- const IR::Value& offset, Id lod, Id ms) {
+Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
+ Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
+ AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
}
@@ -504,7 +541,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
// This image is multisampled, lod must be implicit
lod = Id{};
}
- const ImageOperands operands(ctx, offset, lod, ms);
+ const ImageOperands operands(lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 08fcabd58..5c01b1012 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const IR::Value& offset, const IR::Value& offset2);
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref);
-Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
- const IR::Value& offset, Id lod, Id ms);
+Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
+ Id lod, Id ms);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
const IR::Value& skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 55180f4b5..2de2beb6e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -18,6 +18,7 @@ add_library(video_core STATIC
buffer_cache/usage_tracker.h
buffer_cache/word_manager.h
cache_types.h
+ capture.h
cdma_pusher.cpp
cdma_pusher.h
compatible_formats.cpp
@@ -101,6 +102,7 @@ add_library(video_core STATIC
memory_manager.cpp
memory_manager.h
precompiled_headers.h
+ present.h
pte_kind.h
query_cache/bank_base.h
query_cache/query_base.h
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 3818e00f4..ed7a5b27e 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1130,7 +1130,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
channel_state->vertex_buffers[index] = NULL_BINDING;
return;
}
- if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
+ if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
}
const BufferId buffer_id = FindBuffer(*device_addr, size);
diff --git a/src/video_core/capture.h b/src/video_core/capture.h
new file mode 100644
index 000000000..8db14a8ec
--- /dev/null
+++ b/src/video_core/capture.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/alignment.h"
+#include "common/bit_util.h"
+#include "common/common_types.h"
+#include "core/frontend/framebuffer_layout.h"
+#include "video_core/surface.h"
+
+namespace VideoCore::Capture {
+
+constexpr u32 BlockHeight = 4;
+constexpr u32 BlockDepth = 0;
+constexpr u32 BppLog2 = 2;
+
+constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM;
+
+constexpr auto LinearWidth = Layout::ScreenUndocked::Width;
+constexpr auto LinearHeight = Layout::ScreenUndocked::Height;
+constexpr auto LinearDepth = 1U;
+constexpr auto BytesPerPixel = 4U;
+
+constexpr auto TiledWidth = LinearWidth;
+constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2);
+constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2);
+
+constexpr Layout::FramebufferLayout Layout{
+ .width = LinearWidth,
+ .height = LinearHeight,
+ .screen = {0, 0, LinearWidth, LinearHeight},
+ .is_srgb = false,
+};
+
+} // namespace VideoCore::Capture
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index a94e1f043..0d47b032c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -291,7 +291,9 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
}
void Maxwell3D::ConsumeSinkImpl() {
- SCOPE_EXIT({ method_sink.clear(); });
+ SCOPE_EXIT {
+ method_sink.clear();
+ };
const auto control = shadow_state.shadow_ram_control;
if (control == Regs::ShadowRamControl::Track ||
control == Regs::ShadowRamControl::TrackWithFilter) {
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index c3eda6893..2135f1f2d 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -197,7 +197,9 @@ private:
MicroProfileOnThreadCreate(name.c_str());
// Cleanup
- SCOPE_EXIT({ MicroProfileOnThreadExit(); });
+ SCOPE_EXIT {
+ MicroProfileOnThreadExit();
+ };
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index 6a18b76fb..8b2a49de5 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -11,6 +11,12 @@
namespace Tegra {
+enum class BlendMode {
+ Opaque,
+ Premultiplied,
+ Coverage,
+};
+
/**
* Struct describing framebuffer configuration
*/
@@ -23,6 +29,7 @@ struct FramebufferConfig {
Service::android::PixelFormat pixel_format{};
Service::android::BufferTransformFlags transform_flags{};
Common::Rectangle<int> crop_rect{};
+ BlendMode blending{};
};
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index f4a5d831c..8e663f2a8 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -347,6 +347,17 @@ struct GPU::Impl {
WaitForSyncOperation(wait_fence);
}
+ std::vector<u8> GetAppletCaptureBuffer() {
+ std::vector<u8> out;
+
+ const auto wait_fence =
+ RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
+ gpu_thread.TickGPU();
+ WaitForSyncOperation(wait_fence);
+
+ return out;
+ }
+
GPU& gpu;
Core::System& system;
Host1x::Host1x& host1x;
@@ -505,6 +516,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
impl->RequestComposite(std::move(layers), std::move(fences));
}
+std::vector<u8> GPU::GetAppletCaptureBuffer() {
+ return impl->GetAppletCaptureBuffer();
+}
+
u64 GPU::GetTicks() const {
return impl->GetTicks();
}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c4602ca37..ad535512c 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -215,6 +215,8 @@ public:
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
std::vector<Service::Nvidia::NvFence>&& fences);
+ std::vector<u8> GetAppletCaptureBuffer();
+
/// Performs any additional setup necessary in order to begin GPU emulation.
/// This can be used to launch any necessary threads and register any necessary
/// core timing events.
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 58d8110b8..477e11457 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -22,7 +22,9 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
Tegra::Control::Scheduler& scheduler, SynchState& state) {
std::string name = "GPU";
MicroProfileOnThreadCreate(name.c_str());
- SCOPE_EXIT({ MicroProfileOnThreadExit(); });
+ SCOPE_EXIT {
+ MicroProfileOnThreadExit();
+ };
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
index 96686da59..1003cd38d 100644
--- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
@@ -273,10 +273,10 @@ DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
- SCOPE_EXIT({
+ SCOPE_EXIT {
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
- });
+ };
// Don't know how to get the accurate time_base but it doesn't matter for yadif filter
// so just use 1/1 to make buffer filter happy
diff --git a/src/video_core/host_shaders/fidelityfx_fsr.frag b/src/video_core/host_shaders/fidelityfx_fsr.frag
index a266e1c4e..54eedb450 100644
--- a/src/video_core/host_shaders/fidelityfx_fsr.frag
+++ b/src/video_core/host_shaders/fidelityfx_fsr.frag
@@ -37,6 +37,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture;
#define A_GPU 1
#define A_GLSL 1
+#define FSR_RCAS_PASSTHROUGH_ALPHA 1
#ifndef YUZU_USE_FP16
#include "ffx_a.h"
@@ -71,9 +72,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture;
#include "ffx_fsr1.h"
-#if USE_RCAS
- layout(location = 0) in vec2 frag_texcoord;
-#endif
+layout (location = 0) in vec2 frag_texcoord;
layout (location = 0) out vec4 frag_color;
void CurrFilter(AU2 pos) {
@@ -81,22 +80,22 @@ void CurrFilter(AU2 pos) {
#ifndef YUZU_USE_FP16
AF3 c;
FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
- frag_color = AF4(c, 1.0);
+ frag_color = AF4(c, texture(InputTexture, frag_texcoord).a);
#else
AH3 c;
FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
- frag_color = AH4(c, 1.0);
+ frag_color = AH4(c, texture(InputTexture, frag_texcoord).a);
#endif
#endif
#if USE_RCAS
#ifndef YUZU_USE_FP16
- AF3 c;
- FsrRcasF(c.r, c.g, c.b, pos, Const0);
- frag_color = AF4(c, 1.0);
+ AF4 c;
+ FsrRcasF(c.r, c.g, c.b, c.a, pos, Const0);
+ frag_color = c;
#else
- AH3 c;
- FsrRcasH(c.r, c.g, c.b, pos, Const0);
- frag_color = AH4(c, 1.0);
+ AH4 c;
+ FsrRcasH(c.r, c.g, c.b, c.a, pos, Const0);
+ frag_color = c;
#endif
#endif
}
diff --git a/src/video_core/host_shaders/fxaa.frag b/src/video_core/host_shaders/fxaa.frag
index 9bffc20d5..192a602c1 100644
--- a/src/video_core/host_shaders/fxaa.frag
+++ b/src/video_core/host_shaders/fxaa.frag
@@ -71,5 +71,5 @@ vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) {
}
void main() {
- frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0);
+ frag_color = vec4(FxaaPixelShader(posPos, input_texture), texture(input_texture, posPos.xy).a);
}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
index 16d22f58e..fc47d3810 100644
--- a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
@@ -31,6 +31,7 @@ layout (location = 0) uniform uvec4 constants[4];
#define A_GPU 1
#define A_GLSL 1
+#define FSR_RCAS_PASSTHROUGH_ALPHA 1
#ifdef YUZU_USE_FP16
#define A_HALF
@@ -67,9 +68,7 @@ layout (location = 0) uniform uvec4 constants[4];
#include "ffx_fsr1.h"
-#if USE_RCAS
- layout(location = 0) in vec2 frag_texcoord;
-#endif
+layout (location = 0) in vec2 frag_texcoord;
layout (location = 0) out vec4 frag_color;
void CurrFilter(AU2 pos)
@@ -78,22 +77,22 @@ void CurrFilter(AU2 pos)
#ifndef YUZU_USE_FP16
AF3 c;
FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
- frag_color = AF4(c, 1.0);
+ frag_color = AF4(c, texture(InputTexture, frag_texcoord).a);
#else
AH3 c;
FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
- frag_color = AH4(c, 1.0);
+ frag_color = AH4(c, texture(InputTexture, frag_texcoord).a);
#endif
#endif
#if USE_RCAS
#ifndef YUZU_USE_FP16
- AF3 c;
- FsrRcasF(c.r, c.g, c.b, pos, constants[0]);
- frag_color = AF4(c, 1.0);
+ AF4 c;
+ FsrRcasF(c.r, c.g, c.b, c.a, pos, constants[0]);
+ frag_color = c;
#else
AH3 c;
- FsrRcasH(c.r, c.g, c.b, pos, constants[0]);
- frag_color = AH4(c, 1.0);
+ FsrRcasH(c.r, c.g, c.b, c.a, pos, constants[0]);
+ frag_color = c;
#endif
#endif
}
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
index 5fd7ad297..096b4e4db 100644
--- a/src/video_core/host_shaders/opengl_present.frag
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -9,5 +9,5 @@ layout (location = 0) out vec4 color;
layout (binding = 0) uniform sampler2D color_texture;
void main() {
- color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
+ color = vec4(texture(color_texture, frag_tex_coord));
}
diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag
index c814629cf..a9d9d40a3 100644
--- a/src/video_core/host_shaders/present_bicubic.frag
+++ b/src/video_core/host_shaders/present_bicubic.frag
@@ -52,5 +52,5 @@ vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) {
}
void main() {
- color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f);
+ color = textureBicubic(color_texture, frag_tex_coord);
}
diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag
index ad9bb76a4..78edeb9b4 100644
--- a/src/video_core/host_shaders/present_gaussian.frag
+++ b/src/video_core/host_shaders/present_gaussian.frag
@@ -46,14 +46,14 @@ vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) {
}
void main() {
- vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0];
+ vec4 base = texture(color_texture, vec2(frag_tex_coord)) * weight[0];
vec2 tex_offset = 1.0f / textureSize(color_texture, 0);
// TODO(Blinkhawk): This code can be optimized through shader group instructions.
- vec3 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset).rgb;
- vec3 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb;
- vec3 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset).rgb;
- vec3 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb;
- vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f);
- color = vec4(combination + base, 1.0f);
+ vec4 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset);
+ vec4 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset);
+ vec4 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset);
+ vec4 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0));
+ vec4 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f);
+ color = combination + base;
}
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
index d369bef06..05d033310 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
@@ -6,5 +6,6 @@
#define YUZU_USE_FP16
#define USE_EASU 1
+#define VERSION 1
#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
index 6f25ef00f..7ae11dd66 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
@@ -5,5 +5,6 @@
#extension GL_GOOGLE_include_directive : enable
#define USE_EASU 1
+#define VERSION 1
#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
index 0c953a900..c017214a5 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
@@ -6,5 +6,6 @@
#define YUZU_USE_FP16
#define USE_RCAS 1
+#define VERSION 1
#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
index 02e9a27c6..976825f4b 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
@@ -5,5 +5,6 @@
#extension GL_GOOGLE_include_directive : enable
#define USE_RCAS 1
+#define VERSION 1
#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
index 79ea817c2..cea5dac9d 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
@@ -5,7 +5,7 @@
#extension GL_GOOGLE_include_directive : enable
-#define VERSION 1
+#define VERSION 2
#define YUZU_USE_FP16
#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
index 9605bb58b..10ddf0401 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
@@ -5,6 +5,6 @@
#extension GL_GOOGLE_include_directive : enable
-#define VERSION 1
+#define VERSION 2
#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 46e853e04..fb529f88b 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -92,12 +92,12 @@ public:
private:
void Fallback(const std::vector<u32>& parameters) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (extended) {
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
maxwell3d.replace_table.clear();
}
- });
+ };
maxwell3d.RefreshParameters();
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
@@ -281,12 +281,12 @@ public:
private:
void Fallback(const std::vector<u32>& parameters) {
- SCOPE_EXIT({
+ SCOPE_EXIT {
// Clean everything.
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
maxwell3d.replace_table.clear();
- });
+ };
maxwell3d.RefreshParameters();
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
diff --git a/src/video_core/present.h b/src/video_core/present.h
new file mode 100644
index 000000000..4fdfcca68
--- /dev/null
+++ b/src/video_core/present.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/settings.h"
+
+static inline Settings::ScalingFilter GetScalingFilter() {
+ return Settings::values.scaling_filter.GetValue();
+}
+
+static inline Settings::AntiAliasing GetAntiAliasing() {
+ return Settings::values.anti_aliasing.GetValue();
+}
+
+static inline Settings::ScalingFilter GetScalingFilterForAppletCapture() {
+ return Settings::ScalingFilter::Bilinear;
+}
+
+static inline Settings::AntiAliasing GetAntiAliasingForAppletCapture() {
+ return Settings::AntiAliasing::None;
+}
+
+struct PresentFilters {
+ Settings::ScalingFilter (*get_scaling_filter)();
+ Settings::AntiAliasing (*get_anti_aliasing)();
+};
+
+constexpr PresentFilters PresentFiltersForDisplay{
+ .get_scaling_filter = &GetScalingFilter,
+ .get_anti_aliasing = &GetAntiAliasing,
+};
+
+constexpr PresentFilters PresentFiltersForAppletCapture{
+ .get_scaling_filter = &GetScalingFilterForAppletCapture,
+ .get_anti_aliasing = &GetAntiAliasingForAppletCapture,
+};
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 3ad180f67..67427f937 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -40,6 +40,9 @@ public:
/// Finalize rendering the guest frame and draw into the presentation texture
virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0;
+ /// Get the tiled applet layer capture buffer
+ virtual std::vector<u8> GetAppletCaptureBuffer() = 0;
+
[[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
[[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp
index c89daff53..e6147d66c 100644
--- a/src/video_core/renderer_null/renderer_null.cpp
+++ b/src/video_core/renderer_null/renderer_null.cpp
@@ -3,6 +3,7 @@
#include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
+#include "video_core/capture.h"
#include "video_core/renderer_null/renderer_null.h"
namespace Null {
@@ -22,4 +23,8 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff
render_window.OnFrameDisplayed();
}
+std::vector<u8> RendererNull::GetAppletCaptureBuffer() {
+ return std::vector<u8>(VideoCore::Capture::TiledSize);
+}
+
} // namespace Null
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h
index 063b476bb..34dbe1e4f 100644
--- a/src/video_core/renderer_null/renderer_null.h
+++ b/src/video_core/renderer_null/renderer_null.h
@@ -19,6 +19,8 @@ public:
void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override;
+ std::vector<u8> GetAppletCaptureBuffer() override;
+
VideoCore::RasterizerInterface* ReadRasterizer() override {
return &m_rasterizer;
}
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp
index 6ba8b214b..9260a4dc4 100644
--- a/src/video_core/renderer_opengl/gl_blit_screen.cpp
+++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
+#include "video_core/present.h"
#include "video_core/renderer_opengl/gl_blit_screen.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/present/filters.h"
@@ -13,14 +14,14 @@ namespace OpenGL {
BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
Tegra::MaxwellDeviceMemoryManager& device_memory_,
StateTracker& state_tracker_, ProgramManager& program_manager_,
- Device& device_)
+ Device& device_, const PresentFilters& filters_)
: rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
- program_manager(program_manager_), device(device_) {}
+ program_manager(program_manager_), device(device_), filters(filters_) {}
BlitScreen::~BlitScreen() = default;
void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
- const Layout::FramebufferLayout& layout) {
+ const Layout::FramebufferLayout& layout, bool invert_y) {
// TODO: Signal state tracker about these changes
state_tracker.NotifyScreenDrawVertexArray();
state_tracker.NotifyPolygonModes();
@@ -56,22 +57,22 @@ void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffe
glDepthRangeIndexed(0, 0.0, 0.0);
while (layers.size() < framebuffers.size()) {
- layers.emplace_back(rasterizer, device_memory);
+ layers.emplace_back(rasterizer, device_memory, filters);
}
CreateWindowAdapt();
- window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout);
+ window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout, invert_y);
// TODO
// program_manager.RestoreGuestPipeline();
}
void BlitScreen::CreateWindowAdapt() {
- if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) {
+ if (window_adapt && filters.get_scaling_filter() == current_window_adapt) {
return;
}
- current_window_adapt = Settings::values.scaling_filter.GetValue();
+ current_window_adapt = filters.get_scaling_filter();
switch (current_window_adapt) {
case Settings::ScalingFilter::NearestNeighbor:
window_adapt = MakeNearestNeighbor(device);
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h
index 0c3d838f1..df2da9424 100644
--- a/src/video_core/renderer_opengl/gl_blit_screen.h
+++ b/src/video_core/renderer_opengl/gl_blit_screen.h
@@ -15,6 +15,8 @@ namespace Layout {
struct FramebufferLayout;
}
+struct PresentFilters;
+
namespace Tegra {
struct FramebufferConfig;
}
@@ -46,12 +48,12 @@ public:
explicit BlitScreen(RasterizerOpenGL& rasterizer,
Tegra::MaxwellDeviceMemoryManager& device_memory,
StateTracker& state_tracker, ProgramManager& program_manager,
- Device& device);
+ Device& device, const PresentFilters& filters);
~BlitScreen();
/// Draws the emulated screens to the emulator window.
void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
- const Layout::FramebufferLayout& layout);
+ const Layout::FramebufferLayout& layout, bool invert_y);
private:
void CreateWindowAdapt();
@@ -61,6 +63,7 @@ private:
StateTracker& state_tracker;
ProgramManager& program_manager;
Device& device;
+ const PresentFilters& filters;
Settings::ScalingFilter current_window_adapt{};
std::unique_ptr<WindowAdaptPass> window_adapt;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b42fb110c..16af8e6bd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -230,7 +230,9 @@ template <typename Func>
void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
MICROPROFILE_SCOPE(OpenGL_Drawing);
- SCOPE_EXIT({ gpu.TickWork(); });
+ SCOPE_EXIT {
+ gpu.TickWork();
+ };
gpu_memory->FlushCaching();
GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -355,7 +357,9 @@ void RasterizerOpenGL::DrawIndirect() {
void RasterizerOpenGL::DrawTexture() {
MICROPROFILE_SCOPE(OpenGL_Drawing);
- SCOPE_EXIT({ gpu.TickWork(); });
+ SCOPE_EXIT {
+ gpu.TickWork();
+ };
texture_cache.SynchronizeGraphicsDescriptors();
texture_cache.UpdateRenderTargets(false);
diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp
index 8643e07c6..6c7092d22 100644
--- a/src/video_core/renderer_opengl/present/layer.cpp
+++ b/src/video_core/renderer_opengl/present/layer.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/framebuffer_config.h"
+#include "video_core/present.h"
#include "video_core/renderer_opengl/gl_blit_screen.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/present/fsr.h"
@@ -14,8 +15,9 @@
namespace OpenGL {
-Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_)
- : rasterizer(rasterizer_), device_memory(device_memory_) {
+Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_,
+ const PresentFilters& filters_)
+ : rasterizer(rasterizer_), device_memory(device_memory_), filters(filters_) {
// Allocate textures for the screen
framebuffer_texture.resource.Create(GL_TEXTURE_2D);
@@ -34,12 +36,12 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
std::array<ScreenRectVertex, 4>& out_vertices,
ProgramManager& program_manager,
const Tegra::FramebufferConfig& framebuffer,
- const Layout::FramebufferLayout& layout) {
+ const Layout::FramebufferLayout& layout, bool invert_y) {
FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
GLuint texture = info.display_texture;
- auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
+ auto anti_aliasing = filters.get_anti_aliasing();
if (anti_aliasing != Settings::AntiAliasing::None) {
glEnablei(GL_SCISSOR_TEST, 0);
auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
@@ -64,7 +66,7 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
glDisablei(GL_SCISSOR_TEST, 0);
- if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
+ if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
if (!fsr || fsr->NeedsRecreation(layout.screen)) {
fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
}
@@ -83,10 +85,15 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
const auto w = screen.GetWidth();
const auto h = screen.GetHeight();
- out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
- out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
- out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
- out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
+ const auto left = crop.left;
+ const auto right = crop.right;
+ const auto top = invert_y ? crop.bottom : crop.top;
+ const auto bottom = invert_y ? crop.top : crop.bottom;
+
+ out_vertices[0] = ScreenRectVertex(x, y, left, top);
+ out_vertices[1] = ScreenRectVertex(x + w, y, right, top);
+ out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
+ out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
return texture;
}
@@ -131,10 +138,12 @@ FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig&
const u64 size_in_bytes{Tegra::Texture::CalculateSize(
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
- const std::span<const u8> input_data(host_ptr, size_in_bytes);
- Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
- framebuffer.width, framebuffer.height, 1, block_height_log2,
- 0);
+ if (host_ptr) {
+ const std::span<const u8> input_data(host_ptr, size_in_bytes);
+ Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
+ framebuffer.width, framebuffer.height, 1,
+ block_height_log2, 0);
+ }
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h
index ef1055abf..5b15b730f 100644
--- a/src/video_core/renderer_opengl/present/layer.h
+++ b/src/video_core/renderer_opengl/present/layer.h
@@ -13,6 +13,8 @@ namespace Layout {
struct FramebufferLayout;
}
+struct PresentFilters;
+
namespace Service::android {
enum class PixelFormat : u32;
};
@@ -44,14 +46,15 @@ struct ScreenRectVertex;
class Layer {
public:
- explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory);
+ explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory,
+ const PresentFilters& filters);
~Layer();
GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
std::array<ScreenRectVertex, 4>& out_vertices,
ProgramManager& program_manager,
const Tegra::FramebufferConfig& framebuffer,
- const Layout::FramebufferLayout& layout);
+ const Layout::FramebufferLayout& layout, bool invert_y);
private:
/// Loads framebuffer from emulated memory into the active OpenGL texture.
@@ -65,6 +68,7 @@ private:
private:
RasterizerOpenGL& rasterizer;
Tegra::MaxwellDeviceMemoryManager& device_memory;
+ const PresentFilters& filters;
/// OpenGL framebuffer data
std::vector<u8> gl_framebuffer_data;
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
index 4d681606b..d8b6a11cb 100644
--- a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
@@ -37,7 +37,7 @@ WindowAdaptPass::~WindowAdaptPass() = default;
void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
std::span<const Tegra::FramebufferConfig> framebuffers,
- const Layout::FramebufferLayout& layout) {
+ const Layout::FramebufferLayout& layout, bool invert_y) {
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
@@ -51,7 +51,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
auto layer_it = layers.begin();
for (size_t i = 0; i < layer_count; i++) {
textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
- framebuffers[i], layout);
+ framebuffers[i], layout, invert_y);
layer_it++;
}
@@ -92,6 +92,21 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
glClear(GL_COLOR_BUFFER_BIT);
for (size_t i = 0; i < layer_count; i++) {
+ switch (framebuffers[i].blending) {
+ case Tegra::BlendMode::Opaque:
+ default:
+ glDisablei(GL_BLEND, 0);
+ break;
+ case Tegra::BlendMode::Premultiplied:
+ glEnablei(GL_BLEND, 0);
+ glBlendFuncSeparatei(0, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
+ break;
+ case Tegra::BlendMode::Coverage:
+ glEnablei(GL_BLEND, 0);
+ glBlendFuncSeparatei(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
+ break;
+ }
+
glBindTextureUnit(0, textures[i]);
glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
matrices[i].data());
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h
index 00975a9c6..0a8bcef2f 100644
--- a/src/video_core/renderer_opengl/present/window_adapt_pass.h
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h
@@ -31,7 +31,7 @@ public:
void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
std::span<const Tegra::FramebufferConfig> framebuffers,
- const Layout::FramebufferLayout& layout);
+ const Layout::FramebufferLayout& layout, bool invert_y);
private:
const Device& device;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index e33a32592..5fb54635d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -16,6 +16,8 @@
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/telemetry_session.h"
+#include "video_core/capture.h"
+#include "video_core/present.h"
#include "video_core/renderer_opengl/gl_blit_screen.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -120,7 +122,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
}
blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
- program_manager, device);
+ program_manager, device, PresentFiltersForDisplay);
+ blit_applet =
+ std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, program_manager,
+ device, PresentFiltersForAppletCapture);
+ capture_framebuffer.Create();
+ capture_renderbuffer.Create();
+ glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth,
+ VideoCore::Capture::LinearHeight);
}
RendererOpenGL::~RendererOpenGL() = default;
@@ -130,10 +140,11 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu
return;
}
+ RenderAppletCaptureLayer(framebuffers);
RenderScreenshot(framebuffers);
state_tracker.BindFramebuffer(0);
- blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout());
+ blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false);
++m_current_frame;
@@ -159,11 +170,8 @@ void RendererOpenGL::AddTelemetryFields() {
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
}
-void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
- if (!renderer_settings.screenshot_requested) {
- return;
- }
-
+void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
+ const Layout::FramebufferLayout& layout, void* dst) {
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
@@ -173,29 +181,86 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
screenshot_framebuffer.Create();
glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
- const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
-
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
- blit_screen->DrawScreen(framebuffers, layout);
+ blit_screen->DrawScreen(framebuffers, layout, false);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
- glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
- renderer_settings.screenshot_bits);
+ glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst);
screenshot_framebuffer.Release();
glDeleteRenderbuffers(1, &renderbuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
+}
+
+void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
+ if (!renderer_settings.screenshot_requested) {
+ return;
+ }
+
+ RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout,
+ renderer_settings.screenshot_bits);
renderer_settings.screenshot_complete_callback(true);
renderer_settings.screenshot_requested = false;
}
+void RendererOpenGL::RenderAppletCaptureLayer(
+ std::span<const Tegra::FramebufferConfig> framebuffers) {
+ GLint old_read_fb;
+ GLint old_draw_fb;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+ capture_renderbuffer.handle);
+
+ blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
+}
+
+std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() {
+ using namespace VideoCore::Capture;
+
+ std::vector<u8> linear(TiledSize);
+ std::vector<u8> out(TiledSize);
+
+ GLint old_read_fb;
+ GLint old_draw_fb;
+ GLint old_pixel_pack_buffer;
+ GLint old_pack_row_length;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
+ glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer);
+ glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+ capture_renderbuffer.handle);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ linear.data());
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer);
+ glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length);
+
+ Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight,
+ LinearDepth, BlockHeight, BlockDepth);
+
+ return out;
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index c4625c96e..60d6a1477 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -42,6 +42,8 @@ public:
void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
+ std::vector<u8> GetAppletCaptureBuffer() override;
+
VideoCore::RasterizerInterface* ReadRasterizer() override {
return &rasterizer;
}
@@ -52,7 +54,11 @@ public:
private:
void AddTelemetryFields();
+
+ void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
+ const Layout::FramebufferLayout& layout, void* dst);
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
+ void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
Core::TelemetrySession& telemetry_session;
Core::Frontend::EmuWindow& emu_window;
@@ -64,8 +70,11 @@ private:
ProgramManager program_manager;
RasterizerOpenGL rasterizer;
OGLFramebuffer screenshot_framebuffer;
+ OGLFramebuffer capture_framebuffer;
+ OGLRenderbuffer capture_renderbuffer;
std::unique_ptr<BlitScreen> blit_screen;
+ std::unique_ptr<BlitScreen> blit_applet;
};
} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp
index cfc04be44..4e41afe5b 100644
--- a/src/video_core/renderer_vulkan/present/layer.cpp
+++ b/src/video_core/renderer_vulkan/present/layer.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "video_core/present.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "common/settings.h"
@@ -48,12 +49,12 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_,
- VkExtent2D output_size, VkDescriptorSetLayout layout)
+ VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_)
: device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_),
- device_memory(device_memory_), image_count(image_count_) {
+ device_memory(device_memory_), filters(filters_), image_count(image_count_) {
CreateDescriptorPool();
CreateDescriptorSets(layout);
- if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
+ if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
CreateFSR(output_size);
}
}
@@ -81,7 +82,9 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants,
// Finish any pending renderpass
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Wait(resource_ticks[image_index]);
- SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); });
+ SCOPE_EXIT {
+ resource_ticks[image_index] = scheduler.CurrentTick();
+ };
if (!use_accelerated) {
UpdateRawImage(framebuffer, image_index);
@@ -171,11 +174,11 @@ void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
}
void Layer::SetAntiAliasPass() {
- if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) {
+ if (anti_alias && anti_alias_setting == filters.get_anti_aliasing()) {
return;
}
- anti_alias_setting = Settings::values.anti_aliasing.GetValue();
+ anti_alias_setting = filters.get_anti_aliasing();
const VkExtent2D render_area{
.width = Settings::values.resolution_info.ScaleUp(raw_width),
@@ -270,9 +273,11 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
const u64 linear_size{GetSizeInBytes(framebuffer)};
const u64 tiled_size{Tegra::Texture::CalculateSize(
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
- Tegra::Texture::UnswizzleTexture(
- mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
- bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
+ if (host_ptr) {
+ Tegra::Texture::UnswizzleTexture(
+ mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
+ bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
+ }
const VkBufferImageCopy copy{
.bufferOffset = image_offset,
diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h
index 88d43fc5f..f5effdcd7 100644
--- a/src/video_core/renderer_vulkan/present/layer.h
+++ b/src/video_core/renderer_vulkan/present/layer.h
@@ -11,6 +11,8 @@ namespace Layout {
struct FramebufferLayout;
}
+struct PresentFilters;
+
namespace Tegra {
struct FramebufferConfig;
}
@@ -37,7 +39,8 @@ class Layer final {
public:
explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count,
- VkExtent2D output_size, VkDescriptorSetLayout layout);
+ VkExtent2D output_size, VkDescriptorSetLayout layout,
+ const PresentFilters& filters);
~Layer();
void ConfigureDraw(PresentPushConstants* out_push_constants,
@@ -71,6 +74,7 @@ private:
MemoryAllocator& memory_allocator;
Scheduler& scheduler;
Tegra::MaxwellDeviceMemoryManager& device_memory;
+ const PresentFilters& filters;
const size_t image_count{};
vk::DescriptorPool descriptor_pool{};
vk::DescriptorSets descriptor_sets{};
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp
index 6ee16595d..7f27c7c1b 100644
--- a/src/video_core/renderer_vulkan/present/util.cpp
+++ b/src/video_core/renderer_vulkan/present/util.cpp
@@ -362,10 +362,10 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
});
}
-vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
- vk::PipelineLayout& layout,
- std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
- bool enable_blending) {
+static vk::Pipeline CreateWrappedPipelineImpl(
+ const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
+ VkPipelineColorBlendAttachmentState blending) {
const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@@ -443,30 +443,6 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
.alphaToOneEnable = VK_FALSE,
};
- constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
- .blendEnable = VK_FALSE,
- .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
- .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
- .colorBlendOp = VK_BLEND_OP_ADD,
- .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
- .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
- .alphaBlendOp = VK_BLEND_OP_ADD,
- .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
- VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
- };
-
- constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{
- .blendEnable = VK_TRUE,
- .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
- .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
- .colorBlendOp = VK_BLEND_OP_ADD,
- .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
- .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
- .alphaBlendOp = VK_BLEND_OP_ADD,
- .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
- VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
- };
-
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -474,8 +450,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
- .pAttachments =
- enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled,
+ .pAttachments = &blending,
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
@@ -515,6 +490,63 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
});
}
+vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
+ vk::PipelineLayout& layout,
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
+ constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
+ .blendEnable = VK_FALSE,
+ .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
+ .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
+ .colorBlendOp = VK_BLEND_OP_ADD,
+ .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
+ .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
+ .alphaBlendOp = VK_BLEND_OP_ADD,
+ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
+ };
+
+ return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
+ color_blend_attachment_disabled);
+}
+
+vk::Pipeline CreateWrappedPremultipliedBlendingPipeline(
+ const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
+ constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_premultiplied{
+ .blendEnable = VK_TRUE,
+ .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
+ .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .colorBlendOp = VK_BLEND_OP_ADD,
+ .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
+ .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
+ .alphaBlendOp = VK_BLEND_OP_ADD,
+ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
+ };
+
+ return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
+ color_blend_attachment_premultiplied);
+}
+
+vk::Pipeline CreateWrappedCoverageBlendingPipeline(
+ const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
+ constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_coverage{
+ .blendEnable = VK_TRUE,
+ .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
+ .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .colorBlendOp = VK_BLEND_OP_ADD,
+ .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
+ .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
+ .alphaBlendOp = VK_BLEND_OP_ADD,
+ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
+ };
+
+ return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
+ color_blend_attachment_coverage);
+}
+
VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
VkSampler sampler, VkImageView view,
VkDescriptorSet set, u32 binding) {
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h
index 1104aaa15..5b22f0fa8 100644
--- a/src/video_core/renderer_vulkan/present/util.h
+++ b/src/video_core/renderer_vulkan/present/util.h
@@ -42,8 +42,13 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
vk::DescriptorSetLayout& layout);
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
vk::PipelineLayout& layout,
- std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
- bool enable_blending = false);
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
+vk::Pipeline CreateWrappedPremultipliedBlendingPipeline(
+ const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
+vk::Pipeline CreateWrappedCoverageBlendingPipeline(
+ const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
+ std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
VkSampler sampler, VkImageView view,
VkDescriptorSet set, u32 binding);
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
index c5db0230d..22ffacf11 100644
--- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
@@ -22,7 +22,7 @@ WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format,
CreatePipelineLayout();
CreateVertexShader();
CreateRenderPass(frame_format);
- CreatePipeline();
+ CreatePipelines();
}
WindowAdaptPass::~WindowAdaptPass() = default;
@@ -34,7 +34,6 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
const VkFramebuffer host_framebuffer{*dst->framebuffer};
const VkRenderPass renderpass{*render_pass};
- const VkPipeline graphics_pipeline{*pipeline};
const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
const VkExtent2D render_area{
.width = dst->width,
@@ -44,9 +43,23 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
const size_t layer_count = configs.size();
std::vector<PresentPushConstants> push_constants(layer_count);
std::vector<VkDescriptorSet> descriptor_sets(layer_count);
+ std::vector<VkPipeline> graphics_pipelines(layer_count);
auto layer_it = layers.begin();
for (size_t i = 0; i < layer_count; i++) {
+ switch (configs[i].blending) {
+ case Tegra::BlendMode::Opaque:
+ default:
+ graphics_pipelines[i] = *opaque_pipeline;
+ break;
+ case Tegra::BlendMode::Premultiplied:
+ graphics_pipelines[i] = *premultiplied_pipeline;
+ break;
+ case Tegra::BlendMode::Coverage:
+ graphics_pipelines[i] = *coverage_pipeline;
+ break;
+ }
+
layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler,
image_index, configs[i], layout);
layer_it++;
@@ -77,8 +90,8 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
for (size_t i = 0; i < layer_count; i++) {
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]);
cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
push_constants[i]);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
@@ -129,9 +142,13 @@ void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) {
render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED);
}
-void WindowAdaptPass::CreatePipeline() {
- pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
- std::tie(vertex_shader, fragment_shader), false);
+void WindowAdaptPass::CreatePipelines() {
+ opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
+ std::tie(vertex_shader, fragment_shader));
+ premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline(
+ device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader));
+ coverage_pipeline = CreateWrappedCoverageBlendingPipeline(
+ device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader));
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h
index 0e2edfc31..cf667a4fc 100644
--- a/src/video_core/renderer_vulkan/present/window_adapt_pass.h
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h
@@ -42,7 +42,7 @@ private:
void CreatePipelineLayout();
void CreateVertexShader();
void CreateRenderPass(VkFormat frame_format);
- void CreatePipeline();
+ void CreatePipelines();
private:
const Device& device;
@@ -52,7 +52,9 @@ private:
vk::ShaderModule vertex_shader;
vk::ShaderModule fragment_shader;
vk::RenderPass render_pass;
- vk::Pipeline pipeline;
+ vk::Pipeline opaque_pipeline;
+ vk::Pipeline premultiplied_pipeline;
+ vk::Pipeline coverage_pipeline;
};
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 48a105327..c553f5b3d 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -19,7 +19,9 @@
#include "core/core_timing.h"
#include "core/frontend/graphics_context.h"
#include "core/telemetry_session.h"
+#include "video_core/capture.h"
#include "video_core/gpu.h"
+#include "video_core/present.h"
#include "video_core/renderer_vulkan/present/util.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
@@ -38,6 +40,20 @@
namespace Vulkan {
namespace {
+
+constexpr VkExtent2D CaptureImageSize{
+ .width = VideoCore::Capture::LinearWidth,
+ .height = VideoCore::Capture::LinearHeight,
+};
+
+constexpr VkExtent3D CaptureImageExtent{
+ .width = VideoCore::Capture::LinearWidth,
+ .height = VideoCore::Capture::LinearHeight,
+ .depth = VideoCore::Capture::LinearDepth,
+};
+
+constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
+
std::string GetReadableVersion(u32 version) {
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
VK_VERSION_PATCH(version));
@@ -99,10 +115,15 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
render_window.GetFramebufferLayout().height),
present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
surface),
- blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler),
- blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler),
+ blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler,
+ PresentFiltersForDisplay),
+ blit_capture(device_memory, device, memory_allocator, present_manager, scheduler,
+ PresentFiltersForDisplay),
+ blit_applet(device_memory, device, memory_allocator, present_manager, scheduler,
+ PresentFiltersForAppletCapture),
rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
- scheduler) {
+ scheduler),
+ applet_frame() {
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
turbo_mode.emplace(instance, dld);
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
@@ -123,7 +144,11 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
return;
}
- SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
+ SCOPE_EXIT {
+ render_window.OnFrameDisplayed();
+ };
+
+ RenderAppletCaptureLayer(framebuffers);
if (!render_window.IsShown()) {
return;
@@ -167,30 +192,20 @@ void RendererVulkan::Report() const {
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
}
-void Vulkan::RendererVulkan::RenderScreenshot(
- std::span<const Tegra::FramebufferConfig> framebuffers) {
- if (!renderer_settings.screenshot_requested) {
- return;
- }
-
- constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM};
- const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
-
+vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
+ const Layout::FramebufferLayout& layout, VkFormat format,
+ VkDeviceSize buffer_size) {
auto frame = [&]() {
Frame f{};
- f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height},
- ScreenshotFormat);
- f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat);
- f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat);
+ f.image =
+ CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, format);
+ f.image_view = CreateWrappedImageView(device, f.image, format);
+ f.framebuffer = blit_capture.CreateFramebuffer(layout, *f.image_view, format);
return f;
}();
- blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1,
- VK_FORMAT_B8G8R8A8_UNORM);
-
- const auto dst_buffer = CreateWrappedBuffer(
- memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4),
- MemoryUsage::Download);
+ auto dst_buffer = CreateWrappedBuffer(memory_allocator, buffer_size, MemoryUsage::Download);
+ blit_capture.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, format);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
@@ -198,15 +213,68 @@ void Vulkan::RendererVulkan::RenderScreenshot(
VkExtent3D{layout.width, layout.height, 1});
});
- // Ensure the copy is fully completed before saving the screenshot
+ // Ensure the copy is fully completed before saving the capture
scheduler.Finish();
- // Copy backing image data to the QImage screenshot buffer
+ // Copy backing image data to the capture buffer
dst_buffer.Invalidate();
+ return dst_buffer;
+}
+
+void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
+ if (!renderer_settings.screenshot_requested) {
+ return;
+ }
+
+ const auto& layout{renderer_settings.screenshot_framebuffer_layout};
+ const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM,
+ layout.width * layout.height * 4);
+
std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
dst_buffer.Mapped().size());
renderer_settings.screenshot_complete_callback(false);
renderer_settings.screenshot_requested = false;
}
+std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() {
+ using namespace VideoCore::Capture;
+
+ std::vector<u8> out(VideoCore::Capture::TiledSize);
+
+ if (!applet_frame.image) {
+ return out;
+ }
+
+ const auto dst_buffer =
+ CreateWrappedBuffer(memory_allocator, VideoCore::Capture::TiledSize, MemoryUsage::Download);
+
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([&](vk::CommandBuffer cmdbuf) {
+ DownloadColorImage(cmdbuf, *applet_frame.image, *dst_buffer, CaptureImageExtent);
+ });
+
+ // Ensure the copy is fully completed before writing the capture
+ scheduler.Finish();
+
+ // Swizzle image data to the capture buffer
+ dst_buffer.Invalidate();
+ Tegra::Texture::SwizzleTexture(out, dst_buffer.Mapped(), BytesPerPixel, LinearWidth,
+ LinearHeight, LinearDepth, BlockHeight, BlockDepth);
+
+ return out;
+}
+
+void RendererVulkan::RenderAppletCaptureLayer(
+ std::span<const Tegra::FramebufferConfig> framebuffers) {
+ if (!applet_frame.image) {
+ applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat);
+ applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat);
+ applet_frame.framebuffer = blit_applet.CreateFramebuffer(
+ VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat);
+ }
+
+ blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1,
+ CaptureFormat);
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index c6d8a0f21..fb9d83412 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -48,6 +48,8 @@ public:
void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
+ std::vector<u8> GetAppletCaptureBuffer() override;
+
VideoCore::RasterizerInterface* ReadRasterizer() override {
return &rasterizer;
}
@@ -59,7 +61,11 @@ public:
private:
void Report() const;
+ vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
+ const Layout::FramebufferLayout& layout, VkFormat format,
+ VkDeviceSize buffer_size);
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
+ void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
Core::TelemetrySession& telemetry_session;
Tegra::MaxwellDeviceMemoryManager& device_memory;
@@ -79,9 +85,12 @@ private:
Swapchain swapchain;
PresentManager present_manager;
BlitScreen blit_swapchain;
- BlitScreen blit_screenshot;
+ BlitScreen blit_capture;
+ BlitScreen blit_applet;
RasterizerVulkan rasterizer;
std::optional<TurboMode> turbo_mode;
+
+ Frame applet_frame;
};
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 2275fcc46..b7797f833 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/framebuffer_config.h"
+#include "video_core/present.h"
#include "video_core/renderer_vulkan/present/filters.h"
#include "video_core/renderer_vulkan/present/layer.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
@@ -12,9 +13,9 @@ namespace Vulkan {
BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
- Scheduler& scheduler_)
+ Scheduler& scheduler_, const PresentFilters& filters_)
: device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
- present_manager{present_manager_}, scheduler{scheduler_}, image_count{1},
+ present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, image_count{1},
swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
BlitScreen::~BlitScreen() = default;
@@ -27,7 +28,7 @@ void BlitScreen::WaitIdle() {
void BlitScreen::SetWindowAdaptPass() {
layers.clear();
- scaling_filter = Settings::values.scaling_filter.GetValue();
+ scaling_filter = filters.get_scaling_filter();
switch (scaling_filter) {
case Settings::ScalingFilter::NearestNeighbor:
@@ -59,7 +60,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
bool presentation_recreate_required = false;
// Recreate dynamic resources if the adapting filter changed
- if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) {
+ if (!window_adapt || scaling_filter != filters.get_scaling_filter()) {
resource_update_required = true;
}
@@ -102,7 +103,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
while (layers.size() < framebuffers.size()) {
layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
- window_size, window_adapt->GetDescriptorSetLayout());
+ window_size, window_adapt->GetDescriptorSetLayout(), filters);
}
// Perform the draw
@@ -119,8 +120,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l
VkFormat current_view_format) {
const bool format_updated =
std::exchange(swapchain_view_format, current_view_format) != current_view_format;
- if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() ||
- format_updated) {
+ if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) {
WaitIdle();
SetWindowAdaptPass();
}
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index cbdf2d5d0..531c57fc5 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -16,6 +16,8 @@ namespace Core {
class System;
}
+struct PresentFilters;
+
namespace Tegra {
struct FramebufferConfig;
}
@@ -47,7 +49,7 @@ class BlitScreen {
public:
explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
MemoryAllocator& memory_allocator, PresentManager& present_manager,
- Scheduler& scheduler);
+ Scheduler& scheduler, const PresentFilters& filters);
~BlitScreen();
void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
@@ -70,6 +72,7 @@ private:
MemoryAllocator& memory_allocator;
PresentManager& present_manager;
Scheduler& scheduler;
+ const PresentFilters& filters;
std::size_t image_count{};
std::size_t image_index{};
VkFormat swapchain_view_format{};
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index aa0a027bb..74f9f099e 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -196,7 +196,9 @@ template <typename Func>
void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
MICROPROFILE_SCOPE(Vulkan_Drawing);
- SCOPE_EXIT({ gpu.TickWork(); });
+ SCOPE_EXIT {
+ gpu.TickWork();
+ };
FlushWork();
gpu_memory->FlushCaching();
@@ -288,7 +290,9 @@ void RasterizerVulkan::DrawIndirect() {
void RasterizerVulkan::DrawTexture() {
MICROPROFILE_SCOPE(Vulkan_Drawing);
- SCOPE_EXIT({ gpu.TickWork(); });
+ SCOPE_EXIT {
+ gpu.TickWork();
+ };
FlushWork();
query_cache.NotifySegment(true);
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index b72788c6d..9444becce 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -42,6 +42,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
};
}
rescaleable = false;
+ is_sparse = config.is_sparse != 0;
tile_width_spacing = config.tile_width_spacing;
if (config.texture_type != TextureType::Texture2D &&
config.texture_type != TextureType::Texture2DNoMipmap) {
diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h
index 8a4cb0cbd..eb490a642 100644
--- a/src/video_core/texture_cache/image_info.h
+++ b/src/video_core/texture_cache/image_info.h
@@ -41,6 +41,7 @@ struct ImageInfo {
bool downscaleable = false;
bool forced_flushed = false;
bool dma_downloaded = false;
+ bool is_sparse = false;
};
} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index ca0794214..53b4876f2 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -600,17 +600,17 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
[&](ImageId id, Image&) { deleted_images.push_back(id); });
for (const ImageId id : deleted_images) {
Image& image = slot_images[id];
- if (True(image.flags & ImageFlagBits::CpuModified)) {
- continue;
+ if (False(image.flags & ImageFlagBits::CpuModified)) {
+ image.flags |= ImageFlagBits::CpuModified;
+ if (True(image.flags & ImageFlagBits::Tracked)) {
+ UntrackImage(image, id);
+ }
}
- image.flags |= ImageFlagBits::CpuModified;
+
if (True(image.flags & ImageFlagBits::Remapped)) {
continue;
}
image.flags |= ImageFlagBits::Remapped;
- if (True(image.flags & ImageFlagBits::Tracked)) {
- UntrackImage(image, id);
- }
}
}
@@ -746,7 +746,13 @@ std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImage
}();
const auto GetImageViewForFramebuffer = [&](ImageId image_id) {
- const ImageViewInfo info{ImageViewType::e2D, view_format};
+ ImageViewInfo info{ImageViewType::e2D, view_format};
+ if (config.blending == Tegra::BlendMode::Opaque) {
+ info.x_source = static_cast<u8>(SwizzleSource::R);
+ info.y_source = static_cast<u8>(SwizzleSource::G);
+ info.z_source = static_cast<u8>(SwizzleSource::B);
+ info.w_source = static_cast<u8>(SwizzleSource::OneFloat);
+ }
return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)],
slot_images[image_id].IsRescaled());
};
@@ -1463,7 +1469,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA
const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
Image& new_image = slot_images[new_image_id];
- if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes)) {
+ if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes) &&
+ new_info.is_sparse) {
new_image.flags |= ImageFlagBits::Sparse;
}
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index 5fa0d9620..f41c3e506 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -116,7 +116,9 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
LOG_ERROR(Render_Vulkan, "Failed to create decoder");
return;
}
- SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); });
+ SCOPE_EXIT {
+ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder);
+ };
u32 json_size = 0;
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 76f06da12..0259a8c29 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -41,6 +41,9 @@ add_executable(yuzu
configuration/configuration_shared.cpp
configuration/configuration_shared.h
configuration/configure.ui
+ configuration/configure_applets.cpp
+ configuration/configure_applets.h
+ configuration/configure_applets.ui
configuration/configure_audio.cpp
configuration/configure_audio.h
configuration/configure_audio.ui
diff --git a/src/yuzu/configuration/configure_applets.cpp b/src/yuzu/configuration/configure_applets.cpp
new file mode 100644
index 000000000..513ecb548
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.cpp
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/core.h"
+#include "ui_configure_applets.h"
+#include "yuzu/configuration/configuration_shared.h"
+#include "yuzu/configuration/configure_applets.h"
+#include "yuzu/configuration/shared_widget.h"
+
+ConfigureApplets::ConfigureApplets(Core::System& system_,
+ std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
+ const ConfigurationShared::Builder& builder, QWidget* parent)
+ : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureApplets>()}, system{system_} {
+ ui->setupUi(this);
+
+ Setup(builder);
+
+ SetConfiguration();
+}
+
+ConfigureApplets::~ConfigureApplets() = default;
+
+void ConfigureApplets::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void ConfigureApplets::RetranslateUI() {
+ ui->retranslateUi(this);
+}
+
+void ConfigureApplets::Setup(const ConfigurationShared::Builder& builder) {
+ auto& library_applets_layout = *ui->group_library_applet_modes->layout();
+ std::map<u32, QWidget*> applets_hold{};
+
+ std::vector<Settings::BasicSetting*> settings;
+ auto push = [&settings](auto& list) {
+ for (auto setting : list) {
+ settings.push_back(setting);
+ }
+ };
+
+ push(Settings::values.linkage.by_category[Settings::Category::LibraryApplet]);
+
+ for (auto setting : settings) {
+ ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
+
+ if (widget == nullptr) {
+ continue;
+ }
+ if (!widget->Valid()) {
+ widget->deleteLater();
+ continue;
+ }
+
+ // Untested applets
+ if (setting->Id() == Settings::values.data_erase_applet_mode.Id() ||
+ setting->Id() == Settings::values.error_applet_mode.Id() ||
+ setting->Id() == Settings::values.net_connect_applet_mode.Id() ||
+ setting->Id() == Settings::values.web_applet_mode.Id() ||
+ setting->Id() == Settings::values.shop_applet_mode.Id() ||
+ setting->Id() == Settings::values.login_share_applet_mode.Id() ||
+ setting->Id() == Settings::values.wifi_web_auth_applet_mode.Id() ||
+ setting->Id() == Settings::values.my_page_applet_mode.Id()) {
+ widget->setHidden(true);
+ }
+
+ applets_hold.emplace(setting->Id(), widget);
+ }
+ for (const auto& [label, widget] : applets_hold) {
+ library_applets_layout.addWidget(widget);
+ }
+}
+
+void ConfigureApplets::SetConfiguration() {}
+
+void ConfigureApplets::ApplyConfiguration() {
+ const bool powered_on = system.IsPoweredOn();
+ for (const auto& func : apply_funcs) {
+ func(powered_on);
+ }
+}
diff --git a/src/yuzu/configuration/configure_applets.h b/src/yuzu/configuration/configure_applets.h
new file mode 100644
index 000000000..54f494d2f
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.h
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QWidget>
+#include "yuzu/configuration/configuration_shared.h"
+
+class QCheckBox;
+class QLineEdit;
+class QComboBox;
+class QDateTimeEdit;
+namespace Core {
+class System;
+}
+
+namespace Ui {
+class ConfigureApplets;
+}
+
+namespace ConfigurationShared {
+class Builder;
+}
+
+class ConfigureApplets : public ConfigurationShared::Tab {
+public:
+ explicit ConfigureApplets(Core::System& system_,
+ std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
+ const ConfigurationShared::Builder& builder,
+ QWidget* parent = nullptr);
+ ~ConfigureApplets() override;
+
+ void ApplyConfiguration() override;
+ void SetConfiguration() override;
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ void Setup(const ConfigurationShared::Builder& builder);
+
+ std::vector<std::function<void(bool)>> apply_funcs{};
+
+ std::unique_ptr<Ui::ConfigureApplets> ui;
+ bool enabled = false;
+
+ Core::System& system;
+};
diff --git a/src/yuzu/configuration/configure_applets.ui b/src/yuzu/configuration/configure_applets.ui
new file mode 100644
index 000000000..6f2ca66bd
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureApplets</class>
+ <widget class="QWidget" name="ConfigureApplets">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>605</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <property name="accessibleName">
+ <string>Applets</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_1">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="group_library_applet_modes">
+ <property name="title">
+ <string>Applet mode preference</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QWidget" name="applets_widget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index aab54a1cc..37f23388e 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,6 +8,7 @@
#include "core/core.h"
#include "ui_configure.h"
#include "vk_device_info.h"
+#include "yuzu/configuration/configure_applets.h"
#include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/configure_cpu.h"
#include "yuzu/configuration/configure_debug_tab.h"
@@ -34,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
this, !system_.IsPoweredOn())},
+ applets_tab{std::make_unique<ConfigureApplets>(system_, nullptr, *builder, this)},
audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
@@ -58,6 +60,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
ui->setupUi(this);
+ ui->tabWidget->addTab(applets_tab.get(), tr("Applets"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug"));
@@ -124,6 +127,7 @@ void ConfigureDialog::ApplyConfiguration() {
debug_tab_tab->ApplyConfiguration();
web_tab->ApplyConfiguration();
network_tab->ApplyConfiguration();
+ applets_tab->ApplyConfiguration();
system.ApplySettings();
Settings::LogSettings();
}
@@ -161,7 +165,8 @@ void ConfigureDialog::PopulateSelectionList() {
{{tr("General"),
{general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}},
{tr("System"),
- {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get()}},
+ {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get(),
+ applets_tab.get()}},
{tr("CPU"), {cpu_tab.get()}},
{tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}},
{tr("Audio"), {audio_tab.get()}},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index b28ce288c..d0a24a07b 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -15,6 +15,7 @@ namespace Core {
class System;
}
+class ConfigureApplets;
class ConfigureAudio;
class ConfigureCpu;
class ConfigureDebugTab;
@@ -75,6 +76,7 @@ private:
std::unique_ptr<ConfigurationShared::Builder> builder;
std::vector<ConfigurationShared::Tab*> tab_group;
+ std::unique_ptr<ConfigureApplets> applets_tab;
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCpu> cpu_tab;
std::unique_ptr<ConfigureDebugTab> debug_tab_tab;
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index e28df10bd..28c3baf08 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -8,10 +8,7 @@
#include "common/settings_enums.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/applet_message_queue.h"
-#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index 1051031f2..37951b9c8 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -90,6 +90,7 @@ void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true)[player_index];
+ player.profile_name = "";
return;
}
}
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index ed9c7d859..d138b53c8 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -26,6 +26,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// A setting can be ignored by giving it a blank name
+ // Applets
+ INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral());
+ INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral());
+ INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral());
+ INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral());
+ INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral());
+ INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral());
+ INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral());
+ INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral());
+ INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral());
+ INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral());
+ INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral());
+ INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral());
+ INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral());
+ INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral());
+ INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral());
+
// Audio
INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
@@ -37,13 +54,28 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
QStringLiteral());
// Core
- INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral());
- INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral());
+ INSERT(
+ Settings, use_multi_core, tr("Multicore CPU Emulation"),
+ tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n"
+ "This is mainly a debug option and shouldn’t be disabled."));
+ INSERT(
+ Settings, memory_layout_mode, tr("Memory Layout"),
+ tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the "
+ "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended "
+ "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory "
+ "use. It is not recommended to enable unless a specific game with a texture mod needs "
+ "it."));
INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
- INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral());
+ INSERT(Settings, speed_limit, tr("Limit Speed Percent"),
+ tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs "
+ "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a "
+ "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the "
+ "maximum your PC can reach."));
// Cpu
- INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral());
+ INSERT(Settings, cpu_accuracy, tr("Accuracy:"),
+ tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless "
+ "you know what you are doing."));
INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());
// Cpu Debug
@@ -63,34 +95,75 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes."));
INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
- tr("This option improves speed by removing NaN checking. Please note this also reduces "
+ tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "
"accuracy of certain floating-point instructions."));
INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
tr("This option improves speed by eliminating a safety check before every memory "
- "read/write "
- "in guest. Disabling it may allow a game to read/write the emulator's memory."));
+ "read/write in guest.\nDisabling it may allow a game to read/write the emulator's "
+ "memory."));
INSERT(
Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
- "safety of exclusive access instructions. Please note this may result in deadlocks and "
+ "safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
"other race conditions."));
// Renderer
- INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral());
- INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral());
- INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral());
- INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral());
+ INSERT(
+ Settings, renderer_backend, tr("API:"),
+ tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases."));
+ INSERT(Settings, vulkan_device, tr("Device:"),
+ tr("This setting selects the GPU to use with the Vulkan backend."));
+ INSERT(Settings, shader_backend, tr("Shader Backend:"),
+ tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in "
+ "performance and the best in rendering accuracy.\n"
+ "GLASM is a deprecated NVIDIA-only backend that offers much better shader building "
+ "performance at the cost of FPS and rendering accuracy.\n"
+ "SPIR-V compiles the fastest, but yields poor results on most GPU drivers."));
+ INSERT(Settings, resolution_setup, tr("Resolution:"),
+ tr("Forces the game to render at a different resolution.\nHigher resolutions require "
+ "much more VRAM and bandwidth.\n"
+ "Options lower than 1X can cause rendering issues."));
INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
- INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral());
- INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral());
- INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral());
- INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral());
- INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral());
- INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
- QStringLiteral());
- INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral());
- INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral());
- INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral());
+ INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"),
+ tr("Determines how sharpened the image will look while using FSR’s dynamic contrast."));
+ INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
+ tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a "
+ "lower performance impact and can produce a better and more stable picture under "
+ "very low resolutions."));
+ INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
+ tr("The method used to render the window in fullscreen.\nBorderless offers the best "
+ "compatibility with the on-screen keyboard that some games request for "
+ "input.\nExclusive "
+ "fullscreen may offer better performance and better Freesync/Gsync support."));
+ INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"),
+ tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support "
+ "16:9, so custom game mods are required to get other ratios.\nAlso controls the "
+ "aspect ratio of captured screenshots."));
+ INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"),
+ tr("Allows saving shaders to storage for faster loading on following game "
+ "boots.\nDisabling "
+ "it is only intended for debugging."));
+ INSERT(
+ Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
+ tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
+ INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
+ tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
+ "decoding, or perform no decoding at all (black screen on videos).\n"
+ "In most cases, GPU decoding provides the best performance."));
+ INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"),
+ tr("This option controls how ASTC textures should be decoded.\n"
+ "CPU: Use the CPU for decoding, slowest but safest method.\n"
+ "GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most "
+ "games and users.\n"
+ "CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely "
+ "eliminates ASTC decoding\nstuttering at the cost of rendering issues while the "
+ "texture is being decoded."));
+ INSERT(
+ Settings, astc_recompression, tr("ASTC Recompression Method:"),
+ tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing "
+ "the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
+ "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
+ "negatively affecting image quality."));
INSERT(
Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@@ -104,22 +177,29 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Renderer (Advanced Graphics)
INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
- QStringLiteral());
+ tr("Slightly improves performance by moving presentation to a separate CPU thread."));
INSERT(
Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed."));
- INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral());
- INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral());
- INSERT(
- Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
- tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
- "is experimental."));
+ INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"),
+ tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting "
+ "and safe to set at 16x on most GPUs."));
+ INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"),
+ tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still "
+ "required for some.\nParticles tend to only render correctly with High "
+ "accuracy.\nExtreme should only be used for debugging.\nThis option can "
+ "be changed while playing.\nSome games may require booting on high to render "
+ "properly."));
+ INSERT(Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
+ tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis "
+ "feature "
+ "is experimental."));
INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
tr("Enables Fast GPU Time. This option will force most games to run at their highest "
"native resolution."));
INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
- tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
+ tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally."));
INSERT(
@@ -140,19 +220,27 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Renderer (Debug)
// System
- INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
+ INSERT(Settings, rng_seed, tr("RNG Seed"),
+ tr("Controls the seed of the random number generator.\nMainly used for speedrunning "
+ "purposes."));
INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
- INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
- INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral());
+ INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch."));
+ INSERT(Settings, custom_rtc, tr("Custom RTC Date:"),
+ tr("This option allows to change the emulated clock of the Switch.\n"
+ "Can be used to manipulate time in games."));
INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),
QStringLiteral("The number of seconds from the current unix time"));
INSERT(Settings, language_index, tr("Language:"),
tr("Note: this can be overridden when region setting is auto-select"));
- INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
- INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral());
+ INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch."));
+ INSERT(Settings, time_zone_index, tr("Time Zone:"),
+ tr("The time zone of the emulated Switch."));
INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
- INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral());
+ INSERT(Settings, use_docked_mode, tr("Console Mode:"),
+ tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change "
+ "their resolution, details and supported controllers and depending on this setting.\n"
+ "Setting to Handheld can help improve performance for low end systems."));
INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());
// Controls
@@ -170,14 +258,19 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Ui
// Ui General
- INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral());
+ INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"),
+ tr("Ask to select a user profile on each boot, useful if multiple people use yuzu on "
+ "the same PC."));
INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
- QStringLiteral());
+ tr("This setting pauses yuzu when focusing other windows."));
INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
- QStringLiteral());
- INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral());
+ tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling "
+ "it bypasses such prompts and directly exits the emulation."));
+ INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"),
+ tr("This setting hides the mouse after 2.5s of inactivity."));
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
- QStringLiteral());
+ tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
+ "attempts to open the controller applet, it is immediately closed."));
// Linux
INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());
@@ -203,6 +296,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
// Intentionally skipping VSyncMode to let the UI fill that one out
+ translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(),
+ {
+ PAIR(AppletMode, HLE, tr("Custom frontend")),
+ PAIR(AppletMode, LLE, tr("Real applet")),
+ }});
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 170f14684..1931dcd1f 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -190,10 +190,8 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType
if (type != Core::HID::ControllerTriggerType::Button) {
return;
}
- if (!Settings::values.controller_navigation) {
- return;
- }
- if (button_sequence.npad.raw == Core::HID::NpadButton::None) {
+ if (button_sequence.npad.raw == Core::HID::NpadButton::None &&
+ button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
return;
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 13381fea8..b2ae3db52 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -44,9 +44,6 @@
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/acc/profile_manager.h"
-#include "core/hle/service/am/applet_ae.h"
-#include "core/hle/service/am/applet_message_queue.h"
-#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/set/system_settings_server.h"
#include "frontend_common/content_manager.h"
@@ -649,10 +646,10 @@ void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParamete
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) {
cabinet_applet =
new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device);
- SCOPE_EXIT({
+ SCOPE_EXIT {
cabinet_applet->deleteLater();
cabinet_applet = nullptr;
- });
+ };
cabinet_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
@@ -676,10 +673,10 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
controller_applet =
new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system);
- SCOPE_EXIT({
+ SCOPE_EXIT {
controller_applet->deleteLater();
controller_applet = nullptr;
- });
+ };
controller_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
@@ -706,10 +703,10 @@ void GMainWindow::ControllerSelectorRequestExit() {
void GMainWindow::ProfileSelectorSelectProfile(
const Core::Frontend::ProfileSelectParameters& parameters) {
profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters);
- SCOPE_EXIT({
+ SCOPE_EXIT {
profile_select_applet->deleteLater();
profile_select_applet = nullptr;
- });
+ };
profile_select_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
@@ -1606,6 +1603,8 @@ void GMainWindow::ConnectMenuEvents() {
// Help
connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
+ connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware);
+ connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
connect_menu(ui->action_About, &GMainWindow::OnAbout);
}
@@ -1634,6 +1633,9 @@ void GMainWindow::UpdateMenuState() {
action->setEnabled(emulation_running);
}
+ ui->action_Install_Firmware->setEnabled(!emulation_running);
+ ui->action_Install_Keys->setEnabled(!emulation_running);
+
for (QAction* action : applet_actions) {
action->setEnabled(is_firmware_available && !emulation_running);
}
@@ -2885,17 +2887,19 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
- SCOPE_EXIT({ CoUninitialize(); });
+ SCOPE_EXIT {
+ CoUninitialize();
+ };
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (persist_file != nullptr) {
persist_file->Release();
}
if (ps1 != nullptr) {
ps1->Release();
}
- });
+ };
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) {
@@ -3520,10 +3524,10 @@ void GMainWindow::OnSaveConfig() {
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{},
tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
- SCOPE_EXIT({
+ SCOPE_EXIT {
error_applet->deleteLater();
error_applet = nullptr;
- });
+ };
error_applet->exec();
emit ErrorDisplayFinished();
@@ -4153,6 +4157,221 @@ void GMainWindow::OnVerifyInstalledContents() {
}
}
+void GMainWindow::OnInstallFirmware() {
+ // Don't do this while emulation is running, that'd probably be a bad idea.
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ return;
+ }
+
+ // Check for installed keys, error out, suggest restart?
+ if (!ContentManager::AreKeysPresent()) {
+ QMessageBox::information(
+ this, tr("Keys not installed"),
+ tr("Install decryption keys and restart yuzu before attempting to install firmware."));
+ return;
+ }
+
+ const QString firmware_source_location = QFileDialog::getExistingDirectory(
+ this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly);
+ if (firmware_source_location.isEmpty()) {
+ return;
+ }
+
+ QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(100);
+ progress.setAutoClose(false);
+ progress.setAutoReset(false);
+ progress.show();
+
+ // Declare progress callback.
+ auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(static_cast<int>((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString());
+
+ // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
+ // there.)
+ std::filesystem::path firmware_source_path = firmware_source_location.toStdString();
+ if (!Common::FS::IsDir(firmware_source_path)) {
+ progress.close();
+ return;
+ }
+
+ std::vector<std::filesystem::path> out;
+ const Common::FS::DirEntryCallable callback =
+ [&out](const std::filesystem::directory_entry& entry) {
+ if (entry.path().has_extension() && entry.path().extension() == ".nca") {
+ out.emplace_back(entry.path());
+ }
+
+ return true;
+ };
+
+ QtProgressCallback(100, 10);
+
+ Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
+ if (out.size() <= 0) {
+ progress.close();
+ QMessageBox::warning(this, tr("Firmware install failed"),
+ tr("Unable to locate potential firmware NCA files"));
+ return;
+ }
+
+ // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
+ auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
+ if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
+ progress.close();
+ QMessageBox::critical(this, tr("Firmware install failed"),
+ tr("Failed to delete one or more firmware file."));
+ return;
+ }
+
+ LOG_INFO(Frontend,
+ "Cleaned nand/system/Content/registered folder in preparation for new firmware.");
+
+ QtProgressCallback(100, 20);
+
+ auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
+
+ bool success = true;
+ int i = 0;
+ for (const auto& firmware_src_path : out) {
+ i++;
+ auto firmware_src_vfile =
+ vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read);
+ auto firmware_dst_vfile =
+ firmware_vdir->CreateFileRelative(firmware_src_path.filename().string());
+
+ if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
+ LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
+ firmware_src_path.generic_string(), firmware_src_path.filename().string());
+ success = false;
+ }
+
+ if (QtProgressCallback(
+ 100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
+ progress.close();
+ QMessageBox::warning(
+ this, tr("Firmware install failed"),
+ tr("Firmware installation cancelled, firmware may be in bad state, "
+ "restart yuzu or re-install firmware."));
+ return;
+ }
+ }
+
+ if (!success) {
+ progress.close();
+ QMessageBox::critical(this, tr("Firmware install failed"),
+ tr("One or more firmware files failed to copy into NAND."));
+ return;
+ }
+
+ // Re-scan VFS for the newly placed firmware files.
+ system->GetFileSystemController().CreateFactories(*vfs);
+
+ auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
+ return progress.wasCanceled();
+ };
+
+ auto result =
+ ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true);
+
+ if (result.size() > 0) {
+ const auto failed_names =
+ QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
+ progress.close();
+ QMessageBox::critical(
+ this, tr("Firmware integrity verification failed!"),
+ tr("Verification failed for the following files:\n\n%1").arg(failed_names));
+ return;
+ }
+
+ progress.close();
+ OnCheckFirmwareDecryption();
+}
+
+void GMainWindow::OnInstallDecryptionKeys() {
+ // Don't do this while emulation is running.
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ return;
+ }
+
+ const QString key_source_location = QFileDialog::getOpenFileName(
+ this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {},
+ QFileDialog::ReadOnly);
+ if (key_source_location.isEmpty()) {
+ return;
+ }
+
+ // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin
+ LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString());
+
+ const std::filesystem::path prod_key_path = key_source_location.toStdString();
+ const std::filesystem::path key_source_path = prod_key_path.parent_path();
+ if (!Common::FS::IsDir(key_source_path)) {
+ return;
+ }
+
+ bool prod_keys_found = false;
+ std::vector<std::filesystem::path> source_key_files;
+
+ if (Common::FS::Exists(prod_key_path)) {
+ prod_keys_found = true;
+ source_key_files.emplace_back(prod_key_path);
+ }
+
+ if (Common::FS::Exists(key_source_path / "title.keys")) {
+ source_key_files.emplace_back(key_source_path / "title.keys");
+ }
+
+ if (Common::FS::Exists(key_source_path / "key_retail.bin")) {
+ source_key_files.emplace_back(key_source_path / "key_retail.bin");
+ }
+
+ // There should be at least prod.keys.
+ if (source_key_files.empty() || !prod_keys_found) {
+ QMessageBox::warning(this, tr("Decryption Keys install failed"),
+ tr("prod.keys is a required decryption key file."));
+ return;
+ }
+
+ const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
+ for (auto key_file : source_key_files) {
+ std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename();
+ if (!std::filesystem::copy_file(key_file, destination_key_file,
+ std::filesystem::copy_options::overwrite_existing)) {
+ LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(),
+ destination_key_file.string());
+ QMessageBox::critical(this, tr("Decryption Keys install failed"),
+ tr("One or more keys failed to copy."));
+ return;
+ }
+ }
+
+ // Reinitialize the key manager, re-read the vfs (for update/dlc files),
+ // and re-populate the game list in the UI if the user has already added
+ // game folders.
+ Core::Crypto::KeyManager::Instance().ReloadKeys();
+ system->GetFileSystemController().CreateFactories(*vfs);
+ game_list->PopulateAsync(UISettings::values.game_dirs);
+
+ if (ContentManager::AreKeysPresent()) {
+ QMessageBox::information(this, tr("Decryption Keys install succeeded"),
+ tr("Decryption Keys were successfully installed"));
+ } else {
+ QMessageBox::critical(
+ this, tr("Decryption Keys install failed"),
+ tr("Decryption Keys failed to initialize. Check that your dumping tools are "
+ "up to date and re-dump keys."));
+ }
+
+ OnCheckFirmwareDecryption();
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
@@ -5052,7 +5271,9 @@ int main(int argc, char* argv[]) {
Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend");
- SCOPE_EXIT({ MicroProfileShutdown(); });
+ SCOPE_EXIT {
+ MicroProfileShutdown();
+ };
Common::ConfigureNvidiaEnvironmentFlags();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index aba61e388..fce643f3f 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -380,6 +380,8 @@ private slots:
void OnLoadAmiibo();
void OnOpenYuzuFolder();
void OnVerifyInstalledContents();
+ void OnInstallFirmware();
+ void OnInstallDecryptionKeys();
void OnAbout();
void OnToggleFilterBar();
void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 6a6b0821f..85dc1f2f6 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,16 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
- <property name="margin" stdset="0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
</layout>
@@ -156,7 +165,8 @@
<addaction name="separator"/>
<addaction name="action_Configure_Tas"/>
</widget>
- <addaction name="action_Rederive"/>
+ <addaction name="action_Install_Keys"/>
+ <addaction name="action_Install_Firmware"/>
<addaction name="action_Verify_installed_contents"/>
<addaction name="separator"/>
<addaction name="menu_cabinet_applet"/>
@@ -455,6 +465,16 @@
<string>Open &amp;Controller Menu</string>
</property>
</action>
+ <action name="action_Install_Firmware">
+ <property name="text">
+ <string>Install Firmware</string>
+ </property>
+ </action>
+ <action name="action_Install_Keys">
+ <property name="text">
+ <string>Install Decryption Keys</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
index 995114510..6e0f254b6 100644
--- a/src/yuzu_cmd/sdl_config.cpp
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -103,6 +103,7 @@ void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true)[player_index];
+ player.profile_name = "";
return;
}
}
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3b321dad1..8a8cdbc44 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -327,7 +327,9 @@ int main(int argc, char** argv) {
#endif
MicroProfileOnThreadCreate("EmuThread");
- SCOPE_EXIT({ MicroProfileShutdown(); });
+ SCOPE_EXIT {
+ MicroProfileShutdown();
+ };
Common::ConfigureNvidiaEnvironmentFlags();