diff options
Diffstat (limited to 'src/core')
496 files changed, 21275 insertions, 27940 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index dfba79267..347bbf2d0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -20,26 +20,49 @@ add_library(core STATIC cpu_manager.h crypto/aes_util.cpp crypto/aes_util.h + crypto/ctr_encryption_layer.cpp + crypto/ctr_encryption_layer.h crypto/encryption_layer.cpp crypto/encryption_layer.h crypto/key_manager.cpp crypto/key_manager.h crypto/partition_data_manager.cpp crypto/partition_data_manager.h - crypto/ctr_encryption_layer.cpp - crypto/ctr_encryption_layer.h crypto/xts_encryption_layer.cpp crypto/xts_encryption_layer.h - debugger/debugger_interface.h debugger/debugger.cpp debugger/debugger.h - debugger/gdbstub_arch.cpp - debugger/gdbstub_arch.h + debugger/debugger_interface.h debugger/gdbstub.cpp 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 + file_sys/bis_factory.cpp + file_sys/bis_factory.h + file_sys/card_image.cpp + file_sys/card_image.h + file_sys/common_funcs.h + file_sys/content_archive.cpp + file_sys/content_archive.h + file_sys/control_metadata.cpp + file_sys/control_metadata.h + file_sys/errors.h + file_sys/fs_directory.h + file_sys/fs_file.h + file_sys/fs_filesystem.h + file_sys/fs_memory_management.h + file_sys/fs_operate_range.h + file_sys/fs_path.h + file_sys/fs_path_utility.h + file_sys/fs_string_util.h + file_sys/fsmitm_romfsbuild.cpp + file_sys/fsmitm_romfsbuild.h file_sys/fssystem/fs_i_storage.h + file_sys/fssystem/fs_types.h file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h file_sys/fssystem/fssystem_aes_ctr_storage.cpp @@ -81,25 +104,10 @@ add_library(core STATIC file_sys/fssystem/fssystem_switch_storage.h file_sys/fssystem/fssystem_utility.cpp file_sys/fssystem/fssystem_utility.h - file_sys/fssystem/fs_types.h - file_sys/bis_factory.cpp - file_sys/bis_factory.h - file_sys/card_image.cpp - file_sys/card_image.h - file_sys/common_funcs.h - file_sys/content_archive.cpp - file_sys/content_archive.h - file_sys/control_metadata.cpp - file_sys/control_metadata.h - file_sys/directory.h - file_sys/errors.h - file_sys/fsmitm_romfsbuild.cpp - file_sys/fsmitm_romfsbuild.h file_sys/ips_layer.cpp file_sys/ips_layer.h file_sys/kernel_executable.cpp file_sys/kernel_executable.h - file_sys/mode.h file_sys/nca_metadata.cpp file_sys/nca_metadata.h file_sys/partition_filesystem.cpp @@ -144,22 +152,22 @@ add_library(core STATIC file_sys/system_archive/system_version.h file_sys/system_archive/time_zone_binary.cpp file_sys/system_archive/time_zone_binary.h - file_sys/vfs.cpp - file_sys/vfs.h - file_sys/vfs_cached.cpp - file_sys/vfs_cached.h - file_sys/vfs_concat.cpp - file_sys/vfs_concat.h - file_sys/vfs_layered.cpp - file_sys/vfs_layered.h - file_sys/vfs_offset.cpp - file_sys/vfs_offset.h - file_sys/vfs_real.cpp - file_sys/vfs_real.h - file_sys/vfs_static.h - file_sys/vfs_types.h - file_sys/vfs_vector.cpp - file_sys/vfs_vector.h + file_sys/vfs/vfs.cpp + file_sys/vfs/vfs.h + file_sys/vfs/vfs_cached.cpp + file_sys/vfs/vfs_cached.h + file_sys/vfs/vfs_concat.cpp + file_sys/vfs/vfs_concat.h + file_sys/vfs/vfs_layered.cpp + file_sys/vfs/vfs_layered.h + file_sys/vfs/vfs_offset.cpp + file_sys/vfs/vfs_offset.h + file_sys/vfs/vfs_real.cpp + file_sys/vfs/vfs_real.h + file_sys/vfs/vfs_static.h + file_sys/vfs/vfs_types.h + file_sys/vfs/vfs_vector.cpp + file_sys/vfs/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h frontend/applets/cabinet.cpp @@ -183,22 +191,6 @@ add_library(core STATIC frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h frontend/graphics_context.h - hid/emulated_console.cpp - hid/emulated_console.h - hid/emulated_controller.cpp - hid/emulated_controller.h - hid/emulated_devices.cpp - hid/emulated_devices.h - hid/hid_core.cpp - hid/hid_core.h - hid/hid_types.h - hid/input_converter.cpp - hid/input_converter.h - hid/input_interpreter.cpp - hid/input_interpreter.h - hid/irs_types.h - hid/motion_input.cpp - hid/motion_input.h hle/api_version.h hle/ipc.h hle/kernel/board/nintendo/nx/k_memory_layout.cpp @@ -208,7 +200,6 @@ add_library(core STATIC hle/kernel/board/nintendo/nx/secure_monitor.h hle/kernel/code_set.cpp hle/kernel/code_set.h - hle/kernel/svc_results.h hle/kernel/global_scheduler_context.cpp hle/kernel/global_scheduler_context.h hle/kernel/init/init_slab_setup.cpp @@ -218,11 +209,11 @@ add_library(core STATIC hle/kernel/k_address_arbiter.h hle/kernel/k_address_space_info.cpp hle/kernel/k_address_space_info.h + hle/kernel/k_affinity_mask.h hle/kernel/k_auto_object.cpp hle/kernel/k_auto_object.h hle/kernel/k_auto_object_container.cpp hle/kernel/k_auto_object_container.h - hle/kernel/k_affinity_mask.h hle/kernel/k_capabilities.cpp hle/kernel/k_capabilities.h hle/kernel/k_class_token.cpp @@ -246,9 +237,9 @@ add_library(core STATIC hle/kernel/k_event_info.h hle/kernel/k_handle_table.cpp hle/kernel/k_handle_table.h - hle/kernel/k_hardware_timer_base.h hle/kernel/k_hardware_timer.cpp hle/kernel/k_hardware_timer.h + hle/kernel/k_hardware_timer_base.h hle/kernel/k_interrupt_manager.cpp hle/kernel/k_interrupt_manager.h hle/kernel/k_light_client_session.cpp @@ -275,10 +266,10 @@ add_library(core STATIC hle/kernel/k_page_bitmap.h hle/kernel/k_page_buffer.cpp hle/kernel/k_page_buffer.h - hle/kernel/k_page_heap.cpp - hle/kernel/k_page_heap.h hle/kernel/k_page_group.cpp hle/kernel/k_page_group.h + hle/kernel/k_page_heap.cpp + hle/kernel/k_page_heap.h hle/kernel/k_page_table.h hle/kernel/k_page_table_base.cpp hle/kernel/k_page_table_base.h @@ -343,8 +334,6 @@ add_library(core STATIC hle/kernel/slab_helpers.h hle/kernel/svc.cpp hle/kernel/svc.h - hle/kernel/svc_common.h - hle/kernel/svc_types.h hle/kernel/svc/svc_activity.cpp hle/kernel/svc/svc_address_arbiter.cpp hle/kernel/svc/svc_address_translation.cpp @@ -382,6 +371,9 @@ add_library(core STATIC hle/kernel/svc/svc_thread_profiler.cpp hle/kernel/svc/svc_tick.cpp hle/kernel/svc/svc_transfer_memory.cpp + hle/kernel/svc_common.h + hle/kernel/svc_results.h + hle/kernel/svc_types.h hle/result.h hle/service/acc/acc.cpp hle/service/acc/acc.h @@ -486,6 +478,8 @@ add_library(core STATIC hle/service/caps/caps_types.h hle/service/caps/caps_u.cpp hle/service/caps/caps_u.h + hle/service/cmif_serialization.h + hle/service/cmif_types.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp @@ -498,14 +492,29 @@ add_library(core STATIC hle/service/fatal/fatal_p.h hle/service/fatal/fatal_u.cpp hle/service/fatal/fatal_u.h + hle/service/fgm/fgm.cpp + hle/service/fgm/fgm.h hle/service/filesystem/filesystem.cpp hle/service/filesystem/filesystem.h - hle/service/filesystem/fsp_ldr.cpp - hle/service/filesystem/fsp_ldr.h - hle/service/filesystem/fsp_pr.cpp - hle/service/filesystem/fsp_pr.h - hle/service/filesystem/fsp_srv.cpp - hle/service/filesystem/fsp_srv.h + hle/service/filesystem/fsp/fs_i_directory.cpp + hle/service/filesystem/fsp/fs_i_directory.h + hle/service/filesystem/fsp/fs_i_file.cpp + hle/service/filesystem/fsp/fs_i_file.h + hle/service/filesystem/fsp/fs_i_filesystem.cpp + hle/service/filesystem/fsp/fs_i_filesystem.h + hle/service/filesystem/fsp/fs_i_storage.cpp + hle/service/filesystem/fsp/fs_i_storage.h + hle/service/filesystem/fsp/fsp_ldr.cpp + hle/service/filesystem/fsp/fsp_ldr.h + hle/service/filesystem/fsp/fsp_pr.cpp + hle/service/filesystem/fsp/fsp_pr.h + hle/service/filesystem/fsp/fsp_srv.cpp + hle/service/filesystem/fsp/fsp_srv.h + hle/service/filesystem/fsp/fsp_util.h + hle/service/filesystem/romfs_controller.cpp + 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 @@ -525,103 +534,52 @@ add_library(core STATIC hle/service/glue/glue_manager.h hle/service/glue/notif.cpp hle/service/glue/notif.h + hle/service/glue/time/alarm_worker.cpp + hle/service/glue/time/alarm_worker.h + hle/service/glue/time/file_timestamp_worker.cpp + hle/service/glue/time/file_timestamp_worker.h + hle/service/glue/time/manager.cpp + hle/service/glue/time/manager.h + hle/service/glue/time/pm_state_change_handler.cpp + hle/service/glue/time/pm_state_change_handler.h + hle/service/glue/time/standard_steady_clock_resource.cpp + hle/service/glue/time/standard_steady_clock_resource.h + hle/service/glue/time/static.cpp + hle/service/glue/time/static.h + hle/service/glue/time/time_zone.cpp + hle/service/glue/time/time_zone.h + hle/service/glue/time/time_zone_binary.cpp + hle/service/glue/time/time_zone_binary.h + hle/service/glue/time/worker.cpp + hle/service/glue/time/worker.h hle/service/grc/grc.cpp hle/service/grc/grc.h hle/service/hid/hid.cpp hle/service/hid/hid.h hle/service/hid/hid_debug_server.cpp hle/service/hid/hid_debug_server.h - hle/service/hid/hid_firmware_settings.cpp - hle/service/hid/hid_firmware_settings.h hle/service/hid/hid_server.cpp hle/service/hid/hid_server.h hle/service/hid/hid_system_server.cpp hle/service/hid/hid_system_server.h - hle/service/hid/hid_util.h hle/service/hid/hidbus.cpp hle/service/hid/hidbus.h hle/service/hid/irs.cpp hle/service/hid/irs.h - hle/service/hid/irs_ring_lifo.h - hle/service/hid/resource_manager.cpp - hle/service/hid/resource_manager.h - hle/service/hid/ring_lifo.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h - hle/service/hid/errors.h - hle/service/hid/controllers/types/debug_pad_types.h - hle/service/hid/controllers/types/keyboard_types.h - hle/service/hid/controllers/types/mouse_types.h - hle/service/hid/controllers/types/npad_types.h - hle/service/hid/controllers/types/shared_memory_format.h - hle/service/hid/controllers/types/touch_types.h - hle/service/hid/controllers/applet_resource.cpp - hle/service/hid/controllers/applet_resource.h - hle/service/hid/controllers/capture_button.cpp - hle/service/hid/controllers/capture_button.h - hle/service/hid/controllers/console_six_axis.cpp - hle/service/hid/controllers/console_six_axis.h - hle/service/hid/controllers/controller_base.cpp - hle/service/hid/controllers/controller_base.h - hle/service/hid/controllers/debug_mouse.cpp - hle/service/hid/controllers/debug_mouse.h - hle/service/hid/controllers/debug_pad.cpp - hle/service/hid/controllers/debug_pad.h - hle/service/hid/controllers/digitizer.cpp - hle/service/hid/controllers/digitizer.h - hle/service/hid/controllers/gesture.cpp - hle/service/hid/controllers/gesture.h - hle/service/hid/controllers/home_button.cpp - hle/service/hid/controllers/home_button.h - hle/service/hid/controllers/keyboard.cpp - hle/service/hid/controllers/keyboard.h - hle/service/hid/controllers/mouse.cpp - hle/service/hid/controllers/mouse.h - hle/service/hid/controllers/npad.cpp - hle/service/hid/controllers/npad.h - hle/service/hid/controllers/palma.cpp - hle/service/hid/controllers/palma.h - hle/service/hid/controllers/seven_six_axis.cpp - hle/service/hid/controllers/seven_six_axis.h - hle/service/hid/controllers/shared_memory_holder.cpp - hle/service/hid/controllers/shared_memory_holder.h - hle/service/hid/controllers/six_axis.cpp - hle/service/hid/controllers/six_axis.h - hle/service/hid/controllers/sleep_button.cpp - hle/service/hid/controllers/sleep_button.h - hle/service/hid/controllers/touchscreen.cpp - hle/service/hid/controllers/touchscreen.h - hle/service/hid/controllers/unique_pad.cpp - hle/service/hid/controllers/unique_pad.h - hle/service/hid/hidbus/hidbus_base.cpp - hle/service/hid/hidbus/hidbus_base.h - hle/service/hid/hidbus/ringcon.cpp - hle/service/hid/hidbus/ringcon.h - hle/service/hid/hidbus/starlink.cpp - hle/service/hid/hidbus/starlink.h - hle/service/hid/hidbus/stubbed.cpp - hle/service/hid/hidbus/stubbed.h - hle/service/hid/irsensor/clustering_processor.cpp - hle/service/hid/irsensor/clustering_processor.h - hle/service/hid/irsensor/image_transfer_processor.cpp - hle/service/hid/irsensor/image_transfer_processor.h - hle/service/hid/irsensor/ir_led_processor.cpp - hle/service/hid/irsensor/ir_led_processor.h - hle/service/hid/irsensor/moment_processor.cpp - hle/service/hid/irsensor/moment_processor.h - hle/service/hid/irsensor/pointing_processor.cpp - hle/service/hid/irsensor/pointing_processor.h - hle/service/hid/irsensor/processor_base.cpp - hle/service/hid/irsensor/processor_base.h - hle/service/hid/irsensor/tera_plugin_processor.cpp - hle/service/hid/irsensor/tera_plugin_processor.h + hle/service/hle_ipc.cpp + hle/service/hle_ipc.h + hle/service/ipc_helpers.h + hle/service/kernel_helpers.cpp + hle/service/kernel_helpers.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h hle/service/ldn/lan_discovery.cpp hle/service/ldn/lan_discovery.h - hle/service/ldn/ldn_results.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h + hle/service/ldn/ldn_results.h hle/service/ldn/ldn_types.h hle/service/ldr/ldr.cpp hle/service/ldr/ldr.h @@ -629,16 +587,6 @@ add_library(core STATIC hle/service/lm/lm.h hle/service/mig/mig.cpp hle/service/mig/mig.h - hle/service/mii/types/char_info.cpp - hle/service/mii/types/char_info.h - hle/service/mii/types/core_data.cpp - hle/service/mii/types/core_data.h - hle/service/mii/types/raw_data.cpp - hle/service/mii/types/raw_data.h - hle/service/mii/types/store_data.cpp - hle/service/mii/types/store_data.h - hle/service/mii/types/ver3_store_data.cpp - hle/service/mii/types/ver3_store_data.h hle/service/mii/mii.cpp hle/service/mii/mii.h hle/service/mii/mii_database.cpp @@ -650,10 +598,22 @@ add_library(core STATIC hle/service/mii/mii_result.h hle/service/mii/mii_types.h hle/service/mii/mii_util.h + hle/service/mii/types/char_info.cpp + hle/service/mii/types/char_info.h + hle/service/mii/types/core_data.cpp + hle/service/mii/types/core_data.h + hle/service/mii/types/raw_data.cpp + hle/service/mii/types/raw_data.h + hle/service/mii/types/store_data.cpp + hle/service/mii/types/store_data.h + hle/service/mii/types/ver3_store_data.cpp + hle/service/mii/types/ver3_store_data.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h hle/service/mnpp/mnpp_app.cpp hle/service/mnpp/mnpp_app.h + hle/service/mutex.cpp + hle/service/mutex.h hle/service/ncm/ncm.cpp hle/service/ncm/ncm.h hle/service/nfc/common/amiibo_crypto.cpp @@ -695,6 +655,8 @@ add_library(core STATIC hle/service/ns/pdm_qry.h hle/service/nvdrv/core/container.cpp hle/service/nvdrv/core/container.h + hle/service/nvdrv/core/heap_mapper.cpp + hle/service/nvdrv/core/heap_mapper.h hle/service/nvdrv/core/nvmap.cpp hle/service/nvdrv/core/nvmap.h hle/service/nvdrv/core/syncpoint_manager.cpp @@ -775,47 +737,81 @@ add_library(core STATIC hle/service/prepo/prepo.h hle/service/psc/psc.cpp hle/service/psc/psc.h + hle/service/psc/time/alarms.cpp + hle/service/psc/time/alarms.h + hle/service/psc/time/clocks/context_writers.cpp + hle/service/psc/time/clocks/context_writers.h + hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h + hle/service/psc/time/clocks/standard_local_system_clock_core.cpp + hle/service/psc/time/clocks/standard_local_system_clock_core.h + hle/service/psc/time/clocks/standard_network_system_clock_core.cpp + hle/service/psc/time/clocks/standard_network_system_clock_core.h + hle/service/psc/time/clocks/standard_steady_clock_core.cpp + hle/service/psc/time/clocks/standard_steady_clock_core.h + hle/service/psc/time/clocks/standard_user_system_clock_core.cpp + hle/service/psc/time/clocks/standard_user_system_clock_core.h + hle/service/psc/time/clocks/steady_clock_core.h + hle/service/psc/time/clocks/system_clock_core.cpp + hle/service/psc/time/clocks/system_clock_core.h + hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp + hle/service/psc/time/clocks/tick_based_steady_clock_core.h + 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_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/steady_clock.cpp + hle/service/psc/time/steady_clock.h + hle/service/psc/time/system_clock.cpp + hle/service/psc/time/system_clock.h + hle/service/psc/time/time_zone.cpp + 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 hle/service/ptm/ptm.h hle/service/ptm/ts.cpp hle/service/ptm/ts.h - hle/service/hle_ipc.cpp - hle/service/hle_ipc.h - hle/service/ipc_helpers.h - hle/service/kernel_helpers.cpp - hle/service/kernel_helpers.h - hle/service/mutex.cpp - hle/service/mutex.h + hle/service/ro/ro.cpp + hle/service/ro/ro.h hle/service/ro/ro_nro_utils.cpp hle/service/ro/ro_nro_utils.h hle/service/ro/ro_results.h hle/service/ro/ro_types.h - hle/service/ro/ro.cpp - hle/service/ro/ro.h hle/service/server_manager.cpp hle/service/server_manager.h hle/service/service.cpp hle/service/service.h - hle/service/set/set.cpp - hle/service/set/set.h - hle/service/set/appln_settings.cpp - hle/service/set/appln_settings.h - hle/service/set/device_settings.cpp - hle/service/set/device_settings.h - hle/service/set/private_settings.cpp - hle/service/set/private_settings.h - hle/service/set/set_cal.cpp - hle/service/set/set_cal.h - hle/service/set/set_fd.cpp - hle/service/set/set_fd.h - hle/service/set/set_sys.cpp - hle/service/set/set_sys.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/settings.cpp hle/service/set/settings.h - hle/service/set/system_settings.cpp - hle/service/set/system_settings.h + hle/service/set/settings_server.cpp + hle/service/set/settings_server.h + hle/service/set/settings_types.h + hle/service/set/system_settings_server.cpp + hle/service/set/system_settings_server.h hle/service/sm/sm.cpp hle/service/sm/sm.h hle/service/sm/sm_controller.cpp @@ -841,40 +837,6 @@ add_library(core STATIC hle/service/ssl/ssl.cpp hle/service/ssl/ssl.h hle/service/ssl/ssl_backend.h - hle/service/time/clock_types.h - hle/service/time/ephemeral_network_system_clock_context_writer.h - hle/service/time/ephemeral_network_system_clock_core.h - hle/service/time/errors.h - hle/service/time/local_system_clock_context_writer.h - hle/service/time/network_system_clock_context_writer.h - hle/service/time/standard_local_system_clock_core.h - hle/service/time/standard_network_system_clock_core.h - hle/service/time/standard_steady_clock_core.cpp - hle/service/time/standard_steady_clock_core.h - hle/service/time/standard_user_system_clock_core.cpp - hle/service/time/standard_user_system_clock_core.h - hle/service/time/steady_clock_core.h - hle/service/time/system_clock_context_update_callback.cpp - hle/service/time/system_clock_context_update_callback.h - hle/service/time/system_clock_core.cpp - hle/service/time/system_clock_core.h - hle/service/time/tick_based_steady_clock_core.cpp - hle/service/time/tick_based_steady_clock_core.h - hle/service/time/time.cpp - hle/service/time/time.h - hle/service/time/time_interface.cpp - hle/service/time/time_interface.h - hle/service/time/time_manager.cpp - hle/service/time/time_manager.h - hle/service/time/time_sharedmemory.cpp - hle/service/time/time_sharedmemory.h - hle/service/time/time_zone_content_manager.cpp - hle/service/time/time_zone_content_manager.h - hle/service/time/time_zone_manager.cpp - hle/service/time/time_zone_manager.h - hle/service/time/time_zone_service.cpp - hle/service/time/time_zone_service.h - hle/service/time/time_zone_types.h hle/service/usb/usb.cpp hle/service/usb/usb.h hle/service/vi/display/vi_display.cpp @@ -893,9 +855,9 @@ add_library(core STATIC internal_network/network.h internal_network/network_interface.cpp internal_network/network_interface.h - internal_network/sockets.h internal_network/socket_proxy.cpp internal_network/socket_proxy.h + internal_network/sockets.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/kip.cpp @@ -914,13 +876,13 @@ add_library(core STATIC loader/nsp.h loader/xci.cpp loader/xci.h + memory.cpp + memory.h memory/cheat_engine.cpp memory/cheat_engine.h memory/dmnt_cheat_types.h memory/dmnt_cheat_vm.cpp memory/dmnt_cheat_vm.h - memory.cpp - memory.h perf_stats.cpp perf_stats.h precompiled_headers.h @@ -955,7 +917,7 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) +target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index 47a7a8880..c7285e3a0 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -22,14 +22,10 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; constexpr size_t MaxRelativeBranch = 128_MiB; constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); -Patcher::Patcher() : c(m_patch_instructions) {} - -Patcher::~Patcher() = default; - -void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, - const Kernel::CodeSet::Segment& code) { - // Branch to the first instruction of the module. - this->BranchToModule(0); +Patcher::Patcher() : c(m_patch_instructions) { + // The first word of the patch section is always a branch to the first instruction of the + // module. + c.dw(0); // Write save context helper function. c.l(m_save_context); @@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, // Write load context helper function. c.l(m_load_context); WriteLoadContext(); +} + +Patcher::~Patcher() = default; + +bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image, + const Kernel::CodeSet::Segment& code) { + // If we have patched modules but cannot reach the new module, then it needs its own patcher. + const size_t image_size = program_image.size(); + if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) { + return false; + } + + // Add a new module patch to our list + modules.emplace_back(); + curr_patch = &modules.back(); + + // The first word of the patch section is always a branch to the first instruction of the + // module. + curr_patch->m_branch_to_module_relocations.push_back({0, 0}); // Retrieve text segment data. const auto text = std::span{program_image}.subspan(code.offset, code.size); @@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, } if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { - m_exclusives.push_back(i); + curr_patch->m_exclusives.push_back(i); } } // Determine patching mode for the final relocation step - const size_t image_size = program_image.size(); + total_program_size += image_size; this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; + return true; } -void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, +bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines) { @@ -120,7 +136,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, if (mode == PatchMode::PreText) { rc.B(rel.patch_offset - patch_size - rel.module_offset); } else { - rc.B(image_size - rel.module_offset + rel.patch_offset); + rc.B(total_program_size - rel.module_offset + rel.patch_offset); } }; @@ -129,7 +145,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, if (mode == PatchMode::PreText) { rc.B(patch_size - rel.patch_offset + rel.module_offset); } else { - rc.B(rel.module_offset - image_size - rel.patch_offset); + rc.B(rel.module_offset - total_program_size - rel.patch_offset); } }; @@ -137,7 +153,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, if (mode == PatchMode::PreText) { return GetInteger(load_base) + patch_offset; } else { - return GetInteger(load_base) + image_size + patch_offset; + return GetInteger(load_base) + total_program_size + patch_offset; } }; @@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, }; // We are now ready to relocate! - for (const Relocation& rel : m_branch_to_patch_relocations) { + auto& patch = modules[m_relocate_module_index++]; + for (const Relocation& rel : patch.m_branch_to_patch_relocations) { ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); } - for (const Relocation& rel : m_branch_to_module_relocations) { + for (const Relocation& rel : patch.m_branch_to_module_relocations) { ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), rel); } // Rewrite PC constants and record post trampolines - for (const Relocation& rel : m_write_module_pc_relocations) { + for (const Relocation& rel : patch.m_write_module_pc_relocations) { oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; rc.dx(RebasePc(rel.module_offset)); } - for (const Trampoline& rel : m_trampolines) { + for (const Trampoline& rel : patch.m_trampolines) { out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); } // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. // Convert to ordered to preserve this assumption. - for (const ModuleTextAddress i : m_exclusives) { + for (const ModuleTextAddress i : patch.m_exclusives) { auto exclusive = Exclusive{text_words[i]}; text_words[i] = exclusive.AsOrdered(); } - // Copy to program image - if (this->mode == PatchMode::PreText) { - std::memcpy(program_image.data(), m_patch_instructions.data(), - m_patch_instructions.size() * sizeof(u32)); - } else { - program_image.resize(image_size + patch_size); - std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), - m_patch_instructions.size() * sizeof(u32)); + // Remove the patched module size from the total. This is done so total_program_size + // always represents the distance from the currently patched module to the patch section. + total_program_size -= image_size; + + // Only copy to the program image of the last module + if (m_relocate_module_index == modules.size()) { + if (this->mode == PatchMode::PreText) { + ASSERT(image_size == total_program_size); + std::memcpy(program_image.data(), m_patch_instructions.data(), + m_patch_instructions.size() * sizeof(u32)); + } else { + program_image.resize(image_size + patch_size); + std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), + m_patch_instructions.size() * sizeof(u32)); + } + return true; } + + return false; } size_t Patcher::GetSectionSize() const noexcept { @@ -322,7 +349,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { // Write the post-SVC trampoline address, which will jump back to the guest after restoring its // state. - m_trampolines.push_back({c.offset(), module_dest}); + curr_patch->m_trampolines.push_back({c.offset(), module_dest}); // Host called this location. Save the return address so we can // unwind the stack properly when jumping back. diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h index c6d1608c1..a44f385e2 100644 --- a/src/core/arm/nce/patcher.h +++ b/src/core/arm/nce/patcher.h @@ -31,9 +31,9 @@ public: explicit Patcher(); ~Patcher(); - void PatchText(const Kernel::PhysicalMemory& program_image, + bool PatchText(const Kernel::PhysicalMemory& program_image, const Kernel::CodeSet::Segment& code); - void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, + bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); size_t GetSectionSize() const noexcept; @@ -61,16 +61,16 @@ private: private: void BranchToPatch(uintptr_t module_dest) { - m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); + curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); } void BranchToModule(uintptr_t module_dest) { - m_branch_to_module_relocations.push_back({c.offset(), module_dest}); + curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest}); c.dw(0); } void WriteModulePc(uintptr_t module_dest) { - m_write_module_pc_relocations.push_back({c.offset(), module_dest}); + curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest}); c.dx(0); } @@ -84,15 +84,22 @@ private: uintptr_t module_offset; ///< Offset in bytes from the start of the text section. }; + struct ModulePatch { + std::vector<Trampoline> m_trampolines; + std::vector<Relocation> m_branch_to_patch_relocations{}; + std::vector<Relocation> m_branch_to_module_relocations{}; + std::vector<Relocation> m_write_module_pc_relocations{}; + std::vector<ModuleTextAddress> m_exclusives{}; + }; + oaknut::VectorCodeGenerator c; - std::vector<Trampoline> m_trampolines; - std::vector<Relocation> m_branch_to_patch_relocations{}; - std::vector<Relocation> m_branch_to_module_relocations{}; - std::vector<Relocation> m_write_module_pc_relocations{}; - std::vector<ModuleTextAddress> m_exclusives{}; oaknut::Label m_save_context{}; oaknut::Label m_load_context{}; PatchMode mode{PatchMode::None}; + size_t total_program_size{}; + size_t m_relocate_module_index{}; + std::vector<ModulePatch> modules; + ModulePatch* curr_patch; }; } // namespace Core::NCE diff --git a/src/core/core.cpp b/src/core/core.cpp index 66f444d39..1b412ac98 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -21,14 +21,14 @@ #include "core/debugger/debugger.h" #include "core/device_memory.h" #include "core/file_sys/bis_factory.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_real.h" -#include "core/hid/hid_core.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_real.h" +#include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -40,9 +40,14 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#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/set/system_settings_server.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/time/time_manager.h" #include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -52,6 +57,7 @@ #include "core/telemetry_session.h" #include "core/tools/freezer.h" #include "core/tools/renderdoc.h" +#include "hid_core/hid_core.h" #include "network/network.h" #include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" @@ -96,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, Common::SplitPath(path, &dir_name, &filename, nullptr); if (filename == "00") { - const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read); + const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read); std::vector<FileSys::VirtualFile> concat; for (u32 i = 0; i < 0x10; ++i) { @@ -121,16 +127,16 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, } if (Common::FS::IsDir(path)) { - return vfs->OpenFile(path + "/main", FileSys::Mode::Read); + return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read); } - return vfs->OpenFile(path, FileSys::Mode::Read); + return vfs->OpenFile(path, FileSys::OpenMode::Read); } struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, - reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {} + : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, + cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {} void Initialize(System& system) { device_memory = std::make_unique<Core::DeviceMemory>(); @@ -142,8 +148,6 @@ struct System::Impl { core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); - RefreshTime(); - // Create a default fs if one doesn't already exist. if (virtual_filesystem == nullptr) { virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); @@ -181,14 +185,57 @@ struct System::Impl { Initialize(system); } - void RefreshTime() { + void RefreshTime(System& system) { + if (!system.IsPoweredOn()) { + return; + } + + auto settings_service = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", + true); + auto static_service_a = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true); + + auto static_service_s = + system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock; + static_service_a->GetStandardUserSystemClock(user_clock); + + std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; + static_service_a->GetStandardLocalSystemClock(local_clock); + + std::shared_ptr<Service::PSC::Time::SystemClock> network_clock; + static_service_s->GetStandardNetworkSystemClock(network_clock); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service; + static_service_a->GetTimeZoneService(timezone_service); + + Service::PSC::Time::LocationName name{}; + auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); + std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size())); + + timezone_service->SetDeviceLocation(name); + + u64 time_offset = 0; + if (Settings::values.custom_rtc_enabled) { + time_offset = Settings::values.custom_rtc_offset.GetValue(); + } + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); - const auto current_time = - std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); - Settings::values.custom_rtc_differential = - (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue() - : current_time) - - current_time; + const u64 current_time = + +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); + const u64 new_time = current_time + time_offset; + + Service::PSC::Time::SystemClockContext context{}; + settings_service->SetUserSystemClockContext(context); + user_clock->SetCurrentTime(new_time); + + local_clock->SetCurrentTime(new_time); + + network_clock->GetSystemClockContext(context); + settings_service->SetNetworkSystemClockContext(context); + network_clock->SetCurrentTime(new_time); } void Run() { @@ -264,9 +311,6 @@ struct System::Impl { service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); services = std::make_unique<Service::Services>(service_manager, system); - // Initialize time manager, which must happen after kernel is created - time_manager.Initialize(); - is_powered_on = true; exit_locked = false; exit_requested = false; @@ -413,9 +457,9 @@ struct System::Impl { kernel.ShutdownCores(); services.reset(); service_manager.reset(); + fs_controller.Reset(); cheat_engine.reset(); telemetry_session.reset(); - time_manager.Shutdown(); core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); @@ -531,7 +575,6 @@ struct System::Impl { /// Service State Service::Glue::ARPManager arp_manager; Service::Account::ProfileManager profile_manager; - Service::Time::TimeManager time_manager; /// Service manager std::shared_ptr<Service::SM::ServiceManager> service_manager; @@ -564,6 +607,9 @@ struct System::Impl { std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; + std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> + gpu_dirty_memory_managers; + std::deque<std::vector<u8>> user_channel; }; @@ -650,8 +696,14 @@ size_t System::GetCurrentHostThreadID() const { return impl->kernel.GetCurrentHostThreadID(); } -void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { - return this->ApplicationProcess()->GatherGPUDirtyMemory(callback); +std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager() { + return impl->gpu_dirty_memory_managers; +} + +void System::GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback) { + for (auto& manager : impl->gpu_dirty_memory_managers) { + manager.Gather(callback); + } } PerfStatsResults System::GetAndResetPerfStats() { @@ -900,14 +952,6 @@ const Service::Account::ProfileManager& System::GetProfileManager() const { return impl->profile_manager; } -Service::Time::TimeManager& System::GetTimeManager() { - return impl->time_manager; -} - -const Service::Time::TimeManager& System::GetTimeManager() const { - return impl->time_manager; -} - void System::SetExitLocked(bool locked) { impl->exit_locked = locked; } @@ -1019,13 +1063,9 @@ void System::Exit() { } void System::ApplySettings() { - impl->RefreshTime(); + impl->RefreshTime(*this); if (IsPoweredOn()) { - if (Settings::values.custom_rtc_enabled) { - const s64 posix_time{Settings::values.custom_rtc.GetValue()}; - GetTimeManager().UpdateLocalSystemClockTime(posix_time); - } Renderer().RefreshBaseSettings(); } } diff --git a/src/core/core.h b/src/core/core.h index ba5add0dc..d8862e9ce 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -8,11 +8,12 @@ #include <functional> #include <memory> #include <mutex> +#include <span> #include <string> #include <vector> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Core::Frontend { class EmuWindow; @@ -72,10 +73,6 @@ namespace SM { class ServiceManager; } // namespace SM -namespace Time { -class TimeManager; -} // namespace Time - } // namespace Service namespace Tegra { @@ -116,6 +113,7 @@ class CpuManager; class Debugger; class DeviceMemory; class ExclusiveMonitor; +class GPUDirtyMemoryManager; class PerfStats; class Reporter; class SpeedLimiter; @@ -224,7 +222,9 @@ public: /// Prepare the core emulation for a reschedule void PrepareReschedule(u32 core_index); - void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); + std::span<GPUDirtyMemoryManager> GetGPUDirtyMemoryManager(); + + void GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback); [[nodiscard]] size_t GetCurrentHostThreadID() const; @@ -377,9 +377,6 @@ public: [[nodiscard]] Service::Account::ProfileManager& GetProfileManager(); [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const; - [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); - [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; - [[nodiscard]] Core::Debugger& GetDebugger(); [[nodiscard]] const Core::Debugger& GetDebugger() const; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index fc536413b..1abfa920c 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -157,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, } } - for (auto h : to_remove) { + for (auto& h : to_remove) { event_queue.erase(h); } diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 0deea9c58..a249dc5f7 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -64,7 +64,7 @@ public: return [this] { ShutdownThreadFunction(); }; } - void PreemptSingleCore(bool from_running_enviroment = true); + void PreemptSingleCore(bool from_running_environment = true); std::size_t CurrentCore() const { return current_core.load(); diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index a67ba5352..c2fd587a7 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h @@ -7,7 +7,7 @@ #include <span> #include <type_traits> #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h index d3082ba53..b53f0b12e 100644 --- a/src/core/crypto/encryption_layer.h +++ b/src/core/crypto/encryption_layer.h @@ -4,7 +4,7 @@ #pragma once #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 97f5c8cea..4b45e72c4 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -21,9 +21,9 @@ #include "core/crypto/partition_data_manager.h" #include "core/crypto/xts_encryption_layer.h" #include "core/file_sys/kernel_executable.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/loader/loader.h" using Common::AsArray; diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h index 057a70683..4354a21e6 100644 --- a/src/core/crypto/partition_data_manager.h +++ b/src/core/crypto/partition_data_manager.h @@ -5,7 +5,7 @@ #include <vector> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Core::Crypto { diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 0e270eb50..e86aae846 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -114,7 +114,7 @@ public: } Kernel::KThread* GetActiveThread() override { - return state->active_thread; + return state->active_thread.GetPointerUnsafe(); } private: @@ -147,11 +147,14 @@ private: std::scoped_lock lk{connection_lock}; + // Find the process we are going to debug. + SetDebugProcess(); + // Ensure everything is stopped. PauseEmulation(); // Set up the new frontend. - frontend = std::make_unique<GDBStub>(*this, system); + frontend = std::make_unique<GDBStub>(*this, system, debug_process.GetPointerUnsafe()); // Set the new state. This will tear down any existing state. state = ConnectionState{ @@ -194,15 +197,20 @@ private: UpdateActiveThread(); if (state->info.type == SignalType::Watchpoint) { - frontend->Watchpoint(state->active_thread, *state->info.watchpoint); + frontend->Watchpoint(std::addressof(*state->active_thread), + *state->info.watchpoint); } else { - frontend->Stopped(state->active_thread); + frontend->Stopped(std::addressof(*state->active_thread)); } break; case SignalType::ShuttingDown: frontend->ShuttingDown(); + // Release members. + state->active_thread.Reset(nullptr); + debug_process.Reset(nullptr); + // Wait for emulation to shut down gracefully now. state->signal_pipe.close(); state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); @@ -222,7 +230,7 @@ private: stopped = true; PauseEmulation(); UpdateActiveThread(); - frontend->Stopped(state->active_thread); + frontend->Stopped(state->active_thread.GetPointerUnsafe()); break; } case DebuggerAction::Continue: @@ -232,7 +240,7 @@ private: MarkResumed([&] { state->active_thread->SetStepState(Kernel::StepState::StepPending); state->active_thread->Resume(Kernel::SuspendType::Debug); - ResumeEmulation(state->active_thread); + ResumeEmulation(state->active_thread.GetPointerUnsafe()); }); break; case DebuggerAction::StepThreadLocked: { @@ -255,6 +263,7 @@ private: } void PauseEmulation() { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; Kernel::KScopedSchedulerLock sl{system.Kernel()}; // Put all threads to sleep on next scheduler round. @@ -264,6 +273,9 @@ private: } void ResumeEmulation(Kernel::KThread* except = nullptr) { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + // Wake up all threads. for (auto& thread : ThreadList()) { if (std::addressof(thread) == except) { @@ -277,15 +289,16 @@ private: template <typename Callback> void MarkResumed(Callback&& cb) { - Kernel::KScopedSchedulerLock sl{system.Kernel()}; stopped = false; cb(); } void UpdateActiveThread() { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; + auto& threads{ThreadList()}; for (auto& thread : threads) { - if (std::addressof(thread) == state->active_thread) { + if (std::addressof(thread) == state->active_thread.GetPointerUnsafe()) { // Thread is still alive, no need to update. return; } @@ -293,12 +306,18 @@ private: state->active_thread = std::addressof(threads.front()); } +private: + void SetDebugProcess() { + debug_process = std::move(system.Kernel().GetProcessList().back()); + } + Kernel::KProcess::ThreadList& ThreadList() { - return system.ApplicationProcess()->GetThreadList(); + return debug_process->GetThreadList(); } private: System& system; + Kernel::KScopedAutoObject<Kernel::KProcess> debug_process; std::unique_ptr<DebuggerFrontend> frontend; boost::asio::io_context io_context; @@ -310,7 +329,7 @@ private: boost::process::async_pipe signal_pipe; SignalInfo info; - Kernel::KThread* active_thread; + Kernel::KScopedAutoObject<Kernel::KThread> active_thread; std::array<u8, 4096> client_data; bool pipe_data; }; diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 66e46c4ba..80091cc7e 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -108,9 +108,9 @@ static std::string EscapeXML(std::string_view data) { return escaped; } -GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) - : DebuggerFrontend(backend_), system{system_} { - if (system.ApplicationProcess()->Is64Bit()) { +GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_) + : DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} { + if (GetProcess()->Is64Bit()) { arch = std::make_unique<GDBStubA64>(); } else { arch = std::make_unique<GDBStubA32>(); @@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; std::vector<u8> mem(size); - if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { + if (GetMemory().ReadBlock(addr, mem.data(), size)) { // Restore any bytes belonging to replaced instructions. auto it = replaced_instructions.lower_bound(addr); for (; it != replaced_instructions.end() && it->first < addr + size; it++) { @@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction const auto mem_substr{std::string_view(command).substr(mem_sep)}; const auto mem{Common::HexStringToVector(mem_substr, false)}; - if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) { - Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size); + if (GetMemory().WriteBlock(addr, mem.data(), size)) { + Core::InvalidateInstructionCacheRange(GetProcess(), addr, size); SendReply(GDB_STUB_REPLY_OK); } else { SendReply(GDB_STUB_REPLY_ERR); @@ -353,7 +353,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) { const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; - if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { + if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { SendReply(GDB_STUB_REPLY_ERR); return; } @@ -362,22 +362,20 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) { switch (type) { case BreakpointType::Software: - replaced_instructions[addr] = system.ApplicationMemory().Read32(addr); - system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction()); - Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); + replaced_instructions[addr] = GetMemory().Read32(addr); + GetMemory().Write32(addr, arch->BreakpointInstruction()); + Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); success = true; break; case BreakpointType::WriteWatch: - success = system.ApplicationProcess()->InsertWatchpoint(addr, size, - Kernel::DebugWatchpointType::Write); + success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); break; case BreakpointType::ReadWatch: - success = system.ApplicationProcess()->InsertWatchpoint(addr, size, - Kernel::DebugWatchpointType::Read); + success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); break; case BreakpointType::AccessWatch: - success = system.ApplicationProcess()->InsertWatchpoint( - addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + success = + GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); break; case BreakpointType::Hardware: default: @@ -400,7 +398,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; - if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { + if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { SendReply(GDB_STUB_REPLY_ERR); return; } @@ -411,24 +409,22 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { case BreakpointType::Software: { const auto orig_insn{replaced_instructions.find(addr)}; if (orig_insn != replaced_instructions.end()) { - system.ApplicationMemory().Write32(addr, orig_insn->second); - Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); + GetMemory().Write32(addr, orig_insn->second); + Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); replaced_instructions.erase(addr); success = true; } break; } case BreakpointType::WriteWatch: - success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, - Kernel::DebugWatchpointType::Write); + success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); break; case BreakpointType::ReadWatch: - success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, - Kernel::DebugWatchpointType::Read); + success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); break; case BreakpointType::AccessWatch: - success = system.ApplicationProcess()->RemoveWatchpoint( - addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + success = + GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); break; case BreakpointType::Hardware: default: @@ -466,10 +462,10 @@ void GDBStub::HandleQuery(std::string_view command) { const auto target_xml{arch->GetTargetXML()}; SendReply(PaginateBuffer(target_xml, command.substr(30))); } else if (command.starts_with("Offsets")) { - const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess()); + const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess()); SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); } else if (command.starts_with("Xfer:libraries:read::")) { - auto modules = Core::FindModules(system.ApplicationProcess()); + auto modules = Core::FindModules(GetProcess()); std::string buffer; buffer += R"(<?xml version="1.0"?>)"; @@ -483,7 +479,7 @@ void GDBStub::HandleQuery(std::string_view command) { SendReply(PaginateBuffer(buffer, command.substr(21))); } else if (command.starts_with("fThreadInfo")) { // beginning of list - const auto& threads = system.ApplicationProcess()->GetThreadList(); + const auto& threads = GetProcess()->GetThreadList(); std::vector<std::string> thread_ids; for (const auto& thread : threads) { thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); @@ -497,7 +493,7 @@ void GDBStub::HandleQuery(std::string_view command) { buffer += R"(<?xml version="1.0"?>)"; buffer += "<threads>"; - const auto& threads = system.ApplicationProcess()->GetThreadList(); + const auto& threads = GetProcess()->GetThreadList(); for (const auto& thread : threads) { auto thread_name{Core::GetThreadName(&thread)}; if (!thread_name) { @@ -559,28 +555,28 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& } constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ - {"----- Free -----", Kernel::Svc::MemoryState::Free}, - {"Io ", Kernel::Svc::MemoryState::Io}, - {"Static ", Kernel::Svc::MemoryState::Static}, - {"Code ", Kernel::Svc::MemoryState::Code}, - {"CodeData ", Kernel::Svc::MemoryState::CodeData}, - {"Normal ", Kernel::Svc::MemoryState::Normal}, - {"Shared ", Kernel::Svc::MemoryState::Shared}, - {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, - {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, - {"Ipc ", Kernel::Svc::MemoryState::Ipc}, - {"Stack ", Kernel::Svc::MemoryState::Stack}, - {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, - {"Transfered ", Kernel::Svc::MemoryState::Transfered}, - {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, - {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, - {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, - {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, - {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, - {"Kernel ", Kernel::Svc::MemoryState::Kernel}, - {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, - {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, - {"Coverage ", Kernel::Svc::MemoryState::Coverage}, + {"----- Free ------", Kernel::Svc::MemoryState::Free}, + {"Io ", Kernel::Svc::MemoryState::Io}, + {"Static ", Kernel::Svc::MemoryState::Static}, + {"Code ", Kernel::Svc::MemoryState::Code}, + {"CodeData ", Kernel::Svc::MemoryState::CodeData}, + {"Normal ", Kernel::Svc::MemoryState::Normal}, + {"Shared ", Kernel::Svc::MemoryState::Shared}, + {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, + {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, + {"Ipc ", Kernel::Svc::MemoryState::Ipc}, + {"Stack ", Kernel::Svc::MemoryState::Stack}, + {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, + {"Transferred ", Kernel::Svc::MemoryState::Transferred}, + {"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred}, + {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, + {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, + {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, + {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, + {"Kernel ", Kernel::Svc::MemoryState::Kernel}, + {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, + {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, + {"Coverage ", Kernel::Svc::MemoryState::Coverage}, }}; static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { @@ -613,7 +609,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; std::string reply; - auto* process = system.ApplicationProcess(); + auto* process = GetProcess(); auto& page_table = process->GetPageTable(); const char* commands = "Commands:\n" @@ -714,7 +710,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { } Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { - auto& threads{system.ApplicationProcess()->GetThreadList()}; + auto& threads{GetProcess()->GetThreadList()}; for (auto& thread : threads) { if (thread.GetThreadId() == thread_id) { return std::addressof(thread); @@ -783,4 +779,12 @@ void GDBStub::SendStatus(char status) { backend.WriteToClient(buf); } +Kernel::KProcess* GDBStub::GetProcess() { + return debug_process; +} + +Core::Memory::Memory& GDBStub::GetMemory() { + return GetProcess()->GetMemory(); +} + } // namespace Core diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 368197920..232dcf49f 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -12,13 +12,22 @@ #include "core/debugger/debugger_interface.h" #include "core/debugger/gdbstub_arch.h" +namespace Kernel { +class KProcess; +} + +namespace Core::Memory { +class Memory; +} + namespace Core { class System; class GDBStub : public DebuggerFrontend { public: - explicit GDBStub(DebuggerBackend& backend, Core::System& system); + explicit GDBStub(DebuggerBackend& backend, Core::System& system, + Kernel::KProcess* debug_process); ~GDBStub() override; void Connected() override; @@ -42,8 +51,12 @@ private: void SendReply(std::string_view data); void SendStatus(char status); + Kernel::KProcess* GetProcess(); + Core::Memory::Memory& GetMemory(); + private: Core::System& system; + Kernel::KProcess* debug_process; std::unique_ptr<GDBStubArch> arch; std::vector<char> current_command; std::map<VAddr, u32> replaced_instructions; diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 13388b73e..11bf0e326 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -32,6 +32,12 @@ public: } template <typename T> + PAddr GetRawPhysicalAddr(const T* ptr) const { + return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) - + reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())); + } + + template <typename T> T* GetPointer(Common::PhysicalAddress addr) { return reinterpret_cast<T*>(buffer.BackingBasePointer() + (GetInteger(addr) - DramMemoryMap::Base)); @@ -43,6 +49,16 @@ public: (GetInteger(addr) - DramMemoryMap::Base)); } + template <typename T> + T* GetPointerFromRaw(PAddr addr) { + return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr); + } + + template <typename T> + const T* GetPointerFromRaw(PAddr addr) const { + return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr); + } + Common::HostMemory buffer; }; diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h new file mode 100644 index 000000000..ffeed46cc --- /dev/null +++ b/src/core/device_memory_manager.h @@ -0,0 +1,211 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <atomic> +#include <deque> +#include <memory> +#include <mutex> + +#include "common/common_types.h" +#include "common/scratch_buffer.h" +#include "common/virtual_buffer.h" + +namespace Core { + +constexpr size_t DEVICE_PAGEBITS = 12ULL; +constexpr size_t DEVICE_PAGESIZE = 1ULL << DEVICE_PAGEBITS; +constexpr size_t DEVICE_PAGEMASK = DEVICE_PAGESIZE - 1ULL; + +class DeviceMemory; + +namespace Memory { +class Memory; +} + +template <typename DTraits> +struct DeviceMemoryManagerAllocator; + +struct Asid { + size_t id; +}; + +template <typename Traits> +class DeviceMemoryManager { + using DeviceInterface = typename Traits::DeviceInterface; + using DeviceMethods = typename Traits::DeviceMethods; + +public: + DeviceMemoryManager(const DeviceMemory& device_memory); + ~DeviceMemoryManager(); + + void BindInterface(DeviceInterface* device_inter); + + DAddr Allocate(size_t size); + void AllocateFixed(DAddr start, size_t size); + void Free(DAddr start, size_t size); + + void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false); + + void Unmap(DAddr address, size_t size); + + void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid); + void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) { + std::scoped_lock lk(mapping_guard); + TrackContinuityImpl(address, virtual_address, size, asid); + } + + // Write / Read + template <typename T> + T* GetPointer(DAddr address); + + template <typename T> + const T* GetPointer(DAddr address) const; + + template <typename Func> + void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer<u32>& buffer, Func&& operation) { + DAddr subbits = static_cast<DAddr>(address & page_mask); + const u32 base = compressed_device_addr[(address >> page_bits)]; + if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] { + const DAddr d_address = (static_cast<DAddr>(base) << page_bits) + subbits; + operation(d_address); + return; + } + InnerGatherDeviceAddresses(buffer, address); + for (u32 value : buffer) { + operation((static_cast<DAddr>(value) << page_bits) + subbits); + } + } + + template <typename Func> + void ApplyOpOnPointer(const u8* p, Common::ScratchBuffer<u32>& buffer, Func&& operation) { + PAddr address = GetRawPhysicalAddr<u8>(p); + ApplyOpOnPAddr(address, buffer, operation); + } + + PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const { + PAddr subbits = static_cast<PAddr>(address & page_mask); + auto paddr = compressed_physical_ptr[(address >> page_bits)]; + if (paddr == 0) { + return 0; + } + return (static_cast<PAddr>(paddr - 1) << page_bits) + subbits; + } + + template <typename T> + void Write(DAddr address, T value); + + template <typename T> + T Read(DAddr address) const; + + u8* GetSpan(const DAddr src_addr, const std::size_t size); + const u8* GetSpan(const DAddr src_addr, const std::size_t size) const; + + void ReadBlock(DAddr address, void* dest_pointer, size_t size); + void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size); + void WriteBlock(DAddr address, const void* src_pointer, size_t size); + void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); + + Asid RegisterProcess(Memory::Memory* memory); + void UnregisterProcess(Asid id); + + void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta); + + static constexpr size_t AS_BITS = Traits::device_virtual_bits; + +private: + static constexpr size_t device_virtual_bits = Traits::device_virtual_bits; + static constexpr size_t device_as_size = 1ULL << device_virtual_bits; + static constexpr size_t physical_min_bits = 32; + static constexpr size_t physical_max_bits = 33; + static constexpr size_t page_bits = 12; + static constexpr size_t page_size = 1ULL << page_bits; + static constexpr size_t page_mask = page_size - 1ULL; + static constexpr u32 physical_address_base = 1U << page_bits; + static constexpr u32 MULTI_FLAG_BITS = 31; + static constexpr u32 MULTI_FLAG = 1U << MULTI_FLAG_BITS; + static constexpr u32 MULTI_MASK = ~MULTI_FLAG; + + template <typename T> + T* GetPointerFromRaw(PAddr addr) { + return reinterpret_cast<T*>(physical_base + addr); + } + + template <typename T> + const T* GetPointerFromRaw(PAddr addr) const { + return reinterpret_cast<T*>(physical_base + addr); + } + + template <typename T> + PAddr GetRawPhysicalAddr(const T* ptr) const { + return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) - physical_base); + } + + void WalkBlock(const DAddr addr, const std::size_t size, auto on_unmapped, auto on_memory, + auto increment); + + void InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, PAddr address); + + std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl; + + const uintptr_t physical_base; + DeviceInterface* device_inter; + Common::VirtualBuffer<u32> compressed_physical_ptr; + Common::VirtualBuffer<u32> compressed_device_addr; + Common::VirtualBuffer<u32> continuity_tracker; + + // Process memory interfaces + + std::deque<size_t> id_pool; + std::deque<Memory::Memory*> registered_processes; + + // Memory protection management + + static constexpr size_t guest_max_as_bits = 39; + static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits; + static constexpr size_t guest_mask = guest_as_size - 1ULL; + static constexpr size_t asid_start_bit = guest_max_as_bits; + + std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) { + auto content = cpu_backing_address[page_index]; + const VAddr address = content & guest_mask; + const Asid asid{static_cast<size_t>(content >> asid_start_bit)}; + return std::make_pair(asid, address); + } + + void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { + cpu_backing_address[page_index] = address | (asid.id << asid_start_bit); + } + + Common::VirtualBuffer<VAddr> cpu_backing_address; + static constexpr size_t subentries = 8 / sizeof(u8); + static constexpr size_t subentries_mask = subentries - 1; + class CounterEntry final { + public: + CounterEntry() = default; + + std::atomic_uint8_t& Count(std::size_t page) { + return values[page & subentries_mask]; + } + + const std::atomic_uint8_t& Count(std::size_t page) const { + return values[page & subentries_mask]; + } + + private: + std::array<std::atomic_uint8_t, subentries> values{}; + }; + static_assert(sizeof(CounterEntry) == subentries * sizeof(u8), + "CounterEntry should be 8 bytes!"); + + static constexpr size_t num_counter_entries = + (1ULL << (device_virtual_bits - page_bits)) / subentries; + using CachedPages = std::array<CounterEntry, num_counter_entries>; + std::unique_ptr<CachedPages> cached_pages; + std::mutex counter_guard; + std::mutex mapping_guard; +}; + +} // namespace Core diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc new file mode 100644 index 000000000..eab8a2731 --- /dev/null +++ b/src/core/device_memory_manager.inc @@ -0,0 +1,581 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <atomic> +#include <limits> +#include <memory> +#include <type_traits> + +#include "common/address_space.h" +#include "common/address_space.inc" +#include "common/alignment.h" +#include "common/assert.h" +#include "common/div_ceil.h" +#include "common/scope_exit.h" +#include "common/settings.h" +#include "core/device_memory.h" +#include "core/device_memory_manager.h" +#include "core/memory.h" + +namespace Core { + +namespace { + +class MultiAddressContainer { +public: + MultiAddressContainer() = default; + ~MultiAddressContainer() = default; + + void GatherValues(u32 start_entry, Common::ScratchBuffer<u32>& buffer) { + buffer.resize(8); + buffer.resize(0); + size_t index = 0; + const auto add_value = [&](u32 value) { + buffer.resize(index + 1); + buffer[index++] = value; + }; + + u32 iter_entry = start_entry; + Entry* current = &storage[iter_entry - 1]; + add_value(current->value); + while (current->next_entry != 0) { + iter_entry = current->next_entry; + current = &storage[iter_entry - 1]; + add_value(current->value); + } + } + + u32 Register(u32 value) { + return RegisterImplementation(value); + } + + void Register(u32 value, u32 start_entry) { + auto entry_id = RegisterImplementation(value); + u32 iter_entry = start_entry; + Entry* current = &storage[iter_entry - 1]; + while (current->next_entry != 0) { + iter_entry = current->next_entry; + current = &storage[iter_entry - 1]; + } + current->next_entry = entry_id; + } + + std::pair<bool, u32> Unregister(u32 value, u32 start_entry) { + u32 iter_entry = start_entry; + Entry* previous{}; + Entry* current = &storage[iter_entry - 1]; + Entry* next{}; + bool more_than_one_remaining = false; + u32 result_start{start_entry}; + size_t count = 0; + while (current->value != value) { + count++; + previous = current; + iter_entry = current->next_entry; + current = &storage[iter_entry - 1]; + } + // Find next + u32 next_entry = current->next_entry; + if (next_entry != 0) { + next = &storage[next_entry - 1]; + more_than_one_remaining = next->next_entry != 0 || previous != nullptr; + } + if (previous) { + previous->next_entry = next_entry; + } else { + result_start = next_entry; + } + free_entries.emplace_back(iter_entry); + return std::make_pair(more_than_one_remaining || count > 1, result_start); + } + + u32 ReleaseEntry(u32 start_entry) { + Entry* current = &storage[start_entry - 1]; + free_entries.emplace_back(start_entry); + return current->value; + } + +private: + u32 RegisterImplementation(u32 value) { + auto entry_id = GetNewEntry(); + auto& entry = storage[entry_id - 1]; + entry.next_entry = 0; + entry.value = value; + return entry_id; + } + u32 GetNewEntry() { + if (!free_entries.empty()) { + u32 result = free_entries.front(); + free_entries.pop_front(); + return result; + } + storage.emplace_back(); + u32 new_entry = static_cast<u32>(storage.size()); + return new_entry; + } + + struct Entry { + u32 next_entry{}; + u32 value{}; + }; + + std::deque<Entry> storage; + std::deque<u32> free_entries; +}; + +struct EmptyAllocator { + EmptyAllocator([[maybe_unused]] DAddr address) {} +}; + +} // namespace + +template <typename DTraits> +struct DeviceMemoryManagerAllocator { + static constexpr size_t device_virtual_bits = DTraits::device_virtual_bits; + static constexpr DAddr first_address = 1ULL << Memory::YUZU_PAGEBITS; + static constexpr DAddr max_device_area = 1ULL << device_virtual_bits; + + DeviceMemoryManagerAllocator() : main_allocator(first_address) {} + + Common::FlatAllocator<DAddr, 0, device_virtual_bits> main_allocator; + MultiAddressContainer multi_dev_address; + + /// Returns true when vaddr -> vaddr+size is fully contained in the buffer + template <bool pin_area> + [[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept { + return addr >= 0 && addr + size <= max_device_area; + } + + DAddr Allocate(size_t size) { + return main_allocator.Allocate(size); + } + + void AllocateFixed(DAddr b_address, size_t b_size) { + main_allocator.AllocateFixed(b_address, b_size); + } + + void Free(DAddr b_address, size_t b_size) { + main_allocator.Free(b_address, b_size); + } +}; + +template <typename Traits> +DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_) + : physical_base{reinterpret_cast<const uintptr_t>(device_memory_.buffer.BackingBasePointer())}, + device_inter{nullptr}, compressed_physical_ptr(device_as_size >> Memory::YUZU_PAGEBITS), + compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == + Settings::MemoryLayout::Memory_4Gb + ? physical_min_bits + : physical_max_bits) - + Memory::YUZU_PAGEBITS)), + continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS), + cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) { + impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>(); + cached_pages = std::make_unique<CachedPages>(); + + const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS; + for (size_t i = 0; i < total_virtual; i++) { + compressed_physical_ptr[i] = 0; + continuity_tracker[i] = 1; + cpu_backing_address[i] = 0; + } + const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == + Settings::MemoryLayout::Memory_4Gb + ? physical_min_bits + : physical_max_bits) - + Memory::YUZU_PAGEBITS); + for (size_t i = 0; i < total_phys; i++) { + compressed_device_addr[i] = 0; + } +} + +template <typename Traits> +DeviceMemoryManager<Traits>::~DeviceMemoryManager() = default; + +template <typename Traits> +void DeviceMemoryManager<Traits>::BindInterface(DeviceInterface* device_inter_) { + device_inter = device_inter_; +} + +template <typename Traits> +DAddr DeviceMemoryManager<Traits>::Allocate(size_t size) { + return impl->Allocate(size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::AllocateFixed(DAddr start, size_t size) { + return impl->AllocateFixed(start, size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) { + impl->Free(start, size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size, + Asid asid, bool track) { + Core::Memory::Memory* process_memory = registered_processes[asid.id]; + size_t start_page_d = address >> Memory::YUZU_PAGEBITS; + size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; + std::scoped_lock lk(mapping_guard); + for (size_t i = 0; i < num_pages; i++) { + const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; + auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); + if (ptr == nullptr) [[unlikely]] { + compressed_physical_ptr[start_page_d + i] = 0; + continue; + } + auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; + compressed_physical_ptr[start_page_d + i] = phys_addr; + InsertCPUBacking(start_page_d + i, new_vaddress, asid); + const u32 base_dev = compressed_device_addr[phys_addr - 1U]; + const u32 new_dev = static_cast<u32>(start_page_d + i); + if (base_dev == 0) [[likely]] { + compressed_device_addr[phys_addr - 1U] = new_dev; + continue; + } + u32 start_id = base_dev & MULTI_MASK; + if ((base_dev >> MULTI_FLAG_BITS) == 0) { + start_id = impl->multi_dev_address.Register(base_dev); + compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id; + } + impl->multi_dev_address.Register(new_dev, start_id); + } + if (track) { + TrackContinuityImpl(address, virtual_address, size, asid); + } +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) { + size_t start_page_d = address >> Memory::YUZU_PAGEBITS; + size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; + device_inter->InvalidateRegion(address, size); + std::scoped_lock lk(mapping_guard); + for (size_t i = 0; i < num_pages; i++) { + auto phys_addr = compressed_physical_ptr[start_page_d + i]; + compressed_physical_ptr[start_page_d + i] = 0; + cpu_backing_address[start_page_d + i] = 0; + if (phys_addr != 0) [[likely]] { + const u32 base_dev = compressed_device_addr[phys_addr - 1U]; + if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] { + compressed_device_addr[phys_addr - 1] = 0; + continue; + } + const auto [more_entries, new_start] = impl->multi_dev_address.Unregister( + static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK); + if (!more_entries) { + compressed_device_addr[phys_addr - 1] = + impl->multi_dev_address.ReleaseEntry(new_start); + continue; + } + compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG; + } + } +} +template <typename Traits> +void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address, + size_t size, Asid asid) { + Core::Memory::Memory* process_memory = registered_processes[asid.id]; + size_t start_page_d = address >> Memory::YUZU_PAGEBITS; + size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; + uintptr_t last_ptr = 0; + size_t page_count = 1; + for (size_t i = num_pages; i > 0; i--) { + size_t index = i - 1; + const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE; + const uintptr_t new_ptr = reinterpret_cast<uintptr_t>( + process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress))); + if (new_ptr + page_size == last_ptr) { + page_count++; + } else { + page_count = 1; + } + last_ptr = new_ptr; + continuity_tracker[start_page_d + index] = static_cast<u32>(page_count); + } +} +template <typename Traits> +u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) { + size_t page_index = src_addr >> page_bits; + size_t subbits = src_addr & page_mask; + if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) { + return GetPointer<u8>(src_addr); + } + return nullptr; +} + +template <typename Traits> +const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const { + size_t page_index = src_addr >> page_bits; + size_t subbits = src_addr & page_mask; + if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) { + return GetPointer<u8>(src_addr); + } + return nullptr; +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, + PAddr address) { + size_t phys_addr = address >> page_bits; + std::scoped_lock lk(mapping_guard); + u32 backing = compressed_device_addr[phys_addr]; + if ((backing >> MULTI_FLAG_BITS) != 0) { + impl->multi_dev_address.GatherValues(backing & MULTI_MASK, buffer); + return; + } + buffer.resize(1); + buffer[0] = backing; +} + +template <typename Traits> +template <typename T> +T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) { + const size_t index = address >> Memory::YUZU_PAGEBITS; + const size_t offset = address & Memory::YUZU_PAGEMASK; + auto phys_addr = compressed_physical_ptr[index]; + if (phys_addr == 0) [[unlikely]] { + return nullptr; + } + return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + + offset); +} + +template <typename Traits> +template <typename T> +const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const { + const size_t index = address >> Memory::YUZU_PAGEBITS; + const size_t offset = address & Memory::YUZU_PAGEMASK; + auto phys_addr = compressed_physical_ptr[index]; + if (phys_addr == 0) [[unlikely]] { + return nullptr; + } + return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + + offset); +} + +template <typename Traits> +template <typename T> +void DeviceMemoryManager<Traits>::Write(DAddr address, T value) { + T* ptr = GetPointer<T>(address); + if (!ptr) [[unlikely]] { + return; + } + std::memcpy(ptr, &value, sizeof(T)); +} + +template <typename Traits> +template <typename T> +T DeviceMemoryManager<Traits>::Read(DAddr address) const { + const T* ptr = GetPointer<T>(address); + T result{}; + if (!ptr) [[unlikely]] { + return result; + } + std::memcpy(&result, ptr, sizeof(T)); + return result; +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped, + auto on_memory, auto increment) { + std::size_t remaining_size = size; + std::size_t page_index = addr >> Memory::YUZU_PAGEBITS; + std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; + + while (remaining_size) { + const size_t next_pages = static_cast<std::size_t>(continuity_tracker[page_index]); + const std::size_t copy_amount = + 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({ + 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) { + on_unmapped(copy_amount, current_vaddr); + continue; + } + auto* mem_ptr = GetPointerFromRaw<u8>( + (static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset); + on_memory(copy_amount, mem_ptr); + } +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::ReadBlock(DAddr address, void* dest_pointer, size_t size) { + device_inter->FlushRegion(address, size); + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + std::memset(dest_pointer, 0, copy_amount); + }, + [&](size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_pointer, src_ptr, copy_amount); + }, + [&](const std::size_t copy_amount) { + dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount; + }); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::WriteBlock(DAddr address, const void* src_pointer, size_t size) { + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + }, + [&](size_t copy_amount, u8* const dst_ptr) { + std::memcpy(dst_ptr, src_pointer, copy_amount); + }, + [&](const std::size_t copy_amount) { + src_pointer = static_cast<const u8*>(src_pointer) + copy_amount; + }); + device_inter->InvalidateRegion(address, size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size) { + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + std::memset(dest_pointer, 0, copy_amount); + }, + [&](size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_pointer, src_ptr, copy_amount); + }, + [&](const std::size_t copy_amount) { + dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount; + }); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* src_pointer, + size_t size) { + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + }, + [&](size_t copy_amount, u8* const dst_ptr) { + std::memcpy(dst_ptr, src_pointer, copy_amount); + }, + [&](const std::size_t copy_amount) { + src_pointer = static_cast<const u8*>(src_pointer) + copy_amount; + }); +} + +template <typename Traits> +Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) { + size_t new_id{}; + if (!id_pool.empty()) { + new_id = id_pool.front(); + id_pool.pop_front(); + registered_processes[new_id] = memory_device_inter; + } else { + registered_processes.emplace_back(memory_device_inter); + new_id = registered_processes.size() - 1U; + } + return Asid{new_id}; +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) { + registered_processes[asid.id] = nullptr; + id_pool.push_front(asid.id); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) { + std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock); + const auto Lock = [&] { + if (!lk) { + lk.lock(); + } + }; + u64 uncache_begin = 0; + u64 cache_begin = 0; + u64 uncache_bytes = 0; + u64 cache_bytes = 0; + const auto MarkRegionCaching = &DeviceMemoryManager<Traits>::DeviceMethods::MarkRegionCaching; + + std::atomic_thread_fence(std::memory_order_acquire); + const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE); + size_t page = addr >> Memory::YUZU_PAGEBITS; + auto [asid, base_vaddress] = ExtractCPUBacking(page); + size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS; + auto* memory_device_inter = registered_processes[asid.id]; + for (; page != page_end; ++page) { + std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page); + + if (delta > 0) { + ASSERT_MSG(count.load(std::memory_order::relaxed) < std::numeric_limits<u8>::max(), + "Count may overflow!"); + } else if (delta < 0) { + ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!"); + } else { + ASSERT_MSG(false, "Delta must be non-zero!"); + } + + // Adds or subtracts 1, as count is a unsigned 8-bit value + count.fetch_add(static_cast<u8>(delta), std::memory_order_release); + + // Assume delta is either -1 or 1 + if (count.load(std::memory_order::relaxed) == 0) { + if (uncache_bytes == 0) { + uncache_begin = vpage; + } + uncache_bytes += Memory::YUZU_PAGESIZE; + } else if (uncache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, + uncache_bytes, false); + uncache_bytes = 0; + } + if (count.load(std::memory_order::relaxed) == 1 && delta > 0) { + if (cache_bytes == 0) { + cache_begin = vpage; + } + cache_bytes += Memory::YUZU_PAGESIZE; + } else if (cache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, + true); + cache_bytes = 0; + } + vpage++; + } + if (uncache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes, + false); + } + if (cache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, + true); + } +} + +} // namespace Core diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index c750c0da7..db667438e 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -4,9 +4,8 @@ #include <fmt/format.h> #include "common/fs/path_util.h" #include "core/file_sys/bis_factory.h" -#include "core/file_sys/mode.h" #include "core/file_sys/registered_cache.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { @@ -84,7 +83,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const { auto& keys = Core::Crypto::KeyManager::Instance(); Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( - Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)}; + Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), OpenMode::Read)}; keys.PopulateFromPartitionData(pdm); switch (id) { diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 26f0c6e5e..23680b60c 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -6,7 +6,7 @@ #include <memory> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 8b9a4fc5a..0bcf40cf8 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -13,8 +13,8 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" #include "core/file_sys/submission_package.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 9886123e7..97871da4a 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -8,7 +8,7 @@ #include <vector> #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { class KeyManager; diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 7d2f0abb8..285fe4db6 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -13,7 +13,7 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/loader/loader.h" #include "core/file_sys/fssystem/fssystem_compression_configuration.h" diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index af521d453..f68464eb0 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -13,7 +13,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 0697c29ae..f98594335 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -5,7 +5,7 @@ #include "common/string_util.h" #include "common/swap.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index c98efb00d..555b9d8f7 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -8,7 +8,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h deleted file mode 100644 index a853c00f3..000000000 --- a/src/core/file_sys/directory.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 <cstddef> -#include "common/common_funcs.h" -#include "common/common_types.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -enum class EntryType : u8 { - Directory = 0, - File = 1, -}; - -// Structure of a directory entry, from -// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry -struct Entry { - Entry(std::string_view view, EntryType entry_type, u64 entry_size) - : type{entry_type}, file_size{entry_size} { - const std::size_t copy_size = view.copy(filename, std::size(filename) - 1); - filename[copy_size] = '\0'; - } - - char filename[0x301]; - INSERT_PADDING_BYTES(3); - EntryType type; - INSERT_PADDING_BYTES(3); - u64 file_size; -}; -static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!"); -static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry."); -static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry."); - -} // namespace FileSys diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 2f5045a67..d4e0eb6f4 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -7,18 +7,13 @@ namespace FileSys { -constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; -constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2}; -constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002}; -constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001}; -constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005}; -constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223}; -constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001}; -constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061}; -constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062}; - +constexpr Result ResultPathNotFound{ErrorModule::FS, 1}; +constexpr Result ResultPathAlreadyExists{ErrorModule::FS, 2}; constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50}; constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001}; +constexpr Result ResultTargetNotFound{ErrorModule::FS, 1002}; +constexpr Result ResultPortSdCardNoDevice{ErrorModule::FS, 2001}; +constexpr Result ResultNotImplemented{ErrorModule::FS, 3001}; constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002}; constexpr Result ResultOutOfRange{ErrorModule::FS, 3005}; constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294}; @@ -78,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324}; constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325}; constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326}; constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327}; +constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328}; constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001}; +constexpr Result ResultInvalidPath{ErrorModule::FS, 6002}; +constexpr Result ResultTooLongPath{ErrorModule::FS, 6003}; +constexpr Result ResultInvalidCharacter{ErrorModule::FS, 6004}; +constexpr Result ResultInvalidPathFormat{ErrorModule::FS, 6005}; +constexpr Result ResultDirectoryUnobtainable{ErrorModule::FS, 6006}; +constexpr Result ResultNotNormalized{ErrorModule::FS, 6007}; constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061}; constexpr Result ResultInvalidSize{ErrorModule::FS, 6062}; constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063}; +constexpr Result ResultInvalidOpenMode{ErrorModule::FS, 6072}; +constexpr Result ResultFileExtensionWithoutOpenModeAllowAppend{ErrorModule::FS, 6201}; +constexpr Result ResultReadNotPermitted{ErrorModule::FS, 6202}; +constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203}; constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h new file mode 100644 index 000000000..25c9cb18a --- /dev/null +++ b/src/core/file_sys/fs_directory.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace FileSys { + +constexpr inline size_t EntryNameLengthMax = 0x300; + +struct DirectoryEntry { + DirectoryEntry(std::string_view view, s8 entry_type, u64 entry_size) + : type{entry_type}, file_size{static_cast<s64>(entry_size)} { + const std::size_t copy_size = view.copy(name, std::size(name) - 1); + name[copy_size] = '\0'; + } + + char name[EntryNameLengthMax + 1]; + INSERT_PADDING_BYTES(3); + s8 type; + INSERT_PADDING_BYTES(3); + s64 file_size; +}; + +static_assert(sizeof(DirectoryEntry) == 0x310, + "Directory Entry struct isn't exactly 0x310 bytes long!"); +static_assert(offsetof(DirectoryEntry, type) == 0x304, "Wrong offset for type in Entry."); +static_assert(offsetof(DirectoryEntry, file_size) == 0x308, "Wrong offset for file_size in Entry."); + +struct DirectoryHandle { + void* handle; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_file.h b/src/core/file_sys/fs_file.h new file mode 100644 index 000000000..4fb77e8db --- /dev/null +++ b/src/core/file_sys/fs_file.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace FileSys { + +struct ReadOption { + u32 value; + + static const ReadOption None; +}; + +enum ReadOptionFlag : u32 { + ReadOptionFlag_None = (0 << 0), +}; + +inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None}; + +inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) { + return lhs.value == rhs.value; +} + +inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) { + return !(lhs == rhs); +} + +static_assert(sizeof(ReadOption) == sizeof(u32)); + +enum WriteOptionFlag : u32 { + WriteOptionFlag_None = (0 << 0), + WriteOptionFlag_Flush = (1 << 0), +}; + +struct WriteOption { + u32 value; + + constexpr inline bool HasFlushFlag() const { + return value & WriteOptionFlag_Flush; + } + + static const WriteOption None; + static const WriteOption Flush; +}; + +inline constexpr const WriteOption WriteOption::None = {WriteOptionFlag_None}; +inline constexpr const WriteOption WriteOption::Flush = {WriteOptionFlag_Flush}; + +inline constexpr bool operator==(const WriteOption& lhs, const WriteOption& rhs) { + return lhs.value == rhs.value; +} + +inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) { + return !(lhs == rhs); +} + +static_assert(sizeof(WriteOption) == sizeof(u32)); + +struct FileHandle { + void* handle; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_filesystem.h b/src/core/file_sys/fs_filesystem.h new file mode 100644 index 000000000..7f237b7fa --- /dev/null +++ b/src/core/file_sys/fs_filesystem.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace FileSys { + +enum class OpenMode : u32 { + Read = (1 << 0), + Write = (1 << 1), + AllowAppend = (1 << 2), + + ReadWrite = (Read | Write), + All = (ReadWrite | AllowAppend), +}; +DECLARE_ENUM_FLAG_OPERATORS(OpenMode) + +enum class OpenDirectoryMode : u64 { + Directory = (1 << 0), + File = (1 << 1), + + All = (Directory | File), +}; +DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode) + +enum class DirectoryEntryType : u8 { + Directory = 0, + File = 1, +}; + +enum class CreateOption : u8 { + None = (0 << 0), + BigFile = (1 << 0), +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_memory_management.h b/src/core/file_sys/fs_memory_management.h new file mode 100644 index 000000000..f03c6354b --- /dev/null +++ b/src/core/file_sys/fs_memory_management.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include "common/alignment.h" + +namespace FileSys { + +constexpr size_t RequiredAlignment = alignof(u64); + +void* AllocateUnsafe(size_t size) { + // Allocate + void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment}); + + // Check alignment + ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(ptr), RequiredAlignment)); + + // Return allocated pointer + return ptr; +} + +void DeallocateUnsafe(void* ptr, size_t size) { + // Deallocate the pointer + ::operator delete(ptr, std::align_val_t{RequiredAlignment}); +} + +void* Allocate(size_t size) { + return AllocateUnsafe(size); +} + +void Deallocate(void* ptr, size_t size) { + // If the pointer is non-null, deallocate it + if (ptr != nullptr) { + DeallocateUnsafe(ptr, size); + } +} + +} // namespace FileSys diff --git a/src/core/file_sys/fs_operate_range.h b/src/core/file_sys/fs_operate_range.h new file mode 100644 index 000000000..04ea64cc0 --- /dev/null +++ b/src/core/file_sys/fs_operate_range.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace FileSys { + +enum class OperationId : s64 { + FillZero = 0, + DestroySignature = 1, + Invalidate = 2, + QueryRange = 3, + QueryUnpreparedRange = 4, + QueryLazyLoadCompletionRate = 5, + SetLazyLoadPriority = 6, + + ReadLazyLoadFileForciblyForDebug = 10001, +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_path.h b/src/core/file_sys/fs_path.h new file mode 100644 index 000000000..56ba08a6a --- /dev/null +++ b/src/core/file_sys/fs_path.h @@ -0,0 +1,566 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/common_funcs.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/fs_memory_management.h" +#include "core/file_sys/fs_path_utility.h" +#include "core/file_sys/fs_string_util.h" +#include "core/hle/result.h" + +namespace FileSys { +class DirectoryPathParser; + +class Path { + YUZU_NON_COPYABLE(Path); + YUZU_NON_MOVEABLE(Path); + +private: + static constexpr const char* EmptyPath = ""; + static constexpr size_t WriteBufferAlignmentLength = 8; + +private: + friend class DirectoryPathParser; + +public: + class WriteBuffer { + YUZU_NON_COPYABLE(WriteBuffer); + + private: + char* m_buffer; + size_t m_length_and_is_normalized; + + public: + constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) {} + + constexpr ~WriteBuffer() { + if (m_buffer != nullptr) { + Deallocate(m_buffer, this->GetLength()); + this->ResetBuffer(); + } + } + + constexpr WriteBuffer(WriteBuffer&& rhs) + : m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) { + rhs.ResetBuffer(); + } + + constexpr WriteBuffer& operator=(WriteBuffer&& rhs) { + if (m_buffer != nullptr) { + Deallocate(m_buffer, this->GetLength()); + } + + m_buffer = rhs.m_buffer; + m_length_and_is_normalized = rhs.m_length_and_is_normalized; + + rhs.ResetBuffer(); + + return *this; + } + + constexpr void ResetBuffer() { + m_buffer = nullptr; + this->SetLength(0); + } + + constexpr char* Get() const { + return m_buffer; + } + + constexpr size_t GetLength() const { + return m_length_and_is_normalized >> 1; + } + + constexpr bool IsNormalized() const { + return static_cast<bool>(m_length_and_is_normalized & 1); + } + + constexpr void SetNormalized() { + m_length_and_is_normalized |= static_cast<size_t>(1); + } + + constexpr void SetNotNormalized() { + m_length_and_is_normalized &= ~static_cast<size_t>(1); + } + + private: + constexpr WriteBuffer(char* buffer, size_t length) + : m_buffer(buffer), m_length_and_is_normalized(0) { + this->SetLength(length); + } + + public: + static WriteBuffer Make(size_t length) { + if (void* alloc = Allocate(length); alloc != nullptr) { + return WriteBuffer(static_cast<char*>(alloc), length); + } else { + return WriteBuffer(); + } + } + + private: + constexpr void SetLength(size_t size) { + m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1); + } + }; + +private: + const char* m_str; + WriteBuffer m_write_buffer; + +public: + constexpr Path() : m_str(EmptyPath), m_write_buffer() {} + + constexpr Path(const char* s) : m_str(s), m_write_buffer() { + m_write_buffer.SetNormalized(); + } + + constexpr ~Path() = default; + + constexpr Result SetShallowBuffer(const char* buffer) { + // Check pre-conditions + ASSERT(m_write_buffer.GetLength() == 0); + + // Check the buffer is valid + R_UNLESS(buffer != nullptr, ResultNullptrArgument); + + // Set buffer + this->SetReadOnlyBuffer(buffer); + + // Note that we're normalized + this->SetNormalized(); + + R_SUCCEED(); + } + + constexpr const char* GetString() const { + // Check pre-conditions + ASSERT(this->IsNormalized()); + + return m_str; + } + + constexpr size_t GetLength() const { + if (std::is_constant_evaluated()) { + return Strlen(this->GetString()); + } else { + return std::strlen(this->GetString()); + } + } + + constexpr bool IsEmpty() const { + return *m_str == '\x00'; + } + + constexpr bool IsMatchHead(const char* p, size_t len) const { + return Strncmp(this->GetString(), p, len) == 0; + } + + Result Initialize(const Path& rhs) { + // Check the other path is normalized + const bool normalized = rhs.IsNormalized(); + R_UNLESS(normalized, ResultNotNormalized); + + // Allocate buffer for our path + const auto len = rhs.GetLength(); + R_TRY(this->Preallocate(len + 1)); + + // Copy the path + const size_t copied = Strlcpy<char>(m_write_buffer.Get(), rhs.GetString(), len + 1); + R_UNLESS(copied == len, ResultUnexpectedInPathA); + + // Set normalized + this->SetNormalized(); + R_SUCCEED(); + } + + Result Initialize(const char* path, size_t len) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, len)); + + // Set not normalized + this->SetNotNormalized(); + + R_SUCCEED(); + } + + Result Initialize(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + R_RETURN(this->Initialize(path, std::strlen(path))); + } + + Result InitializeWithReplaceBackslash(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, std::strlen(path))); + + // Replace slashes as desired + if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) { + Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/'); + } + + // Set not normalized + this->SetNotNormalized(); + + R_SUCCEED(); + } + + Result InitializeWithReplaceForwardSlashes(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, std::strlen(path))); + + // Replace slashes as desired + if (m_write_buffer.GetLength() > 1) { + if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') { + p[0] = '\\'; + p[1] = '\\'; + } + } + + // Set not normalized + this->SetNotNormalized(); + + R_SUCCEED(); + } + + Result InitializeWithNormalization(const char* path, size_t size) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, size)); + + // Set not normalized + this->SetNotNormalized(); + + // Perform normalization + PathFlags path_flags; + if (IsPathRelative(m_str)) { + path_flags.AllowRelativePath(); + } else if (IsWindowsPath(m_str, true)) { + path_flags.AllowWindowsPath(); + } else { + /* NOTE: In this case, Nintendo checks is normalized, then sets is normalized, then + * returns success. */ + /* This seems like a bug. */ + size_t dummy; + bool normalized; + R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), + m_str)); + + this->SetNormalized(); + R_SUCCEED(); + } + + // Normalize + R_TRY(this->Normalize(path_flags)); + + this->SetNormalized(); + R_SUCCEED(); + } + + Result InitializeWithNormalization(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + R_RETURN(this->InitializeWithNormalization(path, std::strlen(path))); + } + + Result InitializeAsEmpty() { + // Clear our buffer + this->ClearBuffer(); + + // Set normalized + this->SetNormalized(); + + R_SUCCEED(); + } + + Result AppendChild(const char* child) { + // Check the path is valid + R_UNLESS(child != nullptr, ResultNullptrArgument); + + // Basic checks. If we have a path and the child is empty, we have nothing to do + const char* c = child; + if (m_str[0]) { + // Skip an early separator + if (*c == '/') { + ++c; + } + + R_SUCCEED_IF(*c == '\x00'); + } + + // If we don't have a string, we can just initialize + auto cur_len = std::strlen(m_str); + if (cur_len == 0) { + R_RETURN(this->Initialize(child)); + } + + // Remove a trailing separator + if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') { + --cur_len; + } + + // Get the child path's length + auto child_len = std::strlen(c); + + // Reset our write buffer + WriteBuffer old_write_buffer; + if (m_write_buffer.Get() != nullptr) { + old_write_buffer = std::move(m_write_buffer); + this->ClearBuffer(); + } + + // Pre-allocate the new buffer + R_TRY(this->Preallocate(cur_len + 1 + child_len + 1)); + + // Get our write buffer + auto* dst = m_write_buffer.Get(); + if (old_write_buffer.Get() != nullptr && cur_len > 0) { + Strlcpy<char>(dst, old_write_buffer.Get(), cur_len + 1); + } + + // Add separator + dst[cur_len] = '/'; + + // Copy the child path + const size_t copied = Strlcpy<char>(dst + cur_len + 1, c, child_len + 1); + R_UNLESS(copied == child_len, ResultUnexpectedInPathA); + + R_SUCCEED(); + } + + Result AppendChild(const Path& rhs) { + R_RETURN(this->AppendChild(rhs.GetString())); + } + + Result Combine(const Path& parent, const Path& child) { + // Get the lengths + const auto p_len = parent.GetLength(); + const auto c_len = child.GetLength(); + + // Allocate our buffer + R_TRY(this->Preallocate(p_len + c_len + 1)); + + // Initialize as parent + R_TRY(this->Initialize(parent)); + + // If we're empty, we can just initialize as child + if (this->IsEmpty()) { + R_TRY(this->Initialize(child)); + } else { + // Otherwise, we should append the child + R_TRY(this->AppendChild(child)); + } + + R_SUCCEED(); + } + + Result RemoveChild() { + // If we don't have a write-buffer, ensure that we have one + if (m_write_buffer.Get() == nullptr) { + if (const auto len = std::strlen(m_str); len > 0) { + R_TRY(this->Preallocate(len)); + Strlcpy<char>(m_write_buffer.Get(), m_str, len + 1); + } + } + + // Check that it's possible for us to remove a child + auto* p = m_write_buffer.Get(); + s32 len = std::strlen(p); + R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented); + + // Handle a trailing separator + if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) { + --len; + } + + // Remove the child path segment + while ((--len) >= 0 && p[len]) { + if (p[len] == '/' || p[len] == '\\') { + if (len > 0) { + p[len] = 0; + } else { + p[1] = 0; + len = 1; + } + break; + } + } + + // Check that length remains > 0 + R_UNLESS(len > 0, ResultNotImplemented); + + R_SUCCEED(); + } + + Result Normalize(const PathFlags& flags) { + // If we're already normalized, nothing to do + R_SUCCEED_IF(this->IsNormalized()); + + // Check if we're normalized + bool normalized; + size_t dummy; + R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), m_str, + flags)); + + // If we're not normalized, normalize + if (!normalized) { + // Determine necessary buffer length + auto len = m_write_buffer.GetLength(); + if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) { + len += 2; + } + if (flags.IsWindowsPathAllowed() && IsWindowsPath(m_str, true)) { + len += 1; + } + + // Allocate a new buffer + const size_t size = Common::AlignUp(len, WriteBufferAlignmentLength); + auto buf = WriteBuffer::Make(size); + R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); + + // Normalize into it + R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(), + m_write_buffer.GetLength(), flags)); + + // Set the normalized buffer as our buffer + this->SetModifiableBuffer(std::move(buf)); + } + + // Set normalized + this->SetNormalized(); + R_SUCCEED(); + } + +private: + void ClearBuffer() { + m_write_buffer.ResetBuffer(); + m_str = EmptyPath; + } + + void SetModifiableBuffer(WriteBuffer&& buffer) { + // Check pre-conditions + ASSERT(buffer.Get() != nullptr); + ASSERT(buffer.GetLength() > 0); + ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength)); + + // Get whether we're normalized + if (m_write_buffer.IsNormalized()) { + buffer.SetNormalized(); + } else { + buffer.SetNotNormalized(); + } + + // Set write buffer + m_write_buffer = std::move(buffer); + m_str = m_write_buffer.Get(); + } + + constexpr void SetReadOnlyBuffer(const char* buffer) { + m_str = buffer; + m_write_buffer.ResetBuffer(); + } + + Result Preallocate(size_t length) { + // Allocate additional space, if needed + if (length > m_write_buffer.GetLength()) { + // Allocate buffer + const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength); + auto buf = WriteBuffer::Make(size); + R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); + + // Set write buffer + this->SetModifiableBuffer(std::move(buf)); + } + + R_SUCCEED(); + } + + Result InitializeImpl(const char* path, size_t size) { + if (size > 0 && path[0]) { + // Pre allocate a buffer for the path + R_TRY(this->Preallocate(size + 1)); + + // Copy the path + const size_t copied = Strlcpy<char>(m_write_buffer.Get(), path, size + 1); + R_UNLESS(copied >= size, ResultUnexpectedInPathA); + } else { + // We can just clear the buffer + this->ClearBuffer(); + } + + R_SUCCEED(); + } + + constexpr char* GetWriteBuffer() { + ASSERT(m_write_buffer.Get() != nullptr); + return m_write_buffer.Get(); + } + + constexpr size_t GetWriteBufferLength() const { + return m_write_buffer.GetLength(); + } + + constexpr bool IsNormalized() const { + return m_write_buffer.IsNormalized(); + } + + constexpr void SetNormalized() { + m_write_buffer.SetNormalized(); + } + + constexpr void SetNotNormalized() { + m_write_buffer.SetNotNormalized(); + } + +public: + bool operator==(const FileSys::Path& rhs) const { + return std::strcmp(this->GetString(), rhs.GetString()) == 0; + } + bool operator!=(const FileSys::Path& rhs) const { + return !(*this == rhs); + } + bool operator==(const char* p) const { + return std::strcmp(this->GetString(), p) == 0; + } + bool operator!=(const char* p) const { + return !(*this == p); + } +}; + +inline Result SetUpFixedPath(FileSys::Path* out, const char* s) { + // Verify the path is normalized + bool normalized; + size_t dummy; + R_TRY(PathNormalizer::IsNormalized(std::addressof(normalized), std::addressof(dummy), s)); + + R_UNLESS(normalized, ResultInvalidPathFormat); + + // Set the fixed path + R_RETURN(out->SetShallowBuffer(s)); +} + +constexpr inline bool IsWindowsDriveRootPath(const FileSys::Path& path) { + const char* const str = path.GetString(); + return IsWindowsDrive(str) && + (str[2] == StringTraits::DirectorySeparator || + str[2] == StringTraits::AlternateDirectorySeparator) && + str[3] == StringTraits::NullTerminator; +} + +} // namespace FileSys diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h new file mode 100644 index 000000000..e9011d065 --- /dev/null +++ b/src/core/file_sys/fs_path_utility.h @@ -0,0 +1,1239 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/scope_exit.h" +#include "core/file_sys/fs_directory.h" +#include "core/file_sys/fs_memory_management.h" +#include "core/file_sys/fs_string_util.h" +#include "core/hle/result.h" + +namespace FileSys { + +constexpr inline size_t MountNameLengthMax = 15; + +namespace StringTraits { + +constexpr inline char DirectorySeparator = '/'; +constexpr inline char DriveSeparator = ':'; +constexpr inline char Dot = '.'; +constexpr inline char NullTerminator = '\x00'; + +constexpr inline char AlternateDirectorySeparator = '\\'; + +constexpr inline const char InvalidCharacters[6] = {':', '*', '?', '<', '>', '|'}; +constexpr inline const char InvalidCharactersForHostName[6] = {':', '*', '<', '>', '|', '$'}; +constexpr inline const char InvalidCharactersForMountName[5] = {'*', '?', '<', '>', '|'}; + +namespace impl { + +template <const char* InvalidCharacterSet, size_t NumInvalidCharacters> +consteval u64 MakeInvalidCharacterMask(size_t n) { + u64 mask = 0; + for (size_t i = 0; i < NumInvalidCharacters; ++i) { + if ((static_cast<u64>(InvalidCharacterSet[i]) >> 6) == n) { + mask |= static_cast<u64>(1) << (static_cast<u64>(InvalidCharacterSet[i]) & 0x3F); + } + } + return mask; +} + +template <const char* InvalidCharacterSet, size_t NumInvalidCharacters> +constexpr bool IsInvalidCharacterImpl(char c) { + constexpr u64 Masks[4] = { + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(0), + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(1), + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(2), + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(3)}; + + return (Masks[static_cast<u64>(c) >> 6] & + (static_cast<u64>(1) << (static_cast<u64>(c) & 0x3F))) != 0; +} + +} // namespace impl + +constexpr bool IsInvalidCharacter(char c) { + return impl::IsInvalidCharacterImpl<InvalidCharacters, Common::Size(InvalidCharacters)>(c); +} +constexpr bool IsInvalidCharacterForHostName(char c) { + return impl::IsInvalidCharacterImpl<InvalidCharactersForHostName, + Common::Size(InvalidCharactersForHostName)>(c); +} +constexpr bool IsInvalidCharacterForMountName(char c) { + return impl::IsInvalidCharacterImpl<InvalidCharactersForMountName, + Common::Size(InvalidCharactersForMountName)>(c); +} + +} // namespace StringTraits + +constexpr inline size_t WindowsDriveLength = 2; +constexpr inline size_t UncPathPrefixLength = 2; +constexpr inline size_t DosDevicePathPrefixLength = 4; + +class PathFlags { +private: + static constexpr u32 WindowsPathFlag = (1 << 0); + static constexpr u32 RelativePathFlag = (1 << 1); + static constexpr u32 EmptyPathFlag = (1 << 2); + static constexpr u32 MountNameFlag = (1 << 3); + static constexpr u32 BackslashFlag = (1 << 4); + static constexpr u32 AllCharactersFlag = (1 << 5); + +private: + u32 m_value; + +public: + constexpr PathFlags() : m_value(0) { /* ... */ + } + +#define DECLARE_PATH_FLAG_HANDLER(__WHICH__) \ + constexpr bool Is##__WHICH__##Allowed() const { return (m_value & __WHICH__##Flag) != 0; } \ + constexpr void Allow##__WHICH__() { m_value |= __WHICH__##Flag; } + + DECLARE_PATH_FLAG_HANDLER(WindowsPath) + DECLARE_PATH_FLAG_HANDLER(RelativePath) + DECLARE_PATH_FLAG_HANDLER(EmptyPath) + DECLARE_PATH_FLAG_HANDLER(MountName) + DECLARE_PATH_FLAG_HANDLER(Backslash) + DECLARE_PATH_FLAG_HANDLER(AllCharacters) + +#undef DECLARE_PATH_FLAG_HANDLER +}; + +template <typename T> + requires(std::same_as<T, char> || std::same_as<T, wchar_t>) +constexpr inline bool IsDosDevicePath(const T* path) { + ASSERT(path != nullptr); + + using namespace StringTraits; + + return path[0] == AlternateDirectorySeparator && path[1] == AlternateDirectorySeparator && + (path[2] == Dot || path[2] == '?') && + (path[3] == DirectorySeparator || path[3] == AlternateDirectorySeparator); +} + +template <typename T> + requires(std::same_as<T, char> || std::same_as<T, wchar_t>) +constexpr inline bool IsUncPath(const T* path, bool allow_forward_slash = true, + bool allow_back_slash = true) { + ASSERT(path != nullptr); + + using namespace StringTraits; + + return (allow_forward_slash && path[0] == DirectorySeparator && + path[1] == DirectorySeparator) || + (allow_back_slash && path[0] == AlternateDirectorySeparator && + path[1] == AlternateDirectorySeparator); +} + +constexpr inline bool IsWindowsDrive(const char* path) { + ASSERT(path != nullptr); + + return (('a' <= path[0] && path[0] <= 'z') || ('A' <= path[0] && path[0] <= 'Z')) && + path[1] == StringTraits::DriveSeparator; +} + +constexpr inline bool IsWindowsPath(const char* path, bool allow_forward_slash_unc) { + return IsWindowsDrive(path) || IsDosDevicePath(path) || + IsUncPath(path, allow_forward_slash_unc, true); +} + +constexpr inline int GetWindowsSkipLength(const char* path) { + if (IsDosDevicePath(path)) { + return DosDevicePathPrefixLength; + } else if (IsWindowsDrive(path)) { + return WindowsDriveLength; + } else if (IsUncPath(path)) { + return UncPathPrefixLength; + } else { + return 0; + } +} + +constexpr inline bool IsPathAbsolute(const char* path) { + return IsWindowsPath(path, false) || path[0] == StringTraits::DirectorySeparator; +} + +constexpr inline bool IsPathRelative(const char* path) { + return path[0] && !IsPathAbsolute(path); +} + +constexpr inline bool IsCurrentDirectory(const char* path) { + return path[0] == StringTraits::Dot && + (path[1] == StringTraits::NullTerminator || path[1] == StringTraits::DirectorySeparator); +} + +constexpr inline bool IsParentDirectory(const char* path) { + return path[0] == StringTraits::Dot && path[1] == StringTraits::Dot && + (path[2] == StringTraits::NullTerminator || path[2] == StringTraits::DirectorySeparator); +} + +constexpr inline bool IsPathStartWithCurrentDirectory(const char* path) { + return IsCurrentDirectory(path) || IsParentDirectory(path); +} + +constexpr inline bool IsSubPath(const char* lhs, const char* rhs) { + // Check pre-conditions + ASSERT(lhs != nullptr); + ASSERT(rhs != nullptr); + + // Import StringTraits names for current scope + using namespace StringTraits; + + // Special case certain paths + if (IsUncPath(lhs) && !IsUncPath(rhs)) { + return false; + } + if (!IsUncPath(lhs) && IsUncPath(rhs)) { + return false; + } + + if (lhs[0] == DirectorySeparator && lhs[1] == NullTerminator && rhs[0] == DirectorySeparator && + rhs[1] != NullTerminator) { + return true; + } + if (rhs[0] == DirectorySeparator && rhs[1] == NullTerminator && lhs[0] == DirectorySeparator && + lhs[1] != NullTerminator) { + return true; + } + + // Check subpath + for (size_t i = 0; /* ... */; ++i) { + if (lhs[i] == NullTerminator) { + return rhs[i] == DirectorySeparator; + } else if (rhs[i] == NullTerminator) { + return lhs[i] == DirectorySeparator; + } else if (lhs[i] != rhs[i]) { + return false; + } + } +} + +// Path utilities +constexpr inline void Replace(char* dst, size_t dst_size, char old_char, char new_char) { + ASSERT(dst != nullptr); + for (char* cur = dst; cur < dst + dst_size && *cur; ++cur) { + if (*cur == old_char) { + *cur = new_char; + } + } +} + +constexpr inline Result CheckUtf8(const char* s) { + // Check pre-conditions + ASSERT(s != nullptr); + + // Iterate, checking for utf8-validity + while (*s) { + char utf8_buf[4] = {}; + + const auto pick_res = PickOutCharacterFromUtf8String(utf8_buf, std::addressof(s)); + R_UNLESS(pick_res == CharacterEncodingResult_Success, ResultInvalidPathFormat); + + u32 dummy; + const auto cvt_res = ConvertCharacterUtf8ToUtf32(std::addressof(dummy), utf8_buf); + R_UNLESS(cvt_res == CharacterEncodingResult_Success, ResultInvalidPathFormat); + } + + R_SUCCEED(); +} + +// Path formatting +class PathNormalizer { +private: + enum class PathState { + Start, + Normal, + FirstSeparator, + Separator, + CurrentDir, + ParentDir, + }; + +private: + static constexpr void ReplaceParentDirectoryPath(char* dst, const char* src) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Start with a dir-separator + dst[0] = DirectorySeparator; + + auto i = 1; + while (src[i] != NullTerminator) { + if ((src[i - 1] == DirectorySeparator || src[i - 1] == AlternateDirectorySeparator) && + src[i + 0] == Dot && src[i + 1] == Dot && + (src[i + 2] == DirectorySeparator || src[i + 2] == AlternateDirectorySeparator)) { + dst[i - 1] = DirectorySeparator; + dst[i + 0] = Dot; + dst[i + 1] = Dot; + dst[i + 2] = DirectorySeparator; + i += 3; + } else { + if (src[i - 1] == AlternateDirectorySeparator && src[i + 0] == Dot && + src[i + 1] == Dot && src[i + 2] == NullTerminator) { + dst[i - 1] = DirectorySeparator; + dst[i + 0] = Dot; + dst[i + 1] = Dot; + i += 2; + break; + } + + dst[i] = src[i]; + ++i; + } + } + + dst[i] = StringTraits::NullTerminator; + } + +public: + static constexpr bool IsParentDirectoryPathReplacementNeeded(const char* path) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + if (path[0] != DirectorySeparator && path[0] != AlternateDirectorySeparator) { + return false; + } + + // Check to find a parent reference using alternate separators + if (path[0] != NullTerminator && path[1] != NullTerminator && path[2] != NullTerminator) { + size_t i; + for (i = 0; path[i + 3] != NullTerminator; ++path) { + if (path[i + 1] != Dot || path[i + 2] != Dot) { + continue; + } + + const char c0 = path[i + 0]; + const char c3 = path[i + 3]; + + if (c0 == AlternateDirectorySeparator && + (c3 == DirectorySeparator || c3 == AlternateDirectorySeparator || + c3 == NullTerminator)) { + return true; + } + + if (c3 == AlternateDirectorySeparator && + (c0 == DirectorySeparator || c0 == AlternateDirectorySeparator)) { + return true; + } + } + + if (path[i + 0] == AlternateDirectorySeparator && path[i + 1] == Dot && + path[i + 2] == Dot /* && path[i + 3] == NullTerminator */) { + return true; + } + } + + return false; + } + + static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path, + bool allow_all_characters = false) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Parse the path + auto state = PathState::Start; + size_t len = 0; + while (path[len] != NullTerminator) { + // Get the current character + const char c = path[len++]; + + // Check the current character is valid + if (!allow_all_characters && state != PathState::Start) { + R_UNLESS(!IsInvalidCharacter(c), ResultInvalidCharacter); + } + + // Process depending on current state + switch (state) { + // Import the PathState enums for convenience + using enum PathState; + + case Start: + R_UNLESS(c == DirectorySeparator, ResultInvalidPathFormat); + state = FirstSeparator; + break; + case Normal: + if (c == DirectorySeparator) { + state = Separator; + } + break; + case FirstSeparator: + case Separator: + if (c == DirectorySeparator) { + *out = false; + R_SUCCEED(); + } + + if (c == Dot) { + state = CurrentDir; + } else { + state = Normal; + } + break; + case CurrentDir: + if (c == DirectorySeparator) { + *out = false; + R_SUCCEED(); + } + + if (c == Dot) { + state = ParentDir; + } else { + state = Normal; + } + break; + case ParentDir: + if (c == DirectorySeparator) { + *out = false; + R_SUCCEED(); + } + + state = Normal; + break; + default: + UNREACHABLE(); + break; + } + } + + // Check the final state + switch (state) { + // Import the PathState enums for convenience + using enum PathState; + case Start: + R_THROW(ResultInvalidPathFormat); + case Normal: + case FirstSeparator: + *out = true; + break; + case Separator: + case CurrentDir: + case ParentDir: + *out = false; + break; + default: + UNREACHABLE(); + break; + } + + // Set the output length + *out_len = len; + R_SUCCEED(); + } + + static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size, + bool is_windows_path, bool is_drive_relative_path, + bool allow_all_characters = false) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Prepare to iterate + const char* cur_path = path; + size_t total_len = 0; + + // If path begins with a separator, check that we're not drive relative + if (cur_path[0] != DirectorySeparator) { + R_UNLESS(is_drive_relative_path, ResultInvalidPathFormat); + + dst[total_len++] = DirectorySeparator; + } + + // We're going to need to do path replacement, potentially + char* replacement_path = nullptr; + size_t replacement_path_size = 0; + + SCOPE_EXIT({ + if (replacement_path != nullptr) { + if (std::is_constant_evaluated()) { + delete[] replacement_path; + } else { + Deallocate(replacement_path, replacement_path_size); + } + } + }); + + // Perform path replacement, if necessary + if (IsParentDirectoryPathReplacementNeeded(cur_path)) { + if (std::is_constant_evaluated()) { + replacement_path_size = EntryNameLengthMax + 1; + replacement_path = new char[replacement_path_size]; + } else { + replacement_path_size = EntryNameLengthMax + 1; + replacement_path = static_cast<char*>(Allocate(replacement_path_size)); + } + + ReplaceParentDirectoryPath(replacement_path, cur_path); + + cur_path = replacement_path; + } + + // Iterate, normalizing path components + bool skip_next_sep = false; + size_t i = 0; + + while (cur_path[i] != NullTerminator) { + // Process a directory separator, if we run into one + if (cur_path[i] == DirectorySeparator) { + // Swallow separators + do { + ++i; + } while (cur_path[i] == DirectorySeparator); + + // Check if we hit end of string + if (cur_path[i] == NullTerminator) { + break; + } + + // If we aren't skipping the separator, write it, checking that we remain in bounds. + if (!skip_next_sep) { + if (total_len + 1 == max_out_size) { + dst[total_len] = NullTerminator; + *out_len = total_len; + R_THROW(ResultTooLongPath); + } + + dst[total_len++] = DirectorySeparator; + } + + // Don't skip the next separator + skip_next_sep = false; + } + + // Get the length of the current directory component + size_t dir_len = 0; + while (cur_path[i + dir_len] != DirectorySeparator && + cur_path[i + dir_len] != NullTerminator) { + // Check for validity + if (!allow_all_characters) { + R_UNLESS(!IsInvalidCharacter(cur_path[i + dir_len]), ResultInvalidCharacter); + } + + ++dir_len; + } + + // Handle the current dir component + if (IsCurrentDirectory(cur_path + i)) { + skip_next_sep = true; + } else if (IsParentDirectory(cur_path + i)) { + // We should have just written a separator + ASSERT(dst[total_len - 1] == DirectorySeparator); + + // We should have started with a separator, for non-windows paths + if (!is_windows_path) { + ASSERT(dst[0] == DirectorySeparator); + } + + // Remove the previous component + if (total_len == 1) { + R_UNLESS(is_windows_path, ResultDirectoryUnobtainable); + + --total_len; + } else { + total_len -= 2; + + do { + if (dst[total_len] == DirectorySeparator) { + break; + } + } while ((--total_len) != 0); + } + + // We should be pointing to a directory separator, for non-windows paths + if (!is_windows_path) { + ASSERT(dst[total_len] == DirectorySeparator); + } + + // We should remain in bounds + ASSERT(total_len < max_out_size); + } else { + // Copy, possibly truncating + if (total_len + dir_len + 1 > max_out_size) { + const size_t copy_len = max_out_size - (total_len + 1); + + for (size_t j = 0; j < copy_len; ++j) { + dst[total_len++] = cur_path[i + j]; + } + + dst[total_len] = NullTerminator; + *out_len = total_len; + R_THROW(ResultTooLongPath); + } + + for (size_t j = 0; j < dir_len; ++j) { + dst[total_len++] = cur_path[i + j]; + } + } + + // Advance past the current directory component + i += dir_len; + } + + if (skip_next_sep) { + --total_len; + } + + if (total_len == 0 && max_out_size != 0) { + total_len = 1; + dst[0] = DirectorySeparator; + } + + // NOTE: Probable nintendo bug, as max_out_size must be at least total_len + 1 for the null + // terminator. + R_UNLESS(max_out_size >= total_len - 1, ResultTooLongPath); + + dst[total_len] = NullTerminator; + + // Check that the result path is normalized + bool is_normalized; + size_t dummy; + R_TRY(IsNormalized(std::addressof(is_normalized), std::addressof(dummy), dst, + allow_all_characters)); + + // Assert that the result path is normalized + ASSERT(is_normalized); + + // Set the output length + *out_len = total_len; + R_SUCCEED(); + } +}; + +class PathFormatter { +private: + static constexpr Result CheckSharedName(const char* name, size_t len) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + if (len == 1) { + R_UNLESS(name[0] != Dot, ResultInvalidPathFormat); + } else if (len == 2) { + R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat); + } + + for (size_t i = 0; i < len; ++i) { + R_UNLESS(!IsInvalidCharacter(name[i]), ResultInvalidCharacter); + } + + R_SUCCEED(); + } + + static constexpr Result CheckHostName(const char* name, size_t len) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + if (len == 2) { + R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat); + } + + for (size_t i = 0; i < len; ++i) { + R_UNLESS(!IsInvalidCharacterForHostName(name[i]), ResultInvalidCharacter); + } + + R_SUCCEED(); + } + + static constexpr Result CheckInvalidBackslash(bool* out_contains_backslash, const char* path, + bool allow_backslash) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Default to no backslashes, so we can just write if we see one + *out_contains_backslash = false; + + while (*path != NullTerminator) { + if (*(path++) == AlternateDirectorySeparator) { + *out_contains_backslash = true; + + R_UNLESS(allow_backslash, ResultInvalidCharacter); + } + } + + R_SUCCEED(); + } + +public: + static constexpr Result CheckPathFormat(const char* path, const PathFlags& flags) { + bool normalized; + size_t len; + R_RETURN(IsNormalized(std::addressof(normalized), std::addressof(len), path, flags)); + } + + static constexpr Result SkipMountName(const char** out, size_t* out_len, const char* path) { + R_RETURN(ParseMountName(out, out_len, nullptr, 0, path)); + } + + static constexpr Result ParseMountName(const char** out, size_t* out_len, char* out_mount_name, + size_t out_mount_name_buffer_size, const char* path) { + // Check pre-conditions + ASSERT(path != nullptr); + ASSERT(out_len != nullptr); + ASSERT(out != nullptr); + ASSERT((out_mount_name == nullptr) == (out_mount_name_buffer_size == 0)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Determine max mount length + const auto max_mount_len = + out_mount_name_buffer_size == 0 + ? MountNameLengthMax + 1 + : std::min(MountNameLengthMax + 1, out_mount_name_buffer_size); + + // Parse the path until we see a drive separator + size_t mount_len = 0; + for (/* ... */; mount_len < max_mount_len && path[mount_len]; ++mount_len) { + const char c = path[mount_len]; + + // If we see a drive separator, advance, then we're done with the pre-drive separator + // part of the mount. + if (c == DriveSeparator) { + ++mount_len; + break; + } + + // If we see a directory separator, we're not in a mount name + if (c == DirectorySeparator || c == AlternateDirectorySeparator) { + *out = path; + *out_len = 0; + R_SUCCEED(); + } + } + + // Check to be sure we're actually looking at a mount name + if (mount_len <= 2 || path[mount_len - 1] != DriveSeparator) { + *out = path; + *out_len = 0; + R_SUCCEED(); + } + + // Check that all characters in the mount name are allowable + for (size_t i = 0; i < mount_len; ++i) { + R_UNLESS(!IsInvalidCharacterForMountName(path[i]), ResultInvalidCharacter); + } + + // Copy out the mount name + if (out_mount_name_buffer_size > 0) { + R_UNLESS(mount_len < out_mount_name_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < mount_len; ++i) { + out_mount_name[i] = path[i]; + } + out_mount_name[mount_len] = NullTerminator; + } + + // Set the output + *out = path + mount_len; + *out_len = mount_len; + R_SUCCEED(); + } + + static constexpr Result SkipRelativeDotPath(const char** out, size_t* out_len, + const char* path) { + R_RETURN(ParseRelativeDotPath(out, out_len, nullptr, 0, path)); + } + + static constexpr Result ParseRelativeDotPath(const char** out, size_t* out_len, + char* out_relative, + size_t out_relative_buffer_size, + const char* path) { + // Check pre-conditions + ASSERT(path != nullptr); + ASSERT(out_len != nullptr); + ASSERT(out != nullptr); + ASSERT((out_relative == nullptr) == (out_relative_buffer_size == 0)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Initialize the output buffer, if we have one + if (out_relative_buffer_size > 0) { + out_relative[0] = NullTerminator; + } + + // Check if the path is relative + if (path[0] == Dot && (path[1] == NullTerminator || path[1] == DirectorySeparator || + path[1] == AlternateDirectorySeparator)) { + if (out_relative_buffer_size > 0) { + R_UNLESS(out_relative_buffer_size >= 2, ResultTooLongPath); + + out_relative[0] = Dot; + out_relative[1] = NullTerminator; + } + + *out = path + 1; + *out_len = 1; + R_SUCCEED(); + } + + // Ensure the path isn't a parent directory + R_UNLESS(!(path[0] == Dot && path[1] == Dot), ResultDirectoryUnobtainable); + + // There was no relative dot path + *out = path; + *out_len = 0; + R_SUCCEED(); + } + + static constexpr Result SkipWindowsPath(const char** out, size_t* out_len, bool* out_normalized, + const char* path, bool has_mount_name) { + // We're normalized if and only if the parsing doesn't throw ResultNotNormalized() + *out_normalized = true; + + R_TRY_CATCH(ParseWindowsPath(out, out_len, nullptr, 0, path, has_mount_name)) { + R_CATCH(ResultNotNormalized) { + *out_normalized = false; + } + } + R_END_TRY_CATCH; + ON_RESULT_INCLUDED(ResultNotNormalized) { + *out_normalized = false; + }; + + R_SUCCEED(); + } + + static constexpr Result ParseWindowsPath(const char** out, size_t* out_len, char* out_win, + size_t out_win_buffer_size, const char* path, + bool has_mount_name) { + // Check pre-conditions + ASSERT(path != nullptr); + ASSERT(out_len != nullptr); + ASSERT(out != nullptr); + ASSERT((out_win == nullptr) == (out_win_buffer_size == 0)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Initialize the output buffer, if we have one + if (out_win_buffer_size > 0) { + out_win[0] = NullTerminator; + } + + // Handle path start + const char* cur_path = path; + if (has_mount_name && path[0] == DirectorySeparator) { + if (path[1] == AlternateDirectorySeparator && path[2] == AlternateDirectorySeparator) { + R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized); + + ++cur_path; + } else if (IsWindowsDrive(path + 1)) { + R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized); + + ++cur_path; + } + } + + // Handle windows drive + if (IsWindowsDrive(cur_path)) { + // Parse up to separator + size_t win_path_len = WindowsDriveLength; + for (/* ... */; cur_path[win_path_len] != NullTerminator; ++win_path_len) { + R_UNLESS(!IsInvalidCharacter(cur_path[win_path_len]), ResultInvalidCharacter); + + if (cur_path[win_path_len] == DirectorySeparator || + cur_path[win_path_len] == AlternateDirectorySeparator) { + break; + } + } + + // Ensure that we're normalized, if we're required to be + if (out_win_buffer_size == 0) { + for (size_t i = 0; i < win_path_len; ++i) { + R_UNLESS(cur_path[i] != AlternateDirectorySeparator, ResultNotNormalized); + } + } else { + // Ensure we can copy into the normalized buffer + R_UNLESS(win_path_len < out_win_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < win_path_len; ++i) { + out_win[i] = cur_path[i]; + } + out_win[win_path_len] = NullTerminator; + + Replace(out_win, win_path_len, AlternateDirectorySeparator, DirectorySeparator); + } + + *out = cur_path + win_path_len; + *out_len = win_path_len; + R_SUCCEED(); + } + + // Handle DOS device + if (IsDosDevicePath(cur_path)) { + size_t dos_prefix_len = DosDevicePathPrefixLength; + + if (IsWindowsDrive(cur_path + dos_prefix_len)) { + dos_prefix_len += WindowsDriveLength; + } else { + --dos_prefix_len; + } + + if (out_win_buffer_size > 0) { + // Ensure we can copy into the normalized buffer + R_UNLESS(dos_prefix_len < out_win_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < dos_prefix_len; ++i) { + out_win[i] = cur_path[i]; + } + out_win[dos_prefix_len] = NullTerminator; + + Replace(out_win, dos_prefix_len, DirectorySeparator, AlternateDirectorySeparator); + } + + *out = cur_path + dos_prefix_len; + *out_len = dos_prefix_len; + R_SUCCEED(); + } + + // Handle UNC path + if (IsUncPath(cur_path, false, true)) { + const char* final_path = cur_path; + + R_UNLESS(cur_path[UncPathPrefixLength] != DirectorySeparator, ResultInvalidPathFormat); + R_UNLESS(cur_path[UncPathPrefixLength] != AlternateDirectorySeparator, + ResultInvalidPathFormat); + + size_t cur_component_offset = 0; + size_t pos = UncPathPrefixLength; + for (/* ... */; cur_path[pos] != NullTerminator; ++pos) { + if (cur_path[pos] == DirectorySeparator || + cur_path[pos] == AlternateDirectorySeparator) { + if (cur_component_offset != 0) { + R_TRY(CheckSharedName(cur_path + cur_component_offset, + pos - cur_component_offset)); + + final_path = cur_path + pos; + break; + } + + R_UNLESS(cur_path[pos + 1] != DirectorySeparator, ResultInvalidPathFormat); + R_UNLESS(cur_path[pos + 1] != AlternateDirectorySeparator, + ResultInvalidPathFormat); + + R_TRY(CheckHostName(cur_path + 2, pos - 2)); + + cur_component_offset = pos + 1; + } + } + + R_UNLESS(cur_component_offset != pos, ResultInvalidPathFormat); + + if (cur_component_offset != 0 && final_path == cur_path) { + R_TRY(CheckSharedName(cur_path + cur_component_offset, pos - cur_component_offset)); + + final_path = cur_path + pos; + } + + size_t unc_prefix_len = final_path - cur_path; + + // Ensure that we're normalized, if we're required to be + if (out_win_buffer_size == 0) { + for (size_t i = 0; i < unc_prefix_len; ++i) { + R_UNLESS(cur_path[i] != DirectorySeparator, ResultNotNormalized); + } + } else { + // Ensure we can copy into the normalized buffer + R_UNLESS(unc_prefix_len < out_win_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < unc_prefix_len; ++i) { + out_win[i] = cur_path[i]; + } + out_win[unc_prefix_len] = NullTerminator; + + Replace(out_win, unc_prefix_len, DirectorySeparator, AlternateDirectorySeparator); + } + + *out = cur_path + unc_prefix_len; + *out_len = unc_prefix_len; + R_SUCCEED(); + } + + // There's no windows path to parse + *out = path; + *out_len = 0; + R_SUCCEED(); + } + + static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path, + const PathFlags& flags = {}) { + // Ensure nothing is null + R_UNLESS(out != nullptr, ResultNullptrArgument); + R_UNLESS(out_len != nullptr, ResultNullptrArgument); + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Verify that the path is valid utf-8 + R_TRY(CheckUtf8(path)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Handle the case where the path is empty + if (path[0] == NullTerminator) { + R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat); + + *out = true; + *out_len = 0; + R_SUCCEED(); + } + + // All normalized paths start with a directory separator...unless they're windows paths, + // relative paths, or have mount names. + if (path[0] != DirectorySeparator) { + R_UNLESS(flags.IsWindowsPathAllowed() || flags.IsRelativePathAllowed() || + flags.IsMountNameAllowed(), + ResultInvalidPathFormat); + } + + // Check that the path is allowed to be a windows path, if it is + if (IsWindowsPath(path, false)) { + R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); + } + + // Skip past the mount name, if one is present + size_t total_len = 0; + size_t mount_name_len = 0; + R_TRY(SkipMountName(std::addressof(path), std::addressof(mount_name_len), path)); + + // If we had a mount name, check that that was allowed + if (mount_name_len > 0) { + R_UNLESS(flags.IsMountNameAllowed(), ResultInvalidPathFormat); + + total_len += mount_name_len; + } + + // Check that the path starts as a normalized path should + if (path[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(path) && + !IsWindowsPath(path, false)) { + R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); + R_UNLESS(!IsInvalidCharacter(path[0]), ResultInvalidPathFormat); + + *out = false; + R_SUCCEED(); + } + + // Process relative path + size_t relative_len = 0; + R_TRY(SkipRelativeDotPath(std::addressof(path), std::addressof(relative_len), path)); + + // If we have a relative path, check that was allowed + if (relative_len > 0) { + R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); + + total_len += relative_len; + + if (path[0] == NullTerminator) { + *out = true; + *out_len = total_len; + R_SUCCEED(); + } + } + + // Process windows path + size_t windows_len = 0; + bool normalized_win = false; + R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(windows_len), + std::addressof(normalized_win), path, mount_name_len > 0)); + + // If the windows path wasn't normalized, we're not normalized + if (!normalized_win) { + R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); + + *out = false; + R_SUCCEED(); + } + + // If we had a windows path, check that was allowed + if (windows_len > 0) { + R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); + + total_len += windows_len; + + // We can't have both a relative path and a windows path + R_UNLESS(relative_len == 0, ResultInvalidPathFormat); + + // A path ending in a windows path isn't normalized + if (path[0] == NullTerminator) { + *out = false; + R_SUCCEED(); + } + + // Check that there are no windows directory separators in the path + for (size_t i = 0; path[i] != NullTerminator; ++i) { + if (path[i] == AlternateDirectorySeparator) { + *out = false; + R_SUCCEED(); + } + } + } + + // Check that parent directory replacement is not needed if backslashes are allowed + if (flags.IsBackslashAllowed() && + PathNormalizer::IsParentDirectoryPathReplacementNeeded(path)) { + *out = false; + R_SUCCEED(); + } + + // Check that the backslash state is valid + bool is_backslash_contained = false; + R_TRY(CheckInvalidBackslash(std::addressof(is_backslash_contained), path, + flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed())); + + // Check that backslashes are contained only if allowed + if (is_backslash_contained && !flags.IsBackslashAllowed()) { + *out = false; + R_SUCCEED(); + } + + // Check that the final result path is normalized + size_t normal_len = 0; + R_TRY(PathNormalizer::IsNormalized(out, std::addressof(normal_len), path, + flags.IsAllCharactersAllowed())); + + // Add the normal length + total_len += normal_len; + + // Set the output length + *out_len = total_len; + R_SUCCEED(); + } + + static 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; + + // Prepare to iterate + const char* src = path; + size_t cur_pos = 0; + bool is_windows_path = false; + + // Check if the path is empty + if (src[0] == NullTerminator) { + if (dst_size != 0) { + dst[0] = NullTerminator; + } + + R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat); + + R_SUCCEED(); + } + + // Handle a mount name + size_t mount_name_len = 0; + if (flags.IsMountNameAllowed()) { + R_TRY(ParseMountName(std::addressof(src), std::addressof(mount_name_len), dst + cur_pos, + dst_size - cur_pos, src)); + + cur_pos += mount_name_len; + } + + // Handle a drive-relative prefix + bool is_drive_relative = false; + if (src[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(src) && + !IsWindowsPath(src, false)) { + R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); + R_UNLESS(!IsInvalidCharacter(src[0]), ResultInvalidPathFormat); + + dst[cur_pos++] = Dot; + is_drive_relative = true; + } + + size_t relative_len = 0; + if (flags.IsRelativePathAllowed()) { + R_UNLESS(cur_pos < dst_size, ResultTooLongPath); + + R_TRY(ParseRelativeDotPath(std::addressof(src), std::addressof(relative_len), + dst + cur_pos, dst_size - cur_pos, src)); + + cur_pos += relative_len; + + if (src[0] == NullTerminator) { + R_UNLESS(cur_pos < dst_size, ResultTooLongPath); + + dst[cur_pos] = NullTerminator; + R_SUCCEED(); + } + } + + // Handle a windows path + if (flags.IsWindowsPathAllowed()) { + const char* const orig = src; + + R_UNLESS(cur_pos < dst_size, ResultTooLongPath); + + size_t windows_len = 0; + R_TRY(ParseWindowsPath(std::addressof(src), std::addressof(windows_len), dst + cur_pos, + dst_size - cur_pos, src, mount_name_len != 0)); + + cur_pos += windows_len; + + if (src[0] == NullTerminator) { + /* NOTE: Bug in original code here repeated, should be checking cur_pos + 2. */ + R_UNLESS(cur_pos + 1 < dst_size, ResultTooLongPath); + + dst[cur_pos + 0] = DirectorySeparator; + dst[cur_pos + 1] = NullTerminator; + R_SUCCEED(); + } + + if ((src - orig) > 0) { + is_windows_path = true; + } + } + + // Check for invalid backslash + bool backslash_contained = false; + R_TRY(CheckInvalidBackslash(std::addressof(backslash_contained), src, + flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed())); + + // Handle backslash replacement as necessary + if (backslash_contained && flags.IsWindowsPathAllowed()) { + // Create a temporary buffer holding a slash-replaced version of the path. + // NOTE: Nintendo unnecessarily allocates and replaces here a fully copy of the path, + // despite having skipped some of it already. + const size_t replaced_src_len = path_len - (src - path); + + char* replaced_src = nullptr; + SCOPE_EXIT({ + if (replaced_src != nullptr) { + if (std::is_constant_evaluated()) { + delete[] replaced_src; + } else { + Deallocate(replaced_src, replaced_src_len); + } + } + }); + + if (std::is_constant_evaluated()) { + replaced_src = new char[replaced_src_len]; + } else { + replaced_src = static_cast<char*>(Allocate(replaced_src_len)); + } + + Strlcpy<char>(replaced_src, src, replaced_src_len); + + Replace(replaced_src, replaced_src_len, AlternateDirectorySeparator, + DirectorySeparator); + + size_t dummy; + R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), replaced_src, + dst_size - cur_pos, is_windows_path, is_drive_relative, + flags.IsAllCharactersAllowed())); + } else { + // We can just do normalization + size_t dummy; + R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), src, + dst_size - cur_pos, is_windows_path, is_drive_relative, + flags.IsAllCharactersAllowed())); + } + + R_SUCCEED(); + } +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_string_util.h b/src/core/file_sys/fs_string_util.h new file mode 100644 index 000000000..874e09054 --- /dev/null +++ b/src/core/file_sys/fs_string_util.h @@ -0,0 +1,226 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" + +namespace FileSys { + +template <typename T> +constexpr int Strlen(const T* str) { + ASSERT(str != nullptr); + + int length = 0; + while (*str++) { + ++length; + } + + return length; +} + +template <typename T> +constexpr int Strnlen(const T* str, int count) { + ASSERT(str != nullptr); + ASSERT(count >= 0); + + int length = 0; + while (count-- && *str++) { + ++length; + } + + return length; +} + +template <typename T> +constexpr int Strncmp(const T* lhs, const T* rhs, int count) { + ASSERT(lhs != nullptr); + ASSERT(rhs != nullptr); + ASSERT(count >= 0); + + if (count == 0) { + return 0; + } + + T l, r; + do { + l = *(lhs++); + r = *(rhs++); + } while (l && (l == r) && (--count)); + + return l - r; +} + +template <typename T> +static constexpr int Strlcpy(T* dst, const T* src, int count) { + ASSERT(dst != nullptr); + ASSERT(src != nullptr); + + const T* cur = src; + if (count > 0) { + while ((--count) && *cur) { + *(dst++) = *(cur++); + } + *dst = 0; + } + + while (*cur) { + cur++; + } + + return static_cast<int>(cur - src); +} + +enum CharacterEncodingResult { + CharacterEncodingResult_Success = 0, + CharacterEncodingResult_InsufficientLength = 1, + CharacterEncodingResult_InvalidFormat = 2, +}; + +namespace impl { + +class CharacterEncodingHelper { +public: + static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = { + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, + }; + + static constexpr char GetUtf8NBytes(size_t i) { + return static_cast<char>(Utf8NBytesInnerTable[1 + i]); + } +}; + +} // namespace impl + +constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) { + // Check pre-conditions + ASSERT(dst != nullptr); + ASSERT(src != nullptr); + + // Perform the conversion + const auto* p = src; + switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) { + case 1: + *dst = static_cast<u32>(p[0]); + return CharacterEncodingResult_Success; + case 2: + if ((static_cast<u32>(p[0]) & 0x1E) != 0) { + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == + 0) { + *dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0); + return CharacterEncodingResult_Success; + } + } + break; + case 3: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) { + const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) | + (static_cast<u32>(p[1] & 0x3F) << 6) | + (static_cast<u32>(p[2] & 0x3F) << 0); + if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) { + *dst = c; + return CharacterEncodingResult_Success; + } + } + return CharacterEncodingResult_InvalidFormat; + case 4: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) { + const u32 c = + (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | + (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0); + if (c >= 0x10000 && c < 0x110000) { + *dst = c; + return CharacterEncodingResult_Success; + } + } + return CharacterEncodingResult_InvalidFormat; + default: + break; + } + + // We failed to convert + return CharacterEncodingResult_InvalidFormat; +} + +constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst, + const char** str) { + // Check pre-conditions + ASSERT(dst != nullptr); + ASSERT(str != nullptr); + ASSERT(*str != nullptr); + + // Clear the output + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; + + // Perform the conversion + const auto* p = *str; + u32 c = static_cast<u32>(*p); + switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) { + case 1: + dst[0] = (*str)[0]; + ++(*str); + break; + case 2: + if ((p[0] & 0x1E) != 0) { + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == + 0) { + c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0); + dst[0] = (*str)[0]; + dst[1] = (*str)[1]; + (*str) += 2; + break; + } + } + return CharacterEncodingResult_InvalidFormat; + case 3: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) { + c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) | + (static_cast<u32>(p[2] & 0x3F) << 0); + if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) { + dst[0] = (*str)[0]; + dst[1] = (*str)[1]; + dst[2] = (*str)[2]; + (*str) += 3; + break; + } + } + return CharacterEncodingResult_InvalidFormat; + case 4: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) { + c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | + (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0); + if (c >= 0x10000 && c < 0x110000) { + dst[0] = (*str)[0]; + dst[1] = (*str)[1]; + dst[2] = (*str)[2]; + dst[3] = (*str)[3]; + (*str) += 4; + break; + } + } + return CharacterEncodingResult_InvalidFormat; + default: + return CharacterEncodingResult_InvalidFormat; + } + + return CharacterEncodingResult_Success; +} + +} // namespace FileSys diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index dd9cca103..8807bbd0f 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -8,8 +8,8 @@ #include "common/assert.h" #include "core/file_sys/fsmitm_romfsbuild.h" #include "core/file_sys/ips_layer.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h index f387c79f1..dd7ed4a7b 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.h +++ b/src/core/file_sys/fsmitm_romfsbuild.h @@ -7,7 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fs_i_storage.h b/src/core/file_sys/fssystem/fs_i_storage.h index 416dd57b8..37336c9ae 100644 --- a/src/core/file_sys/fssystem/fs_i_storage.h +++ b/src/core/file_sys/fssystem/fs_i_storage.h @@ -5,7 +5,7 @@ #include "common/overflow.h" #include "core/file_sys/errors.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp b/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp index f25c95472..bc1cddbb0 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp @@ -4,7 +4,7 @@ #include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h" #include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h" #include "core/file_sys/fssystem/fssystem_nca_header.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h index 339e49697..5abd93d33 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h +++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h @@ -9,7 +9,7 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/errors.h" #include "core/file_sys/fssystem/fs_i_storage.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_bucket_tree.h b/src/core/file_sys/fssystem/fssystem_bucket_tree.h index 46850cd48..3a5e21d1a 100644 --- a/src/core/file_sys/fssystem/fssystem_bucket_tree.h +++ b/src/core/file_sys/fssystem/fssystem_bucket_tree.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "common/literals.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_compressed_storage.h b/src/core/file_sys/fssystem/fssystem_compressed_storage.h index 33d93938e..74c98630e 100644 --- a/src/core/file_sys/fssystem/fssystem_compressed_storage.h +++ b/src/core/file_sys/fssystem/fssystem_compressed_storage.h @@ -10,7 +10,7 @@ #include "core/file_sys/fssystem/fssystem_bucket_tree.h" #include "core/file_sys/fssystem/fssystem_compression_common.h" #include "core/file_sys/fssystem/fssystem_pooled_buffer.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp index 4a75b5308..39bb7b808 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h index 5cf697efe..bd129db47 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h @@ -8,7 +8,7 @@ #include "core/file_sys/fssystem/fs_types.h" #include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h" #include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h index 18df400af..41d3960b8 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h @@ -7,7 +7,7 @@ #include "core/file_sys/errors.h" #include "core/file_sys/fssystem/fs_i_storage.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.h b/src/core/file_sys/fssystem/fssystem_indirect_storage.h index 7854335bf..d4b95fd27 100644 --- a/src/core/file_sys/fssystem/fssystem_indirect_storage.h +++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.h @@ -7,8 +7,8 @@ #include "core/file_sys/fssystem/fs_i_storage.h" #include "core/file_sys/fssystem/fssystem_bucket_tree.h" #include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h b/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h index 5f8512b2a..240d1e388 100644 --- a/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h +++ b/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h @@ -5,7 +5,7 @@ #include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h" #include "core/file_sys/fssystem/fssystem_nca_header.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp index 0f5432203..ab5a7984e 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp +++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp @@ -14,8 +14,8 @@ #include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" #include "core/file_sys/fssystem/fssystem_sparse_storage.h" #include "core/file_sys/fssystem/fssystem_switch_storage.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h index 5771a21fc..5bc838de6 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h +++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h @@ -5,7 +5,7 @@ #include "core/file_sys/fssystem/fssystem_compression_common.h" #include "core/file_sys/fssystem/fssystem_nca_header.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_nca_reader.cpp b/src/core/file_sys/fssystem/fssystem_nca_reader.cpp index a3714ab37..08924e2a6 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_reader.cpp +++ b/src/core/file_sys/fssystem/fssystem_nca_reader.cpp @@ -3,7 +3,7 @@ #include "core/file_sys/fssystem/fssystem_aes_xts_storage.h" #include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 31033634c..d1ac24072 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -12,7 +12,7 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/ips_layer.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index f2717bae7..d81378e8a 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h @@ -8,7 +8,7 @@ #include <vector> #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index 70c062f4c..b84492d30 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -5,7 +5,7 @@ #include "common/string_util.h" #include "core/file_sys/kernel_executable.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index d5b9199b5..928ba2d99 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h @@ -10,7 +10,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h deleted file mode 100644 index 9596ef4fd..000000000 --- a/src/core/file_sys/mode.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" - -namespace FileSys { - -enum class Mode : u32 { - Read = 1 << 0, - Write = 1 << 1, - ReadWrite = Read | Write, - Append = 1 << 2, - ReadAppend = Read | Append, - WriteAppend = Write | Append, - All = ReadWrite | Append, -}; - -DECLARE_ENUM_FLAG_OPERATORS(Mode) - -} // namespace FileSys diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index f4a774675..9e855c50d 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 68e463b5f..6243b822a 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -8,7 +8,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { class CNMT; diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 2422cb51b..dd8de9d8a 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -9,7 +9,7 @@ #include "common/logging/log.h" #include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index b6e3a2b0c..777b9ead9 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h @@ -9,7 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index cc7af2ea3..21d45235e 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -21,12 +21,12 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_layered.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_layered.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/language.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings_server.h" #include "core/loader/loader.h" #include "core/loader/nso.h" #include "core/memory/cheat_engine.h" @@ -466,12 +466,12 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs return romfs; } -PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { +std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const { if (title_id == 0) { return {}; } - std::map<std::string, std::string, std::less<>> out; + std::vector<Patch> out; const auto& disabled = Settings::values.disabled_addons[title_id]; // Game Updates @@ -482,20 +482,28 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u const auto update_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); - const auto update_label = update_disabled ? "[D] Update" : "Update"; + Patch update_patch = {.enabled = !update_disabled, + .name = "Update", + .version = "", + .type = PatchType::Update, + .program_id = title_id, + .title_id = title_id}; if (nacp != nullptr) { - out.insert_or_assign(update_label, nacp->GetVersionString()); + update_patch.version = nacp->GetVersionString(); + out.push_back(update_patch); } else { if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { const auto meta_ver = content_provider.GetEntryVersion(update_tid); if (meta_ver.value_or(0) == 0) { - out.insert_or_assign(update_label, ""); + out.push_back(update_patch); } else { - out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver)); + update_patch.version = FormatTitleVersion(*meta_ver); + out.push_back(update_patch); } } else if (update_raw != nullptr) { - out.insert_or_assign(update_label, "PACKED"); + update_patch.version = "PACKED"; + out.push_back(update_patch); } } @@ -539,7 +547,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u const auto mod_disabled = std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end(); - out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types); + out.push_back({.enabled = !mod_disabled, + .name = mod->GetName(), + .version = types, + .type = PatchType::Mod, + .program_id = title_id, + .title_id = title_id}); } } @@ -557,7 +570,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u if (!types.empty()) { const auto mod_disabled = std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); - out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types); + out.push_back({.enabled = !mod_disabled, + .name = "SDMC", + .version = types, + .type = PatchType::Mod, + .program_id = title_id, + .title_id = title_id}); } } @@ -584,7 +602,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u const auto dlc_disabled = std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); - out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list)); + out.push_back({.enabled = !dlc_disabled, + .name = "DLC", + .version = std::move(list), + .type = PatchType::DLC, + .program_id = title_id, + .title_id = dlc_match.back().title_id}); } return out; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 03e9c7301..552c0fbe2 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -9,7 +9,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/memory/dmnt_cheat_types.h" namespace Core { @@ -26,12 +26,22 @@ class ContentProvider; class NCA; class NACP; +enum class PatchType { Update, DLC, Mod }; + +struct Patch { + bool enabled; + std::string name; + std::string version; + PatchType type; + u64 program_id; + u64 title_id; +}; + // A centralized class to manage patches to games. class PatchManager { public: using BuildID = std::array<u8, 0x20>; using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; - using PatchVersionNames = std::map<std::string, std::string, std::less<>>; explicit PatchManager(u64 title_id_, const Service::FileSystem::FileSystemController& fs_controller_, @@ -66,9 +76,8 @@ public: VirtualFile packed_update_raw = nullptr, bool apply_layeredfs = true) const; - // Returns a vector of pairs between patch names and patch versions. - // i.e. Update 3.2.2 will return {"Update", "3.2.2"} - [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const; + // Returns a vector of patches + [[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const; // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 539c7f7af..ae4e441c9 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -7,7 +7,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "core/file_sys/program_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index a53092b87..115e6d6cd 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -10,7 +10,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 1cc77ad14..85d30543c 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -17,7 +17,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" -#include "core/file_sys/vfs_concat.h" +#include "core/file_sys/vfs/vfs_concat.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 64815a845..a7fc55673 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -11,7 +11,7 @@ #include <boost/container/flat_map.hpp> #include "common/common_types.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { class CNMT; diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 6182598ae..a2b280973 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -9,11 +9,11 @@ #include "common/swap.h" #include "core/file_sys/fsmitm_romfsbuild.h" #include "core/file_sys/romfs.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { namespace { diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index b75ff1aad..3c0aca291 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index e4809bc94..11ecfabdf 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -6,7 +6,7 @@ #include <memory> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" namespace Loader { diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 8d5d593e8..cbf411a20 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -8,7 +8,7 @@ #include "common/uuid.h" #include "core/core.h" #include "core/file_sys/savedata_factory.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { @@ -97,8 +97,9 @@ std::string SaveDataAttribute::DebugInfo() const { static_cast<u8>(rank), index); } -SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_) - : dir{std::move(save_directory_)}, system{system_} { +SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_, + VirtualDir save_directory_) + : system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} { // Delete all temporary storages // On hardware, it is expected that temporary storage be empty at first use. dir->DeleteSubdirectoryRecursive("temp"); @@ -110,7 +111,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut PrintSaveDataAttributeWarnings(meta); const auto save_directory = - GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); return dir->CreateDirectoryRelative(save_directory); } @@ -118,7 +119,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { const auto save_directory = - GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->GetDirectoryRelative(save_directory); @@ -147,14 +148,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { } } -std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, +std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) { // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { if (title_id == 0) { - title_id = system.GetApplicationProcessProgramID(); + title_id = program_id; } } @@ -189,10 +190,19 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, } } +std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) { + if (future) { + Common::UUID uuid; + std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); + return fmt::format("/user/save/account/{}", uuid.RawString()); + } + return fmt::format("/user/save/{:016X}/{:016X}{:016X}", 0, user_id[1], user_id[0]); +} + SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const { const auto path = - GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); @@ -211,7 +221,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value) const { const auto path = - GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index e3a0f8cef..5ab7e4d32 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -7,7 +7,7 @@ #include <string> #include "common/common_funcs.h" #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" namespace Core { @@ -87,10 +87,13 @@ constexpr const char* GetSaveDataSizeFileName() { return ".yuzu_save_size"; } +using ProgramId = u64; + /// File system interface to the SaveData archive class SaveDataFactory { public: - explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); + explicit SaveDataFactory(Core::System& system_, ProgramId program_id_, + VirtualDir save_directory_); ~SaveDataFactory(); VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; @@ -99,8 +102,9 @@ public: VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); - static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, + static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); + static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, @@ -109,8 +113,9 @@ public: void SetAutoCreate(bool state); private: - VirtualDir dir; Core::System& system; + ProgramId program_id; + VirtualDir dir; bool auto_create{true}; }; diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index d5158cd64..f3e2e21f4 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -4,7 +4,7 @@ #include <memory> #include "core/file_sys/registered_cache.h" #include "core/file_sys/sdmc_factory.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/xts_archive.h" namespace FileSys { diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index a445fdb16..ee69ccd07 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h @@ -4,7 +4,7 @@ #pragma once #include <memory> -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" namespace FileSys { diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 915bffca9..935e9589d 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -9,7 +9,7 @@ #include <vector> #include "common/common_types.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { class KeyManager; diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp index 5c87b42f8..a96cb2cd2 100644 --- a/src/core/file_sys/system_archive/mii_model.cpp +++ b/src/core/file_sys/system_archive/mii_model.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/mii_model.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h index b6cbefe24..61723ed0d 100644 --- a/src/core/file_sys/system_archive/mii_model.h +++ b/src/core/file_sys/system_archive/mii_model.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp index 5cf6749da..1fa67877d 100644 --- a/src/core/file_sys/system_archive/ng_word.cpp +++ b/src/core/file_sys/system_archive/ng_word.cpp @@ -4,7 +4,7 @@ #include <fmt/format.h> #include "common/common_types.h" #include "core/file_sys/system_archive/ng_word.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h index 1d7b49532..51bcc3327 100644 --- a/src/core/file_sys/system_archive/ng_word.h +++ b/src/core/file_sys/system_archive/ng_word.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index 3210583f0..deb52069d 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp @@ -8,7 +8,7 @@ #include "core/file_sys/system_archive/data/font_nintendo_extended.h" #include "core/file_sys/system_archive/data/font_standard.h" #include "core/file_sys/system_archive/shared_font.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/hle/service/ns/iplatform_service_manager.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/shared_font.h b/src/core/file_sys/system_archive/shared_font.h index d1cd1dc44..2d19fcde3 100644 --- a/src/core/file_sys/system_archive/shared_font.h +++ b/src/core/file_sys/system_archive/shared_font.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index 6abac793b..b53eef877 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI }}; VirtualFile SynthesizeSystemArchive(const u64 title_id) { - if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) + if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) { return nullptr; + } const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); - if (desc.supplier == nullptr) + if (desc.supplier == nullptr) { return nullptr; + } const auto dir = desc.supplier(); - if (dir == nullptr) + if (dir == nullptr) { return nullptr; + } const auto romfs = CreateRomFS(dir); - if (romfs == nullptr) + if (romfs == nullptr) { return nullptr; + } LOG_INFO(Service_FS, " - System archive generation successful!"); return romfs; diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h index 02d9157bb..2f64247bc 100644 --- a/src/core/file_sys/system_archive/system_archive.h +++ b/src/core/file_sys/system_archive/system_archive.h @@ -4,7 +4,7 @@ #pragma once #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp index e4751c2b4..5662004b7 100644 --- a/src/core/file_sys/system_archive/system_version.cpp +++ b/src/core/file_sys/system_archive/system_version.cpp @@ -3,7 +3,7 @@ #include "common/logging/log.h" #include "core/file_sys/system_archive/system_version.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/hle/api_version.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h index 21b5514a9..e5f7b952e 100644 --- a/src/core/file_sys/system_archive/system_version.h +++ b/src/core/file_sys/system_archive/system_version.h @@ -4,7 +4,7 @@ #pragma once #include <string> -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 7c17bbefa..316ff0dc6 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -5,8 +5,7 @@ #include "common/swap.h" #include "core/file_sys/system_archive/time_zone_binary.h" -#include "core/file_sys/vfs_vector.h" -#include "core/hle/service/time/time_zone_types.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "nx_tzdb.h" diff --git a/src/core/file_sys/system_archive/time_zone_binary.h b/src/core/file_sys/system_archive/time_zone_binary.h index d0e1a4acd..e44fc5007 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.h +++ b/src/core/file_sys/system_archive/time_zone_binary.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp deleted file mode 100644 index b7105c8ff..000000000 --- a/src/core/file_sys/vfs.cpp +++ /dev/null @@ -1,552 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <numeric> -#include <string> -#include "common/fs/path_util.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/vfs.h" - -namespace FileSys { - -VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} - -VfsFilesystem::~VfsFilesystem() = default; - -std::string VfsFilesystem::GetName() const { - return root->GetName(); -} - -bool VfsFilesystem::IsReadable() const { - return root->IsReadable(); -} - -bool VfsFilesystem::IsWritable() const { - return root->IsWritable(); -} - -VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { - const auto path = Common::FS::SanitizePath(path_); - if (root->GetFileRelative(path) != nullptr) - return VfsEntryType::File; - if (root->GetDirectoryRelative(path) != nullptr) - return VfsEntryType::Directory; - - return VfsEntryType::None; -} - -VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->GetFileRelative(path); -} - -VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->CreateFileRelative(path); -} - -VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { - const auto old_path = Common::FS::SanitizePath(old_path_); - const auto new_path = Common::FS::SanitizePath(new_path_); - - // VfsDirectory impls are only required to implement copy across the current directory. - if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { - if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) - return nullptr; - return OpenFile(new_path, Mode::ReadWrite); - } - - // Do it using RawCopy. Non-default impls are encouraged to optimize this. - const auto old_file = OpenFile(old_path, Mode::Read); - if (old_file == nullptr) - return nullptr; - auto new_file = OpenFile(new_path, Mode::Read); - if (new_file != nullptr) - return nullptr; - new_file = CreateFile(new_path, Mode::Write); - if (new_file == nullptr) - return nullptr; - if (!VfsRawCopy(old_file, new_file)) - return nullptr; - return new_file; -} - -VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { - const auto sanitized_old_path = Common::FS::SanitizePath(old_path); - const auto sanitized_new_path = Common::FS::SanitizePath(new_path); - - // Again, non-default impls are highly encouraged to provide a more optimized version of this. - auto out = CopyFile(sanitized_old_path, sanitized_new_path); - if (out == nullptr) - return nullptr; - if (DeleteFile(sanitized_old_path)) - return out; - return nullptr; -} - -bool VfsFilesystem::DeleteFile(std::string_view path_) { - const auto path = Common::FS::SanitizePath(path_); - auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); - if (parent == nullptr) - return false; - return parent->DeleteFile(Common::FS::GetFilename(path)); -} - -VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->GetDirectoryRelative(path); -} - -VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->CreateDirectoryRelative(path); -} - -VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { - const auto old_path = Common::FS::SanitizePath(old_path_); - const auto new_path = Common::FS::SanitizePath(new_path_); - - // Non-default impls are highly encouraged to provide a more optimized version of this. - auto old_dir = OpenDirectory(old_path, Mode::Read); - if (old_dir == nullptr) - return nullptr; - auto new_dir = OpenDirectory(new_path, Mode::Read); - if (new_dir != nullptr) - return nullptr; - new_dir = CreateDirectory(new_path, Mode::Write); - if (new_dir == nullptr) - return nullptr; - - for (const auto& file : old_dir->GetFiles()) { - const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName()); - if (x == nullptr) - return nullptr; - } - - for (const auto& dir : old_dir->GetSubdirectories()) { - const auto x = - CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName()); - if (x == nullptr) - return nullptr; - } - - return new_dir; -} - -VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { - const auto sanitized_old_path = Common::FS::SanitizePath(old_path); - const auto sanitized_new_path = Common::FS::SanitizePath(new_path); - - // Non-default impls are highly encouraged to provide a more optimized version of this. - auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); - if (out == nullptr) - return nullptr; - if (DeleteDirectory(sanitized_old_path)) - return out; - return nullptr; -} - -bool VfsFilesystem::DeleteDirectory(std::string_view path_) { - const auto path = Common::FS::SanitizePath(path_); - auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); - if (parent == nullptr) - return false; - return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); -} - -VfsFile::~VfsFile() = default; - -std::string VfsFile::GetExtension() const { - return std::string(Common::FS::GetExtensionFromFilename(GetName())); -} - -VfsDirectory::~VfsDirectory() = default; - -std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { - u8 out{}; - const std::size_t size = Read(&out, sizeof(u8), offset); - if (size == 1) { - return out; - } - - return std::nullopt; -} - -std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { - std::vector<u8> out(size); - std::size_t read_size = Read(out.data(), size, offset); - out.resize(read_size); - return out; -} - -std::vector<u8> VfsFile::ReadAllBytes() const { - return ReadBytes(GetSize()); -} - -bool VfsFile::WriteByte(u8 data, std::size_t offset) { - return Write(&data, 1, offset) == 1; -} - -std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) { - return Write(data.data(), data.size(), offset); -} - -std::string VfsFile::GetFullPath() const { - if (GetContainingDirectory() == nullptr) - return '/' + GetName(); - - return GetContainingDirectory()->GetFullPath() + '/' + GetName(); -} - -VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - return nullptr; - } - - if (vec.size() == 1) { - return GetFile(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - for (std::size_t component = 1; component < vec.size() - 1; ++component) { - if (dir == nullptr) { - return nullptr; - } - - dir = dir->GetSubdirectory(vec[component]); - } - - if (dir == nullptr) { - return nullptr; - } - - return dir->GetFile(vec.back()); -} - -VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { - if (IsRoot()) { - return GetFileRelative(path); - } - - return GetParentDirectory()->GetFileAbsolute(path); -} - -VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently - // because of const-ness - return nullptr; - } - - auto dir = GetSubdirectory(vec[0]); - for (std::size_t component = 1; component < vec.size(); ++component) { - if (dir == nullptr) { - return nullptr; - } - - dir = dir->GetSubdirectory(vec[component]); - } - - return dir; -} - -VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const { - if (IsRoot()) { - return GetDirectoryRelative(path); - } - - return GetParentDirectory()->GetDirectoryAbsolute(path); -} - -VirtualFile VfsDirectory::GetFile(std::string_view name) const { - const auto& files = GetFiles(); - const auto iter = std::find_if(files.begin(), files.end(), - [&name](const auto& file1) { return name == file1->GetName(); }); - return iter == files.end() ? nullptr : *iter; -} - -FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const { - return {}; -} - -VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { - const auto& subs = GetSubdirectories(); - const auto iter = std::find_if(subs.begin(), subs.end(), - [&name](const auto& file1) { return name == file1->GetName(); }); - return iter == subs.end() ? nullptr : *iter; -} - -bool VfsDirectory::IsRoot() const { - return GetParentDirectory() == nullptr; -} - -std::size_t VfsDirectory::GetSize() const { - const auto& files = GetFiles(); - const auto sum_sizes = [](const auto& range) { - return std::accumulate(range.begin(), range.end(), 0ULL, - [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); - }; - - const auto file_total = sum_sizes(files); - const auto& sub_dir = GetSubdirectories(); - const auto subdir_total = sum_sizes(sub_dir); - - return file_total + subdir_total; -} - -VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - return nullptr; - } - - if (vec.size() == 1) { - return CreateFile(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - if (dir == nullptr) { - dir = CreateSubdirectory(vec[0]); - if (dir == nullptr) { - return nullptr; - } - } - - return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path)); -} - -VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) { - if (IsRoot()) { - return CreateFileRelative(path); - } - - return GetParentDirectory()->CreateFileAbsolute(path); -} - -VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - return nullptr; - } - - if (vec.size() == 1) { - return CreateSubdirectory(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - if (dir == nullptr) { - dir = CreateSubdirectory(vec[0]); - if (dir == nullptr) { - return nullptr; - } - } - - return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path)); -} - -VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { - if (IsRoot()) { - return CreateDirectoryRelative(path); - } - - return GetParentDirectory()->CreateDirectoryAbsolute(path); -} - -bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { - auto dir = GetSubdirectory(name); - if (dir == nullptr) { - return false; - } - - bool success = true; - for (const auto& file : dir->GetFiles()) { - if (!DeleteFile(file->GetName())) { - success = false; - } - } - - for (const auto& sdir : dir->GetSubdirectories()) { - if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { - success = false; - } - } - - return success; -} - -bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { - auto dir = GetSubdirectory(name); - if (dir == nullptr) { - return false; - } - - bool success = true; - for (const auto& file : dir->GetFiles()) { - if (!dir->DeleteFile(file->GetName())) { - success = false; - } - } - - for (const auto& sdir : dir->GetSubdirectories()) { - if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { - success = false; - } - } - - return success; -} - -bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { - const auto f1 = GetFile(src); - auto f2 = CreateFile(dest); - if (f1 == nullptr || f2 == nullptr) { - return false; - } - - if (!f2->Resize(f1->GetSize())) { - DeleteFile(dest); - return false; - } - - return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); -} - -std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const { - std::map<std::string, VfsEntryType, std::less<>> out; - for (const auto& dir : GetSubdirectories()) - out.emplace(dir->GetName(), VfsEntryType::Directory); - for (const auto& file : GetFiles()) - out.emplace(file->GetName(), VfsEntryType::File); - return out; -} - -std::string VfsDirectory::GetFullPath() const { - if (IsRoot()) - return GetName(); - - return GetParentDirectory()->GetFullPath() + '/' + GetName(); -} - -bool ReadOnlyVfsDirectory::IsWritable() const { - return false; -} - -bool ReadOnlyVfsDirectory::IsReadable() const { - return true; -} - -VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) { - return nullptr; -} - -VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) { - return nullptr; -} - -VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) { - return nullptr; -} - -VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) { - return nullptr; -} - -VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) { - return nullptr; -} - -VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) { - return nullptr; -} - -bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::Rename(std::string_view name) { - return false; -} - -bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { - if (file1->GetSize() != file2->GetSize()) - return false; - - std::vector<u8> f1_v(block_size); - std::vector<u8> f2_v(block_size); - for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { - auto f1_vs = file1->Read(f1_v.data(), block_size, i); - auto f2_vs = file2->Read(f2_v.data(), block_size, i); - - if (f1_vs != f2_vs) - return false; - auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); - if (iters.first != f1_v.end() && iters.second != f2_v.end()) - return false; - } - - return true; -} - -bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - if (!dest->Resize(src->GetSize())) - return false; - - std::vector<u8> temp(std::min(block_size, src->GetSize())); - for (std::size_t i = 0; i < src->GetSize(); i += block_size) { - const auto read = std::min(block_size, src->GetSize() - i); - - if (src->Read(temp.data(), read, i) != read) { - return false; - } - - if (dest->Write(temp.data(), read, i) != read) { - return false; - } - } - - return true; -} - -bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - - for (const auto& file : src->GetFiles()) { - const auto out = dest->CreateFile(file->GetName()); - if (!VfsRawCopy(file, out, block_size)) - return false; - } - - for (const auto& dir : src->GetSubdirectories()) { - const auto out = dest->CreateSubdirectory(dir->GetName()); - if (!VfsRawCopyD(dir, out, block_size)) - return false; - } - - return true; -} - -VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { - const auto res = rel->GetDirectoryRelative(path); - if (res == nullptr) - return rel->CreateDirectoryRelative(path); - return res; -} -} // namespace FileSys diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h deleted file mode 100644 index a7cd1cae3..000000000 --- a/src/core/file_sys/vfs.h +++ /dev/null @@ -1,327 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <functional> -#include <map> -#include <memory> -#include <optional> -#include <string> -#include <type_traits> -#include <vector> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" - -namespace FileSys { - -enum class Mode : u32; - -// An enumeration representing what can be at the end of a path in a VfsFilesystem -enum class VfsEntryType { - None, - File, - Directory, -}; - -// A class representing an abstract filesystem. A default implementation given the root VirtualDir -// is provided for convenience, but if the Vfs implementation has any additional state or -// functionality, they will need to override. -class VfsFilesystem { -public: - YUZU_NON_COPYABLE(VfsFilesystem); - YUZU_NON_MOVEABLE(VfsFilesystem); - - explicit VfsFilesystem(VirtualDir root); - virtual ~VfsFilesystem(); - - // Gets the friendly name for the filesystem. - virtual std::string GetName() const; - - // Return whether or not the user has read permissions on this filesystem. - virtual bool IsReadable() const; - // Return whether or not the user has write permission on this filesystem. - virtual bool IsWritable() const; - - // Determine if the entry at path is non-existent, a file, or a directory. - virtual VfsEntryType GetEntryType(std::string_view path) const; - - // Opens the file with path relative to root. If it doesn't exist, returns nullptr. - virtual VirtualFile OpenFile(std::string_view path, Mode perms); - // Creates a new, empty file at path - virtual VirtualFile CreateFile(std::string_view path, Mode perms); - // Copies the file from old_path to new_path, returning the new file on success and nullptr on - // failure. - virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); - // Moves the file from old_path to new_path, returning the moved file on success and nullptr on - // failure. - virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); - // Deletes the file with path relative to root, returning true on success. - virtual bool DeleteFile(std::string_view path); - - // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. - virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); - // Creates a new, empty directory at path - virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); - // Copies the directory from old_path to new_path, returning the new directory on success and - // nullptr on failure. - virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); - // Moves the directory from old_path to new_path, returning the moved directory on success and - // nullptr on failure. - virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); - // Deletes the directory with path relative to root, returning true on success. - virtual bool DeleteDirectory(std::string_view path); - -protected: - // Root directory in default implementation. - VirtualDir root; -}; - -// A class representing a file in an abstract filesystem. -class VfsFile { -public: - YUZU_NON_COPYABLE(VfsFile); - YUZU_NON_MOVEABLE(VfsFile); - - VfsFile() = default; - virtual ~VfsFile(); - - // Retrieves the file name. - virtual std::string GetName() const = 0; - // Retrieves the extension of the file name. - virtual std::string GetExtension() const; - // Retrieves the size of the file. - virtual std::size_t GetSize() const = 0; - // Resizes the file to new_size. Returns whether or not the operation was successful. - virtual bool Resize(std::size_t new_size) = 0; - // Gets a pointer to the directory containing this file, returning nullptr if there is none. - virtual VirtualDir GetContainingDirectory() const = 0; - - // Returns whether or not the file can be written to. - virtual bool IsWritable() const = 0; - // Returns whether or not the file can be read from. - virtual bool IsReadable() const = 0; - - // The primary method of reading from the file. Reads length bytes into data starting at offset - // into file. Returns number of bytes successfully read. - virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; - // The primary method of writing to the file. Writes length bytes from data starting at offset - // into file. Returns number of bytes successfully written. - virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; - - // Reads exactly one byte at the offset provided, returning std::nullopt on error. - virtual std::optional<u8> ReadByte(std::size_t offset = 0) const; - // Reads size bytes starting at offset in file into a vector. - virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; - // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), - // 0)' - virtual std::vector<u8> ReadAllBytes() const; - - // Reads an array of type T, size number_elements starting at offset. - // Returns the number of bytes (sizeof(T)*number_elements) read successfully. - template <typename T> - std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - - return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); - } - - // Reads size bytes into the memory starting at data starting at offset into the file. - // Returns the number of bytes read successfully. - template <typename T> - std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - return Read(reinterpret_cast<u8*>(data), size, offset); - } - - // Reads one object of type T starting at offset in file. - // Returns the number of bytes read successfully (sizeof(T)). - template <typename T> - std::size_t ReadObject(T* data, std::size_t offset = 0) const { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); - } - - // Writes exactly one byte to offset in file and returns whether or not the byte was written - // successfully. - virtual bool WriteByte(u8 data, std::size_t offset = 0); - // Writes a vector of bytes to offset in file and returns the number of bytes successfully - // written. - virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0); - - // Writes an array of type T, size number_elements to offset in file. - // Returns the number of bytes (sizeof(T)*number_elements) written successfully. - template <typename T> - std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset); - } - - // Writes size bytes starting at memory location data to offset in file. - // Returns the number of bytes written successfully. - template <typename T> - std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - return Write(reinterpret_cast<const u8*>(data), size, offset); - } - - // Writes one object of type T to offset in file. - // Returns the number of bytes written successfully (sizeof(T)). - template <typename T> - std::size_t WriteObject(const T& data, std::size_t offset = 0) { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset); - } - - // Renames the file to name. Returns whether or not the operation was successful. - virtual bool Rename(std::string_view name) = 0; - - // Returns the full path of this file as a string, recursively - virtual std::string GetFullPath() const; -}; - -// A class representing a directory in an abstract filesystem. -class VfsDirectory { -public: - YUZU_NON_COPYABLE(VfsDirectory); - YUZU_NON_MOVEABLE(VfsDirectory); - - VfsDirectory() = default; - virtual ~VfsDirectory(); - - // Retrieves the file located at path as if the current directory was root. Returns nullptr if - // not found. - virtual VirtualFile GetFileRelative(std::string_view path) const; - // Calls GetFileRelative(path) on the root of the current directory. - virtual VirtualFile GetFileAbsolute(std::string_view path) const; - - // Retrieves the directory located at path as if the current directory was root. Returns nullptr - // if not found. - virtual VirtualDir GetDirectoryRelative(std::string_view path) const; - // Calls GetDirectoryRelative(path) on the root of the current directory. - virtual VirtualDir GetDirectoryAbsolute(std::string_view path) const; - - // Returns a vector containing all of the files in this directory. - virtual std::vector<VirtualFile> GetFiles() const = 0; - // Returns the file with filename matching name. Returns nullptr if directory doesn't have a - // file with name. - virtual VirtualFile GetFile(std::string_view name) const; - - // Returns a struct containing the file's timestamp. - virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const; - - // Returns a vector containing all of the subdirectories in this directory. - virtual std::vector<VirtualDir> GetSubdirectories() const = 0; - // Returns the directory with name matching name. Returns nullptr if directory doesn't have a - // directory with name. - virtual VirtualDir GetSubdirectory(std::string_view name) const; - - // Returns whether or not the directory can be written to. - virtual bool IsWritable() const = 0; - // Returns whether of not the directory can be read from. - virtual bool IsReadable() const = 0; - - // Returns whether or not the directory is the root of the current file tree. - virtual bool IsRoot() const; - - // Returns the name of the directory. - virtual std::string GetName() const = 0; - // Returns the total size of all files and subdirectories in this directory. - virtual std::size_t GetSize() const; - // Returns the parent directory of this directory. Returns nullptr if this directory is root or - // has no parent. - virtual VirtualDir GetParentDirectory() const = 0; - - // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr - // if the operation failed. - virtual VirtualDir CreateSubdirectory(std::string_view name) = 0; - // Creates a new file with name name. Returns a pointer to the new file or nullptr if the - // operation failed. - virtual VirtualFile CreateFile(std::string_view name) = 0; - - // Creates a new file at the path relative to this directory. Also creates directories if - // they do not exist and is supported by this implementation. Returns nullptr on any failure. - virtual VirtualFile CreateFileRelative(std::string_view path); - - // Creates a new file at the path relative to root of this directory. Also creates directories - // if they do not exist and is supported by this implementation. Returns nullptr on any failure. - virtual VirtualFile CreateFileAbsolute(std::string_view path); - - // Creates a new directory at the path relative to this directory. Also creates directories if - // they do not exist and is supported by this implementation. Returns nullptr on any failure. - virtual VirtualDir CreateDirectoryRelative(std::string_view path); - - // Creates a new directory at the path relative to root of this directory. Also creates - // directories if they do not exist and is supported by this implementation. Returns nullptr on - // any failure. - virtual VirtualDir CreateDirectoryAbsolute(std::string_view path); - - // Deletes the subdirectory with the given name and returns true on success. - virtual bool DeleteSubdirectory(std::string_view name) = 0; - - // Deletes all subdirectories and files within the provided directory and then deletes - // the directory itself. Returns true on success. - virtual bool DeleteSubdirectoryRecursive(std::string_view name); - - // Deletes all subdirectories and files within the provided directory. - // Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory. - virtual bool CleanSubdirectoryRecursive(std::string_view name); - - // Returns whether or not the file with name name was deleted successfully. - virtual bool DeleteFile(std::string_view name) = 0; - - // Returns whether or not this directory was renamed to name. - virtual bool Rename(std::string_view name) = 0; - - // Returns whether or not the file with name src was successfully copied to a new file with name - // dest. - virtual bool Copy(std::string_view src, std::string_view dest); - - // Gets all of the entries directly in the directory (files and dirs), returning a map between - // item name -> type. - virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; - - // Returns the full path of this directory as a string, recursively - virtual std::string GetFullPath() const; -}; - -// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work -// if writable. This is to avoid redundant empty methods everywhere. -class ReadOnlyVfsDirectory : public VfsDirectory { -public: - bool IsWritable() const override; - bool IsReadable() const override; - VirtualDir CreateSubdirectory(std::string_view name) override; - VirtualFile CreateFile(std::string_view name) override; - VirtualFile CreateFileAbsolute(std::string_view path) override; - VirtualFile CreateFileRelative(std::string_view path) override; - VirtualDir CreateDirectoryAbsolute(std::string_view path) override; - VirtualDir CreateDirectoryRelative(std::string_view path) override; - bool DeleteSubdirectory(std::string_view name) override; - bool DeleteSubdirectoryRecursive(std::string_view name) override; - bool CleanSubdirectoryRecursive(std::string_view name) override; - bool DeleteFile(std::string_view name) override; - bool Rename(std::string_view name) override; -}; - -// Compare the two files, byte-for-byte, in increments specified by block_size -bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, - std::size_t block_size = 0x1000); - -// A method that copies the raw data between two different implementations of VirtualFile. If you -// are using the same implementation, it is probably better to use the Copy method in the parent -// directory of src/dest. -bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000); - -// A method that performs a similar function to VfsRawCopy above, but instead copies entire -// directories. It suffers the same performance penalties as above and an implementation-specific -// Copy should always be preferred. -bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000); - -// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not -// it attempts to create it and returns the new dir or nullptr on failure. -VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path); - -} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp new file mode 100644 index 000000000..a04292760 --- /dev/null +++ b/src/core/file_sys/vfs/vfs.cpp @@ -0,0 +1,551 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <numeric> +#include <string> +#include "common/fs/path_util.h" +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} + +VfsFilesystem::~VfsFilesystem() = default; + +std::string VfsFilesystem::GetName() const { + return root->GetName(); +} + +bool VfsFilesystem::IsReadable() const { + return root->IsReadable(); +} + +bool VfsFilesystem::IsWritable() const { + return root->IsWritable(); +} + +VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = Common::FS::SanitizePath(path_); + if (root->GetFileRelative(path) != nullptr) + return VfsEntryType::File; + if (root->GetDirectoryRelative(path) != nullptr) + return VfsEntryType::Directory; + + return VfsEntryType::None; +} + +VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->GetFileRelative(path); +} + +VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->CreateFileRelative(path); +} + +VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = Common::FS::SanitizePath(old_path_); + const auto new_path = Common::FS::SanitizePath(new_path_); + + // VfsDirectory impls are only required to implement copy across the current directory. + if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { + if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) + return nullptr; + return OpenFile(new_path, OpenMode::ReadWrite); + } + + // Do it using RawCopy. Non-default impls are encouraged to optimize this. + const auto old_file = OpenFile(old_path, OpenMode::Read); + if (old_file == nullptr) + return nullptr; + auto new_file = OpenFile(new_path, OpenMode::Read); + if (new_file != nullptr) + return nullptr; + new_file = CreateFile(new_path, OpenMode::Write); + if (new_file == nullptr) + return nullptr; + if (!VfsRawCopy(old_file, new_file)) + return nullptr; + return new_file; +} + +VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { + const auto sanitized_old_path = Common::FS::SanitizePath(old_path); + const auto sanitized_new_path = Common::FS::SanitizePath(new_path); + + // Again, non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyFile(sanitized_old_path, sanitized_new_path); + if (out == nullptr) + return nullptr; + if (DeleteFile(sanitized_old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = Common::FS::SanitizePath(path_); + auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write); + if (parent == nullptr) + return false; + return parent->DeleteFile(Common::FS::GetFilename(path)); +} + +VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->GetDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->CreateDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = Common::FS::SanitizePath(old_path_); + const auto new_path = Common::FS::SanitizePath(new_path_); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto old_dir = OpenDirectory(old_path, OpenMode::Read); + if (old_dir == nullptr) + return nullptr; + auto new_dir = OpenDirectory(new_path, OpenMode::Read); + if (new_dir != nullptr) + return nullptr; + new_dir = CreateDirectory(new_path, OpenMode::Write); + if (new_dir == nullptr) + return nullptr; + + for (const auto& file : old_dir->GetFiles()) { + const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName()); + if (x == nullptr) + return nullptr; + } + + for (const auto& dir : old_dir->GetSubdirectories()) { + const auto x = + CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName()); + if (x == nullptr) + return nullptr; + } + + return new_dir; +} + +VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { + const auto sanitized_old_path = Common::FS::SanitizePath(old_path); + const auto sanitized_new_path = Common::FS::SanitizePath(new_path); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); + if (out == nullptr) + return nullptr; + if (DeleteDirectory(sanitized_old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = Common::FS::SanitizePath(path_); + auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write); + if (parent == nullptr) + return false; + return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); +} + +VfsFile::~VfsFile() = default; + +std::string VfsFile::GetExtension() const { + return std::string(Common::FS::GetExtensionFromFilename(GetName())); +} + +VfsDirectory::~VfsDirectory() = default; + +std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { + u8 out{}; + const std::size_t size = Read(&out, sizeof(u8), offset); + if (size == 1) { + return out; + } + + return std::nullopt; +} + +std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { + std::vector<u8> out(size); + std::size_t read_size = Read(out.data(), size, offset); + out.resize(read_size); + return out; +} + +std::vector<u8> VfsFile::ReadAllBytes() const { + return ReadBytes(GetSize()); +} + +bool VfsFile::WriteByte(u8 data, std::size_t offset) { + return Write(&data, 1, offset) == 1; +} + +std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) { + return Write(data.data(), data.size(), offset); +} + +std::string VfsFile::GetFullPath() const { + if (GetContainingDirectory() == nullptr) + return '/' + GetName(); + + return GetContainingDirectory()->GetFullPath() + '/' + GetName(); +} + +VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + return nullptr; + } + + if (vec.size() == 1) { + return GetFile(vec[0]); + } + + auto dir = GetSubdirectory(vec[0]); + for (std::size_t component = 1; component < vec.size() - 1; ++component) { + if (dir == nullptr) { + return nullptr; + } + + dir = dir->GetSubdirectory(vec[component]); + } + + if (dir == nullptr) { + return nullptr; + } + + return dir->GetFile(vec.back()); +} + +VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { + if (IsRoot()) { + return GetFileRelative(path); + } + + return GetParentDirectory()->GetFileAbsolute(path); +} + +VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently + // because of const-ness + return nullptr; + } + + auto dir = GetSubdirectory(vec[0]); + for (std::size_t component = 1; component < vec.size(); ++component) { + if (dir == nullptr) { + return nullptr; + } + + dir = dir->GetSubdirectory(vec[component]); + } + + return dir; +} + +VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const { + if (IsRoot()) { + return GetDirectoryRelative(path); + } + + return GetParentDirectory()->GetDirectoryAbsolute(path); +} + +VirtualFile VfsDirectory::GetFile(std::string_view name) const { + const auto& files = GetFiles(); + const auto iter = std::find_if(files.begin(), files.end(), + [&name](const auto& file1) { return name == file1->GetName(); }); + return iter == files.end() ? nullptr : *iter; +} + +FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const { + return {}; +} + +VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { + const auto& subs = GetSubdirectories(); + const auto iter = std::find_if(subs.begin(), subs.end(), + [&name](const auto& file1) { return name == file1->GetName(); }); + return iter == subs.end() ? nullptr : *iter; +} + +bool VfsDirectory::IsRoot() const { + return GetParentDirectory() == nullptr; +} + +std::size_t VfsDirectory::GetSize() const { + const auto& files = GetFiles(); + const auto sum_sizes = [](const auto& range) { + return std::accumulate(range.begin(), range.end(), 0ULL, + [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); + }; + + const auto file_total = sum_sizes(files); + const auto& sub_dir = GetSubdirectories(); + const auto subdir_total = sum_sizes(sub_dir); + + return file_total + subdir_total; +} + +VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + return nullptr; + } + + if (vec.size() == 1) { + return CreateFile(vec[0]); + } + + auto dir = GetSubdirectory(vec[0]); + if (dir == nullptr) { + dir = CreateSubdirectory(vec[0]); + if (dir == nullptr) { + return nullptr; + } + } + + return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path)); +} + +VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) { + if (IsRoot()) { + return CreateFileRelative(path); + } + + return GetParentDirectory()->CreateFileAbsolute(path); +} + +VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + return nullptr; + } + + if (vec.size() == 1) { + return CreateSubdirectory(vec[0]); + } + + auto dir = GetSubdirectory(vec[0]); + if (dir == nullptr) { + dir = CreateSubdirectory(vec[0]); + if (dir == nullptr) { + return nullptr; + } + } + + return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path)); +} + +VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { + if (IsRoot()) { + return CreateDirectoryRelative(path); + } + + return GetParentDirectory()->CreateDirectoryAbsolute(path); +} + +bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + auto dir = GetSubdirectory(name); + if (dir == nullptr) { + return false; + } + + bool success = true; + for (const auto& file : dir->GetFiles()) { + if (!DeleteFile(file->GetName())) { + success = false; + } + } + + for (const auto& sdir : dir->GetSubdirectories()) { + if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { + success = false; + } + } + + return success; +} + +bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { + auto dir = GetSubdirectory(name); + if (dir == nullptr) { + return false; + } + + bool success = true; + for (const auto& file : dir->GetFiles()) { + if (!dir->DeleteFile(file->GetName())) { + success = false; + } + } + + for (const auto& sdir : dir->GetSubdirectories()) { + if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { + success = false; + } + } + + return success; +} + +bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { + const auto f1 = GetFile(src); + auto f2 = CreateFile(dest); + if (f1 == nullptr || f2 == nullptr) { + return false; + } + + if (!f2->Resize(f1->GetSize())) { + DeleteFile(dest); + return false; + } + + return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); +} + +std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const { + std::map<std::string, VfsEntryType, std::less<>> out; + for (const auto& dir : GetSubdirectories()) + out.emplace(dir->GetName(), VfsEntryType::Directory); + for (const auto& file : GetFiles()) + out.emplace(file->GetName(), VfsEntryType::File); + return out; +} + +std::string VfsDirectory::GetFullPath() const { + if (IsRoot()) + return GetName(); + + return GetParentDirectory()->GetFullPath() + '/' + GetName(); +} + +bool ReadOnlyVfsDirectory::IsWritable() const { + return false; +} + +bool ReadOnlyVfsDirectory::IsReadable() const { + return true; +} + +VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) { + return nullptr; +} + +VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) { + return nullptr; +} + +VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) { + return nullptr; +} + +VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) { + return nullptr; +} + +VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) { + return nullptr; +} + +VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) { + return nullptr; +} + +bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::Rename(std::string_view name) { + return false; +} + +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { + if (file1->GetSize() != file2->GetSize()) + return false; + + std::vector<u8> f1_v(block_size); + std::vector<u8> f2_v(block_size); + for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { + auto f1_vs = file1->Read(f1_v.data(), block_size, i); + auto f2_vs = file2->Read(f2_v.data(), block_size, i); + + if (f1_vs != f2_vs) + return false; + auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); + if (iters.first != f1_v.end() && iters.second != f2_v.end()) + return false; + } + + return true; +} + +bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + if (!dest->Resize(src->GetSize())) + return false; + + std::vector<u8> temp(std::min(block_size, src->GetSize())); + for (std::size_t i = 0; i < src->GetSize(); i += block_size) { + const auto read = std::min(block_size, src->GetSize() - i); + + if (src->Read(temp.data(), read, i) != read) { + return false; + } + + if (dest->Write(temp.data(), read, i) != read) { + return false; + } + } + + return true; +} + +bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + + for (const auto& file : src->GetFiles()) { + const auto out = dest->CreateFile(file->GetName()); + if (!VfsRawCopy(file, out, block_size)) + return false; + } + + for (const auto& dir : src->GetSubdirectories()) { + const auto out = dest->CreateSubdirectory(dir->GetName()); + if (!VfsRawCopyD(dir, out, block_size)) + return false; + } + + return true; +} + +VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { + const auto res = rel->GetDirectoryRelative(path); + if (res == nullptr) + return rel->CreateDirectoryRelative(path); + return res; +} +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs.h b/src/core/file_sys/vfs/vfs.h new file mode 100644 index 000000000..f846a9669 --- /dev/null +++ b/src/core/file_sys/vfs/vfs.h @@ -0,0 +1,326 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> +#include <map> +#include <memory> +#include <optional> +#include <string> +#include <type_traits> +#include <vector> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/vfs/vfs_types.h" + +namespace FileSys { + +// An enumeration representing what can be at the end of a path in a VfsFilesystem +enum class VfsEntryType { + None, + File, + Directory, +}; + +// A class representing an abstract filesystem. A default implementation given the root VirtualDir +// is provided for convenience, but if the Vfs implementation has any additional state or +// functionality, they will need to override. +class VfsFilesystem { +public: + YUZU_NON_COPYABLE(VfsFilesystem); + YUZU_NON_MOVEABLE(VfsFilesystem); + + explicit VfsFilesystem(VirtualDir root); + virtual ~VfsFilesystem(); + + // Gets the friendly name for the filesystem. + virtual std::string GetName() const; + + // Return whether or not the user has read permissions on this filesystem. + virtual bool IsReadable() const; + // Return whether or not the user has write permission on this filesystem. + virtual bool IsWritable() const; + + // Determine if the entry at path is non-existent, a file, or a directory. + virtual VfsEntryType GetEntryType(std::string_view path) const; + + // Opens the file with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualFile OpenFile(std::string_view path, OpenMode perms); + // Creates a new, empty file at path + virtual VirtualFile CreateFile(std::string_view path, OpenMode perms); + // Copies the file from old_path to new_path, returning the new file on success and nullptr on + // failure. + virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); + // Moves the file from old_path to new_path, returning the moved file on success and nullptr on + // failure. + virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); + // Deletes the file with path relative to root, returning true on success. + virtual bool DeleteFile(std::string_view path); + + // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualDir OpenDirectory(std::string_view path, OpenMode perms); + // Creates a new, empty directory at path + virtual VirtualDir CreateDirectory(std::string_view path, OpenMode perms); + // Copies the directory from old_path to new_path, returning the new directory on success and + // nullptr on failure. + virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); + // Moves the directory from old_path to new_path, returning the moved directory on success and + // nullptr on failure. + virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); + // Deletes the directory with path relative to root, returning true on success. + virtual bool DeleteDirectory(std::string_view path); + +protected: + // Root directory in default implementation. + VirtualDir root; +}; + +// A class representing a file in an abstract filesystem. +class VfsFile { +public: + YUZU_NON_COPYABLE(VfsFile); + YUZU_NON_MOVEABLE(VfsFile); + + VfsFile() = default; + virtual ~VfsFile(); + + // Retrieves the file name. + virtual std::string GetName() const = 0; + // Retrieves the extension of the file name. + virtual std::string GetExtension() const; + // Retrieves the size of the file. + virtual std::size_t GetSize() const = 0; + // Resizes the file to new_size. Returns whether or not the operation was successful. + virtual bool Resize(std::size_t new_size) = 0; + // Gets a pointer to the directory containing this file, returning nullptr if there is none. + virtual VirtualDir GetContainingDirectory() const = 0; + + // Returns whether or not the file can be written to. + virtual bool IsWritable() const = 0; + // Returns whether or not the file can be read from. + virtual bool IsReadable() const = 0; + + // The primary method of reading from the file. Reads length bytes into data starting at offset + // into file. Returns number of bytes successfully read. + virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; + // The primary method of writing to the file. Writes length bytes from data starting at offset + // into file. Returns number of bytes successfully written. + virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; + + // Reads exactly one byte at the offset provided, returning std::nullopt on error. + virtual std::optional<u8> ReadByte(std::size_t offset = 0) const; + // Reads size bytes starting at offset in file into a vector. + virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; + // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), + // 0)' + virtual std::vector<u8> ReadAllBytes() const; + + // Reads an array of type T, size number_elements starting at offset. + // Returns the number of bytes (sizeof(T)*number_elements) read successfully. + template <typename T> + std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { + static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); + + return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); + } + + // Reads size bytes into the memory starting at data starting at offset into the file. + // Returns the number of bytes read successfully. + template <typename T> + std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { + static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); + return Read(reinterpret_cast<u8*>(data), size, offset); + } + + // Reads one object of type T starting at offset in file. + // Returns the number of bytes read successfully (sizeof(T)). + template <typename T> + std::size_t ReadObject(T* data, std::size_t offset = 0) const { + static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); + return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); + } + + // Writes exactly one byte to offset in file and returns whether or not the byte was written + // successfully. + virtual bool WriteByte(u8 data, std::size_t offset = 0); + // Writes a vector of bytes to offset in file and returns the number of bytes successfully + // written. + virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0); + + // Writes an array of type T, size number_elements to offset in file. + // Returns the number of bytes (sizeof(T)*number_elements) written successfully. + template <typename T> + std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { + static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); + return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset); + } + + // Writes size bytes starting at memory location data to offset in file. + // Returns the number of bytes written successfully. + template <typename T> + std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { + static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); + return Write(reinterpret_cast<const u8*>(data), size, offset); + } + + // Writes one object of type T to offset in file. + // Returns the number of bytes written successfully (sizeof(T)). + template <typename T> + std::size_t WriteObject(const T& data, std::size_t offset = 0) { + static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); + return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset); + } + + // Renames the file to name. Returns whether or not the operation was successful. + virtual bool Rename(std::string_view name) = 0; + + // Returns the full path of this file as a string, recursively + virtual std::string GetFullPath() const; +}; + +// A class representing a directory in an abstract filesystem. +class VfsDirectory { +public: + YUZU_NON_COPYABLE(VfsDirectory); + YUZU_NON_MOVEABLE(VfsDirectory); + + VfsDirectory() = default; + virtual ~VfsDirectory(); + + // Retrieves the file located at path as if the current directory was root. Returns nullptr if + // not found. + virtual VirtualFile GetFileRelative(std::string_view path) const; + // Calls GetFileRelative(path) on the root of the current directory. + virtual VirtualFile GetFileAbsolute(std::string_view path) const; + + // Retrieves the directory located at path as if the current directory was root. Returns nullptr + // if not found. + virtual VirtualDir GetDirectoryRelative(std::string_view path) const; + // Calls GetDirectoryRelative(path) on the root of the current directory. + virtual VirtualDir GetDirectoryAbsolute(std::string_view path) const; + + // Returns a vector containing all of the files in this directory. + virtual std::vector<VirtualFile> GetFiles() const = 0; + // Returns the file with filename matching name. Returns nullptr if directory doesn't have a + // file with name. + virtual VirtualFile GetFile(std::string_view name) const; + + // Returns a struct containing the file's timestamp. + virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const; + + // Returns a vector containing all of the subdirectories in this directory. + virtual std::vector<VirtualDir> GetSubdirectories() const = 0; + // Returns the directory with name matching name. Returns nullptr if directory doesn't have a + // directory with name. + virtual VirtualDir GetSubdirectory(std::string_view name) const; + + // Returns whether or not the directory can be written to. + virtual bool IsWritable() const = 0; + // Returns whether of not the directory can be read from. + virtual bool IsReadable() const = 0; + + // Returns whether or not the directory is the root of the current file tree. + virtual bool IsRoot() const; + + // Returns the name of the directory. + virtual std::string GetName() const = 0; + // Returns the total size of all files and subdirectories in this directory. + virtual std::size_t GetSize() const; + // Returns the parent directory of this directory. Returns nullptr if this directory is root or + // has no parent. + virtual VirtualDir GetParentDirectory() const = 0; + + // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr + // if the operation failed. + virtual VirtualDir CreateSubdirectory(std::string_view name) = 0; + // Creates a new file with name name. Returns a pointer to the new file or nullptr if the + // operation failed. + virtual VirtualFile CreateFile(std::string_view name) = 0; + + // Creates a new file at the path relative to this directory. Also creates directories if + // they do not exist and is supported by this implementation. Returns nullptr on any failure. + virtual VirtualFile CreateFileRelative(std::string_view path); + + // Creates a new file at the path relative to root of this directory. Also creates directories + // if they do not exist and is supported by this implementation. Returns nullptr on any failure. + virtual VirtualFile CreateFileAbsolute(std::string_view path); + + // Creates a new directory at the path relative to this directory. Also creates directories if + // they do not exist and is supported by this implementation. Returns nullptr on any failure. + virtual VirtualDir CreateDirectoryRelative(std::string_view path); + + // Creates a new directory at the path relative to root of this directory. Also creates + // directories if they do not exist and is supported by this implementation. Returns nullptr on + // any failure. + virtual VirtualDir CreateDirectoryAbsolute(std::string_view path); + + // Deletes the subdirectory with the given name and returns true on success. + virtual bool DeleteSubdirectory(std::string_view name) = 0; + + // Deletes all subdirectories and files within the provided directory and then deletes + // the directory itself. Returns true on success. + virtual bool DeleteSubdirectoryRecursive(std::string_view name); + + // Deletes all subdirectories and files within the provided directory. + // Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory. + virtual bool CleanSubdirectoryRecursive(std::string_view name); + + // Returns whether or not the file with name name was deleted successfully. + virtual bool DeleteFile(std::string_view name) = 0; + + // Returns whether or not this directory was renamed to name. + virtual bool Rename(std::string_view name) = 0; + + // Returns whether or not the file with name src was successfully copied to a new file with name + // dest. + virtual bool Copy(std::string_view src, std::string_view dest); + + // Gets all of the entries directly in the directory (files and dirs), returning a map between + // item name -> type. + virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; + + // Returns the full path of this directory as a string, recursively + virtual std::string GetFullPath() const; +}; + +// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work +// if writable. This is to avoid redundant empty methods everywhere. +class ReadOnlyVfsDirectory : public VfsDirectory { +public: + bool IsWritable() const override; + bool IsReadable() const override; + VirtualDir CreateSubdirectory(std::string_view name) override; + VirtualFile CreateFile(std::string_view name) override; + VirtualFile CreateFileAbsolute(std::string_view path) override; + VirtualFile CreateFileRelative(std::string_view path) override; + VirtualDir CreateDirectoryAbsolute(std::string_view path) override; + VirtualDir CreateDirectoryRelative(std::string_view path) override; + bool DeleteSubdirectory(std::string_view name) override; + bool DeleteSubdirectoryRecursive(std::string_view name) override; + bool CleanSubdirectoryRecursive(std::string_view name) override; + bool DeleteFile(std::string_view name) override; + bool Rename(std::string_view name) override; +}; + +// Compare the two files, byte-for-byte, in increments specified by block_size +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, + std::size_t block_size = 0x1000); + +// A method that copies the raw data between two different implementations of VirtualFile. If you +// are using the same implementation, it is probably better to use the Copy method in the parent +// directory of src/dest. +bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000); + +// A method that performs a similar function to VfsRawCopy above, but instead copies entire +// directories. It suffers the same performance penalties as above and an implementation-specific +// Copy should always be preferred. +bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000); + +// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not +// it attempts to create it and returns the new dir or nullptr on failure. +VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path); + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_cached.cpp b/src/core/file_sys/vfs/vfs_cached.cpp new file mode 100644 index 000000000..01cd0f1e0 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_cached.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_types.h" + +namespace FileSys { + +CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir) + : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { + for (auto& dir : source_dir->GetSubdirectories()) { + dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir))); + } + for (auto& file : source_dir->GetFiles()) { + files.emplace(file->GetName(), std::move(file)); + } +} + +CachedVfsDirectory::~CachedVfsDirectory() = default; + +VirtualFile CachedVfsDirectory::GetFile(std::string_view file_name) const { + auto it = files.find(file_name); + if (it != files.end()) { + return it->second; + } + + return nullptr; +} + +VirtualDir CachedVfsDirectory::GetSubdirectory(std::string_view dir_name) const { + auto it = dirs.find(dir_name); + if (it != dirs.end()) { + return it->second; + } + + return nullptr; +} + +std::vector<VirtualFile> CachedVfsDirectory::GetFiles() const { + std::vector<VirtualFile> out; + for (auto& [file_name, file] : files) { + out.push_back(file); + } + return out; +} + +std::vector<VirtualDir> CachedVfsDirectory::GetSubdirectories() const { + std::vector<VirtualDir> out; + for (auto& [dir_name, dir] : dirs) { + out.push_back(dir); + } + return out; +} + +std::string CachedVfsDirectory::GetName() const { + return name; +} + +VirtualDir CachedVfsDirectory::GetParentDirectory() const { + return parent; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_cached.h b/src/core/file_sys/vfs/vfs_cached.h new file mode 100644 index 000000000..47dff7224 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_cached.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string_view> +#include <vector> +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +class CachedVfsDirectory : public ReadOnlyVfsDirectory { +public: + CachedVfsDirectory(VirtualDir&& source_directory); + + ~CachedVfsDirectory() override; + VirtualFile GetFile(std::string_view file_name) const override; + VirtualDir GetSubdirectory(std::string_view dir_name) const override; + std::vector<VirtualFile> GetFiles() const override; + std::vector<VirtualDir> GetSubdirectories() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + +private: + std::string name; + VirtualDir parent; + std::map<std::string, VirtualDir, std::less<>> dirs; + std::map<std::string, VirtualFile, std::less<>> files; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_concat.cpp b/src/core/file_sys/vfs/vfs_concat.cpp new file mode 100644 index 000000000..b5cc9a9e9 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_concat.cpp @@ -0,0 +1,192 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <utility> + +#include "common/assert.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_static.h" + +namespace FileSys { + +ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_) + : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { + DEBUG_ASSERT(this->VerifyContinuity()); +} + +bool ConcatenatedVfsFile::VerifyContinuity() const { + u64 last_offset = 0; + for (auto& entry : concatenation_map) { + if (entry.offset != last_offset) { + return false; + } + + last_offset = entry.offset + entry.file->GetSize(); + } + + return true; +} + +ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; + +VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name, + std::vector<VirtualFile>&& files) { + // Fold trivial cases. + if (files.empty()) { + return nullptr; + } + if (files.size() == 1) { + return files.front(); + } + + // Make the concatenation map from the input. + std::vector<ConcatenationEntry> concatenation_map; + concatenation_map.reserve(files.size()); + u64 last_offset = 0; + + for (auto& file : files) { + const auto size = file->GetSize(); + + concatenation_map.emplace_back(ConcatenationEntry{ + .offset = last_offset, + .file = std::move(file), + }); + + last_offset += size; + } + + return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); +} + +VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile( + u8 filler_byte, std::string&& name, std::vector<std::pair<u64, VirtualFile>>&& files) { + // Fold trivial cases. + if (files.empty()) { + return nullptr; + } + if (files.size() == 1) { + return files.begin()->second; + } + + // Make the concatenation map from the input. + std::vector<ConcatenationEntry> concatenation_map; + + concatenation_map.reserve(files.size()); + u64 last_offset = 0; + + // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. + for (auto& [offset, file] : files) { + const auto size = file->GetSize(); + + if (offset > last_offset) { + concatenation_map.emplace_back(ConcatenationEntry{ + .offset = last_offset, + .file = std::make_shared<StaticVfsFile>(filler_byte, offset - last_offset), + }); + } + + concatenation_map.emplace_back(ConcatenationEntry{ + .offset = offset, + .file = std::move(file), + }); + + last_offset = offset + size; + } + + return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); +} + +std::string ConcatenatedVfsFile::GetName() const { + if (concatenation_map.empty()) { + return ""; + } + if (!name.empty()) { + return name; + } + return concatenation_map.front().file->GetName(); +} + +std::size_t ConcatenatedVfsFile::GetSize() const { + if (concatenation_map.empty()) { + return 0; + } + return concatenation_map.back().offset + concatenation_map.back().file->GetSize(); +} + +bool ConcatenatedVfsFile::Resize(std::size_t new_size) { + return false; +} + +VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const { + if (concatenation_map.empty()) { + return nullptr; + } + return concatenation_map.front().file->GetContainingDirectory(); +} + +bool ConcatenatedVfsFile::IsWritable() const { + return false; +} + +bool ConcatenatedVfsFile::IsReadable() const { + return true; +} + +std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { + const ConcatenationEntry key{ + .offset = offset, + .file = nullptr, + }; + + // Read nothing if the map is empty. + if (concatenation_map.empty()) { + return 0; + } + + // Binary search to find the iterator to the first position we can check. + // It must exist, since we are not empty and are comparing unsigned integers. + auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key)); + u64 cur_length = length; + u64 cur_offset = offset; + + while (cur_length > 0 && it != concatenation_map.end()) { + // Check if we can read the file at this position. + const auto& file = it->file; + const u64 map_offset = it->offset; + const u64 file_size = file->GetSize(); + + if (cur_offset > map_offset + file_size) { + // Entirely out of bounds read. + break; + } + + // Read the file at this position. + const u64 file_seek = cur_offset - map_offset; + const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek); + const u64 actual_read_size = + file->Read(data + (cur_offset - offset), intended_read_size, file_seek); + + // Update tracking. + cur_offset += actual_read_size; + cur_length -= actual_read_size; + it++; + + // If we encountered a short read, we're done. + if (actual_read_size < intended_read_size) { + break; + } + } + + return cur_offset - offset; +} + +std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { + return 0; +} + +bool ConcatenatedVfsFile::Rename(std::string_view new_name) { + return false; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_concat.h b/src/core/file_sys/vfs/vfs_concat.h new file mode 100644 index 000000000..6d12af762 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_concat.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <compare> +#include <map> +#include <memory> +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently +// read-only. +class ConcatenatedVfsFile : public VfsFile { +private: + struct ConcatenationEntry { + u64 offset; + VirtualFile file; + + auto operator<=>(const ConcatenationEntry& other) const { + return this->offset <=> other.offset; + } + }; + using ConcatenationMap = std::vector<ConcatenationEntry>; + + explicit ConcatenatedVfsFile(std::string&& name, + std::vector<ConcatenationEntry>&& concatenation_map); + bool VerifyContinuity() const; + +public: + ~ConcatenatedVfsFile() override; + + /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. + static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files); + + /// Convenience function that turns a map of offsets to files into a concatenated file, filling + /// gaps with a given filler byte. + static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, + std::vector<std::pair<u64, VirtualFile>>&& files); + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view new_name) override; + +private: + ConcatenationMap concatenation_map; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_layered.cpp b/src/core/file_sys/vfs/vfs_layered.cpp new file mode 100644 index 000000000..47b2a3c78 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_layered.cpp @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <set> +#include <unordered_set> +#include <utility> +#include "core/file_sys/vfs/vfs_layered.h" + +namespace FileSys { + +LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_) + : dirs(std::move(dirs_)), name(std::move(name_)) {} + +LayeredVfsDirectory::~LayeredVfsDirectory() = default; + +VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs, + std::string name) { + if (dirs.empty()) + return nullptr; + if (dirs.size() == 1) + return dirs[0]; + + return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); +} + +VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const { + for (const auto& layer : dirs) { + const auto file = layer->GetFileRelative(path); + if (file != nullptr) + return file; + } + + return nullptr; +} + +VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) const { + std::vector<VirtualDir> out; + for (const auto& layer : dirs) { + auto dir = layer->GetDirectoryRelative(path); + if (dir != nullptr) { + out.emplace_back(std::move(dir)); + } + } + + return MakeLayeredDirectory(std::move(out)); +} + +VirtualFile LayeredVfsDirectory::GetFile(std::string_view file_name) const { + return GetFileRelative(file_name); +} + +VirtualDir LayeredVfsDirectory::GetSubdirectory(std::string_view subdir_name) const { + return GetDirectoryRelative(subdir_name); +} + +std::string LayeredVfsDirectory::GetFullPath() const { + return dirs[0]->GetFullPath(); +} + +std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const { + std::vector<VirtualFile> out; + std::unordered_set<std::string> out_names; + + for (const auto& layer : dirs) { + for (auto& file : layer->GetFiles()) { + const auto [it, is_new] = out_names.emplace(file->GetName()); + if (is_new) { + out.emplace_back(std::move(file)); + } + } + } + + return out; +} + +std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const { + std::vector<VirtualDir> out; + std::unordered_set<std::string> out_names; + + for (const auto& layer : dirs) { + for (const auto& sd : layer->GetSubdirectories()) { + out_names.emplace(sd->GetName()); + } + } + + out.reserve(out_names.size()); + for (const auto& subdir : out_names) { + out.emplace_back(GetSubdirectory(subdir)); + } + + return out; +} + +bool LayeredVfsDirectory::IsWritable() const { + return false; +} + +bool LayeredVfsDirectory::IsReadable() const { + return true; +} + +std::string LayeredVfsDirectory::GetName() const { + return name.empty() ? dirs[0]->GetName() : name; +} + +VirtualDir LayeredVfsDirectory::GetParentDirectory() const { + return dirs[0]->GetParentDirectory(); +} + +VirtualDir LayeredVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { + return nullptr; +} + +VirtualFile LayeredVfsDirectory::CreateFile(std::string_view file_name) { + return nullptr; +} + +bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { + return false; +} + +bool LayeredVfsDirectory::DeleteFile(std::string_view file_name) { + return false; +} + +bool LayeredVfsDirectory::Rename(std::string_view new_name) { + name = new_name; + return true; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_layered.h b/src/core/file_sys/vfs/vfs_layered.h new file mode 100644 index 000000000..0027ffa9a --- /dev/null +++ b/src/core/file_sys/vfs/vfs_layered.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first +// one and falling back to the one after. The highest priority directory (overwrites all others) +// should be element 0 in the dirs vector. +class LayeredVfsDirectory : public VfsDirectory { + explicit LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_); + +public: + ~LayeredVfsDirectory() override; + + /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. + static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = ""); + + VirtualFile GetFileRelative(std::string_view path) const override; + VirtualDir GetDirectoryRelative(std::string_view path) const override; + VirtualFile GetFile(std::string_view file_name) const override; + VirtualDir GetSubdirectory(std::string_view subdir_name) const override; + std::string GetFullPath() const override; + + std::vector<VirtualFile> GetFiles() const override; + std::vector<VirtualDir> GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + VirtualDir CreateSubdirectory(std::string_view subdir_name) override; + VirtualFile CreateFile(std::string_view file_name) override; + bool DeleteSubdirectory(std::string_view subdir_name) override; + bool DeleteFile(std::string_view file_name) override; + bool Rename(std::string_view new_name) override; + +private: + std::vector<VirtualDir> dirs; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_offset.cpp b/src/core/file_sys/vfs/vfs_offset.cpp new file mode 100644 index 000000000..1a37d2670 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_offset.cpp @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <utility> + +#include "core/file_sys/vfs/vfs_offset.h" + +namespace FileSys { + +OffsetVfsFile::OffsetVfsFile(VirtualFile file_, std::size_t size_, std::size_t offset_, + std::string name_, VirtualDir parent_) + : file(file_), offset(offset_), size(size_), name(std::move(name_)), + parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} + +OffsetVfsFile::~OffsetVfsFile() = default; + +std::string OffsetVfsFile::GetName() const { + return name.empty() ? file->GetName() : name; +} + +std::size_t OffsetVfsFile::GetSize() const { + return size; +} + +bool OffsetVfsFile::Resize(std::size_t new_size) { + if (offset + new_size < file->GetSize()) { + size = new_size; + } else { + auto res = file->Resize(offset + new_size); + if (!res) + return false; + size = new_size; + } + + return true; +} + +VirtualDir OffsetVfsFile::GetContainingDirectory() const { + return parent; +} + +bool OffsetVfsFile::IsWritable() const { + return file->IsWritable(); +} + +bool OffsetVfsFile::IsReadable() const { + return file->IsReadable(); +} + +std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { + return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); +} + +std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { + return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); +} + +std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { + if (r_offset >= size) { + return std::nullopt; + } + + return file->ReadByte(offset + r_offset); +} + +std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { + return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); +} + +std::vector<u8> OffsetVfsFile::ReadAllBytes() const { + return file->ReadBytes(size, offset); +} + +bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { + if (r_offset < size) + return file->WriteByte(data, offset + r_offset); + + return false; +} + +std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) { + return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); +} + +bool OffsetVfsFile::Rename(std::string_view new_name) { + return file->Rename(new_name); +} + +std::size_t OffsetVfsFile::GetOffset() const { + return offset; +} + +std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { + return std::clamp(r_size, std::size_t{0}, size - r_offset); +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_offset.h b/src/core/file_sys/vfs/vfs_offset.h new file mode 100644 index 000000000..4abe41d8e --- /dev/null +++ b/src/core/file_sys/vfs/vfs_offset.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// An implementation of VfsFile that wraps around another VfsFile at a certain offset. +// Similar to seeking to an offset. +// If the file is writable, operations that would write past the end of the offset file will expand +// the size of this wrapper. +class OffsetVfsFile : public VfsFile { +public: + OffsetVfsFile(VirtualFile file, std::size_t size, std::size_t offset = 0, + std::string new_name = "", VirtualDir new_parent = nullptr); + ~OffsetVfsFile() override; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + std::optional<u8> ReadByte(std::size_t offset) const override; + std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; + std::vector<u8> ReadAllBytes() const override; + bool WriteByte(u8 data, std::size_t offset) override; + std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override; + + bool Rename(std::string_view new_name) override; + + std::size_t GetOffset() const; + +private: + std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; + + VirtualFile file; + std::size_t offset; + std::size_t size; + std::string name; + VirtualDir parent; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp new file mode 100644 index 000000000..3ad073e4a --- /dev/null +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -0,0 +1,536 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <utility> +#include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_real.h" + +// For FileTimeStampRaw +#include <sys/stat.h> + +#ifdef _MSC_VER +#define stat _stat64 +#endif + +#ifdef ANDROID +#include "common/fs/fs_android.h" +#endif + +namespace FileSys { + +namespace FS = Common::FS; + +namespace { + +constexpr size_t MaxOpenFiles = 512; + +constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) { + switch (mode) { + case OpenMode::Read: + return FS::FileAccessMode::Read; + case OpenMode::Write: + case OpenMode::ReadWrite: + case OpenMode::AllowAppend: + case OpenMode::All: + return FS::FileAccessMode::ReadWrite; + default: + return {}; + } +} + +} // Anonymous namespace + +RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} +RealVfsFilesystem::~RealVfsFilesystem() = default; + +std::string RealVfsFilesystem::GetName() const { + return "Real"; +} + +bool RealVfsFilesystem::IsReadable() const { + return true; +} + +bool RealVfsFilesystem::IsWritable() const { + return true; +} + +VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + if (!FS::Exists(path)) { + return VfsEntryType::None; + } + if (FS::IsDir(path)) { + return VfsEntryType::Directory; + } + + return VfsEntryType::File; +} + +VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, + OpenMode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + std::scoped_lock lk{list_lock}; + + if (auto it = cache.find(path); it != cache.end()) { + if (auto file = it->second.lock(); file) { + return file; + } + } + + if (!size && !FS::IsFile(path)) { + return nullptr; + } + + auto reference = std::make_unique<FileReference>(); + this->InsertReferenceIntoListLocked(*reference); + + auto file = std::shared_ptr<RealVfsFile>( + new RealVfsFile(*this, std::move(reference), path, perms, size)); + cache[path] = file; + + return file; +} + +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) { + return OpenFileFromEntry(path_, {}, perms); +} + +VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(path); + } + + // Current usages of CreateFile expect to delete the contents of an existing file. + if (FS::IsFile(path)) { + FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; + + if (!temp.IsOpen()) { + return nullptr; + } + + temp.Close(); + + return OpenFile(path, perms); + } + + if (!FS::NewFile(path)) { + return nullptr; + } + + return OpenFile(path, perms); +} + +VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + // Unused + return nullptr; +} + +VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); + const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(old_path); + cache.erase(new_path); + } + if (!FS::RenameFile(old_path, new_path)) { + return nullptr; + } + return OpenFile(new_path, OpenMode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(path); + } + return FS::RemoveFile(path); +} + +VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + if (!FS::CreateDirs(path)) { + return nullptr; + } + return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, + std::string_view new_path_) { + // Unused + return nullptr; +} + +VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, + std::string_view new_path_) { + const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); + const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); + + if (!FS::RenameDir(old_path, new_path)) { + return nullptr; + } + return OpenDirectory(new_path, OpenMode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + return FS::RemoveDirRecursively(path); +} + +std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, + OpenMode perms, + FileReference& reference) { + std::unique_lock lk{list_lock}; + + // Temporarily remove from list. + this->RemoveReferenceFromListLocked(reference); + + // Restore file if needed. + if (!reference.file) { + this->EvictSingleReferenceLocked(); + + reference.file = + FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); + if (reference.file) { + num_open_files++; + } + } + + // Reinsert into list. + this->InsertReferenceIntoListLocked(reference); + + return lk; +} + +void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) { + std::scoped_lock lk{list_lock}; + + // Remove from list. + this->RemoveReferenceFromListLocked(*reference); + + // Close the file. + if (reference->file) { + reference->file.reset(); + num_open_files--; + } +} + +void RealVfsFilesystem::EvictSingleReferenceLocked() { + if (num_open_files < MaxOpenFiles || open_references.empty()) { + return; + } + + // Get and remove from list. + auto& reference = open_references.back(); + this->RemoveReferenceFromListLocked(reference); + + // Close the file. + if (reference.file) { + reference.file.reset(); + num_open_files--; + } + + // Reinsert into closed list. + this->InsertReferenceIntoListLocked(reference); +} + +void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) { + if (reference.file) { + open_references.push_front(reference); + } else { + closed_references.push_front(reference); + } +} + +void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) { + if (reference.file) { + open_references.erase(open_references.iterator_to(reference)); + } else { + closed_references.erase(closed_references.iterator_to(reference)); + } +} + +RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, + const std::string& path_, OpenMode perms_, std::optional<u64> size_) + : base(base_), reference(std::move(reference_)), path(path_), + parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), + size(size_), perms(perms_) {} + +RealVfsFile::~RealVfsFile() { + base.DropReference(std::move(reference)); +} + +std::string RealVfsFile::GetName() const { +#ifdef ANDROID + if (path[0] != '/') { + return FS::Android::GetFilename(path); + } +#endif + return path_components.empty() ? "" : std::string(path_components.back()); +} + +std::size_t RealVfsFile::GetSize() const { + if (size) { + return *size; + } + auto lk = base.RefreshReference(path, perms, *reference); + return reference->file ? reference->file->GetSize() : 0; +} + +bool RealVfsFile::Resize(std::size_t new_size) { + size.reset(); + auto lk = base.RefreshReference(path, perms, *reference); + return reference->file ? reference->file->SetSize(new_size) : false; +} + +VirtualDir RealVfsFile::GetContainingDirectory() const { + return base.OpenDirectory(parent_path, perms); +} + +bool RealVfsFile::IsWritable() const { + return True(perms & OpenMode::Write); +} + +bool RealVfsFile::IsReadable() const { + return True(perms & OpenMode::Read); +} + +std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { + auto lk = base.RefreshReference(path, perms, *reference); + if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { + return 0; + } + return reference->file->ReadSpan(std::span{data, length}); +} + +std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { + size.reset(); + auto lk = base.RefreshReference(path, perms, *reference); + if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { + return 0; + } + return reference->file->WriteSpan(std::span{data, length}); +} + +bool RealVfsFile::Rename(std::string_view name) { + return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; +} + +// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if +// constexpr' because there is a compile error in the branch not used. + +template <> +std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { + if (perms == OpenMode::AllowAppend) { + return {}; + } + + std::vector<VirtualFile> out; + + const FS::DirEntryCallable callback = [this, + &out](const std::filesystem::directory_entry& entry) { + const auto full_path_string = FS::PathToUTF8String(entry.path()); + + out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms)); + + return true; + }; + + FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File); + + return out; +} + +template <> +std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { + if (perms == OpenMode::AllowAppend) { + return {}; + } + + std::vector<VirtualDir> out; + + const FS::DirEntryCallable callback = [this, + &out](const std::filesystem::directory_entry& entry) { + const auto full_path_string = FS::PathToUTF8String(entry.path()); + + out.emplace_back(base.OpenDirectory(full_path_string, perms)); + + return true; + }; + + FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory); + + return out; +} + +RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, + OpenMode perms_) + : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), + path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { + if (!FS::Exists(path) && True(perms & OpenMode::Write)) { + void(FS::CreateDirs(path)); + } +} + +RealVfsDirectory::~RealVfsDirectory() = default; + +VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + if (!FS::Exists(full_path) || FS::IsDir(full_path)) { + return nullptr; + } + return base.OpenFile(full_path, perms); +} + +VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + if (!FS::Exists(full_path) || !FS::IsDir(full_path)) { + return nullptr; + } + return base.OpenDirectory(full_path, perms); +} + +VirtualFile RealVfsDirectory::GetFile(std::string_view name) const { + return GetFileRelative(name); +} + +VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const { + return GetDirectoryRelative(name); +} + +VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + if (!FS::CreateParentDirs(full_path)) { + return nullptr; + } + return base.CreateFile(full_path, perms); +} + +VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + return base.CreateDirectory(full_path, perms); +} + +bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name)); + return base.DeleteDirectory(full_path); +} + +std::vector<VirtualFile> RealVfsDirectory::GetFiles() const { + return IterateEntries<RealVfsFile, VfsFile>(); +} + +FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const { + const auto full_path = FS::SanitizePath(path + '/' + std::string(path_)); + const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)}; + struct stat file_status; + +#ifdef _WIN32 + const auto stat_result = _wstat64(fs_path.c_str(), &file_status); +#else + const auto stat_result = stat(fs_path.c_str(), &file_status); +#endif + + if (stat_result != 0) { + return {}; + } + + return { + .created{static_cast<u64>(file_status.st_ctime)}, + .accessed{static_cast<u64>(file_status.st_atime)}, + .modified{static_cast<u64>(file_status.st_mtime)}, + }; +} + +std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { + return IterateEntries<RealVfsDirectory, VfsDirectory>(); +} + +bool RealVfsDirectory::IsWritable() const { + return True(perms & OpenMode::Write); +} + +bool RealVfsDirectory::IsReadable() const { + return True(perms & OpenMode::Read); +} + +std::string RealVfsDirectory::GetName() const { + return path_components.empty() ? "" : std::string(path_components.back()); +} + +VirtualDir RealVfsDirectory::GetParentDirectory() const { + if (path_components.size() <= 1) { + return nullptr; + } + + return base.OpenDirectory(parent_path, perms); +} + +VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { + const std::string subdir_path = (path + '/').append(name); + return base.CreateDirectory(subdir_path, perms); +} + +VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { + const std::string file_path = (path + '/').append(name); + return base.CreateFile(file_path, perms); +} + +bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { + const std::string subdir_path = (path + '/').append(name); + return base.DeleteDirectory(subdir_path); +} + +bool RealVfsDirectory::DeleteFile(std::string_view name) { + const std::string file_path = (path + '/').append(name); + return base.DeleteFile(file_path); +} + +bool RealVfsDirectory::Rename(std::string_view name) { + const std::string new_name = (parent_path + '/').append(name); + return base.MoveFile(path, new_name) != nullptr; +} + +std::string RealVfsDirectory::GetFullPath() const { + auto out = path; + std::replace(out.begin(), out.end(), '\\', '/'); + return out; +} + +std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { + if (perms == OpenMode::AllowAppend) { + return {}; + } + + std::map<std::string, VfsEntryType, std::less<>> out; + + const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { + const auto filename = FS::PathToUTF8String(entry.path().filename()); + out.insert_or_assign(filename, + entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File); + return true; + }; + + FS::IterateDirEntries(path, callback); + + return out; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h new file mode 100644 index 000000000..5c2172cce --- /dev/null +++ b/src/core/file_sys/vfs/vfs_real.h @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <map> +#include <mutex> +#include <optional> +#include <string_view> +#include "common/intrusive_list.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/vfs/vfs.h" + +namespace Common::FS { +class IOFile; +} + +namespace FileSys { + +struct FileReference : public Common::IntrusiveListBaseNode<FileReference> { + std::shared_ptr<Common::FS::IOFile> file{}; +}; + +class RealVfsFile; +class RealVfsDirectory; + +class RealVfsFilesystem : public VfsFilesystem { +public: + RealVfsFilesystem(); + ~RealVfsFilesystem() override; + + std::string GetName() const override; + bool IsReadable() const override; + bool IsWritable() const override; + VfsEntryType GetEntryType(std::string_view path) const override; + VirtualFile OpenFile(std::string_view path, OpenMode perms = OpenMode::Read) override; + VirtualFile CreateFile(std::string_view path, OpenMode perms = OpenMode::ReadWrite) override; + VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; + VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; + bool DeleteFile(std::string_view path) override; + VirtualDir OpenDirectory(std::string_view path, OpenMode perms = OpenMode::Read) override; + VirtualDir CreateDirectory(std::string_view path, + OpenMode perms = OpenMode::ReadWrite) override; + VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; + VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; + bool DeleteDirectory(std::string_view path) override; + +private: + using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType; + std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache; + ReferenceListType open_references; + ReferenceListType closed_references; + std::mutex list_lock; + size_t num_open_files{}; + +private: + friend class RealVfsFile; + std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms, + FileReference& reference); + void DropReference(std::unique_ptr<FileReference>&& reference); + +private: + friend class RealVfsDirectory; + VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, + OpenMode perms = OpenMode::Read); + +private: + void EvictSingleReferenceLocked(); + void InsertReferenceIntoListLocked(FileReference& reference); + void RemoveReferenceFromListLocked(FileReference& reference); +}; + +// An implementation of VfsFile that represents a file on the user's computer. +class RealVfsFile : public VfsFile { + friend class RealVfsDirectory; + friend class RealVfsFilesystem; + +public: + ~RealVfsFile() override; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view name) override; + +private: + RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, + const std::string& path, OpenMode perms = OpenMode::Read, + std::optional<u64> size = {}); + + RealVfsFilesystem& base; + std::unique_ptr<FileReference> reference; + std::string path; + std::string parent_path; + std::vector<std::string> path_components; + std::optional<u64> size; + OpenMode perms; +}; + +// An implementation of VfsDirectory that represents a directory on the user's computer. +class RealVfsDirectory : public VfsDirectory { + friend class RealVfsFilesystem; + +public: + ~RealVfsDirectory() override; + + VirtualFile GetFileRelative(std::string_view relative_path) const override; + VirtualDir GetDirectoryRelative(std::string_view relative_path) const override; + VirtualFile GetFile(std::string_view name) const override; + VirtualDir GetSubdirectory(std::string_view name) const override; + VirtualFile CreateFileRelative(std::string_view relative_path) override; + VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; + bool DeleteSubdirectoryRecursive(std::string_view name) override; + std::vector<VirtualFile> GetFiles() const override; + FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override; + std::vector<VirtualDir> GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + VirtualDir CreateSubdirectory(std::string_view name) override; + VirtualFile CreateFile(std::string_view name) override; + bool DeleteSubdirectory(std::string_view name) override; + bool DeleteFile(std::string_view name) override; + bool Rename(std::string_view name) override; + std::string GetFullPath() const override; + std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; + +private: + RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, + OpenMode perms = OpenMode::Read); + + template <typename T, typename R> + std::vector<std::shared_ptr<R>> IterateEntries() const; + + RealVfsFilesystem& base; + std::string path; + std::string parent_path; + std::vector<std::string> path_components; + OpenMode perms; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_static.h b/src/core/file_sys/vfs/vfs_static.h new file mode 100644 index 000000000..bb53560ac --- /dev/null +++ b/src/core/file_sys/vfs/vfs_static.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <algorithm> +#include <memory> +#include <string_view> + +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +class StaticVfsFile : public VfsFile { +public: + explicit StaticVfsFile(u8 value_, std::size_t size_ = 0, std::string name_ = "", + VirtualDir parent_ = nullptr) + : value{value_}, size{size_}, name{std::move(name_)}, parent{std::move(parent_)} {} + + std::string GetName() const override { + return name; + } + + std::size_t GetSize() const override { + return size; + } + + bool Resize(std::size_t new_size) override { + size = new_size; + return true; + } + + VirtualDir GetContainingDirectory() const override { + return parent; + } + + bool IsWritable() const override { + return false; + } + + bool IsReadable() const override { + return true; + } + + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + std::fill(data, data + read, value); + return read; + } + + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { + return 0; + } + + std::optional<u8> ReadByte(std::size_t offset) const override { + if (offset >= size) { + return std::nullopt; + } + + return value; + } + + std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + return std::vector<u8>(read, value); + } + + bool Rename(std::string_view new_name) override { + name = new_name; + return true; + } + +private: + u8 value; + std::size_t size; + std::string name; + VirtualDir parent; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs/vfs_types.h index 4a583ed64..4a583ed64 100644 --- a/src/core/file_sys/vfs_types.h +++ b/src/core/file_sys/vfs/vfs_types.h diff --git a/src/core/file_sys/vfs/vfs_vector.cpp b/src/core/file_sys/vfs/vfs_vector.cpp new file mode 100644 index 000000000..0d54461c8 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_vector.cpp @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <utility> +#include "core/file_sys/vfs/vfs_vector.h" + +namespace FileSys { +VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name_, VirtualDir parent_) + : data(std::move(initial_data)), parent(std::move(parent_)), name(std::move(name_)) {} + +VectorVfsFile::~VectorVfsFile() = default; + +std::string VectorVfsFile::GetName() const { + return name; +} + +size_t VectorVfsFile::GetSize() const { + return data.size(); +} + +bool VectorVfsFile::Resize(size_t new_size) { + data.resize(new_size); + return true; +} + +VirtualDir VectorVfsFile::GetContainingDirectory() const { + return parent; +} + +bool VectorVfsFile::IsWritable() const { + return true; +} + +bool VectorVfsFile::IsReadable() const { + return true; +} + +std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const { + const auto read = std::min(length, data.size() - offset); + std::memcpy(data_, data.data() + offset, read); + return read; +} + +std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) { + if (offset + length > data.size()) + data.resize(offset + length); + const auto write = std::min(length, data.size() - offset); + std::memcpy(data.data() + offset, data_, write); + return write; +} + +bool VectorVfsFile::Rename(std::string_view name_) { + name = name_; + return true; +} + +void VectorVfsFile::Assign(std::vector<u8> new_data) { + data = std::move(new_data); +} + +VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, + std::vector<VirtualDir> dirs_, std::string name_, + VirtualDir parent_) + : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), + name(std::move(name_)) {} + +VectorVfsDirectory::~VectorVfsDirectory() = default; + +std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const { + return files; +} + +std::vector<VirtualDir> VectorVfsDirectory::GetSubdirectories() const { + return dirs; +} + +bool VectorVfsDirectory::IsWritable() const { + return false; +} + +bool VectorVfsDirectory::IsReadable() const { + return true; +} + +std::string VectorVfsDirectory::GetName() const { + return name; +} + +VirtualDir VectorVfsDirectory::GetParentDirectory() const { + return parent; +} + +template <typename T> +static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) { + const auto iter = + std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); + if (iter == vec.end()) + return false; + + vec.erase(iter); + return true; +} + +bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { + return FindAndRemoveVectorElement(dirs, subdir_name); +} + +bool VectorVfsDirectory::DeleteFile(std::string_view file_name) { + return FindAndRemoveVectorElement(files, file_name); +} + +bool VectorVfsDirectory::Rename(std::string_view name_) { + name = name_; + return true; +} + +VirtualDir VectorVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { + return nullptr; +} + +VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) { + return nullptr; +} + +void VectorVfsDirectory::AddFile(VirtualFile file) { + files.push_back(std::move(file)); +} + +void VectorVfsDirectory::AddDirectory(VirtualDir dir) { + dirs.push_back(std::move(dir)); +} +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_vector.h b/src/core/file_sys/vfs/vfs_vector.h new file mode 100644 index 000000000..587187dd2 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_vector.h @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstring> +#include <memory> +#include <string> +#include <vector> +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// An implementation of VfsFile that is backed by a statically-sized array +template <std::size_t size> +class ArrayVfsFile : public VfsFile { +public: + explicit ArrayVfsFile(const std::array<u8, size>& data_, std::string name_ = "", + VirtualDir parent_ = nullptr) + : data(data_), name(std::move(name_)), parent(std::move(parent_)) {} + + std::string GetName() const override { + return name; + } + + std::size_t GetSize() const override { + return size; + } + + bool Resize(std::size_t new_size) override { + return false; + } + + VirtualDir GetContainingDirectory() const override { + return parent; + } + + bool IsWritable() const override { + return false; + } + + bool IsReadable() const override { + return true; + } + + std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + std::memcpy(data_, data.data() + offset, read); + return read; + } + + std::size_t Write(const u8* data_, std::size_t length, std::size_t offset) override { + return 0; + } + + bool Rename(std::string_view new_name) override { + name = new_name; + return true; + } + +private: + std::array<u8, size> data; + std::string name; + VirtualDir parent; +}; + +template <std::size_t Size, typename... Args> +std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data, + Args&&... args) { + return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...); +} + +// An implementation of VfsFile that is backed by a vector optionally supplied upon construction +class VectorVfsFile : public VfsFile { +public: + explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name_ = "", + VirtualDir parent_ = nullptr); + ~VectorVfsFile() override; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view name) override; + + virtual void Assign(std::vector<u8> new_data); + +private: + std::vector<u8> data; + VirtualDir parent; + std::string name; +}; + +// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. +// Vector data is supplied upon construction. +class VectorVfsDirectory : public VfsDirectory { +public: + explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, + std::vector<VirtualDir> dirs = {}, std::string name = "", + VirtualDir parent = nullptr); + ~VectorVfsDirectory() override; + + std::vector<VirtualFile> GetFiles() const override; + std::vector<VirtualDir> GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + bool DeleteSubdirectory(std::string_view subdir_name) override; + bool DeleteFile(std::string_view file_name) override; + bool Rename(std::string_view name) override; + VirtualDir CreateSubdirectory(std::string_view subdir_name) override; + VirtualFile CreateFile(std::string_view file_name) override; + + virtual void AddFile(VirtualFile file); + virtual void AddDirectory(VirtualDir dir); + +private: + std::vector<VirtualFile> files; + std::vector<VirtualDir> dirs; + + VirtualDir parent; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs_cached.cpp deleted file mode 100644 index 7ee5300e5..000000000 --- a/src/core/file_sys/vfs_cached.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_types.h" - -namespace FileSys { - -CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir) - : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { - for (auto& dir : source_dir->GetSubdirectories()) { - dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir))); - } - for (auto& file : source_dir->GetFiles()) { - files.emplace(file->GetName(), std::move(file)); - } -} - -CachedVfsDirectory::~CachedVfsDirectory() = default; - -VirtualFile CachedVfsDirectory::GetFile(std::string_view file_name) const { - auto it = files.find(file_name); - if (it != files.end()) { - return it->second; - } - - return nullptr; -} - -VirtualDir CachedVfsDirectory::GetSubdirectory(std::string_view dir_name) const { - auto it = dirs.find(dir_name); - if (it != dirs.end()) { - return it->second; - } - - return nullptr; -} - -std::vector<VirtualFile> CachedVfsDirectory::GetFiles() const { - std::vector<VirtualFile> out; - for (auto& [file_name, file] : files) { - out.push_back(file); - } - return out; -} - -std::vector<VirtualDir> CachedVfsDirectory::GetSubdirectories() const { - std::vector<VirtualDir> out; - for (auto& [dir_name, dir] : dirs) { - out.push_back(dir); - } - return out; -} - -std::string CachedVfsDirectory::GetName() const { - return name; -} - -VirtualDir CachedVfsDirectory::GetParentDirectory() const { - return parent; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs_cached.h deleted file mode 100644 index 1e5300784..000000000 --- a/src/core/file_sys/vfs_cached.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string_view> -#include <vector> -#include "core/file_sys/vfs.h" - -namespace FileSys { - -class CachedVfsDirectory : public ReadOnlyVfsDirectory { -public: - CachedVfsDirectory(VirtualDir&& source_directory); - - ~CachedVfsDirectory() override; - VirtualFile GetFile(std::string_view file_name) const override; - VirtualDir GetSubdirectory(std::string_view dir_name) const override; - std::vector<VirtualFile> GetFiles() const override; - std::vector<VirtualDir> GetSubdirectories() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - -private: - std::string name; - VirtualDir parent; - std::map<std::string, VirtualDir, std::less<>> dirs; - std::map<std::string, VirtualFile, std::less<>> files; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp deleted file mode 100644 index 7c7298527..000000000 --- a/src/core/file_sys/vfs_concat.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <utility> - -#include "common/assert.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_static.h" - -namespace FileSys { - -ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_) - : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { - DEBUG_ASSERT(this->VerifyContinuity()); -} - -bool ConcatenatedVfsFile::VerifyContinuity() const { - u64 last_offset = 0; - for (auto& entry : concatenation_map) { - if (entry.offset != last_offset) { - return false; - } - - last_offset = entry.offset + entry.file->GetSize(); - } - - return true; -} - -ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; - -VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name, - std::vector<VirtualFile>&& files) { - // Fold trivial cases. - if (files.empty()) { - return nullptr; - } - if (files.size() == 1) { - return files.front(); - } - - // Make the concatenation map from the input. - std::vector<ConcatenationEntry> concatenation_map; - concatenation_map.reserve(files.size()); - u64 last_offset = 0; - - for (auto& file : files) { - const auto size = file->GetSize(); - - concatenation_map.emplace_back(ConcatenationEntry{ - .offset = last_offset, - .file = std::move(file), - }); - - last_offset += size; - } - - return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); -} - -VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile( - u8 filler_byte, std::string&& name, std::vector<std::pair<u64, VirtualFile>>&& files) { - // Fold trivial cases. - if (files.empty()) { - return nullptr; - } - if (files.size() == 1) { - return files.begin()->second; - } - - // Make the concatenation map from the input. - std::vector<ConcatenationEntry> concatenation_map; - - concatenation_map.reserve(files.size()); - u64 last_offset = 0; - - // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. - for (auto& [offset, file] : files) { - const auto size = file->GetSize(); - - if (offset > last_offset) { - concatenation_map.emplace_back(ConcatenationEntry{ - .offset = last_offset, - .file = std::make_shared<StaticVfsFile>(filler_byte, offset - last_offset), - }); - } - - concatenation_map.emplace_back(ConcatenationEntry{ - .offset = offset, - .file = std::move(file), - }); - - last_offset = offset + size; - } - - return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); -} - -std::string ConcatenatedVfsFile::GetName() const { - if (concatenation_map.empty()) { - return ""; - } - if (!name.empty()) { - return name; - } - return concatenation_map.front().file->GetName(); -} - -std::size_t ConcatenatedVfsFile::GetSize() const { - if (concatenation_map.empty()) { - return 0; - } - return concatenation_map.back().offset + concatenation_map.back().file->GetSize(); -} - -bool ConcatenatedVfsFile::Resize(std::size_t new_size) { - return false; -} - -VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const { - if (concatenation_map.empty()) { - return nullptr; - } - return concatenation_map.front().file->GetContainingDirectory(); -} - -bool ConcatenatedVfsFile::IsWritable() const { - return false; -} - -bool ConcatenatedVfsFile::IsReadable() const { - return true; -} - -std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - const ConcatenationEntry key{ - .offset = offset, - .file = nullptr, - }; - - // Read nothing if the map is empty. - if (concatenation_map.empty()) { - return 0; - } - - // Binary search to find the iterator to the first position we can check. - // It must exist, since we are not empty and are comparing unsigned integers. - auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key)); - u64 cur_length = length; - u64 cur_offset = offset; - - while (cur_length > 0 && it != concatenation_map.end()) { - // Check if we can read the file at this position. - const auto& file = it->file; - const u64 map_offset = it->offset; - const u64 file_size = file->GetSize(); - - if (cur_offset > map_offset + file_size) { - // Entirely out of bounds read. - break; - } - - // Read the file at this position. - const u64 file_seek = cur_offset - map_offset; - const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek); - const u64 actual_read_size = - file->Read(data + (cur_offset - offset), intended_read_size, file_seek); - - // Update tracking. - cur_offset += actual_read_size; - cur_length -= actual_read_size; - it++; - - // If we encountered a short read, we're done. - if (actual_read_size < intended_read_size) { - break; - } - } - - return cur_offset - offset; -} - -std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - return 0; -} - -bool ConcatenatedVfsFile::Rename(std::string_view new_name) { - return false; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h deleted file mode 100644 index b5f3d72e3..000000000 --- a/src/core/file_sys/vfs_concat.h +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <compare> -#include <map> -#include <memory> -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently -// read-only. -class ConcatenatedVfsFile : public VfsFile { -private: - struct ConcatenationEntry { - u64 offset; - VirtualFile file; - - auto operator<=>(const ConcatenationEntry& other) const { - return this->offset <=> other.offset; - } - }; - using ConcatenationMap = std::vector<ConcatenationEntry>; - - explicit ConcatenatedVfsFile(std::string&& name, - std::vector<ConcatenationEntry>&& concatenation_map); - bool VerifyContinuity() const; - -public: - ~ConcatenatedVfsFile() override; - - /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. - static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files); - - /// Convenience function that turns a map of offsets to files into a concatenated file, filling - /// gaps with a given filler byte. - static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, - std::vector<std::pair<u64, VirtualFile>>&& files); - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - bool Rename(std::string_view new_name) override; - -private: - ConcatenationMap concatenation_map; - std::string name; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp deleted file mode 100644 index 5551743fb..000000000 --- a/src/core/file_sys/vfs_layered.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <set> -#include <unordered_set> -#include <utility> -#include "core/file_sys/vfs_layered.h" - -namespace FileSys { - -LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_) - : dirs(std::move(dirs_)), name(std::move(name_)) {} - -LayeredVfsDirectory::~LayeredVfsDirectory() = default; - -VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs, - std::string name) { - if (dirs.empty()) - return nullptr; - if (dirs.size() == 1) - return dirs[0]; - - return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); -} - -VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const { - for (const auto& layer : dirs) { - const auto file = layer->GetFileRelative(path); - if (file != nullptr) - return file; - } - - return nullptr; -} - -VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) const { - std::vector<VirtualDir> out; - for (const auto& layer : dirs) { - auto dir = layer->GetDirectoryRelative(path); - if (dir != nullptr) { - out.emplace_back(std::move(dir)); - } - } - - return MakeLayeredDirectory(std::move(out)); -} - -VirtualFile LayeredVfsDirectory::GetFile(std::string_view file_name) const { - return GetFileRelative(file_name); -} - -VirtualDir LayeredVfsDirectory::GetSubdirectory(std::string_view subdir_name) const { - return GetDirectoryRelative(subdir_name); -} - -std::string LayeredVfsDirectory::GetFullPath() const { - return dirs[0]->GetFullPath(); -} - -std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const { - std::vector<VirtualFile> out; - std::unordered_set<std::string> out_names; - - for (const auto& layer : dirs) { - for (auto& file : layer->GetFiles()) { - const auto [it, is_new] = out_names.emplace(file->GetName()); - if (is_new) { - out.emplace_back(std::move(file)); - } - } - } - - return out; -} - -std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const { - std::vector<VirtualDir> out; - std::unordered_set<std::string> out_names; - - for (const auto& layer : dirs) { - for (const auto& sd : layer->GetSubdirectories()) { - out_names.emplace(sd->GetName()); - } - } - - out.reserve(out_names.size()); - for (const auto& subdir : out_names) { - out.emplace_back(GetSubdirectory(subdir)); - } - - return out; -} - -bool LayeredVfsDirectory::IsWritable() const { - return false; -} - -bool LayeredVfsDirectory::IsReadable() const { - return true; -} - -std::string LayeredVfsDirectory::GetName() const { - return name.empty() ? dirs[0]->GetName() : name; -} - -VirtualDir LayeredVfsDirectory::GetParentDirectory() const { - return dirs[0]->GetParentDirectory(); -} - -VirtualDir LayeredVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { - return nullptr; -} - -VirtualFile LayeredVfsDirectory::CreateFile(std::string_view file_name) { - return nullptr; -} - -bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { - return false; -} - -bool LayeredVfsDirectory::DeleteFile(std::string_view file_name) { - return false; -} - -bool LayeredVfsDirectory::Rename(std::string_view new_name) { - name = new_name; - return true; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h deleted file mode 100644 index a62112e9d..000000000 --- a/src/core/file_sys/vfs_layered.h +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first -// one and falling back to the one after. The highest priority directory (overwrites all others) -// should be element 0 in the dirs vector. -class LayeredVfsDirectory : public VfsDirectory { - explicit LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_); - -public: - ~LayeredVfsDirectory() override; - - /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. - static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = ""); - - VirtualFile GetFileRelative(std::string_view path) const override; - VirtualDir GetDirectoryRelative(std::string_view path) const override; - VirtualFile GetFile(std::string_view file_name) const override; - VirtualDir GetSubdirectory(std::string_view subdir_name) const override; - std::string GetFullPath() const override; - - std::vector<VirtualFile> GetFiles() const override; - std::vector<VirtualDir> GetSubdirectories() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - VirtualDir CreateSubdirectory(std::string_view subdir_name) override; - VirtualFile CreateFile(std::string_view file_name) override; - bool DeleteSubdirectory(std::string_view subdir_name) override; - bool DeleteFile(std::string_view file_name) override; - bool Rename(std::string_view new_name) override; - -private: - std::vector<VirtualDir> dirs; - std::string name; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp deleted file mode 100644 index d950a6633..000000000 --- a/src/core/file_sys/vfs_offset.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <utility> - -#include "core/file_sys/vfs_offset.h" - -namespace FileSys { - -OffsetVfsFile::OffsetVfsFile(VirtualFile file_, std::size_t size_, std::size_t offset_, - std::string name_, VirtualDir parent_) - : file(file_), offset(offset_), size(size_), name(std::move(name_)), - parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} - -OffsetVfsFile::~OffsetVfsFile() = default; - -std::string OffsetVfsFile::GetName() const { - return name.empty() ? file->GetName() : name; -} - -std::size_t OffsetVfsFile::GetSize() const { - return size; -} - -bool OffsetVfsFile::Resize(std::size_t new_size) { - if (offset + new_size < file->GetSize()) { - size = new_size; - } else { - auto res = file->Resize(offset + new_size); - if (!res) - return false; - size = new_size; - } - - return true; -} - -VirtualDir OffsetVfsFile::GetContainingDirectory() const { - return parent; -} - -bool OffsetVfsFile::IsWritable() const { - return file->IsWritable(); -} - -bool OffsetVfsFile::IsReadable() const { - return file->IsReadable(); -} - -std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { - return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); -} - -std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { - return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); -} - -std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { - if (r_offset >= size) { - return std::nullopt; - } - - return file->ReadByte(offset + r_offset); -} - -std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { - return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); -} - -std::vector<u8> OffsetVfsFile::ReadAllBytes() const { - return file->ReadBytes(size, offset); -} - -bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { - if (r_offset < size) - return file->WriteByte(data, offset + r_offset); - - return false; -} - -std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) { - return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); -} - -bool OffsetVfsFile::Rename(std::string_view new_name) { - return file->Rename(new_name); -} - -std::size_t OffsetVfsFile::GetOffset() const { - return offset; -} - -std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { - return std::clamp(r_size, std::size_t{0}, size - r_offset); -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h deleted file mode 100644 index 6c051ca00..000000000 --- a/src/core/file_sys/vfs_offset.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 <memory> - -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// An implementation of VfsFile that wraps around another VfsFile at a certain offset. -// Similar to seeking to an offset. -// If the file is writable, operations that would write past the end of the offset file will expand -// the size of this wrapper. -class OffsetVfsFile : public VfsFile { -public: - OffsetVfsFile(VirtualFile file, std::size_t size, std::size_t offset = 0, - std::string new_name = "", VirtualDir new_parent = nullptr); - ~OffsetVfsFile() override; - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - std::optional<u8> ReadByte(std::size_t offset) const override; - std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; - std::vector<u8> ReadAllBytes() const override; - bool WriteByte(u8 data, std::size_t offset) override; - std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override; - - bool Rename(std::string_view new_name) override; - - std::size_t GetOffset() const; - -private: - std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; - - VirtualFile file; - std::size_t offset; - std::size_t size; - std::string name; - VirtualDir parent; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp deleted file mode 100644 index cd9b79786..000000000 --- a/src/core/file_sys/vfs_real.cpp +++ /dev/null @@ -1,528 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <cstddef> -#include <iterator> -#include <utility> -#include "common/assert.h" -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_real.h" - -// For FileTimeStampRaw -#include <sys/stat.h> - -#ifdef _MSC_VER -#define stat _stat64 -#endif - -namespace FileSys { - -namespace FS = Common::FS; - -namespace { - -constexpr size_t MaxOpenFiles = 512; - -constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { - switch (mode) { - case Mode::Read: - return FS::FileAccessMode::Read; - case Mode::Write: - case Mode::ReadWrite: - case Mode::Append: - case Mode::ReadAppend: - case Mode::WriteAppend: - case Mode::All: - return FS::FileAccessMode::ReadWrite; - default: - return {}; - } -} - -} // Anonymous namespace - -RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} -RealVfsFilesystem::~RealVfsFilesystem() = default; - -std::string RealVfsFilesystem::GetName() const { - return "Real"; -} - -bool RealVfsFilesystem::IsReadable() const { - return true; -} - -bool RealVfsFilesystem::IsWritable() const { - return true; -} - -VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (!FS::Exists(path)) { - return VfsEntryType::None; - } - if (FS::IsDir(path)) { - return VfsEntryType::Directory; - } - - return VfsEntryType::File; -} - -VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, - Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - std::scoped_lock lk{list_lock}; - - if (auto it = cache.find(path); it != cache.end()) { - if (auto file = it->second.lock(); file) { - return file; - } - } - - if (!size && !FS::IsFile(path)) { - return nullptr; - } - - auto reference = std::make_unique<FileReference>(); - this->InsertReferenceIntoListLocked(*reference); - - auto file = std::shared_ptr<RealVfsFile>( - new RealVfsFile(*this, std::move(reference), path, perms, size)); - cache[path] = file; - - return file; -} - -VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { - return OpenFileFromEntry(path_, {}, perms); -} - -VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - { - std::scoped_lock lk{list_lock}; - cache.erase(path); - } - - // Current usages of CreateFile expect to delete the contents of an existing file. - if (FS::IsFile(path)) { - FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; - - if (!temp.IsOpen()) { - return nullptr; - } - - temp.Close(); - - return OpenFile(path, perms); - } - - if (!FS::NewFile(path)) { - return nullptr; - } - - return OpenFile(path, perms); -} - -VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { - // Unused - return nullptr; -} - -VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { - const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); - const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); - { - std::scoped_lock lk{list_lock}; - cache.erase(old_path); - cache.erase(new_path); - } - if (!FS::RenameFile(old_path, new_path)) { - return nullptr; - } - return OpenFile(new_path, Mode::ReadWrite); -} - -bool RealVfsFilesystem::DeleteFile(std::string_view path_) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - { - std::scoped_lock lk{list_lock}; - cache.erase(path); - } - return FS::RemoveFile(path); -} - -VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); -} - -VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (!FS::CreateDirs(path)) { - return nullptr; - } - return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); -} - -VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, - std::string_view new_path_) { - // Unused - return nullptr; -} - -VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, - std::string_view new_path_) { - const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); - const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); - - if (!FS::RenameDir(old_path, new_path)) { - return nullptr; - } - return OpenDirectory(new_path, Mode::ReadWrite); -} - -bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - return FS::RemoveDirRecursively(path); -} - -std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, - Mode perms, - FileReference& reference) { - std::unique_lock lk{list_lock}; - - // Temporarily remove from list. - this->RemoveReferenceFromListLocked(reference); - - // Restore file if needed. - if (!reference.file) { - this->EvictSingleReferenceLocked(); - - reference.file = - FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); - if (reference.file) { - num_open_files++; - } - } - - // Reinsert into list. - this->InsertReferenceIntoListLocked(reference); - - return lk; -} - -void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) { - std::scoped_lock lk{list_lock}; - - // Remove from list. - this->RemoveReferenceFromListLocked(*reference); - - // Close the file. - if (reference->file) { - reference->file.reset(); - num_open_files--; - } -} - -void RealVfsFilesystem::EvictSingleReferenceLocked() { - if (num_open_files < MaxOpenFiles || open_references.empty()) { - return; - } - - // Get and remove from list. - auto& reference = open_references.back(); - this->RemoveReferenceFromListLocked(reference); - - // Close the file. - if (reference.file) { - reference.file.reset(); - num_open_files--; - } - - // Reinsert into closed list. - this->InsertReferenceIntoListLocked(reference); -} - -void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) { - if (reference.file) { - open_references.push_front(reference); - } else { - closed_references.push_front(reference); - } -} - -void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) { - if (reference.file) { - open_references.erase(open_references.iterator_to(reference)); - } else { - closed_references.erase(closed_references.iterator_to(reference)); - } -} - -RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, - const std::string& path_, Mode perms_, std::optional<u64> size_) - : base(base_), reference(std::move(reference_)), path(path_), - parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), - size(size_), perms(perms_) {} - -RealVfsFile::~RealVfsFile() { - base.DropReference(std::move(reference)); -} - -std::string RealVfsFile::GetName() const { - return path_components.empty() ? "" : std::string(path_components.back()); -} - -std::size_t RealVfsFile::GetSize() const { - if (size) { - return *size; - } - auto lk = base.RefreshReference(path, perms, *reference); - return reference->file ? reference->file->GetSize() : 0; -} - -bool RealVfsFile::Resize(std::size_t new_size) { - size.reset(); - auto lk = base.RefreshReference(path, perms, *reference); - return reference->file ? reference->file->SetSize(new_size) : false; -} - -VirtualDir RealVfsFile::GetContainingDirectory() const { - return base.OpenDirectory(parent_path, perms); -} - -bool RealVfsFile::IsWritable() const { - return True(perms & Mode::Write); -} - -bool RealVfsFile::IsReadable() const { - return True(perms & Mode::Read); -} - -std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - auto lk = base.RefreshReference(path, perms, *reference); - if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { - return 0; - } - return reference->file->ReadSpan(std::span{data, length}); -} - -std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - size.reset(); - auto lk = base.RefreshReference(path, perms, *reference); - if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { - return 0; - } - return reference->file->WriteSpan(std::span{data, length}); -} - -bool RealVfsFile::Rename(std::string_view name) { - return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; -} - -// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if -// constexpr' because there is a compile error in the branch not used. - -template <> -std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { - if (perms == Mode::Append) { - return {}; - } - - std::vector<VirtualFile> out; - - const FS::DirEntryCallable callback = [this, - &out](const std::filesystem::directory_entry& entry) { - const auto full_path_string = FS::PathToUTF8String(entry.path()); - - out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms)); - - return true; - }; - - FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File); - - return out; -} - -template <> -std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { - if (perms == Mode::Append) { - return {}; - } - - std::vector<VirtualDir> out; - - const FS::DirEntryCallable callback = [this, - &out](const std::filesystem::directory_entry& entry) { - const auto full_path_string = FS::PathToUTF8String(entry.path()); - - out.emplace_back(base.OpenDirectory(full_path_string, perms)); - - return true; - }; - - FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory); - - return out; -} - -RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) - : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), - path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { - if (!FS::Exists(path) && True(perms & Mode::Write)) { - void(FS::CreateDirs(path)); - } -} - -RealVfsDirectory::~RealVfsDirectory() = default; - -VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - if (!FS::Exists(full_path) || FS::IsDir(full_path)) { - return nullptr; - } - return base.OpenFile(full_path, perms); -} - -VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - if (!FS::Exists(full_path) || !FS::IsDir(full_path)) { - return nullptr; - } - return base.OpenDirectory(full_path, perms); -} - -VirtualFile RealVfsDirectory::GetFile(std::string_view name) const { - return GetFileRelative(name); -} - -VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const { - return GetDirectoryRelative(name); -} - -VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - if (!FS::CreateParentDirs(full_path)) { - return nullptr; - } - return base.CreateFile(full_path, perms); -} - -VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - return base.CreateDirectory(full_path, perms); -} - -bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { - const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name)); - return base.DeleteDirectory(full_path); -} - -std::vector<VirtualFile> RealVfsDirectory::GetFiles() const { - return IterateEntries<RealVfsFile, VfsFile>(); -} - -FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const { - const auto full_path = FS::SanitizePath(path + '/' + std::string(path_)); - const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)}; - struct stat file_status; - -#ifdef _WIN32 - const auto stat_result = _wstat64(fs_path.c_str(), &file_status); -#else - const auto stat_result = stat(fs_path.c_str(), &file_status); -#endif - - if (stat_result != 0) { - return {}; - } - - return { - .created{static_cast<u64>(file_status.st_ctime)}, - .accessed{static_cast<u64>(file_status.st_atime)}, - .modified{static_cast<u64>(file_status.st_mtime)}, - }; -} - -std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { - return IterateEntries<RealVfsDirectory, VfsDirectory>(); -} - -bool RealVfsDirectory::IsWritable() const { - return True(perms & Mode::Write); -} - -bool RealVfsDirectory::IsReadable() const { - return True(perms & Mode::Read); -} - -std::string RealVfsDirectory::GetName() const { - return path_components.empty() ? "" : std::string(path_components.back()); -} - -VirtualDir RealVfsDirectory::GetParentDirectory() const { - if (path_components.size() <= 1) { - return nullptr; - } - - return base.OpenDirectory(parent_path, perms); -} - -VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { - const std::string subdir_path = (path + '/').append(name); - return base.CreateDirectory(subdir_path, perms); -} - -VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { - const std::string file_path = (path + '/').append(name); - return base.CreateFile(file_path, perms); -} - -bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { - const std::string subdir_path = (path + '/').append(name); - return base.DeleteDirectory(subdir_path); -} - -bool RealVfsDirectory::DeleteFile(std::string_view name) { - const std::string file_path = (path + '/').append(name); - return base.DeleteFile(file_path); -} - -bool RealVfsDirectory::Rename(std::string_view name) { - const std::string new_name = (parent_path + '/').append(name); - return base.MoveFile(path, new_name) != nullptr; -} - -std::string RealVfsDirectory::GetFullPath() const { - auto out = path; - std::replace(out.begin(), out.end(), '\\', '/'); - return out; -} - -std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { - if (perms == Mode::Append) { - return {}; - } - - std::map<std::string, VfsEntryType, std::less<>> out; - - const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { - const auto filename = FS::PathToUTF8String(entry.path().filename()); - out.insert_or_assign(filename, - entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File); - return true; - }; - - FS::IterateDirEntries(path, callback); - - return out; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h deleted file mode 100644 index 26ea7df62..000000000 --- a/src/core/file_sys/vfs_real.h +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <map> -#include <mutex> -#include <optional> -#include <string_view> -#include "common/intrusive_list.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/vfs.h" - -namespace Common::FS { -class IOFile; -} - -namespace FileSys { - -struct FileReference : public Common::IntrusiveListBaseNode<FileReference> { - std::shared_ptr<Common::FS::IOFile> file{}; -}; - -class RealVfsFile; -class RealVfsDirectory; - -class RealVfsFilesystem : public VfsFilesystem { -public: - RealVfsFilesystem(); - ~RealVfsFilesystem() override; - - std::string GetName() const override; - bool IsReadable() const override; - bool IsWritable() const override; - VfsEntryType GetEntryType(std::string_view path) const override; - VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; - VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; - VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; - VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; - bool DeleteFile(std::string_view path) override; - VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; - VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; - VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; - VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; - bool DeleteDirectory(std::string_view path) override; - -private: - using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType; - std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache; - ReferenceListType open_references; - ReferenceListType closed_references; - std::mutex list_lock; - size_t num_open_files{}; - -private: - friend class RealVfsFile; - std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms, - FileReference& reference); - void DropReference(std::unique_ptr<FileReference>&& reference); - -private: - friend class RealVfsDirectory; - VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, - Mode perms = Mode::Read); - -private: - void EvictSingleReferenceLocked(); - void InsertReferenceIntoListLocked(FileReference& reference); - void RemoveReferenceFromListLocked(FileReference& reference); -}; - -// An implementation of VfsFile that represents a file on the user's computer. -class RealVfsFile : public VfsFile { - friend class RealVfsDirectory; - friend class RealVfsFilesystem; - -public: - ~RealVfsFile() override; - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - bool Rename(std::string_view name) override; - -private: - RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, - const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {}); - - RealVfsFilesystem& base; - std::unique_ptr<FileReference> reference; - std::string path; - std::string parent_path; - std::vector<std::string> path_components; - std::optional<u64> size; - Mode perms; -}; - -// An implementation of VfsDirectory that represents a directory on the user's computer. -class RealVfsDirectory : public VfsDirectory { - friend class RealVfsFilesystem; - -public: - ~RealVfsDirectory() override; - - VirtualFile GetFileRelative(std::string_view relative_path) const override; - VirtualDir GetDirectoryRelative(std::string_view relative_path) const override; - VirtualFile GetFile(std::string_view name) const override; - VirtualDir GetSubdirectory(std::string_view name) const override; - VirtualFile CreateFileRelative(std::string_view relative_path) override; - VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; - bool DeleteSubdirectoryRecursive(std::string_view name) override; - std::vector<VirtualFile> GetFiles() const override; - FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override; - std::vector<VirtualDir> GetSubdirectories() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - VirtualDir CreateSubdirectory(std::string_view name) override; - VirtualFile CreateFile(std::string_view name) override; - bool DeleteSubdirectory(std::string_view name) override; - bool DeleteFile(std::string_view name) override; - bool Rename(std::string_view name) override; - std::string GetFullPath() const override; - std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; - -private: - RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); - - template <typename T, typename R> - std::vector<std::shared_ptr<R>> IterateEntries() const; - - RealVfsFilesystem& base; - std::string path; - std::string parent_path; - std::vector<std::string> path_components; - Mode perms; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h deleted file mode 100644 index ca3f989ef..000000000 --- a/src/core/file_sys/vfs_static.h +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <algorithm> -#include <memory> -#include <string_view> - -#include "core/file_sys/vfs.h" - -namespace FileSys { - -class StaticVfsFile : public VfsFile { -public: - explicit StaticVfsFile(u8 value_, std::size_t size_ = 0, std::string name_ = "", - VirtualDir parent_ = nullptr) - : value{value_}, size{size_}, name{std::move(name_)}, parent{std::move(parent_)} {} - - std::string GetName() const override { - return name; - } - - std::size_t GetSize() const override { - return size; - } - - bool Resize(std::size_t new_size) override { - size = new_size; - return true; - } - - VirtualDir GetContainingDirectory() const override { - return parent; - } - - bool IsWritable() const override { - return false; - } - - bool IsReadable() const override { - return true; - } - - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override { - const auto read = std::min(length, size - offset); - std::fill(data, data + read, value); - return read; - } - - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { - return 0; - } - - std::optional<u8> ReadByte(std::size_t offset) const override { - if (offset >= size) { - return std::nullopt; - } - - return value; - } - - std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { - const auto read = std::min(length, size - offset); - return std::vector<u8>(read, value); - } - - bool Rename(std::string_view new_name) override { - name = new_name; - return true; - } - -private: - u8 value; - std::size_t size; - std::string name; - VirtualDir parent; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp deleted file mode 100644 index 251d9d7c9..000000000 --- a/src/core/file_sys/vfs_vector.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <utility> -#include "core/file_sys/vfs_vector.h" - -namespace FileSys { -VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name_, VirtualDir parent_) - : data(std::move(initial_data)), parent(std::move(parent_)), name(std::move(name_)) {} - -VectorVfsFile::~VectorVfsFile() = default; - -std::string VectorVfsFile::GetName() const { - return name; -} - -size_t VectorVfsFile::GetSize() const { - return data.size(); -} - -bool VectorVfsFile::Resize(size_t new_size) { - data.resize(new_size); - return true; -} - -VirtualDir VectorVfsFile::GetContainingDirectory() const { - return parent; -} - -bool VectorVfsFile::IsWritable() const { - return true; -} - -bool VectorVfsFile::IsReadable() const { - return true; -} - -std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const { - const auto read = std::min(length, data.size() - offset); - std::memcpy(data_, data.data() + offset, read); - return read; -} - -std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) { - if (offset + length > data.size()) - data.resize(offset + length); - const auto write = std::min(length, data.size() - offset); - std::memcpy(data.data() + offset, data_, write); - return write; -} - -bool VectorVfsFile::Rename(std::string_view name_) { - name = name_; - return true; -} - -void VectorVfsFile::Assign(std::vector<u8> new_data) { - data = std::move(new_data); -} - -VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, - std::vector<VirtualDir> dirs_, std::string name_, - VirtualDir parent_) - : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), - name(std::move(name_)) {} - -VectorVfsDirectory::~VectorVfsDirectory() = default; - -std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const { - return files; -} - -std::vector<VirtualDir> VectorVfsDirectory::GetSubdirectories() const { - return dirs; -} - -bool VectorVfsDirectory::IsWritable() const { - return false; -} - -bool VectorVfsDirectory::IsReadable() const { - return true; -} - -std::string VectorVfsDirectory::GetName() const { - return name; -} - -VirtualDir VectorVfsDirectory::GetParentDirectory() const { - return parent; -} - -template <typename T> -static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) { - const auto iter = - std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); - if (iter == vec.end()) - return false; - - vec.erase(iter); - return true; -} - -bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { - return FindAndRemoveVectorElement(dirs, subdir_name); -} - -bool VectorVfsDirectory::DeleteFile(std::string_view file_name) { - return FindAndRemoveVectorElement(files, file_name); -} - -bool VectorVfsDirectory::Rename(std::string_view name_) { - name = name_; - return true; -} - -VirtualDir VectorVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { - return nullptr; -} - -VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) { - return nullptr; -} - -void VectorVfsDirectory::AddFile(VirtualFile file) { - files.push_back(std::move(file)); -} - -void VectorVfsDirectory::AddDirectory(VirtualDir dir) { - dirs.push_back(std::move(dir)); -} -} // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h deleted file mode 100644 index bfedb6e42..000000000 --- a/src/core/file_sys/vfs_vector.h +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <cstring> -#include <memory> -#include <string> -#include <vector> -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// An implementation of VfsFile that is backed by a statically-sized array -template <std::size_t size> -class ArrayVfsFile : public VfsFile { -public: - explicit ArrayVfsFile(const std::array<u8, size>& data_, std::string name_ = "", - VirtualDir parent_ = nullptr) - : data(data_), name(std::move(name_)), parent(std::move(parent_)) {} - - std::string GetName() const override { - return name; - } - - std::size_t GetSize() const override { - return size; - } - - bool Resize(std::size_t new_size) override { - return false; - } - - VirtualDir GetContainingDirectory() const override { - return parent; - } - - bool IsWritable() const override { - return false; - } - - bool IsReadable() const override { - return true; - } - - std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { - const auto read = std::min(length, size - offset); - std::memcpy(data_, data.data() + offset, read); - return read; - } - - std::size_t Write(const u8* data_, std::size_t length, std::size_t offset) override { - return 0; - } - - bool Rename(std::string_view new_name) override { - name = new_name; - return true; - } - -private: - std::array<u8, size> data; - std::string name; - VirtualDir parent; -}; - -template <std::size_t Size, typename... Args> -std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data, - Args&&... args) { - return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...); -} - -// An implementation of VfsFile that is backed by a vector optionally supplied upon construction -class VectorVfsFile : public VfsFile { -public: - explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name_ = "", - VirtualDir parent_ = nullptr); - ~VectorVfsFile() override; - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - bool Rename(std::string_view name) override; - - virtual void Assign(std::vector<u8> new_data); - -private: - std::vector<u8> data; - VirtualDir parent; - std::string name; -}; - -// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. -// Vector data is supplied upon construction. -class VectorVfsDirectory : public VfsDirectory { -public: - explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, - std::vector<VirtualDir> dirs = {}, std::string name = "", - VirtualDir parent = nullptr); - ~VectorVfsDirectory() override; - - std::vector<VirtualFile> GetFiles() const override; - std::vector<VirtualDir> GetSubdirectories() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - bool DeleteSubdirectory(std::string_view subdir_name) override; - bool DeleteFile(std::string_view file_name) override; - bool Rename(std::string_view name) override; - VirtualDir CreateSubdirectory(std::string_view subdir_name) override; - VirtualFile CreateFile(std::string_view file_name) override; - - virtual void AddFile(VirtualFile file); - virtual void AddDirectory(VirtualDir dir); - -private: - std::vector<VirtualFile> files; - std::vector<VirtualDir> dirs; - - VirtualDir parent; - std::string name; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index ede0aa11a..6692211e1 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -17,7 +17,7 @@ #include "core/crypto/key_manager.h" #include "core/crypto/xts_encryption_layer.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index abbe5f716..7589b7c38 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 27755cb58..e04d884ba 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -6,9 +6,9 @@ #include "common/settings.h" #include "common/settings_enums.h" #include "core/frontend/applets/controller.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hid_types.h" namespace Core::Frontend { @@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac // Connect controllers based on the following priority list from highest to lowest priority: // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld if (parameters.allow_pro_controller) { - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); controller->Connect(true); } else if (parameters.allow_dual_joycons) { controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp index 2e6f7a3d9..53d4be2e5 100644 --- a/src/core/frontend/applets/error.cpp +++ b/src/core/frontend/applets/error.cpp @@ -12,7 +12,7 @@ void DefaultErrorApplet::Close() const {} void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", - error.module.Value(), error.description.Value(), error.raw); + error.GetModule(), error.GetDescription(), error.raw); } void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, @@ -20,7 +20,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon LOG_CRITICAL( Service_Fatal, "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", - error.module.Value(), error.description.Value(), error.raw, time.count()); + error.GetModule(), error.GetDescription(), error.raw, time.count()); } void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, @@ -28,7 +28,7 @@ void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", - error.module.Value(), error.description.Value(), error.raw); + error.GetModule(), error.GetDescription(), error.raw); LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text); LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text); } diff --git a/src/core/gpu_dirty_memory_manager.h b/src/core/gpu_dirty_memory_manager.h index 9687531e8..cc8fc176f 100644 --- a/src/core/gpu_dirty_memory_manager.h +++ b/src/core/gpu_dirty_memory_manager.h @@ -10,7 +10,7 @@ #include <utility> #include <vector> -#include "core/memory.h" +#include "core/device_memory_manager.h" namespace Core { @@ -23,7 +23,7 @@ public: ~GPUDirtyMemoryManager() = default; - void Collect(VAddr address, size_t size) { + void Collect(PAddr address, size_t size) { TransformAddress t = BuildTransform(address, size); TransformAddress tmp, original; do { @@ -47,7 +47,7 @@ public: std::memory_order_relaxed)); } - void Gather(std::function<void(VAddr, size_t)>& callback) { + void Gather(std::function<void(PAddr, size_t)>& callback) { { std::scoped_lock lk(guard); TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed); @@ -65,7 +65,7 @@ public: mask = mask >> empty_bits; const size_t continuous_bits = std::countr_one(mask); - callback((static_cast<VAddr>(transform.address) << page_bits) + offset, + callback((static_cast<PAddr>(transform.address) << page_bits) + offset, continuous_bits << align_bits); mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0; offset += continuous_bits << align_bits; @@ -80,7 +80,7 @@ private: u32 mask; }; - constexpr static size_t page_bits = Memory::YUZU_PAGEBITS - 1; + constexpr static size_t page_bits = DEVICE_PAGEBITS - 1; constexpr static size_t page_size = 1ULL << page_bits; constexpr static size_t page_mask = page_size - 1; @@ -89,7 +89,7 @@ private: constexpr static size_t align_mask = align_size - 1; constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U}; - bool IsValid(VAddr address) { + bool IsValid(PAddr address) { return address < (1ULL << 39); } @@ -103,7 +103,7 @@ private: return mask; } - TransformAddress BuildTransform(VAddr address, size_t size) { + TransformAddress BuildTransform(PAddr address, size_t size) { const size_t minor_address = address & page_mask; const size_t minor_bit = minor_address >> align_bits; const size_t top_bit = (minor_address + size + align_mask) >> align_bits; diff --git a/src/core/guest_memory.h b/src/core/guest_memory.h new file mode 100644 index 000000000..7ee18c126 --- /dev/null +++ b/src/core/guest_memory.h @@ -0,0 +1,214 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <iterator> +#include <memory> +#include <optional> +#include <span> +#include <vector> + +#include "common/assert.h" +#include "common/scratch_buffer.h" + +namespace Core::Memory { + +enum GuestMemoryFlags : u32 { + Read = 1 << 0, + Write = 1 << 1, + Safe = 1 << 2, + Cached = 1 << 3, + + SafeRead = Read | Safe, + SafeWrite = Write | Safe, + SafeReadWrite = SafeRead | SafeWrite, + SafeReadCachedWrite = SafeReadWrite | Cached, + + UnsafeRead = Read, + UnsafeWrite = Write, + UnsafeReadWrite = UnsafeRead | UnsafeWrite, + UnsafeReadCachedWrite = UnsafeReadWrite | Cached, +}; + +namespace { +template <typename M, typename T, GuestMemoryFlags FLAGS> +class GuestMemory { + using iterator = T*; + using const_iterator = const T*; + using value_type = T; + using element_type = T; + using iterator_category = std::contiguous_iterator_tag; + +public: + GuestMemory() = delete; + explicit GuestMemory(M& memory, u64 addr, std::size_t size, + Common::ScratchBuffer<T>* backup = nullptr) + : m_memory{memory}, m_addr{addr}, m_size{size} { + static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); + if constexpr (FLAGS & GuestMemoryFlags::Read) { + Read(addr, size, backup); + } + } + + ~GuestMemory() = default; + + T* data() noexcept { + return m_data_span.data(); + } + + const T* data() const noexcept { + return m_data_span.data(); + } + + size_t size() const noexcept { + return m_size; + } + + size_t size_bytes() const noexcept { + return this->size() * sizeof(T); + } + + [[nodiscard]] T* begin() noexcept { + return this->data(); + } + + [[nodiscard]] const T* begin() const noexcept { + return this->data(); + } + + [[nodiscard]] T* end() noexcept { + return this->data() + this->size(); + } + + [[nodiscard]] const T* end() const noexcept { + return this->data() + this->size(); + } + + T& operator[](size_t index) noexcept { + return m_data_span[index]; + } + + const T& operator[](size_t index) const noexcept { + return m_data_span[index]; + } + + void SetAddressAndSize(u64 addr, std::size_t size) noexcept { + m_addr = addr; + m_size = size; + m_addr_changed = true; + } + + std::span<T> Read(u64 addr, std::size_t size, + Common::ScratchBuffer<T>* backup = nullptr) noexcept { + m_addr = addr; + m_size = size; + if (m_size == 0) { + m_is_data_copy = true; + return {}; + } + + if (this->TrySetSpan()) { + if constexpr (FLAGS & GuestMemoryFlags::Safe) { + m_memory.FlushRegion(m_addr, this->size_bytes()); + } + } else { + if (backup) { + backup->resize_destructive(this->size()); + m_data_span = *backup; + } else { + m_data_copy.resize(this->size()); + m_data_span = std::span(m_data_copy); + } + m_is_data_copy = true; + m_span_valid = true; + if constexpr (FLAGS & GuestMemoryFlags::Safe) { + m_memory.ReadBlock(m_addr, this->data(), this->size_bytes()); + } else { + m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes()); + } + } + return m_data_span; + } + + void Write(std::span<T> write_data) noexcept { + if constexpr (FLAGS & GuestMemoryFlags::Cached) { + m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes()); + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes()); + } else { + m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes()); + } + } + + bool TrySetSpan() noexcept { + if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) { + m_data_span = {reinterpret_cast<T*>(ptr), this->size()}; + m_span_valid = true; + return true; + } + return false; + } + +protected: + bool IsDataCopy() const noexcept { + return m_is_data_copy; + } + + bool AddressChanged() const noexcept { + return m_addr_changed; + } + + M& m_memory; + u64 m_addr{}; + size_t m_size{}; + std::span<T> m_data_span{}; + std::vector<T> m_data_copy{}; + bool m_span_valid{false}; + bool m_is_data_copy{false}; + bool m_addr_changed{false}; +}; + +template <typename M, typename T, GuestMemoryFlags FLAGS> +class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { +public: + GuestMemoryScoped() = delete; + explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size, + Common::ScratchBuffer<T>* backup = nullptr) + : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) { + if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { + if (!this->TrySetSpan()) { + if (backup) { + this->m_data_span = *backup; + this->m_span_valid = true; + this->m_is_data_copy = true; + } + } + } + } + + ~GuestMemoryScoped() { + if constexpr (FLAGS & GuestMemoryFlags::Write) { + if (this->size() == 0) [[unlikely]] { + return; + } + + if (this->AddressChanged() || this->IsDataCopy()) { + ASSERT(this->m_span_valid); + if constexpr (FLAGS & GuestMemoryFlags::Cached) { + this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes()); + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes()); + } else { + this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); + } + } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) || + (FLAGS & GuestMemoryFlags::Cached)) { + this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); + } + } + } +}; +} // namespace + +} // namespace Core::Memory diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp deleted file mode 100644 index b4afd930e..000000000 --- a/src/core/hid/emulated_console.cpp +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "core/hid/emulated_console.h" -#include "core/hid/input_converter.h" - -namespace Core::HID { -EmulatedConsole::EmulatedConsole() = default; - -EmulatedConsole::~EmulatedConsole() = default; - -void EmulatedConsole::ReloadFromSettings() { - // Using first motion device from player 1. No need to assign any unique config at the moment - const auto& player = Settings::values.players.GetValue()[0]; - motion_params[0] = Common::ParamPackage(player.motions[0]); - - ReloadInput(); -} - -void EmulatedConsole::SetTouchParams() { - std::size_t index = 0; - - // We can't use mouse as touch if native mouse is enabled - if (!Settings::values.mouse_enabled) { - touch_params[index++] = - Common::ParamPackage{"engine:mouse,axis_x:0,axis_y:1,button:0,port:2"}; - } - - touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; - touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; - - for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) { - Common::ParamPackage touchscreen_param{}; - touchscreen_param.Set("engine", "touch"); - touchscreen_param.Set("axis_x", i * 2); - touchscreen_param.Set("axis_y", (i * 2) + 1); - touchscreen_param.Set("button", i); - touch_params[index++] = std::move(touchscreen_param); - } - - if (Settings::values.touch_from_button_maps.empty()) { - LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config"); - return; - } - - const auto button_index = - static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); - const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons; - - // Map the rest of the fingers from touch from button configuration - for (const auto& config_entry : touch_buttons) { - if (index >= MaxTouchDevices) { - continue; - } - Common::ParamPackage params{config_entry}; - Common::ParamPackage touch_button_params; - const int x = params.Get("x", 0); - const int y = params.Get("y", 0); - params.Erase("x"); - params.Erase("y"); - touch_button_params.Set("engine", "touch_from_button"); - touch_button_params.Set("button", params.Serialize()); - touch_button_params.Set("x", x); - touch_button_params.Set("y", y); - touch_params[index] = std::move(touch_button_params); - index++; - } -} - -void EmulatedConsole::ReloadInput() { - // If you load any device here add the equivalent to the UnloadInput() function - SetTouchParams(); - - motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"}; - - for (std::size_t index = 0; index < motion_devices.size(); ++index) { - motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]); - if (!motion_devices[index]) { - continue; - } - motion_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); }, - }); - } - - // Restore motion state - auto& emulated_motion = console.motion_values.emulated; - auto& motion = console.motion_state; - emulated_motion.ResetRotations(); - emulated_motion.ResetQuaternion(); - motion.accel = emulated_motion.GetAcceleration(); - motion.gyro = emulated_motion.GetGyroscope(); - motion.rotation = emulated_motion.GetRotations(); - motion.orientation = emulated_motion.GetOrientation(); - motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); - - // Unique index for identifying touch device source - std::size_t index = 0; - for (auto& touch_device : touch_devices) { - touch_device = Common::Input::CreateInputDevice(touch_params[index]); - if (!touch_device) { - continue; - } - touch_device->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetTouch(callback, index); - }, - }); - index++; - } -} - -void EmulatedConsole::UnloadInput() { - for (auto& motion : motion_devices) { - motion.reset(); - } - for (auto& touch : touch_devices) { - touch.reset(); - } -} - -void EmulatedConsole::EnableConfiguration() { - is_configuring = true; - SaveCurrentConfig(); -} - -void EmulatedConsole::DisableConfiguration() { - is_configuring = false; -} - -bool EmulatedConsole::IsConfiguring() const { - return is_configuring; -} - -void EmulatedConsole::SaveCurrentConfig() { - if (!is_configuring) { - return; - } -} - -void EmulatedConsole::RestoreConfig() { - if (!is_configuring) { - return; - } - ReloadFromSettings(); -} - -Common::ParamPackage EmulatedConsole::GetMotionParam() const { - return motion_params[0]; -} - -void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { - motion_params[0] = std::move(param); - ReloadInput(); -} - -void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { - std::unique_lock lock{mutex}; - auto& raw_status = console.motion_values.raw_status; - auto& emulated = console.motion_values.emulated; - - raw_status = TransformToMotion(callback); - emulated.SetAcceleration(Common::Vec3f{ - raw_status.accel.x.value, - raw_status.accel.y.value, - raw_status.accel.z.value, - }); - emulated.SetGyroscope(Common::Vec3f{ - raw_status.gyro.x.value, - raw_status.gyro.y.value, - raw_status.gyro.z.value, - }); - emulated.UpdateRotation(raw_status.delta_timestamp); - emulated.UpdateOrientation(raw_status.delta_timestamp); - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Motion); - return; - } - - auto& motion = console.motion_state; - motion.accel = emulated.GetAcceleration(); - motion.gyro = emulated.GetGyroscope(); - motion.rotation = emulated.GetRotations(); - motion.orientation = emulated.GetOrientation(); - motion.quaternion = emulated.GetQuaternion(); - motion.gyro_bias = emulated.GetGyroBias(); - motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); - // Find what is this value - motion.verticalization_error = 0.0f; - - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Motion); -} - -void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { - if (index >= MaxTouchDevices) { - return; - } - std::unique_lock lock{mutex}; - - const auto touch_input = TransformToTouch(callback); - auto touch_index = GetIndexFromFingerId(index); - bool is_new_input = false; - - if (!touch_index.has_value() && touch_input.pressed.value) { - touch_index = GetNextFreeIndex(); - is_new_input = true; - } - - // No free entries or invalid state. Ignore input - if (!touch_index.has_value()) { - return; - } - - auto& touch_value = console.touch_values[touch_index.value()]; - - if (is_new_input) { - touch_value.pressed.value = true; - touch_value.id = static_cast<int>(index); - } - - touch_value.x = touch_input.x; - touch_value.y = touch_input.y; - - if (!touch_input.pressed.value) { - touch_value.pressed.value = false; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Touch); - return; - } - - // Touch outside allowed range. Ignore input - if (touch_index.value() >= MaxActiveTouchInputs) { - return; - } - - console.touch_state[touch_index.value()] = { - .position = {touch_value.x.value, touch_value.y.value}, - .id = static_cast<u32>(touch_index.value()), - .pressed = touch_input.pressed.value, - }; - - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Touch); -} - -ConsoleMotionValues EmulatedConsole::GetMotionValues() const { - std::scoped_lock lock{mutex}; - return console.motion_values; -} - -TouchValues EmulatedConsole::GetTouchValues() const { - std::scoped_lock lock{mutex}; - return console.touch_values; -} - -ConsoleMotion EmulatedConsole::GetMotion() const { - std::scoped_lock lock{mutex}; - return console.motion_state; -} - -TouchFingerState EmulatedConsole::GetTouch() const { - std::scoped_lock lock{mutex}; - return console.touch_state; -} - -std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const { - for (std::size_t index = 0; index < MaxTouchDevices; ++index) { - const auto& finger = console.touch_values[index]; - if (!finger.pressed.value) { - continue; - } - if (finger.id == static_cast<int>(finger_id)) { - return index; - } - } - return std::nullopt; -} - -std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const { - for (std::size_t index = 0; index < MaxTouchDevices; ++index) { - if (!console.touch_values[index].pressed.value) { - return index; - } - } - return std::nullopt; -} - -void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { - std::scoped_lock lock{callback_mutex}; - for (const auto& poller_pair : callback_list) { - const ConsoleUpdateCallback& poller = poller_pair.second; - if (poller.on_change) { - poller.on_change(type); - } - } -} - -int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { - std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); - return last_callback_key++; -} - -void EmulatedConsole::DeleteCallback(int key) { - std::scoped_lock lock{callback_mutex}; - const auto& iterator = callback_list.find(key); - if (iterator == callback_list.end()) { - LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); - return; - } - callback_list.erase(iterator); -} -} // namespace Core::HID diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h deleted file mode 100644 index fae15a556..000000000 --- a/src/core/hid/emulated_console.h +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <functional> -#include <memory> -#include <mutex> -#include <optional> -#include <unordered_map> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/input.h" -#include "common/param_package.h" -#include "common/point.h" -#include "common/quaternion.h" -#include "common/vector_math.h" -#include "core/hid/hid_types.h" -#include "core/hid/motion_input.h" - -namespace Core::HID { -static constexpr std::size_t MaxTouchDevices = 32; -static constexpr std::size_t MaxActiveTouchInputs = 16; - -struct ConsoleMotionInfo { - Common::Input::MotionStatus raw_status{}; - MotionInput emulated{}; -}; - -using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>; -using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>; - -using ConsoleMotionParams = std::array<Common::ParamPackage, 2>; -using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; - -using ConsoleMotionValues = ConsoleMotionInfo; -using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; - -// Contains all motion related data that is used on the services -struct ConsoleMotion { - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - std::array<Common::Vec3f, 3> orientation{}; - Common::Quaternion<f32> quaternion{}; - Common::Vec3f gyro_bias{}; - f32 verticalization_error{}; - bool is_at_rest{}; -}; - -using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>; - -struct ConsoleStatus { - // Data from input_common - ConsoleMotionValues motion_values{}; - TouchValues touch_values{}; - - // Data for HID services - ConsoleMotion motion_state{}; - TouchFingerState touch_state{}; -}; - -enum class ConsoleTriggerType { - Motion, - Touch, - All, -}; - -struct ConsoleUpdateCallback { - std::function<void(ConsoleTriggerType)> on_change; -}; - -class EmulatedConsole { -public: - /** - * Contains all input data within the emulated switch console tablet such as touch and motion - */ - explicit EmulatedConsole(); - ~EmulatedConsole(); - - YUZU_NON_COPYABLE(EmulatedConsole); - YUZU_NON_MOVEABLE(EmulatedConsole); - - /// Removes all callbacks created from input devices - void UnloadInput(); - - /** - * Sets the emulated console into configuring mode - * This prevents the modification of the HID state of the emulated console by input commands - */ - void EnableConfiguration(); - - /// Returns the emulated console into normal mode, allowing the modification of the HID state - void DisableConfiguration(); - - /// Returns true if the emulated console is in configuring mode - bool IsConfiguring() const; - - /// Reload all input devices - void ReloadInput(); - - /// Overrides current mapped devices with the stored configuration and reloads all input devices - void ReloadFromSettings(); - - /// Saves the current mapped configuration - void SaveCurrentConfig(); - - /// Reverts any mapped changes made that weren't saved - void RestoreConfig(); - - // Returns the current mapped motion device - Common::ParamPackage GetMotionParam() const; - - /** - * Updates the current mapped motion device - * @param param ParamPackage with controller data to be mapped - */ - void SetMotionParam(Common::ParamPackage param); - - /// Returns the latest status of motion input from the console with parameters - ConsoleMotionValues GetMotionValues() const; - - /// Returns the latest status of touch input from the console with parameters - TouchValues GetTouchValues() const; - - /// Returns the latest status of motion input from the console - ConsoleMotion GetMotion() const; - - /// Returns the latest status of touch input from the console - TouchFingerState GetTouch() const; - - /** - * Adds a callback to the list of events - * @param update_callback A ConsoleUpdateCallback that will be triggered - * @return an unique key corresponding to the callback index in the list - */ - int SetCallback(ConsoleUpdateCallback update_callback); - - /** - * Removes a callback from the list stopping any future events to this object - * @param key Key corresponding to the callback index in the list - */ - void DeleteCallback(int key); - -private: - /// Creates and stores the touch params - void SetTouchParams(); - - /** - * Updates the motion status of the console - * @param callback A CallbackStatus containing gyro and accelerometer data - */ - void SetMotion(const Common::Input::CallbackStatus& callback); - - /** - * Updates the touch status of the console - * @param callback A CallbackStatus containing the touch position - * @param index Finger ID to be updated - */ - void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); - - std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const; - - std::optional<std::size_t> GetNextFreeIndex() const; - - /** - * Triggers a callback that something has changed on the console status - * @param type Input type of the event to trigger - */ - void TriggerOnChange(ConsoleTriggerType type); - - bool is_configuring{false}; - f32 motion_sensitivity{0.01f}; - - ConsoleMotionParams motion_params; - TouchParams touch_params; - - ConsoleMotionDevices motion_devices; - TouchDevices touch_devices; - - mutable std::mutex mutex; - mutable std::mutex callback_mutex; - std::unordered_map<int, ConsoleUpdateCallback> callback_list; - int last_callback_key = 0; - - // Stores the current status of all console input - ConsoleStatus console; -}; - -} // namespace Core::HID diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp deleted file mode 100644 index a6e681e15..000000000 --- a/src/core/hid/emulated_controller.cpp +++ /dev/null @@ -1,1972 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <common/scope_exit.h> - -#include "common/polyfill_ranges.h" -#include "common/thread.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/input_converter.h" -#include "core/hle/service/hid/hid_util.h" - -namespace Core::HID { -constexpr s32 HID_JOYSTICK_MAX = 0x7fff; -constexpr s32 HID_TRIGGER_MAX = 0x7fff; -constexpr u32 TURBO_BUTTON_DELAY = 4; -// Use a common UUID for TAS and Virtual Gamepad -constexpr Common::UUID TAS_UUID = - Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; -constexpr Common::UUID VIRTUAL_UUID = - Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; - -EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {} - -EmulatedController::~EmulatedController() = default; - -NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { - switch (type) { - case Settings::ControllerType::ProController: - return NpadStyleIndex::ProController; - case Settings::ControllerType::DualJoyconDetached: - return NpadStyleIndex::JoyconDual; - case Settings::ControllerType::LeftJoycon: - return NpadStyleIndex::JoyconLeft; - case Settings::ControllerType::RightJoycon: - return NpadStyleIndex::JoyconRight; - case Settings::ControllerType::Handheld: - return NpadStyleIndex::Handheld; - case Settings::ControllerType::GameCube: - return NpadStyleIndex::GameCube; - case Settings::ControllerType::Pokeball: - return NpadStyleIndex::Pokeball; - case Settings::ControllerType::NES: - return NpadStyleIndex::NES; - case Settings::ControllerType::SNES: - return NpadStyleIndex::SNES; - case Settings::ControllerType::N64: - return NpadStyleIndex::N64; - case Settings::ControllerType::SegaGenesis: - return NpadStyleIndex::SegaGenesis; - default: - return NpadStyleIndex::ProController; - } -} - -Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { - switch (type) { - case NpadStyleIndex::ProController: - return Settings::ControllerType::ProController; - case NpadStyleIndex::JoyconDual: - return Settings::ControllerType::DualJoyconDetached; - case NpadStyleIndex::JoyconLeft: - return Settings::ControllerType::LeftJoycon; - case NpadStyleIndex::JoyconRight: - return Settings::ControllerType::RightJoycon; - case NpadStyleIndex::Handheld: - return Settings::ControllerType::Handheld; - case NpadStyleIndex::GameCube: - return Settings::ControllerType::GameCube; - case NpadStyleIndex::Pokeball: - return Settings::ControllerType::Pokeball; - case NpadStyleIndex::NES: - return Settings::ControllerType::NES; - case NpadStyleIndex::SNES: - return Settings::ControllerType::SNES; - case NpadStyleIndex::N64: - return Settings::ControllerType::N64; - case NpadStyleIndex::SegaGenesis: - return Settings::ControllerType::SegaGenesis; - default: - return Settings::ControllerType::ProController; - } -} - -void EmulatedController::ReloadFromSettings() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - - for (std::size_t index = 0; index < player.buttons.size(); ++index) { - button_params[index] = Common::ParamPackage(player.buttons[index]); - } - for (std::size_t index = 0; index < player.analogs.size(); ++index) { - stick_params[index] = Common::ParamPackage(player.analogs[index]); - } - for (std::size_t index = 0; index < player.motions.size(); ++index) { - motion_params[index] = Common::ParamPackage(player.motions[index]); - } - - controller.color_values = {}; - ReloadColorsFromSettings(); - - ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); - - // Other or debug controller should always be a pro controller - if (npad_id_type != NpadIdType::Other) { - SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); - original_npad_type = npad_type; - } else { - SetNpadStyleIndex(NpadStyleIndex::ProController); - original_npad_type = npad_type; - } - - Disconnect(); - if (player.connected) { - Connect(); - } - - ReloadInput(); -} - -void EmulatedController::ReloadColorsFromSettings() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - - // Avoid updating colors if overridden by physical controller - if (controller.color_values[LeftIndex].body != 0 && - controller.color_values[RightIndex].body != 0) { - return; - } - - controller.colors_state.fullkey = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.left = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.right = { - .body = GetNpadColor(player.body_color_right), - .button = GetNpadColor(player.button_color_right), - }; -} - -void EmulatedController::LoadDevices() { - // TODO(german77): Use more buttons to detect the correct device - const auto left_joycon = button_params[Settings::NativeButton::DRight]; - const auto right_joycon = button_params[Settings::NativeButton::A]; - - // Triggers for GC controllers - trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; - trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; - - color_params[LeftIndex] = left_joycon; - color_params[RightIndex] = right_joycon; - color_params[LeftIndex].Set("color", true); - color_params[RightIndex].Set("color", true); - - battery_params[LeftIndex] = left_joycon; - battery_params[RightIndex] = right_joycon; - battery_params[LeftIndex].Set("battery", true); - battery_params[RightIndex].Set("battery", true); - - camera_params[0] = right_joycon; - camera_params[0].Set("camera", true); - nfc_params[1] = right_joycon; - nfc_params[1].Set("nfc", true); - - // 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"}; - } - - output_params[LeftIndex] = left_joycon; - output_params[RightIndex] = right_joycon; - output_params[2] = camera_params[1]; - output_params[3] = nfc_params[0]; - output_params[LeftIndex].Set("output", true); - output_params[RightIndex].Set("output", true); - output_params[2].Set("output", true); - output_params[3].Set("output", true); - - LoadTASParams(); - LoadVirtualGamepadParams(); - - std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(trigger_params, trigger_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(battery_params, battery_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(ring_params, ring_analog_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(output_params, output_devices.begin(), - Common::Input::CreateOutputDevice); - - // Initialize TAS devices - std::ranges::transform(tas_button_params, tas_button_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), - Common::Input::CreateInputDevice); - - // Initialize virtual gamepad devices - std::ranges::transform(virtual_button_params, virtual_button_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(virtual_motion_params, virtual_motion_devices.begin(), - Common::Input::CreateInputDevice); -} - -void EmulatedController::LoadTASParams() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - Common::ParamPackage common_params{}; - common_params.Set("engine", "tas"); - common_params.Set("port", static_cast<int>(player_index)); - for (auto& param : tas_button_params) { - param = common_params; - } - for (auto& param : tas_stick_params) { - param = common_params; - } - - // TODO(german77): Replace this with an input profile or something better - tas_button_params[Settings::NativeButton::A].Set("button", 0); - tas_button_params[Settings::NativeButton::B].Set("button", 1); - tas_button_params[Settings::NativeButton::X].Set("button", 2); - tas_button_params[Settings::NativeButton::Y].Set("button", 3); - tas_button_params[Settings::NativeButton::LStick].Set("button", 4); - tas_button_params[Settings::NativeButton::RStick].Set("button", 5); - tas_button_params[Settings::NativeButton::L].Set("button", 6); - tas_button_params[Settings::NativeButton::R].Set("button", 7); - tas_button_params[Settings::NativeButton::ZL].Set("button", 8); - tas_button_params[Settings::NativeButton::ZR].Set("button", 9); - tas_button_params[Settings::NativeButton::Plus].Set("button", 10); - tas_button_params[Settings::NativeButton::Minus].Set("button", 11); - tas_button_params[Settings::NativeButton::DLeft].Set("button", 12); - tas_button_params[Settings::NativeButton::DUp].Set("button", 13); - tas_button_params[Settings::NativeButton::DRight].Set("button", 14); - tas_button_params[Settings::NativeButton::DDown].Set("button", 15); - tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16); - tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17); - tas_button_params[Settings::NativeButton::Home].Set("button", 18); - tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); - tas_button_params[Settings::NativeButton::SLRight].Set("button", 20); - tas_button_params[Settings::NativeButton::SRRight].Set("button", 21); - - tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); - tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); - tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); - tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); - - // set to optimal stick to avoid sanitizing the stick and tweaking the coordinates - // making sure they play back in the game as originally written down in the script file - tas_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f); - tas_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); - tas_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); - tas_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); -} - -void EmulatedController::LoadVirtualGamepadParams() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - Common::ParamPackage common_params{}; - common_params.Set("engine", "virtual_gamepad"); - common_params.Set("port", static_cast<int>(player_index)); - for (auto& param : virtual_button_params) { - param = common_params; - } - for (auto& param : virtual_stick_params) { - param = common_params; - } - for (auto& param : virtual_stick_params) { - param = common_params; - } - for (auto& param : virtual_motion_params) { - param = common_params; - } - - // TODO(german77): Replace this with an input profile or something better - virtual_button_params[Settings::NativeButton::A].Set("button", 0); - virtual_button_params[Settings::NativeButton::B].Set("button", 1); - virtual_button_params[Settings::NativeButton::X].Set("button", 2); - virtual_button_params[Settings::NativeButton::Y].Set("button", 3); - virtual_button_params[Settings::NativeButton::LStick].Set("button", 4); - virtual_button_params[Settings::NativeButton::RStick].Set("button", 5); - virtual_button_params[Settings::NativeButton::L].Set("button", 6); - virtual_button_params[Settings::NativeButton::R].Set("button", 7); - virtual_button_params[Settings::NativeButton::ZL].Set("button", 8); - virtual_button_params[Settings::NativeButton::ZR].Set("button", 9); - virtual_button_params[Settings::NativeButton::Plus].Set("button", 10); - virtual_button_params[Settings::NativeButton::Minus].Set("button", 11); - virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12); - virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); - virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); - virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); - virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16); - virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17); - virtual_button_params[Settings::NativeButton::Home].Set("button", 18); - virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); - virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20); - virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21); - - virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); - virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); - virtual_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f); - virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); - - virtual_motion_params[Settings::NativeMotion::MotionLeft].Set("motion", 0); - virtual_motion_params[Settings::NativeMotion::MotionRight].Set("motion", 0); -} - -void EmulatedController::ReloadInput() { - // If you load any device here add the equivalent to the UnloadInput() function - LoadDevices(); - for (std::size_t index = 0; index < button_devices.size(); ++index) { - if (!button_devices[index]) { - continue; - } - const auto uuid = Common::UUID{button_params[index].Get("guid", "")}; - button_devices[index]->SetCallback({ - .on_change = - [this, index, uuid](const Common::Input::CallbackStatus& callback) { - SetButton(callback, index, uuid); - }, - }); - button_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < stick_devices.size(); ++index) { - if (!stick_devices[index]) { - continue; - } - const auto uuid = Common::UUID{stick_params[index].Get("guid", "")}; - stick_devices[index]->SetCallback({ - .on_change = - [this, index, uuid](const Common::Input::CallbackStatus& callback) { - SetStick(callback, index, uuid); - }, - }); - stick_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < trigger_devices.size(); ++index) { - if (!trigger_devices[index]) { - continue; - } - const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")}; - trigger_devices[index]->SetCallback({ - .on_change = - [this, index, uuid](const Common::Input::CallbackStatus& callback) { - SetTrigger(callback, index, uuid); - }, - }); - trigger_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < battery_devices.size(); ++index) { - if (!battery_devices[index]) { - continue; - } - battery_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetBattery(callback, index); - }, - }); - battery_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < color_devices.size(); ++index) { - if (!color_devices[index]) { - continue; - } - color_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetColors(callback, index); - }, - }); - color_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < motion_devices.size(); ++index) { - if (!motion_devices[index]) { - continue; - } - motion_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMotion(callback, index); - }, - }); - - // Restore motion state - auto& emulated_motion = controller.motion_values[index].emulated; - auto& motion = controller.motion_state[index]; - emulated_motion.ResetRotations(); - emulated_motion.ResetQuaternion(); - motion.accel = emulated_motion.GetAcceleration(); - motion.gyro = emulated_motion.GetGyroscope(); - motion.rotation = emulated_motion.GetRotations(); - motion.euler = emulated_motion.GetEulerAngles(); - motion.orientation = emulated_motion.GetOrientation(); - motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); - } - - for (std::size_t index = 0; index < camera_devices.size(); ++index) { - if (!camera_devices[index]) { - continue; - } - camera_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, - }); - camera_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) { - if (!ring_analog_devices[index]) { - continue; - } - ring_analog_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); }, - }); - ring_analog_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < nfc_devices.size(); ++index) { - if (!nfc_devices[index]) { - continue; - } - nfc_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, - }); - nfc_devices[index]->ForceUpdate(); - } - - // Register TAS devices. No need to force update - for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { - if (!tas_button_devices[index]) { - continue; - } - tas_button_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetButton(callback, index, TAS_UUID); - }, - }); - } - - for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) { - if (!tas_stick_devices[index]) { - continue; - } - tas_stick_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetStick(callback, index, TAS_UUID); - }, - }); - } - - // Register virtual devices. No need to force update - for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { - if (!virtual_button_devices[index]) { - continue; - } - virtual_button_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetButton(callback, index, VIRTUAL_UUID); - }, - }); - } - - for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) { - if (!virtual_stick_devices[index]) { - continue; - } - virtual_stick_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetStick(callback, index, VIRTUAL_UUID); - }, - }); - } - - for (std::size_t index = 0; index < virtual_motion_devices.size(); ++index) { - if (!virtual_motion_devices[index]) { - continue; - } - virtual_motion_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMotion(callback, index); - }, - }); - } - turbo_button_state = 0; - is_initalized = true; -} - -void EmulatedController::UnloadInput() { - is_initalized = false; - for (auto& button : button_devices) { - button.reset(); - } - for (auto& stick : stick_devices) { - stick.reset(); - } - for (auto& motion : motion_devices) { - motion.reset(); - } - for (auto& trigger : trigger_devices) { - trigger.reset(); - } - for (auto& battery : battery_devices) { - battery.reset(); - } - for (auto& color : color_devices) { - color.reset(); - } - for (auto& output : output_devices) { - output.reset(); - } - for (auto& button : tas_button_devices) { - button.reset(); - } - for (auto& stick : tas_stick_devices) { - stick.reset(); - } - for (auto& button : virtual_button_devices) { - button.reset(); - } - for (auto& stick : virtual_stick_devices) { - stick.reset(); - } - for (auto& motion : virtual_motion_devices) { - motion.reset(); - } - for (auto& camera : camera_devices) { - camera.reset(); - } - for (auto& ring : ring_analog_devices) { - ring.reset(); - } - for (auto& nfc : nfc_devices) { - nfc.reset(); - } -} - -void EmulatedController::EnableConfiguration() { - std::scoped_lock lock{connect_mutex, npad_mutex}; - is_configuring = true; - tmp_is_connected = is_connected; - tmp_npad_type = npad_type; -} - -void EmulatedController::DisableConfiguration() { - is_configuring = false; - - // Get Joycon colors before turning on the controller - for (const auto& color_device : color_devices) { - color_device->ForceUpdate(); - } - - // Apply temporary npad type to the real controller - if (tmp_npad_type != npad_type) { - if (is_connected) { - Disconnect(); - } - SetNpadStyleIndex(tmp_npad_type); - original_npad_type = tmp_npad_type; - } - - // Apply temporary connected status to the real controller - if (tmp_is_connected != is_connected) { - if (tmp_is_connected) { - Connect(); - return; - } - Disconnect(); - } -} - -void EmulatedController::EnableSystemButtons() { - std::scoped_lock lock{mutex}; - system_buttons_enabled = true; -} - -void EmulatedController::DisableSystemButtons() { - std::scoped_lock lock{mutex}; - system_buttons_enabled = false; - controller.home_button_state.raw = 0; - controller.capture_button_state.raw = 0; -} - -void EmulatedController::ResetSystemButtons() { - std::scoped_lock lock{mutex}; - controller.home_button_state.home.Assign(false); - controller.capture_button_state.capture.Assign(false); -} - -bool EmulatedController::IsConfiguring() const { - return is_configuring; -} - -void EmulatedController::SaveCurrentConfig() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - auto& player = Settings::values.players.GetValue()[player_index]; - player.connected = is_connected; - player.controller_type = MapNPadToSettingsType(npad_type); - for (std::size_t index = 0; index < player.buttons.size(); ++index) { - player.buttons[index] = button_params[index].Serialize(); - } - for (std::size_t index = 0; index < player.analogs.size(); ++index) { - player.analogs[index] = stick_params[index].Serialize(); - } - for (std::size_t index = 0; index < player.motions.size(); ++index) { - player.motions[index] = motion_params[index].Serialize(); - } - if (npad_id_type == NpadIdType::Player1) { - Settings::values.ringcon_analogs = ring_params[0].Serialize(); - } -} - -void EmulatedController::RestoreConfig() { - if (!is_configuring) { - return; - } - ReloadFromSettings(); -} - -std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const { - std::vector<Common::ParamPackage> devices; - for (const auto& param : button_params) { - if (!param.Has("engine")) { - continue; - } - const auto devices_it = std::find_if( - devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { - return param.Get("engine", "") == param_.Get("engine", "") && - param.Get("guid", "") == param_.Get("guid", "") && - param.Get("port", 0) == param_.Get("port", 0) && - param.Get("pad", 0) == param_.Get("pad", 0); - }); - if (devices_it != devices.end()) { - continue; - } - - auto& device = devices.emplace_back(); - device.Set("engine", param.Get("engine", "")); - device.Set("guid", param.Get("guid", "")); - device.Set("port", param.Get("port", 0)); - device.Set("pad", param.Get("pad", 0)); - } - - for (const auto& param : stick_params) { - if (!param.Has("engine")) { - continue; - } - if (param.Get("engine", "") == "analog_from_button") { - continue; - } - const auto devices_it = std::find_if( - devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { - return param.Get("engine", "") == param_.Get("engine", "") && - param.Get("guid", "") == param_.Get("guid", "") && - param.Get("port", 0) == param_.Get("port", 0) && - param.Get("pad", 0) == param_.Get("pad", 0); - }); - if (devices_it != devices.end()) { - continue; - } - - auto& device = devices.emplace_back(); - device.Set("engine", param.Get("engine", "")); - device.Set("guid", param.Get("guid", "")); - device.Set("port", param.Get("port", 0)); - device.Set("pad", param.Get("pad", 0)); - } - return devices; -} - -Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const { - if (index >= button_params.size()) { - return {}; - } - return button_params[index]; -} - -Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const { - if (index >= stick_params.size()) { - return {}; - } - return stick_params[index]; -} - -Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const { - if (index >= motion_params.size()) { - return {}; - } - return motion_params[index]; -} - -void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) { - if (index >= button_params.size()) { - return; - } - button_params[index] = std::move(param); - ReloadInput(); -} - -void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) { - if (index >= stick_params.size()) { - return; - } - stick_params[index] = std::move(param); - ReloadInput(); -} - -void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) { - if (index >= motion_params.size()) { - return; - } - motion_params[index] = std::move(param); - ReloadInput(); -} - -void EmulatedController::StartMotionCalibration() { - for (ControllerMotionInfo& motion : controller.motion_values) { - motion.emulated.Calibrate(); - } -} - -void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid) { - if (index >= controller.button_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = controller.button_values[index]; - - // Only read button values that have the same uuid or are pressed once - if (current_status.uuid != uuid) { - if (!new_status.value) { - return; - } - } - - current_status.toggle = new_status.toggle; - current_status.turbo = new_status.turbo; - current_status.uuid = uuid; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - controller.npad_button_state.raw = NpadButton::None; - controller.debug_pad_button_state.raw = 0; - controller.home_button_state.raw = 0; - controller.capture_button_state.raw = 0; - lock.unlock(); - TriggerOnChange(ControllerTriggerType::Button, false); - return; - } - - // GC controllers have triggers not buttons - if (npad_type == NpadStyleIndex::GameCube) { - if (index == Settings::NativeButton::ZR) { - return; - } - if (index == Settings::NativeButton::ZL) { - return; - } - } - - switch (index) { - case Settings::NativeButton::A: - controller.npad_button_state.a.Assign(current_status.value); - controller.debug_pad_button_state.a.Assign(current_status.value); - break; - case Settings::NativeButton::B: - controller.npad_button_state.b.Assign(current_status.value); - controller.debug_pad_button_state.b.Assign(current_status.value); - break; - case Settings::NativeButton::X: - controller.npad_button_state.x.Assign(current_status.value); - controller.debug_pad_button_state.x.Assign(current_status.value); - break; - case Settings::NativeButton::Y: - controller.npad_button_state.y.Assign(current_status.value); - controller.debug_pad_button_state.y.Assign(current_status.value); - break; - case Settings::NativeButton::LStick: - controller.npad_button_state.stick_l.Assign(current_status.value); - break; - case Settings::NativeButton::RStick: - controller.npad_button_state.stick_r.Assign(current_status.value); - break; - case Settings::NativeButton::L: - controller.npad_button_state.l.Assign(current_status.value); - controller.debug_pad_button_state.l.Assign(current_status.value); - break; - case Settings::NativeButton::R: - controller.npad_button_state.r.Assign(current_status.value); - controller.debug_pad_button_state.r.Assign(current_status.value); - break; - case Settings::NativeButton::ZL: - controller.npad_button_state.zl.Assign(current_status.value); - controller.debug_pad_button_state.zl.Assign(current_status.value); - break; - case Settings::NativeButton::ZR: - controller.npad_button_state.zr.Assign(current_status.value); - controller.debug_pad_button_state.zr.Assign(current_status.value); - break; - case Settings::NativeButton::Plus: - controller.npad_button_state.plus.Assign(current_status.value); - controller.debug_pad_button_state.plus.Assign(current_status.value); - break; - case Settings::NativeButton::Minus: - controller.npad_button_state.minus.Assign(current_status.value); - controller.debug_pad_button_state.minus.Assign(current_status.value); - break; - case Settings::NativeButton::DLeft: - controller.npad_button_state.left.Assign(current_status.value); - controller.debug_pad_button_state.d_left.Assign(current_status.value); - break; - case Settings::NativeButton::DUp: - controller.npad_button_state.up.Assign(current_status.value); - controller.debug_pad_button_state.d_up.Assign(current_status.value); - break; - case Settings::NativeButton::DRight: - controller.npad_button_state.right.Assign(current_status.value); - controller.debug_pad_button_state.d_right.Assign(current_status.value); - break; - case Settings::NativeButton::DDown: - controller.npad_button_state.down.Assign(current_status.value); - controller.debug_pad_button_state.d_down.Assign(current_status.value); - break; - case Settings::NativeButton::SLLeft: - controller.npad_button_state.left_sl.Assign(current_status.value); - break; - case Settings::NativeButton::SLRight: - controller.npad_button_state.right_sl.Assign(current_status.value); - break; - case Settings::NativeButton::SRLeft: - controller.npad_button_state.left_sr.Assign(current_status.value); - break; - case Settings::NativeButton::SRRight: - controller.npad_button_state.right_sr.Assign(current_status.value); - break; - case Settings::NativeButton::Home: - if (!system_buttons_enabled) { - break; - } - controller.home_button_state.home.Assign(current_status.value); - break; - case Settings::NativeButton::Screenshot: - if (!system_buttons_enabled) { - break; - } - controller.capture_button_state.capture.Assign(current_status.value); - break; - } - - lock.unlock(); - - if (!is_connected) { - if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { - Connect(); - } - if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) { - Connect(); - } - } - TriggerOnChange(ControllerTriggerType::Button, true); -} - -void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid) { - if (index >= controller.stick_values.size()) { - return; - } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); }); - std::scoped_lock lock{mutex}; - const auto stick_value = TransformToStick(callback); - - // Only read stick values that have the same uuid or are over the threshold to avoid flapping - if (controller.stick_values[index].uuid != uuid) { - const bool is_tas = uuid == TAS_UUID; - if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) { - trigger_guard.Cancel(); - return; - } - if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left && - !stick_value.right) { - trigger_guard.Cancel(); - return; - } - } - - controller.stick_values[index] = stick_value; - controller.stick_values[index].uuid = uuid; - - if (is_configuring) { - controller.analog_stick_state.left = {}; - controller.analog_stick_state.right = {}; - return; - } - - const AnalogStickState stick{ - .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX), - .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX), - }; - - switch (index) { - case Settings::NativeAnalog::LStick: - controller.analog_stick_state.left = stick; - controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left); - controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up); - controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right); - controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down); - break; - case Settings::NativeAnalog::RStick: - controller.analog_stick_state.right = stick; - controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left); - controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up); - controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right); - controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); - break; - } -} - -void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, - std::size_t index, Common::UUID uuid) { - if (index >= controller.trigger_values.size()) { - return; - } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); }); - std::scoped_lock lock{mutex}; - const auto trigger_value = TransformToTrigger(callback); - - // Only read trigger values that have the same uuid or are pressed once - if (controller.trigger_values[index].uuid != uuid) { - if (!trigger_value.pressed.value) { - return; - } - } - - controller.trigger_values[index] = trigger_value; - controller.trigger_values[index].uuid = uuid; - - if (is_configuring) { - controller.gc_trigger_state.left = 0; - controller.gc_trigger_state.right = 0; - return; - } - - // Only GC controllers have analog triggers - if (npad_type != NpadStyleIndex::GameCube) { - trigger_guard.Cancel(); - return; - } - - const auto& trigger = controller.trigger_values[index]; - - switch (index) { - case Settings::NativeTrigger::LTrigger: - controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); - controller.npad_button_state.zl.Assign(trigger.pressed.value); - break; - case Settings::NativeTrigger::RTrigger: - controller.gc_trigger_state.right = - static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); - controller.npad_button_state.zr.Assign(trigger.pressed.value); - break; - } -} - -void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= controller.motion_values.size()) { - return; - } - 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; - - raw_status = TransformToMotion(callback); - emulated.SetAcceleration(Common::Vec3f{ - raw_status.accel.x.value, - raw_status.accel.y.value, - raw_status.accel.z.value, - }); - emulated.SetGyroscope(Common::Vec3f{ - raw_status.gyro.x.value, - raw_status.gyro.y.value, - raw_status.gyro.z.value, - }); - emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold); - emulated.UpdateRotation(raw_status.delta_timestamp); - emulated.UpdateOrientation(raw_status.delta_timestamp); - - auto& motion = controller.motion_state[index]; - motion.accel = emulated.GetAcceleration(); - motion.gyro = emulated.GetGyroscope(); - motion.rotation = emulated.GetRotations(); - motion.euler = emulated.GetEulerAngles(); - motion.orientation = emulated.GetOrientation(); - motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); -} - -void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= controller.color_values.size()) { - return; - } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.color_values[index] = TransformToColor(callback); - - if (is_configuring) { - return; - } - - if (controller.color_values[index].body == 0) { - trigger_guard.Cancel(); - return; - } - - controller.colors_state.fullkey = { - .body = GetNpadColor(controller.color_values[index].body), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - if (npad_type == NpadStyleIndex::ProController) { - controller.colors_state.left = { - .body = GetNpadColor(controller.color_values[index].left_grip), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - controller.colors_state.right = { - .body = GetNpadColor(controller.color_values[index].right_grip), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - } else { - switch (index) { - case LeftIndex: - controller.colors_state.left = { - .body = GetNpadColor(controller.color_values[index].body), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - break; - case RightIndex: - controller.colors_state.right = { - .body = GetNpadColor(controller.color_values[index].body), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - break; - } - } -} - -void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= controller.battery_values.size()) { - return; - } - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.battery_values[index] = TransformToBattery(callback); - - if (is_configuring) { - return; - } - - bool is_charging = false; - bool is_powered = false; - NpadBatteryLevel battery_level = NpadBatteryLevel::Empty; - switch (controller.battery_values[index]) { - case Common::Input::BatteryLevel::Charging: - is_charging = true; - is_powered = true; - battery_level = NpadBatteryLevel::Full; - break; - case Common::Input::BatteryLevel::Medium: - battery_level = NpadBatteryLevel::High; - break; - case Common::Input::BatteryLevel::Low: - battery_level = NpadBatteryLevel::Low; - break; - case Common::Input::BatteryLevel::Critical: - battery_level = NpadBatteryLevel::Critical; - break; - case Common::Input::BatteryLevel::Empty: - battery_level = NpadBatteryLevel::Empty; - break; - case Common::Input::BatteryLevel::None: - case Common::Input::BatteryLevel::Full: - default: - is_powered = true; - battery_level = NpadBatteryLevel::Full; - break; - } - - switch (index) { - case LeftIndex: - controller.battery_state.left = { - .is_powered = is_powered, - .is_charging = is_charging, - .battery_level = battery_level, - }; - break; - case RightIndex: - controller.battery_state.right = { - .is_powered = is_powered, - .is_charging = is_charging, - .battery_level = battery_level, - }; - break; - case DualIndex: - controller.battery_state.dual = { - .is_powered = is_powered, - .is_charging = is_charging, - .battery_level = battery_level, - }; - break; - } -} - -void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.camera_values = TransformToCamera(callback); - - if (is_configuring) { - return; - } - - controller.camera_state.sample++; - controller.camera_state.format = - static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); - controller.camera_state.data = controller.camera_values.data; -} - -void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); }); - std::scoped_lock lock{mutex}; - const auto force_value = TransformToStick(callback); - - controller.ring_analog_value = force_value.x; - - if (is_configuring) { - return; - } - - controller.ring_analog_state.force = force_value.x.value; -} - -void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.nfc_values = TransformToNfc(callback); - - if (is_configuring) { - return; - } - - controller.nfc_state = controller.nfc_values; -} - -bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { - if (!is_initalized) { - return false; - } - if (device_index >= output_devices.size()) { - return false; - } - if (!output_devices[device_index]) { - return false; - } - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; - - if (!player.vibration_enabled) { - return false; - } - - // Exponential amplification is too strong at low amplitudes. Switch to a linear - // amplification if strength is set below 0.7f - const Common::Input::VibrationAmplificationType type = - strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential - : Common::Input::VibrationAmplificationType::Linear; - - const Common::Input::VibrationStatus status = { - .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f), - .low_frequency = vibration.low_frequency, - .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f), - .high_frequency = vibration.high_frequency, - .type = type, - }; - return output_devices[device_index]->SetVibration(status) == - Common::Input::DriverResult::Success; -} - -bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - - if (!is_initalized) { - return false; - } - - if (!player.vibration_enabled) { - return false; - } - - if (device_index >= output_devices.size()) { - return false; - } - - if (!output_devices[device_index]) { - return false; - } - - return output_devices[device_index]->IsVibrationEnabled(); -} - -Common::Input::DriverResult EmulatedController::SetPollingMode( - EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { - LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); - - if (!is_initalized) { - return Common::Input::DriverResult::InvalidHandle; - } - - auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; - auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_output_device = output_devices[3]; - - if (device_index == EmulatedDeviceIndex::LeftIndex) { - controller.left_polling_mode = polling_mode; - return left_output_device->SetPollingMode(polling_mode); - } - - if (device_index == EmulatedDeviceIndex::RightIndex) { - controller.right_polling_mode = polling_mode; - const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); - const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); - - // Restore previous state - if (mapped_nfc_result != Common::Input::DriverResult::Success) { - right_output_device->SetPollingMode(Common::Input::PollingMode::Active); - } - - if (virtual_nfc_result == Common::Input::DriverResult::Success) { - return virtual_nfc_result; - } - return mapped_nfc_result; - } - - controller.left_polling_mode = polling_mode; - controller.right_polling_mode = polling_mode; - left_output_device->SetPollingMode(polling_mode); - right_output_device->SetPollingMode(polling_mode); - nfc_output_device->SetPollingMode(polling_mode); - return Common::Input::DriverResult::Success; -} - -Common::Input::PollingMode EmulatedController::GetPollingMode( - EmulatedDeviceIndex device_index) const { - if (device_index == EmulatedDeviceIndex::LeftIndex) { - return controller.left_polling_mode; - } - return controller.right_polling_mode; -} - -bool EmulatedController::SetCameraFormat( - Core::IrSensor::ImageTransferProcessorFormat camera_format) { - LOG_INFO(Service_HID, "Set camera format {}", camera_format); - - if (!is_initalized) { - return false; - } - - auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& camera_output_device = output_devices[2]; - - if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( - camera_format)) == Common::Input::DriverResult::Success) { - return true; - } - - // Fallback to Qt camera if native device doesn't have support - return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( - camera_format)) == Common::Input::DriverResult::Success; -} - -Common::ParamPackage EmulatedController::GetRingParam() const { - return ring_params[0]; -} - -void EmulatedController::SetRingParam(Common::ParamPackage param) { - ring_params[0] = std::move(param); - ReloadInput(); -} - -bool EmulatedController::HasNfc() const { - - if (!is_initalized) { - return false; - } - - const auto& nfc_output_device = output_devices[3]; - - switch (npad_type) { - case NpadStyleIndex::JoyconRight: - case NpadStyleIndex::JoyconDual: - case NpadStyleIndex::ProController: - case NpadStyleIndex::Handheld: - break; - default: - return false; - } - - const bool has_virtual_nfc = - npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; - const bool is_virtual_nfc_supported = - nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; - - return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); -} - -bool EmulatedController::AddNfcHandle() { - nfc_handles++; - return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) == - Common::Input::DriverResult::Success; -} - -bool EmulatedController::RemoveNfcHandle() { - nfc_handles--; - if (nfc_handles <= 0) { - return SetPollingMode(EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active) == - Common::Input::DriverResult::Success; - } - return true; -} - -bool EmulatedController::StartNfcPolling() { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - const auto device_result = nfc_output_device->StartNfcPolling(); - const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling(); - - return device_result == Common::Input::NfcState::Success || - virtual_device_result == Common::Input::NfcState::Success; -} - -bool EmulatedController::StopNfcPolling() { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - const auto device_result = nfc_output_device->StopNfcPolling(); - const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling(); - - return device_result == Common::Input::NfcState::Success || - virtual_device_result == Common::Input::NfcState::Success; -} - -bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) { - return true; - } - - return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success; -} - -bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, - Common::Input::MifareRequest& out_data) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) { - return true; - } - - return nfc_virtual_output_device->ReadMifareData(request, out_data) == - Common::Input::NfcState::Success; -} - -bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) { - return true; - } - - return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success; -} - -bool EmulatedController::WriteNfc(const std::vector<u8>& data) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) { - return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; - } - - return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; -} - -void EmulatedController::SetLedPattern() { - if (!is_initalized) { - return; - } - - for (auto& device : output_devices) { - if (!device) { - continue; - } - - const LedPattern pattern = GetLedPattern(); - const Common::Input::LedStatus status = { - .led_1 = pattern.position1 != 0, - .led_2 = pattern.position2 != 0, - .led_3 = pattern.position3 != 0, - .led_4 = pattern.position4 != 0, - }; - device->SetLED(status); - } -} - -void EmulatedController::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode) { - for (auto& motion : controller.motion_values) { - switch (mode) { - case GyroscopeZeroDriftMode::Loose: - motion_sensitivity = motion.emulated.IsAtRestLoose; - motion.emulated.SetGyroThreshold(motion.emulated.ThresholdLoose); - break; - case GyroscopeZeroDriftMode::Tight: - motion_sensitivity = motion.emulated.IsAtRestThight; - motion.emulated.SetGyroThreshold(motion.emulated.ThresholdThight); - break; - case GyroscopeZeroDriftMode::Standard: - default: - motion_sensitivity = motion.emulated.IsAtRestStandard; - motion.emulated.SetGyroThreshold(motion.emulated.ThresholdStandard); - break; - } - } -} - -void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) { - supported_style_tag = supported_styles; - if (!is_connected) { - return; - } - - // Attempt to reconnect with the original type - if (npad_type != original_npad_type) { - Disconnect(); - const auto current_npad_type = npad_type; - SetNpadStyleIndex(original_npad_type); - if (IsControllerSupported()) { - Connect(); - return; - } - SetNpadStyleIndex(current_npad_type); - Connect(); - } - - if (IsControllerSupported()) { - return; - } - - Disconnect(); - - // Fallback Fullkey controllers to Pro controllers - if (IsControllerFullkey() && supported_style_tag.fullkey) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); - Connect(); - return; - } - - // Fallback Dual joycon controllers to Pro controllers - if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); - Connect(); - return; - } - - // Fallback Pro controllers to Dual joycon - if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); - SetNpadStyleIndex(NpadStyleIndex::JoyconDual); - Connect(); - return; - } - - LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", - npad_type); -} - -bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { - std::scoped_lock lock{mutex}; - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; - switch (type) { - case NpadStyleIndex::ProController: - case NpadStyleIndex::GameCube: - case NpadStyleIndex::NES: - case NpadStyleIndex::SNES: - case NpadStyleIndex::N64: - case NpadStyleIndex::SegaGenesis: - return true; - default: - return false; - } -} - -bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { - std::scoped_lock lock{mutex}; - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; - switch (type) { - case NpadStyleIndex::ProController: - return supported_style_tag.fullkey.As<bool>(); - case NpadStyleIndex::Handheld: - return supported_style_tag.handheld.As<bool>(); - case NpadStyleIndex::JoyconDual: - return supported_style_tag.joycon_dual.As<bool>(); - case NpadStyleIndex::JoyconLeft: - return supported_style_tag.joycon_left.As<bool>(); - case NpadStyleIndex::JoyconRight: - return supported_style_tag.joycon_right.As<bool>(); - case NpadStyleIndex::GameCube: - return supported_style_tag.gamecube.As<bool>(); - case NpadStyleIndex::Pokeball: - return supported_style_tag.palma.As<bool>(); - case NpadStyleIndex::NES: - return supported_style_tag.lark.As<bool>(); - case NpadStyleIndex::SNES: - return supported_style_tag.lucia.As<bool>(); - case NpadStyleIndex::N64: - return supported_style_tag.lagoon.As<bool>(); - case NpadStyleIndex::SegaGenesis: - return supported_style_tag.lager.As<bool>(); - default: - return false; - } -} - -void EmulatedController::Connect(bool use_temporary_value) { - if (!IsControllerSupported(use_temporary_value)) { - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; - LOG_ERROR(Service_HID, "Controller type {} is not supported", type); - return; - } - - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }); - std::scoped_lock lock{connect_mutex, mutex}; - if (is_configuring) { - tmp_is_connected = true; - return; - } - - if (is_connected) { - trigger_guard.Cancel(); - return; - } - is_connected = true; -} - -void EmulatedController::Disconnect() { - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }); - std::scoped_lock lock{connect_mutex, mutex}; - if (is_configuring) { - tmp_is_connected = false; - return; - } - - if (!is_connected) { - trigger_guard.Cancel(); - return; - } - is_connected = false; -} - -bool EmulatedController::IsConnected(bool get_temporary_value) const { - std::scoped_lock lock{connect_mutex}; - if (get_temporary_value && is_configuring) { - return tmp_is_connected; - } - return is_connected; -} - -NpadIdType EmulatedController::GetNpadIdType() const { - std::scoped_lock lock{mutex}; - return npad_id_type; -} - -NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { - std::scoped_lock lock{npad_mutex}; - if (get_temporary_value && is_configuring) { - return tmp_npad_type; - } - return npad_type; -} - -void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }); - std::scoped_lock lock{mutex, npad_mutex}; - - if (is_configuring) { - if (tmp_npad_type == npad_type_) { - trigger_guard.Cancel(); - return; - } - tmp_npad_type = npad_type_; - return; - } - - if (npad_type == npad_type_) { - trigger_guard.Cancel(); - return; - } - if (is_connected) { - LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", - Service::HID::NpadIdTypeToIndex(npad_id_type)); - } - npad_type = npad_type_; -} - -LedPattern EmulatedController::GetLedPattern() const { - switch (npad_id_type) { - case NpadIdType::Player1: - return LedPattern{1, 0, 0, 0}; - case NpadIdType::Player2: - return LedPattern{1, 1, 0, 0}; - case NpadIdType::Player3: - return LedPattern{1, 1, 1, 0}; - case NpadIdType::Player4: - return LedPattern{1, 1, 1, 1}; - case NpadIdType::Player5: - return LedPattern{1, 0, 0, 1}; - case NpadIdType::Player6: - return LedPattern{1, 0, 1, 0}; - case NpadIdType::Player7: - return LedPattern{1, 0, 1, 1}; - case NpadIdType::Player8: - return LedPattern{0, 1, 1, 0}; - default: - return LedPattern{0, 0, 0, 0}; - } -} - -ButtonValues EmulatedController::GetButtonsValues() const { - std::scoped_lock lock{mutex}; - return controller.button_values; -} - -SticksValues EmulatedController::GetSticksValues() const { - std::scoped_lock lock{mutex}; - return controller.stick_values; -} - -TriggerValues EmulatedController::GetTriggersValues() const { - std::scoped_lock lock{mutex}; - return controller.trigger_values; -} - -ControllerMotionValues EmulatedController::GetMotionValues() const { - std::scoped_lock lock{mutex}; - return controller.motion_values; -} - -ColorValues EmulatedController::GetColorsValues() const { - std::scoped_lock lock{mutex}; - return controller.color_values; -} - -BatteryValues EmulatedController::GetBatteryValues() const { - std::scoped_lock lock{mutex}; - return controller.battery_values; -} - -CameraValues EmulatedController::GetCameraValues() const { - std::scoped_lock lock{mutex}; - return controller.camera_values; -} - -RingAnalogValue EmulatedController::GetRingSensorValues() const { - return controller.ring_analog_value; -} - -HomeButtonState EmulatedController::GetHomeButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.home_button_state; -} - -CaptureButtonState EmulatedController::GetCaptureButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.capture_button_state; -} - -NpadButtonState EmulatedController::GetNpadButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return {controller.npad_button_state.raw & GetTurboButtonMask()}; -} - -DebugPadButton EmulatedController::GetDebugPadButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.debug_pad_button_state; -} - -AnalogSticks EmulatedController::GetSticks() const { - std::scoped_lock lock{mutex}; - - if (is_configuring) { - return {}; - } - - return controller.analog_stick_state; -} - -NpadGcTriggerState EmulatedController::GetTriggers() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.gc_trigger_state; -} - -MotionState EmulatedController::GetMotions() const { - std::unique_lock lock{mutex}; - return controller.motion_state; -} - -ControllerColors EmulatedController::GetColors() const { - std::scoped_lock lock{mutex}; - return controller.colors_state; -} - -BatteryLevelState EmulatedController::GetBattery() const { - std::scoped_lock lock{mutex}; - return controller.battery_state; -} - -const CameraState& EmulatedController::GetCamera() const { - std::scoped_lock lock{mutex}; - return controller.camera_state; -} - -RingSensorForce EmulatedController::GetRingSensorForce() const { - return controller.ring_analog_state; -} - -const NfcState& EmulatedController::GetNfc() const { - std::scoped_lock lock{mutex}; - return controller.nfc_state; -} - -NpadColor EmulatedController::GetNpadColor(u32 color) { - return { - .r = static_cast<u8>((color >> 16) & 0xFF), - .g = static_cast<u8>((color >> 8) & 0xFF), - .b = static_cast<u8>(color & 0xFF), - .a = 0xff, - }; -} - -void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { - std::scoped_lock lock{callback_mutex}; - for (const auto& poller_pair : callback_list) { - const ControllerUpdateCallback& poller = poller_pair.second; - if (!is_npad_service_update && poller.is_npad_service) { - continue; - } - if (poller.on_change) { - poller.on_change(type); - } - } -} - -int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { - std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); - return last_callback_key++; -} - -void EmulatedController::DeleteCallback(int key) { - std::scoped_lock lock{callback_mutex}; - const auto& iterator = callback_list.find(key); - if (iterator == callback_list.end()) { - LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); - return; - } - callback_list.erase(iterator); -} - -void EmulatedController::StatusUpdate() { - turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2); - - // Some drivers like key motion need constant refreshing - for (std::size_t index = 0; index < motion_devices.size(); ++index) { - const auto& raw_status = controller.motion_values[index].raw_status; - auto& device = motion_devices[index]; - if (!raw_status.force_update) { - continue; - } - if (!device) { - continue; - } - device->ForceUpdate(); - } -} - -NpadButton EmulatedController::GetTurboButtonMask() const { - // Apply no mask when disabled - if (turbo_button_state < TURBO_BUTTON_DELAY) { - return {NpadButton::All}; - } - - NpadButtonState button_mask{}; - for (std::size_t index = 0; index < controller.button_values.size(); ++index) { - if (!controller.button_values[index].turbo) { - continue; - } - - switch (index) { - case Settings::NativeButton::A: - button_mask.a.Assign(1); - break; - case Settings::NativeButton::B: - button_mask.b.Assign(1); - break; - case Settings::NativeButton::X: - button_mask.x.Assign(1); - break; - case Settings::NativeButton::Y: - button_mask.y.Assign(1); - break; - case Settings::NativeButton::L: - button_mask.l.Assign(1); - break; - case Settings::NativeButton::R: - button_mask.r.Assign(1); - break; - case Settings::NativeButton::ZL: - button_mask.zl.Assign(1); - break; - case Settings::NativeButton::ZR: - button_mask.zr.Assign(1); - break; - case Settings::NativeButton::DLeft: - button_mask.left.Assign(1); - break; - case Settings::NativeButton::DUp: - button_mask.up.Assign(1); - break; - case Settings::NativeButton::DRight: - button_mask.right.Assign(1); - break; - case Settings::NativeButton::DDown: - button_mask.down.Assign(1); - break; - case Settings::NativeButton::SLLeft: - button_mask.left_sl.Assign(1); - break; - case Settings::NativeButton::SLRight: - button_mask.right_sl.Assign(1); - break; - case Settings::NativeButton::SRLeft: - button_mask.left_sr.Assign(1); - break; - case Settings::NativeButton::SRRight: - button_mask.right_sr.Assign(1); - break; - default: - break; - } - } - - return static_cast<NpadButton>(~button_mask.raw); -} - -} // namespace Core::HID diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h deleted file mode 100644 index d6e20ab66..000000000 --- a/src/core/hid/emulated_controller.h +++ /dev/null @@ -1,619 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <functional> -#include <memory> -#include <mutex> -#include <unordered_map> -#include <vector> - -#include "common/common_types.h" -#include "common/input.h" -#include "common/param_package.h" -#include "common/settings.h" -#include "common/vector_math.h" -#include "core/hid/hid_types.h" -#include "core/hid/irs_types.h" -#include "core/hid/motion_input.h" - -namespace Core::HID { -const std::size_t max_emulated_controllers = 2; -const std::size_t output_devices_size = 4; -struct ControllerMotionInfo { - Common::Input::MotionStatus raw_status{}; - MotionInput emulated{}; -}; - -using ButtonDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>; -using StickDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>; -using ControllerMotionDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; -using TriggerDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; -using ColorDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using BatteryDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using CameraDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using RingAnalogDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using NfcDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; - -using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; -using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; -using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; -using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; -using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using OutputParams = std::array<Common::ParamPackage, output_devices_size>; - -using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; -using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; -using TriggerValues = - std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>; -using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; -using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; -using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; -using CameraValues = Common::Input::CameraStatus; -using RingAnalogValue = Common::Input::AnalogStatus; -using NfcValues = Common::Input::NfcStatus; -using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; - -struct AnalogSticks { - AnalogStickState left{}; - AnalogStickState right{}; -}; - -struct ControllerColors { - NpadControllerColor fullkey{}; - NpadControllerColor left{}; - NpadControllerColor right{}; -}; - -struct BatteryLevelState { - NpadPowerInfo dual{}; - NpadPowerInfo left{}; - NpadPowerInfo right{}; -}; - -struct CameraState { - Core::IrSensor::ImageTransferProcessorFormat format{}; - std::vector<u8> data{}; - std::size_t sample{}; -}; - -struct RingSensorForce { - f32 force; -}; - -using NfcState = Common::Input::NfcStatus; - -struct ControllerMotion { - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - Common::Vec3f euler{}; - std::array<Common::Vec3f, 3> orientation{}; - bool is_at_rest{}; -}; - -enum EmulatedDeviceIndex : u8 { - LeftIndex, - RightIndex, - DualIndex, - AllDevices, -}; - -using MotionState = std::array<ControllerMotion, 2>; - -struct ControllerStatus { - // Data from input_common - ButtonValues button_values{}; - SticksValues stick_values{}; - ControllerMotionValues motion_values{}; - TriggerValues trigger_values{}; - ColorValues color_values{}; - BatteryValues battery_values{}; - VibrationValues vibration_values{}; - CameraValues camera_values{}; - RingAnalogValue ring_analog_value{}; - NfcValues nfc_values{}; - - // Data for HID services - HomeButtonState home_button_state{}; - CaptureButtonState capture_button_state{}; - NpadButtonState npad_button_state{}; - DebugPadButton debug_pad_button_state{}; - AnalogSticks analog_stick_state{}; - MotionState motion_state{}; - NpadGcTriggerState gc_trigger_state{}; - ControllerColors colors_state{}; - BatteryLevelState battery_state{}; - CameraState camera_state{}; - RingSensorForce ring_analog_state{}; - NfcState nfc_state{}; - Common::Input::PollingMode left_polling_mode{}; - Common::Input::PollingMode right_polling_mode{}; -}; - -enum class ControllerTriggerType { - Button, - Stick, - Trigger, - Motion, - Color, - Battery, - Vibration, - IrSensor, - RingController, - Nfc, - Connected, - Disconnected, - Type, - All, -}; - -struct ControllerUpdateCallback { - std::function<void(ControllerTriggerType)> on_change; - bool is_npad_service; -}; - -class EmulatedController { -public: - /** - * Contains all input data (buttons, joysticks, vibration, and motion) within this controller. - * @param npad_id_type npad id type for this specific controller - */ - explicit EmulatedController(NpadIdType npad_id_type_); - ~EmulatedController(); - - YUZU_NON_COPYABLE(EmulatedController); - YUZU_NON_MOVEABLE(EmulatedController); - - /// Converts the controller type from settings to npad type - static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type); - - /// Converts npad type to the equivalent of controller type from settings - static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type); - - /// Gets the NpadIdType for this controller - NpadIdType GetNpadIdType() const; - - /// Sets the NpadStyleIndex for this controller - void SetNpadStyleIndex(NpadStyleIndex npad_type_); - - /** - * Gets the NpadStyleIndex for this controller - * @param get_temporary_value If true tmp_npad_type will be returned - * @return NpadStyleIndex set on the controller - */ - NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const; - - /** - * Sets the supported controller types. Disconnects the controller if current type is not - * supported - * @param supported_styles bitflag with supported types - */ - void SetSupportedNpadStyleTag(NpadStyleTag supported_styles); - - /** - * Sets the connected status to true - * @param use_temporary_value If true tmp_npad_type will be used - */ - void Connect(bool use_temporary_value = false); - - /// Sets the connected status to false - void Disconnect(); - - /** - * Is the emulated connected - * @param get_temporary_value If true tmp_is_connected will be returned - * @return true if the controller has the connected status - */ - bool IsConnected(bool get_temporary_value = false) const; - - /// Removes all callbacks created from input devices - void UnloadInput(); - - /** - * Sets the emulated controller into configuring mode - * This prevents the modification of the HID state of the emulated controller by input commands - */ - void EnableConfiguration(); - - /// Returns the emulated controller into normal mode, allowing the modification of the HID state - void DisableConfiguration(); - - /// Enables Home and Screenshot buttons - void EnableSystemButtons(); - - /// Disables Home and Screenshot buttons - void DisableSystemButtons(); - - /// Sets Home and Screenshot buttons to false - void ResetSystemButtons(); - - /// Returns true if the emulated controller is in configuring mode - bool IsConfiguring() const; - - /// Reload all input devices - void ReloadInput(); - - /// Overrides current mapped devices with the stored configuration and reloads all input devices - void ReloadFromSettings(); - - /// Updates current colors with the ones stored in the configuration - void ReloadColorsFromSettings(); - - /// Saves the current mapped configuration - void SaveCurrentConfig(); - - /// Reverts any mapped changes made that weren't saved - void RestoreConfig(); - - /// Returns a vector of mapped devices from the mapped button and stick parameters - std::vector<Common::ParamPackage> GetMappedDevices() const; - - // Returns the current mapped button device - Common::ParamPackage GetButtonParam(std::size_t index) const; - - // Returns the current mapped stick device - Common::ParamPackage GetStickParam(std::size_t index) const; - - // Returns the current mapped motion device - Common::ParamPackage GetMotionParam(std::size_t index) const; - - /** - * Updates the current mapped button device - * @param param ParamPackage with controller data to be mapped - */ - void SetButtonParam(std::size_t index, Common::ParamPackage param); - - /** - * Updates the current mapped stick device - * @param param ParamPackage with controller data to be mapped - */ - void SetStickParam(std::size_t index, Common::ParamPackage param); - - /** - * Updates the current mapped motion device - * @param param ParamPackage with controller data to be mapped - */ - void SetMotionParam(std::size_t index, Common::ParamPackage param); - - /// Auto calibrates the current motion devices - void StartMotionCalibration(); - - /// Returns the latest button status from the controller with parameters - ButtonValues GetButtonsValues() const; - - /// Returns the latest analog stick status from the controller with parameters - SticksValues GetSticksValues() const; - - /// Returns the latest trigger status from the controller with parameters - TriggerValues GetTriggersValues() const; - - /// Returns the latest motion status from the controller with parameters - ControllerMotionValues GetMotionValues() const; - - /// Returns the latest color status from the controller with parameters - ColorValues GetColorsValues() const; - - /// Returns the latest battery status from the controller with parameters - BatteryValues GetBatteryValues() const; - - /// Returns the latest camera status from the controller with parameters - CameraValues GetCameraValues() const; - - /// Returns the latest status of analog input from the ring sensor with parameters - RingAnalogValue GetRingSensorValues() const; - - /// Returns the latest status of button input for the hid::HomeButton service - HomeButtonState GetHomeButtons() const; - - /// Returns the latest status of button input for the hid::CaptureButton service - CaptureButtonState GetCaptureButtons() const; - - /// Returns the latest status of button input for the hid::Npad service - NpadButtonState GetNpadButtons() const; - - /// Returns the latest status of button input for the debug pad service - DebugPadButton GetDebugPadButtons() const; - - /// Returns the latest status of stick input from the mouse - AnalogSticks GetSticks() const; - - /// Returns the latest status of trigger input from the mouse - NpadGcTriggerState GetTriggers() const; - - /// Returns the latest status of motion input from the mouse - MotionState GetMotions() const; - - /// Returns the latest color value from the controller - ControllerColors GetColors() const; - - /// Returns the latest battery status from the controller - BatteryLevelState GetBattery() const; - - /// Returns the latest camera status from the controller - const CameraState& GetCamera() const; - - /// Returns the latest ringcon force sensor value - RingSensorForce GetRingSensorForce() const; - - /// Returns the latest ntag status from the controller - const NfcState& GetNfc() const; - - /** - * Sends a specific vibration to the output device - * @return true if vibration had no errors - */ - bool SetVibration(std::size_t device_index, VibrationValue vibration); - - /** - * Sends a small vibration to the output device - * @return true if SetVibration was successful - */ - bool IsVibrationEnabled(std::size_t device_index); - - /** - * Sets the desired data to be polled from a controller - * @param device_index index of the controller to set the polling mode - * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. - * @return driver result from this command - */ - Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index, - Common::Input::PollingMode polling_mode); - /** - * Get the current polling mode from a controller - * @param device_index index of the controller to set the polling mode - * @return current polling mode - */ - Common::Input::PollingMode GetPollingMode(EmulatedDeviceIndex device_index) const; - - /** - * Sets the desired camera format to be polled from a controller - * @param camera_format size of each frame - * @return true if SetCameraFormat was successful - */ - bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); - - // Returns the current mapped ring device - Common::ParamPackage GetRingParam() const; - - /** - * Updates the current mapped ring device - * @param param ParamPackage with ring sensor data to be mapped - */ - void SetRingParam(Common::ParamPackage param); - - /// Returns true if the device has nfc support - bool HasNfc() const; - - /// Sets the joycon in nfc mode and increments the handle count - bool AddNfcHandle(); - - /// Decrements the handle count if zero sets the joycon in active mode - bool RemoveNfcHandle(); - - /// Start searching for nfc tags - bool StartNfcPolling(); - - /// Stop searching for nfc tags - bool StopNfcPolling(); - - /// Returns true if the nfc tag was readable - bool ReadAmiiboData(std::vector<u8>& data); - - /// Returns true if the nfc tag was written - bool WriteNfc(const std::vector<u8>& data); - - /// Returns true if the nfc tag was readable - bool ReadMifareData(const Common::Input::MifareRequest& request, - Common::Input::MifareRequest& out_data); - - /// Returns true if the nfc tag was written - bool WriteMifareData(const Common::Input::MifareRequest& request); - - /// Returns the led pattern corresponding to this emulated controller - LedPattern GetLedPattern() const; - - /// Asks the output device to change the player led pattern - void SetLedPattern(); - - /// Changes sensitivity of the motion sensor - void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode); - - /** - * Adds a callback to the list of events - * @param update_callback A ConsoleUpdateCallback that will be triggered - * @return an unique key corresponding to the callback index in the list - */ - int SetCallback(ControllerUpdateCallback update_callback); - - /** - * Removes a callback from the list stopping any future events to this object - * @param key Key corresponding to the callback index in the list - */ - void DeleteCallback(int key); - - /// Swaps the state of the turbo buttons and updates motion input - void StatusUpdate(); - -private: - /// creates input devices from params - void LoadDevices(); - - /// Set the params for TAS devices - void LoadTASParams(); - - /// Set the params for virtual pad devices - void LoadVirtualGamepadParams(); - - /** - * @param use_temporary_value If true tmp_npad_type will be used - * @return true if the controller style is fullkey - */ - bool IsControllerFullkey(bool use_temporary_value = false) const; - - /** - * Checks the current controller type against the supported_style_tag - * @param use_temporary_value If true tmp_npad_type will be used - * @return true if the controller is supported - */ - bool IsControllerSupported(bool use_temporary_value = false) const; - - /** - * Updates the button status of the controller - * @param callback A CallbackStatus containing the button status - * @param index Button ID of the to be updated - */ - void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid); - - /** - * Updates the analog stick status of the controller - * @param callback A CallbackStatus containing the analog stick status - * @param index stick ID of the to be updated - */ - void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid); - - /** - * Updates the trigger status of the controller - * @param callback A CallbackStatus containing the trigger status - * @param index trigger ID of the to be updated - */ - void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid); - - /** - * Updates the motion status of the controller - * @param callback A CallbackStatus containing gyro and accelerometer data - * @param index motion ID of the to be updated - */ - void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the color status of the controller - * @param callback A CallbackStatus containing the color status - * @param index color ID of the to be updated - */ - void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the battery status of the controller - * @param callback A CallbackStatus containing the battery status - * @param index battery ID of the to be updated - */ - void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the camera status of the controller - * @param callback A CallbackStatus containing the camera status - */ - void SetCamera(const Common::Input::CallbackStatus& callback); - - /** - * Updates the ring analog sensor status of the ring controller - * @param callback A CallbackStatus containing the force status - */ - void SetRingAnalog(const Common::Input::CallbackStatus& callback); - - /** - * Updates the nfc status of the controller - * @param callback A CallbackStatus containing the nfc status - */ - void SetNfc(const Common::Input::CallbackStatus& callback); - - /** - * Converts a color format from bgra to rgba - * @param color in bgra format - * @return NpadColor in rgba format - */ - NpadColor GetNpadColor(u32 color); - - /** - * Triggers a callback that something has changed on the controller status - * @param type Input type of the event to trigger - * @param is_service_update indicates if this event should only be sent to HID services - */ - void TriggerOnChange(ControllerTriggerType type, bool is_service_update); - - NpadButton GetTurboButtonMask() const; - - const NpadIdType npad_id_type; - NpadStyleIndex npad_type{NpadStyleIndex::None}; - NpadStyleIndex original_npad_type{NpadStyleIndex::None}; - NpadStyleTag supported_style_tag{NpadStyleSet::All}; - bool is_connected{false}; - bool is_configuring{false}; - bool is_initalized{false}; - bool system_buttons_enabled{true}; - f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; - u32 turbo_button_state{0}; - std::size_t nfc_handles{0}; - - // Temporary values to avoid doing changes while the controller is in configuring mode - NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; - bool tmp_is_connected{false}; - - ButtonParams button_params; - StickParams stick_params; - ControllerMotionParams motion_params; - TriggerParams trigger_params; - BatteryParams battery_params; - ColorParams color_params; - CameraParams camera_params; - RingAnalogParams ring_params; - NfcParams nfc_params; - OutputParams output_params; - - ButtonDevices button_devices; - StickDevices stick_devices; - ControllerMotionDevices motion_devices; - TriggerDevices trigger_devices; - BatteryDevices battery_devices; - ColorDevices color_devices; - CameraDevices camera_devices; - RingAnalogDevices ring_analog_devices; - NfcDevices nfc_devices; - OutputDevices output_devices; - - // TAS related variables - ButtonParams tas_button_params; - StickParams tas_stick_params; - ButtonDevices tas_button_devices; - StickDevices tas_stick_devices; - - // Virtual gamepad related variables - ButtonParams virtual_button_params; - StickParams virtual_stick_params; - ControllerMotionParams virtual_motion_params; - ButtonDevices virtual_button_devices; - StickDevices virtual_stick_devices; - ControllerMotionDevices virtual_motion_devices; - - mutable std::mutex mutex; - mutable std::mutex callback_mutex; - mutable std::mutex npad_mutex; - mutable std::mutex connect_mutex; - std::unordered_map<int, ControllerUpdateCallback> callback_list; - int last_callback_key = 0; - - // Stores the current status of all controller input - ControllerStatus controller; -}; - -} // namespace Core::HID diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp deleted file mode 100644 index 8e165dded..000000000 --- a/src/core/hid/emulated_devices.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <fmt/format.h> - -#include "core/hid/emulated_devices.h" -#include "core/hid/input_converter.h" - -namespace Core::HID { - -EmulatedDevices::EmulatedDevices() = default; - -EmulatedDevices::~EmulatedDevices() = default; - -void EmulatedDevices::ReloadFromSettings() { - ReloadInput(); -} - -void EmulatedDevices::ReloadInput() { - // If you load any device here add the equivalent to the UnloadInput() function - - // Native Mouse is mapped on port 1, pad 0 - const Common::ParamPackage mouse_params{"engine:mouse,port:1,pad:0"}; - - // Keyboard keys is mapped on port 1, pad 0 for normal keys, pad 1 for moddifier keys - const Common::ParamPackage keyboard_params{"engine:keyboard,port:1"}; - - std::size_t key_index = 0; - for (auto& mouse_device : mouse_button_devices) { - Common::ParamPackage mouse_button_params = mouse_params; - mouse_button_params.Set("button", static_cast<int>(key_index)); - mouse_device = Common::Input::CreateInputDevice(mouse_button_params); - key_index++; - } - - Common::ParamPackage mouse_position_params = mouse_params; - mouse_position_params.Set("axis_x", 0); - mouse_position_params.Set("axis_y", 1); - mouse_position_params.Set("deadzone", 0.0f); - mouse_position_params.Set("range", 1.0f); - mouse_position_params.Set("threshold", 0.0f); - mouse_stick_device = Common::Input::CreateInputDevice(mouse_position_params); - - // First two axis are reserved for mouse position - key_index = 2; - for (auto& mouse_device : mouse_wheel_devices) { - Common::ParamPackage mouse_wheel_params = mouse_params; - mouse_wheel_params.Set("axis", static_cast<int>(key_index)); - mouse_device = Common::Input::CreateInputDevice(mouse_wheel_params); - key_index++; - } - - key_index = 0; - for (auto& keyboard_device : keyboard_devices) { - Common::ParamPackage keyboard_key_params = keyboard_params; - keyboard_key_params.Set("button", static_cast<int>(key_index)); - keyboard_key_params.Set("pad", 0); - keyboard_device = Common::Input::CreateInputDevice(keyboard_key_params); - key_index++; - } - - key_index = 0; - for (auto& keyboard_device : keyboard_modifier_devices) { - Common::ParamPackage keyboard_moddifier_params = keyboard_params; - keyboard_moddifier_params.Set("button", static_cast<int>(key_index)); - keyboard_moddifier_params.Set("pad", 1); - keyboard_device = Common::Input::CreateInputDevice(keyboard_moddifier_params); - key_index++; - } - - for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { - if (!mouse_button_devices[index]) { - continue; - } - mouse_button_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMouseButton(callback, index); - }, - }); - } - - for (std::size_t index = 0; index < mouse_wheel_devices.size(); ++index) { - if (!mouse_wheel_devices[index]) { - continue; - } - mouse_wheel_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMouseWheel(callback, index); - }, - }); - } - - if (mouse_stick_device) { - mouse_stick_device->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { - SetMousePosition(callback); - }, - }); - } - - for (std::size_t index = 0; index < keyboard_devices.size(); ++index) { - if (!keyboard_devices[index]) { - continue; - } - keyboard_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetKeyboardButton(callback, index); - }, - }); - } - - for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) { - if (!keyboard_modifier_devices[index]) { - continue; - } - keyboard_modifier_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetKeyboardModifier(callback, index); - }, - }); - } -} - -void EmulatedDevices::UnloadInput() { - for (auto& button : mouse_button_devices) { - button.reset(); - } - for (auto& analog : mouse_wheel_devices) { - analog.reset(); - } - mouse_stick_device.reset(); - for (auto& button : keyboard_devices) { - button.reset(); - } - for (auto& button : keyboard_modifier_devices) { - button.reset(); - } -} - -void EmulatedDevices::EnableConfiguration() { - is_configuring = true; - SaveCurrentConfig(); -} - -void EmulatedDevices::DisableConfiguration() { - is_configuring = false; -} - -bool EmulatedDevices::IsConfiguring() const { - return is_configuring; -} - -void EmulatedDevices::SaveCurrentConfig() { - if (!is_configuring) { - return; - } -} - -void EmulatedDevices::RestoreConfig() { - if (!is_configuring) { - return; - } - ReloadFromSettings(); -} - -void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.keyboard_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = device_status.keyboard_values[index]; - current_status.toggle = new_status.toggle; - - // Update button status with current status - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button, ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Keyboard); - return; - } - - // Index should be converted from NativeKeyboard to KeyboardKeyIndex - UpdateKey(index, current_status.value); - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Keyboard); -} - -void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) { - constexpr std::size_t KEYS_PER_BYTE = 8; - auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE]; - const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE)); - if (status) { - entry = entry | mask; - } else { - entry = static_cast<u8>(entry & ~mask); - } -} - -void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.keyboard_moddifier_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = device_status.keyboard_moddifier_values[index]; - current_status.toggle = new_status.toggle; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(DeviceTriggerType::KeyboardModdifier); - return; - } - - switch (index) { - case Settings::NativeKeyboard::LeftControl: - case Settings::NativeKeyboard::RightControl: - device_status.keyboard_moddifier_state.control.Assign(current_status.value); - break; - case Settings::NativeKeyboard::LeftShift: - case Settings::NativeKeyboard::RightShift: - device_status.keyboard_moddifier_state.shift.Assign(current_status.value); - break; - case Settings::NativeKeyboard::LeftAlt: - device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value); - break; - case Settings::NativeKeyboard::RightAlt: - device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value); - break; - case Settings::NativeKeyboard::CapsLock: - device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value); - break; - case Settings::NativeKeyboard::ScrollLock: - device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value); - break; - case Settings::NativeKeyboard::NumLock: - device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value); - break; - } - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::KeyboardModdifier); -} - -void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.mouse_button_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = device_status.mouse_button_values[index]; - current_status.toggle = new_status.toggle; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); - return; - } - - switch (index) { - case Settings::NativeMouseButton::Left: - device_status.mouse_button_state.left.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Right: - device_status.mouse_button_state.right.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Middle: - device_status.mouse_button_state.middle.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Forward: - device_status.mouse_button_state.forward.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Back: - device_status.mouse_button_state.back.Assign(current_status.value); - break; - } - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); -} - -void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.mouse_wheel_values.size()) { - return; - } - std::unique_lock lock{mutex}; - const auto analog_value = TransformToAnalog(callback); - - device_status.mouse_wheel_values[index] = analog_value; - - if (is_configuring) { - device_status.mouse_wheel_state = {}; - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); - return; - } - - switch (index) { - case Settings::NativeMouseWheel::X: - device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value); - break; - case Settings::NativeMouseWheel::Y: - device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value); - break; - } - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); -} - -void EmulatedDevices::SetMousePosition(const Common::Input::CallbackStatus& callback) { - std::unique_lock lock{mutex}; - const auto touch_value = TransformToTouch(callback); - - device_status.mouse_stick_value = touch_value; - - if (is_configuring) { - device_status.mouse_position_state = {}; - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); - return; - } - - device_status.mouse_position_state.x = touch_value.x.value; - device_status.mouse_position_state.y = touch_value.y.value; - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); -} - -KeyboardValues EmulatedDevices::GetKeyboardValues() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_values; -} - -KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_moddifier_values; -} - -MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_button_values; -} - -KeyboardKey EmulatedDevices::GetKeyboard() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_state; -} - -KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_moddifier_state; -} - -MouseButton EmulatedDevices::GetMouseButtons() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_button_state; -} - -MousePosition EmulatedDevices::GetMousePosition() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_position_state; -} - -AnalogStickState EmulatedDevices::GetMouseWheel() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_wheel_state; -} - -void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { - std::scoped_lock lock{callback_mutex}; - for (const auto& poller_pair : callback_list) { - const InterfaceUpdateCallback& poller = poller_pair.second; - if (poller.on_change) { - poller.on_change(type); - } - } -} - -int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { - std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); - return last_callback_key++; -} - -void EmulatedDevices::DeleteCallback(int key) { - std::scoped_lock lock{callback_mutex}; - const auto& iterator = callback_list.find(key); - if (iterator == callback_list.end()) { - LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); - return; - } - callback_list.erase(iterator); -} -} // namespace Core::HID diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h deleted file mode 100644 index 5eab693e4..000000000 --- a/src/core/hid/emulated_devices.h +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <functional> -#include <memory> -#include <mutex> -#include <unordered_map> -#include <vector> - -#include "common/common_types.h" -#include "common/input.h" -#include "common/param_package.h" -#include "common/settings.h" -#include "core/hid/hid_types.h" - -namespace Core::HID { -using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeKeyboard::NumKeyboardKeys>; -using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeKeyboard::NumKeyboardMods>; -using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeMouseButton::NumMouseButtons>; -using MouseWheelDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeMouseWheel::NumMouseWheels>; -using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; - -using MouseButtonParams = - std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; - -using KeyboardValues = - std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; -using KeyboardModifierValues = - std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>; -using MouseButtonValues = - std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>; -using MouseWheelValues = - std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; -using MouseStickValue = Common::Input::TouchStatus; - -struct MousePosition { - f32 x; - f32 y; -}; - -struct DeviceStatus { - // Data from input_common - KeyboardValues keyboard_values{}; - KeyboardModifierValues keyboard_moddifier_values{}; - MouseButtonValues mouse_button_values{}; - MouseWheelValues mouse_wheel_values{}; - MouseStickValue mouse_stick_value{}; - - // Data for HID services - KeyboardKey keyboard_state{}; - KeyboardModifier keyboard_moddifier_state{}; - MouseButton mouse_button_state{}; - MousePosition mouse_position_state{}; - AnalogStickState mouse_wheel_state{}; -}; - -enum class DeviceTriggerType { - Keyboard, - KeyboardModdifier, - Mouse, - RingController, -}; - -struct InterfaceUpdateCallback { - std::function<void(DeviceTriggerType)> on_change; -}; - -class EmulatedDevices { -public: - /** - * Contains all input data related to external devices that aren't necessarily a controller - * This includes devices such as the keyboard or mouse - */ - explicit EmulatedDevices(); - ~EmulatedDevices(); - - YUZU_NON_COPYABLE(EmulatedDevices); - YUZU_NON_MOVEABLE(EmulatedDevices); - - /// Removes all callbacks created from input devices - void UnloadInput(); - - /** - * Sets the emulated devices into configuring mode - * This prevents the modification of the HID state of the emulated devices by input commands - */ - void EnableConfiguration(); - - /// Returns the emulated devices into normal mode, allowing the modification of the HID state - void DisableConfiguration(); - - /// Returns true if the emulated device is in configuring mode - bool IsConfiguring() const; - - /// Reload all input devices - void ReloadInput(); - - /// Overrides current mapped devices with the stored configuration and reloads all input devices - void ReloadFromSettings(); - - /// Saves the current mapped configuration - void SaveCurrentConfig(); - - /// Reverts any mapped changes made that weren't saved - void RestoreConfig(); - - /// Returns the latest status of button input from the keyboard with parameters - KeyboardValues GetKeyboardValues() const; - - /// Returns the latest status of button input from the keyboard modifiers with parameters - KeyboardModifierValues GetKeyboardModdifierValues() const; - - /// Returns the latest status of button input from the mouse with parameters - MouseButtonValues GetMouseButtonsValues() const; - - /// Returns the latest status of button input from the keyboard - KeyboardKey GetKeyboard() const; - - /// Returns the latest status of button input from the keyboard modifiers - KeyboardModifier GetKeyboardModifier() const; - - /// Returns the latest status of button input from the mouse - MouseButton GetMouseButtons() const; - - /// Returns the latest mouse coordinates - MousePosition GetMousePosition() const; - - /// Returns the latest mouse wheel change - AnalogStickState GetMouseWheel() const; - - /** - * Adds a callback to the list of events - * @param update_callback InterfaceUpdateCallback that will be triggered - * @return an unique key corresponding to the callback index in the list - */ - int SetCallback(InterfaceUpdateCallback update_callback); - - /** - * Removes a callback from the list stopping any future events to this object - * @param key Key corresponding to the callback index in the list - */ - void DeleteCallback(int key); - -private: - /// Helps assigning a value to keyboard_state - void UpdateKey(std::size_t key_index, bool status); - - /** - * Updates the touch status of the keyboard device - * @param callback A CallbackStatus containing the key status - * @param index key ID to be updated - */ - void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the keyboard status of the keyboard device - * @param callback A CallbackStatus containing the modifier key status - * @param index modifier key ID to be updated - */ - void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the mouse button status of the mouse device - * @param callback A CallbackStatus containing the button status - * @param index Button ID to be updated - */ - void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the mouse wheel status of the mouse device - * @param callback A CallbackStatus containing the wheel status - * @param index wheel ID to be updated - */ - void SetMouseWheel(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the mouse position status of the mouse device - * @param callback A CallbackStatus containing the position status - */ - void SetMousePosition(const Common::Input::CallbackStatus& callback); - - /** - * Triggers a callback that something has changed on the device status - * @param type Input type of the event to trigger - */ - void TriggerOnChange(DeviceTriggerType type); - - bool is_configuring{false}; - - KeyboardDevices keyboard_devices; - KeyboardModifierDevices keyboard_modifier_devices; - MouseButtonDevices mouse_button_devices; - MouseWheelDevices mouse_wheel_devices; - MouseStickDevice mouse_stick_device; - - mutable std::mutex mutex; - mutable std::mutex callback_mutex; - std::unordered_map<int, InterfaceUpdateCallback> callback_list; - int last_callback_key = 0; - - // Stores the current status of all external device input - DeviceStatus device_status; -}; - -} // namespace Core::HID diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp deleted file mode 100644 index 2cf25a870..000000000 --- a/src/core/hid/hid_core.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/hid/emulated_console.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/emulated_devices.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/hid_util.h" - -namespace Core::HID { - -HIDCore::HIDCore() - : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)}, - player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)}, - player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)}, - player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)}, - player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)}, - player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)}, - player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)}, - player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)}, - other{std::make_unique<EmulatedController>(NpadIdType::Other)}, - handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)}, - console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {} - -HIDCore::~HIDCore() = default; - -EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) { - switch (npad_id_type) { - case NpadIdType::Player1: - return player_1.get(); - case NpadIdType::Player2: - return player_2.get(); - case NpadIdType::Player3: - return player_3.get(); - case NpadIdType::Player4: - return player_4.get(); - case NpadIdType::Player5: - return player_5.get(); - case NpadIdType::Player6: - return player_6.get(); - case NpadIdType::Player7: - return player_7.get(); - case NpadIdType::Player8: - return player_8.get(); - case NpadIdType::Other: - return other.get(); - case NpadIdType::Handheld: - return handheld.get(); - case NpadIdType::Invalid: - default: - ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type); - return nullptr; - } -} - -const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const { - switch (npad_id_type) { - case NpadIdType::Player1: - return player_1.get(); - case NpadIdType::Player2: - return player_2.get(); - case NpadIdType::Player3: - return player_3.get(); - case NpadIdType::Player4: - return player_4.get(); - case NpadIdType::Player5: - return player_5.get(); - case NpadIdType::Player6: - return player_6.get(); - case NpadIdType::Player7: - return player_7.get(); - case NpadIdType::Player8: - return player_8.get(); - case NpadIdType::Other: - return other.get(); - case NpadIdType::Handheld: - return handheld.get(); - case NpadIdType::Invalid: - default: - ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type); - return nullptr; - } -} -EmulatedConsole* HIDCore::GetEmulatedConsole() { - return console.get(); -} - -const EmulatedConsole* HIDCore::GetEmulatedConsole() const { - return console.get(); -} - -EmulatedDevices* HIDCore::GetEmulatedDevices() { - return devices.get(); -} - -const EmulatedDevices* HIDCore::GetEmulatedDevices() const { - return devices.get(); -} - -EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { - return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); -} - -const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { - return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); -} - -void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { - supported_style_tag.raw = style_tag.raw; - player_1->SetSupportedNpadStyleTag(supported_style_tag); - player_2->SetSupportedNpadStyleTag(supported_style_tag); - player_3->SetSupportedNpadStyleTag(supported_style_tag); - player_4->SetSupportedNpadStyleTag(supported_style_tag); - player_5->SetSupportedNpadStyleTag(supported_style_tag); - player_6->SetSupportedNpadStyleTag(supported_style_tag); - player_7->SetSupportedNpadStyleTag(supported_style_tag); - player_8->SetSupportedNpadStyleTag(supported_style_tag); - other->SetSupportedNpadStyleTag(supported_style_tag); - handheld->SetSupportedNpadStyleTag(supported_style_tag); -} - -NpadStyleTag HIDCore::GetSupportedStyleTag() const { - return supported_style_tag; -} - -s8 HIDCore::GetPlayerCount() const { - s8 active_players = 0; - for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) { - const auto* const controller = GetEmulatedControllerByIndex(player_index); - if (controller->IsConnected()) { - active_players++; - } - } - return active_players; -} - -NpadIdType HIDCore::GetFirstNpadId() const { - for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { - const auto* const controller = GetEmulatedControllerByIndex(player_index); - if (controller->IsConnected()) { - return controller->GetNpadIdType(); - } - } - return NpadIdType::Player1; -} - -NpadIdType HIDCore::GetFirstDisconnectedNpadId() const { - for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { - const auto* const controller = GetEmulatedControllerByIndex(player_index); - if (!controller->IsConnected()) { - return controller->GetNpadIdType(); - } - } - return NpadIdType::Player1; -} - -void HIDCore::SetLastActiveController(NpadIdType npad_id) { - last_active_controller = npad_id; -} - -NpadIdType HIDCore::GetLastActiveController() const { - return last_active_controller; -} - -void HIDCore::EnableAllControllerConfiguration() { - player_1->EnableConfiguration(); - player_2->EnableConfiguration(); - player_3->EnableConfiguration(); - player_4->EnableConfiguration(); - player_5->EnableConfiguration(); - player_6->EnableConfiguration(); - player_7->EnableConfiguration(); - player_8->EnableConfiguration(); - other->EnableConfiguration(); - handheld->EnableConfiguration(); -} - -void HIDCore::DisableAllControllerConfiguration() { - player_1->DisableConfiguration(); - player_2->DisableConfiguration(); - player_3->DisableConfiguration(); - player_4->DisableConfiguration(); - player_5->DisableConfiguration(); - player_6->DisableConfiguration(); - player_7->DisableConfiguration(); - player_8->DisableConfiguration(); - other->DisableConfiguration(); - handheld->DisableConfiguration(); -} - -void HIDCore::ReloadInputDevices() { - player_1->ReloadFromSettings(); - player_2->ReloadFromSettings(); - player_3->ReloadFromSettings(); - player_4->ReloadFromSettings(); - player_5->ReloadFromSettings(); - player_6->ReloadFromSettings(); - player_7->ReloadFromSettings(); - player_8->ReloadFromSettings(); - other->ReloadFromSettings(); - handheld->ReloadFromSettings(); - console->ReloadFromSettings(); - devices->ReloadFromSettings(); -} - -void HIDCore::UnloadInputDevices() { - player_1->UnloadInput(); - player_2->UnloadInput(); - player_3->UnloadInput(); - player_4->UnloadInput(); - player_5->UnloadInput(); - player_6->UnloadInput(); - player_7->UnloadInput(); - player_8->UnloadInput(); - other->UnloadInput(); - handheld->UnloadInput(); - console->UnloadInput(); - devices->UnloadInput(); -} - -} // namespace Core::HID diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h deleted file mode 100644 index 80abab18b..000000000 --- a/src/core/hid/hid_core.h +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "common/common_funcs.h" -#include "core/hid/hid_types.h" - -namespace Core::HID { -class EmulatedConsole; -class EmulatedController; -class EmulatedDevices; -} // namespace Core::HID - -namespace Core::HID { - -class HIDCore { -public: - explicit HIDCore(); - ~HIDCore(); - - YUZU_NON_COPYABLE(HIDCore); - YUZU_NON_MOVEABLE(HIDCore); - - EmulatedController* GetEmulatedController(NpadIdType npad_id_type); - const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const; - - EmulatedController* GetEmulatedControllerByIndex(std::size_t index); - const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const; - - EmulatedConsole* GetEmulatedConsole(); - const EmulatedConsole* GetEmulatedConsole() const; - - EmulatedDevices* GetEmulatedDevices(); - const EmulatedDevices* GetEmulatedDevices() const; - - void SetSupportedStyleTag(NpadStyleTag style_tag); - NpadStyleTag GetSupportedStyleTag() const; - - /// Counts the connected players from P1-P8 - s8 GetPlayerCount() const; - - /// Returns the first connected npad id - NpadIdType GetFirstNpadId() const; - - /// Returns the first disconnected npad id - NpadIdType GetFirstDisconnectedNpadId() const; - - /// Sets the npad id of the last active controller - void SetLastActiveController(NpadIdType npad_id); - - /// Returns the npad id of the last controller that pushed a button - NpadIdType GetLastActiveController() const; - - /// Sets all emulated controllers into configuring mode. - void EnableAllControllerConfiguration(); - - /// Sets all emulated controllers into normal mode. - void DisableAllControllerConfiguration(); - - /// Reloads all input devices from settings - void ReloadInputDevices(); - - /// Removes all callbacks from input common - void UnloadInputDevices(); - - /// Number of emulated controllers - static constexpr std::size_t available_controllers{10}; - -private: - std::unique_ptr<EmulatedController> player_1; - std::unique_ptr<EmulatedController> player_2; - std::unique_ptr<EmulatedController> player_3; - std::unique_ptr<EmulatedController> player_4; - std::unique_ptr<EmulatedController> player_5; - std::unique_ptr<EmulatedController> player_6; - std::unique_ptr<EmulatedController> player_7; - std::unique_ptr<EmulatedController> player_8; - std::unique_ptr<EmulatedController> other; - std::unique_ptr<EmulatedController> handheld; - std::unique_ptr<EmulatedConsole> console; - std::unique_ptr<EmulatedDevices> devices; - NpadStyleTag supported_style_tag{NpadStyleSet::All}; - NpadIdType last_active_controller{NpadIdType::Handheld}; -}; - -} // namespace Core::HID diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h deleted file mode 100644 index 4bf285f36..000000000 --- a/src/core/hid/hid_types.h +++ /dev/null @@ -1,735 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/point.h" -#include "common/uuid.h" -#include "common/vector_math.h" - -namespace Core::HID { - -enum class DeviceIndex : u8 { - Left = 0, - Right = 1, - None = 2, - MaxDeviceIndex = 3, -}; - -// This is nn::hid::NpadButton -enum class NpadButton : u64 { - None = 0, - A = 1U << 0, - B = 1U << 1, - X = 1U << 2, - Y = 1U << 3, - StickL = 1U << 4, - StickR = 1U << 5, - L = 1U << 6, - R = 1U << 7, - ZL = 1U << 8, - ZR = 1U << 9, - Plus = 1U << 10, - Minus = 1U << 11, - - Left = 1U << 12, - Up = 1U << 13, - Right = 1U << 14, - Down = 1U << 15, - - StickLLeft = 1U << 16, - StickLUp = 1U << 17, - StickLRight = 1U << 18, - StickLDown = 1U << 19, - - StickRLeft = 1U << 20, - StickRUp = 1U << 21, - StickRRight = 1U << 22, - StickRDown = 1U << 23, - - LeftSL = 1U << 24, - LeftSR = 1U << 25, - - RightSL = 1U << 26, - RightSR = 1U << 27, - - Palma = 1U << 28, - Verification = 1U << 29, - HandheldLeftB = 1U << 30, - LagonCLeft = 1U << 31, - LagonCUp = 1ULL << 32, - LagonCRight = 1ULL << 33, - LagonCDown = 1ULL << 34, - - All = 0xFFFFFFFFFFFFFFFFULL, -}; -DECLARE_ENUM_FLAG_OPERATORS(NpadButton); - -enum class KeyboardKeyIndex : u32 { - A = 4, - B = 5, - C = 6, - D = 7, - E = 8, - F = 9, - G = 10, - H = 11, - I = 12, - J = 13, - K = 14, - L = 15, - M = 16, - N = 17, - O = 18, - P = 19, - Q = 20, - R = 21, - S = 22, - T = 23, - U = 24, - V = 25, - W = 26, - X = 27, - Y = 28, - Z = 29, - D1 = 30, - D2 = 31, - D3 = 32, - D4 = 33, - D5 = 34, - D6 = 35, - D7 = 36, - D8 = 37, - D9 = 38, - D0 = 39, - Return = 40, - Escape = 41, - Backspace = 42, - Tab = 43, - Space = 44, - Minus = 45, - Plus = 46, - OpenBracket = 47, - CloseBracket = 48, - Pipe = 49, - Tilde = 50, - Semicolon = 51, - Quote = 52, - Backquote = 53, - Comma = 54, - Period = 55, - Slash = 56, - CapsLock = 57, - F1 = 58, - F2 = 59, - F3 = 60, - F4 = 61, - F5 = 62, - F6 = 63, - F7 = 64, - F8 = 65, - F9 = 66, - F10 = 67, - F11 = 68, - F12 = 69, - PrintScreen = 70, - ScrollLock = 71, - Pause = 72, - Insert = 73, - Home = 74, - PageUp = 75, - Delete = 76, - End = 77, - PageDown = 78, - RightArrow = 79, - LeftArrow = 80, - DownArrow = 81, - UpArrow = 82, - NumLock = 83, - NumPadDivide = 84, - NumPadMultiply = 85, - NumPadSubtract = 86, - NumPadAdd = 87, - NumPadEnter = 88, - NumPad1 = 89, - NumPad2 = 90, - NumPad3 = 91, - NumPad4 = 92, - NumPad5 = 93, - NumPad6 = 94, - NumPad7 = 95, - NumPad8 = 96, - NumPad9 = 97, - NumPad0 = 98, - NumPadDot = 99, - Backslash = 100, - Application = 101, - Power = 102, - NumPadEquals = 103, - F13 = 104, - F14 = 105, - F15 = 106, - F16 = 107, - F17 = 108, - F18 = 109, - F19 = 110, - F20 = 111, - F21 = 112, - F22 = 113, - F23 = 114, - F24 = 115, - NumPadComma = 133, - Ro = 135, - KatakanaHiragana = 136, - Yen = 137, - Henkan = 138, - Muhenkan = 139, - NumPadCommaPc98 = 140, - HangulEnglish = 144, - Hanja = 145, - Katakana = 146, - Hiragana = 147, - ZenkakuHankaku = 148, - LeftControl = 224, - LeftShift = 225, - LeftAlt = 226, - LeftGui = 227, - RightControl = 228, - RightShift = 229, - RightAlt = 230, - RightGui = 231, -}; - -// This is nn::hid::NpadIdType -enum class NpadIdType : u32 { - Player1 = 0x0, - Player2 = 0x1, - Player3 = 0x2, - Player4 = 0x3, - Player5 = 0x4, - Player6 = 0x5, - Player7 = 0x6, - Player8 = 0x7, - Other = 0x10, - Handheld = 0x20, - - Invalid = 0xFFFFFFFF, -}; - -enum class NpadInterfaceType : u8 { - Bluetooth = 1, - Rail = 2, - Usb = 3, - Embedded = 4, -}; - -// This is nn::hid::NpadStyleIndex -enum class NpadStyleIndex : u8 { - None = 0, - ProController = 3, - Handheld = 4, - HandheldNES = 4, - JoyconDual = 5, - JoyconLeft = 6, - JoyconRight = 7, - GameCube = 8, - Pokeball = 9, - NES = 10, - SNES = 12, - N64 = 13, - SegaGenesis = 14, - SystemExt = 32, - System = 33, - MaxNpadType = 34, -}; - -// This is nn::hid::NpadStyleSet -enum class NpadStyleSet : u32 { - None = 0, - Fullkey = 1U << 0, - Handheld = 1U << 1, - JoyDual = 1U << 2, - JoyLeft = 1U << 3, - JoyRight = 1U << 4, - Gc = 1U << 5, - Palma = 1U << 6, - Lark = 1U << 7, - HandheldLark = 1U << 8, - Lucia = 1U << 9, - Lagoon = 1U << 10, - Lager = 1U << 11, - SystemExt = 1U << 29, - System = 1U << 30, - - All = 0xFFFFFFFFU, -}; -static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); - -// This is nn::hid::VibrationDevicePosition -enum class VibrationDevicePosition : u32 { - None = 0, - Left = 1, - Right = 2, -}; - -// This is nn::hid::VibrationDeviceType -enum class VibrationDeviceType : u32 { - Unknown = 0, - LinearResonantActuator = 1, - GcErm = 2, - N64 = 3, -}; - -// This is nn::hid::VibrationGcErmCommand -enum class VibrationGcErmCommand : u64 { - Stop = 0, - Start = 1, - StopHard = 2, -}; - -// This is nn::hid::GyroscopeZeroDriftMode -enum class GyroscopeZeroDriftMode : u32 { - Loose = 0, - Standard = 1, - Tight = 2, -}; - -// This is nn::settings::system::TouchScreenMode -enum class TouchScreenMode : u32 { - Stylus = 0, - Standard = 1, -}; - -// This is nn::hid::TouchScreenModeForNx -enum class TouchScreenModeForNx : u8 { - UseSystemSetting, - Finger, - Heat2, -}; - -// This is nn::hid::system::NpadBatteryLevel -enum class NpadBatteryLevel : u32 { - Empty, - Critical, - Low, - High, - Full, -}; - -// This is nn::hid::NpadStyleTag -struct NpadStyleTag { - union { - NpadStyleSet raw{}; - - BitField<0, 1, u32> fullkey; - BitField<1, 1, u32> handheld; - BitField<2, 1, u32> joycon_dual; - BitField<3, 1, u32> joycon_left; - BitField<4, 1, u32> joycon_right; - BitField<5, 1, u32> gamecube; - BitField<6, 1, u32> palma; - BitField<7, 1, u32> lark; - BitField<8, 1, u32> handheld_lark; - BitField<9, 1, u32> lucia; - BitField<10, 1, u32> lagoon; - BitField<11, 1, u32> lager; - BitField<29, 1, u32> system_ext; - BitField<30, 1, u32> system; - }; -}; -static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size"); - -// This is nn::hid::TouchAttribute -struct TouchAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> start_touch; - BitField<1, 1, u32> end_touch; - }; -}; -static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); - -// This is nn::hid::TouchState -struct TouchState { - u64 delta_time{}; - TouchAttribute attribute{}; - u32 finger{}; - Common::Point<u32> position{}; - u32 diameter_x{}; - u32 diameter_y{}; - u32 rotation_angle{}; -}; -static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); - -struct TouchFinger { - u64 last_touch{}; - Common::Point<float> position{}; - u32 id{}; - TouchAttribute attribute{}; - bool pressed{}; -}; - -// This is nn::hid::TouchScreenConfigurationForNx -struct TouchScreenConfigurationForNx { - TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; - INSERT_PADDING_BYTES(0xF); -}; -static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10, - "TouchScreenConfigurationForNx is an invalid size"); - -struct NpadColor { - u8 r{}; - u8 g{}; - u8 b{}; - u8 a{}; -}; -static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size"); - -// This is nn::hid::NpadControllerColor -struct NpadControllerColor { - NpadColor body{}; - NpadColor button{}; -}; -static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); - -// This is nn::hid::AnalogStickState -struct AnalogStickState { - s32 x{}; - s32 y{}; -}; -static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); - -// This is nn::hid::server::NpadGcTriggerState -struct NpadGcTriggerState { - s64 sampling_number{}; - s32 left{}; - s32 right{}; -}; -static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); - -// This is nn::hid::system::NpadPowerInfo -struct NpadPowerInfo { - bool is_powered{}; - bool is_charging{}; - INSERT_PADDING_BYTES(0x6); - NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; -}; -static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); - -struct LedPattern { - explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { - position1.Assign(light1); - position2.Assign(light2); - position3.Assign(light3); - position4.Assign(light4); - } - union { - u64 raw{}; - BitField<0, 1, u64> position1; - BitField<1, 1, u64> position2; - BitField<2, 1, u64> position3; - BitField<3, 1, u64> position4; - }; -}; - -struct HomeButtonState { - union { - u64 raw{}; - - // Buttons - BitField<0, 1, u64> home; - }; -}; -static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size."); - -struct CaptureButtonState { - union { - u64 raw{}; - - // Buttons - BitField<0, 1, u64> capture; - }; -}; -static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size."); - -struct NpadButtonState { - union { - NpadButton raw{}; - - // Buttons - BitField<0, 1, u64> a; - BitField<1, 1, u64> b; - BitField<2, 1, u64> x; - BitField<3, 1, u64> y; - BitField<4, 1, u64> stick_l; - BitField<5, 1, u64> stick_r; - BitField<6, 1, u64> l; - BitField<7, 1, u64> r; - BitField<8, 1, u64> zl; - BitField<9, 1, u64> zr; - BitField<10, 1, u64> plus; - BitField<11, 1, u64> minus; - - // D-Pad - BitField<12, 1, u64> left; - BitField<13, 1, u64> up; - BitField<14, 1, u64> right; - BitField<15, 1, u64> down; - - // Left JoyStick - BitField<16, 1, u64> stick_l_left; - BitField<17, 1, u64> stick_l_up; - BitField<18, 1, u64> stick_l_right; - BitField<19, 1, u64> stick_l_down; - - // Right JoyStick - BitField<20, 1, u64> stick_r_left; - BitField<21, 1, u64> stick_r_up; - BitField<22, 1, u64> stick_r_right; - BitField<23, 1, u64> stick_r_down; - - BitField<24, 1, u64> left_sl; - BitField<25, 1, u64> left_sr; - - BitField<26, 1, u64> right_sl; - BitField<27, 1, u64> right_sr; - - BitField<28, 1, u64> palma; - BitField<29, 1, u64> verification; - BitField<30, 1, u64> handheld_left_b; - BitField<31, 1, u64> lagon_c_left; - BitField<32, 1, u64> lagon_c_up; - BitField<33, 1, u64> lagon_c_right; - BitField<34, 1, u64> lagon_c_down; - }; -}; -static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size."); - -// This is nn::hid::DebugPadButton -struct DebugPadButton { - union { - u32 raw{}; - BitField<0, 1, u32> a; - BitField<1, 1, u32> b; - BitField<2, 1, u32> x; - BitField<3, 1, u32> y; - BitField<4, 1, u32> l; - BitField<5, 1, u32> r; - BitField<6, 1, u32> zl; - BitField<7, 1, u32> zr; - BitField<8, 1, u32> plus; - BitField<9, 1, u32> minus; - BitField<10, 1, u32> d_left; - BitField<11, 1, u32> d_up; - BitField<12, 1, u32> d_right; - BitField<13, 1, u32> d_down; - }; -}; -static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"); - -// This is nn::hid::ConsoleSixAxisSensorHandle -struct ConsoleSixAxisSensorHandle { - u8 unknown_1{}; - u8 unknown_2{}; - INSERT_PADDING_BYTES_NOINIT(2); -}; -static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, - "ConsoleSixAxisSensorHandle is an invalid size"); - -// This is nn::hid::SixAxisSensorHandle -struct SixAxisSensorHandle { - NpadStyleIndex npad_type{NpadStyleIndex::None}; - u8 npad_id{}; - DeviceIndex device_index{DeviceIndex::None}; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); - -// These parameters seem related to how much gyro/accelerometer is used -struct SixAxisSensorFusionParameters { - f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03 - f32 parameter2{0.4f}; // Default 0.4 -}; -static_assert(sizeof(SixAxisSensorFusionParameters) == 8, - "SixAxisSensorFusionParameters is an invalid size"); - -// This is nn::hid::server::SixAxisSensorProperties -struct SixAxisSensorProperties { - union { - u8 raw{}; - BitField<0, 1, u8> is_newly_assigned; - BitField<1, 1, u8> is_firmware_update_available; - }; -}; -static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size"); - -// This is nn::hid::SixAxisSensorCalibrationParameter -struct SixAxisSensorCalibrationParameter { - std::array<u8, 0x744> unknown_data{}; -}; -static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744, - "SixAxisSensorCalibrationParameter is an invalid size"); - -// This is nn::hid::SixAxisSensorIcInformation -struct SixAxisSensorIcInformation { - f32 angular_rate{2000.0f}; // dps - std::array<f32, 6> unknown_gyro_data1{ - -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f, - }; // dps - std::array<f32, 9> unknown_gyro_data2{ - 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, - }; - std::array<f32, 9> unknown_gyro_data3{ - 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, - }; - f32 acceleration_range{8.0f}; // g force - std::array<f32, 6> unknown_accel_data1{ - -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f, - }; // g force - std::array<f32, 9> unknown_accel_data2{ - 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, - }; - std::array<f32, 9> unknown_accel_data3{ - 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, - }; -}; -static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, - "SixAxisSensorIcInformation is an invalid size"); - -// This is nn::hid::SixAxisSensorAttribute -struct SixAxisSensorAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> is_connected; - BitField<1, 1, u32> is_interpolated; - }; -}; -static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); - -// This is nn::hid::SixAxisSensorState -struct SixAxisSensorState { - s64 delta_time{}; - s64 sampling_number{}; - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - std::array<Common::Vec3f, 3> orientation{}; - SixAxisSensorAttribute attribute{}; - INSERT_PADDING_BYTES(4); // Reserved -}; -static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); - -// This is nn::hid::VibrationDeviceHandle -struct VibrationDeviceHandle { - NpadStyleIndex npad_type{NpadStyleIndex::None}; - u8 npad_id{}; - DeviceIndex device_index{DeviceIndex::None}; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size"); - -// This is nn::hid::VibrationValue -struct VibrationValue { - f32 low_amplitude{}; - f32 low_frequency{}; - f32 high_amplitude{}; - f32 high_frequency{}; -}; -static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); - -constexpr VibrationValue DEFAULT_VIBRATION_VALUE{ - .low_amplitude = 0.0f, - .low_frequency = 160.0f, - .high_amplitude = 0.0f, - .high_frequency = 320.0f, -}; - -// This is nn::hid::VibrationDeviceInfo -struct VibrationDeviceInfo { - VibrationDeviceType type{}; - VibrationDevicePosition position{}; -}; -static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); - -// This is nn::hid::KeyboardModifier -struct KeyboardModifier { - union { - u32 raw{}; - BitField<0, 1, u32> control; - BitField<1, 1, u32> shift; - BitField<2, 1, u32> left_alt; - BitField<3, 1, u32> right_alt; - BitField<4, 1, u32> gui; - BitField<8, 1, u32> caps_lock; - BitField<9, 1, u32> scroll_lock; - BitField<10, 1, u32> num_lock; - BitField<11, 1, u32> katakana; - BitField<12, 1, u32> hiragana; - }; -}; - -static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size"); - -// This is nn::hid::KeyboardAttribute -struct KeyboardAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> is_connected; - }; -}; -static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size"); - -// This is nn::hid::KeyboardKey -struct KeyboardKey { - // This should be a 256 bit flag - std::array<u8, 32> key{}; -}; -static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); - -// This is nn::hid::MouseButton -struct MouseButton { - union { - u32_le raw{}; - BitField<0, 1, u32> left; - BitField<1, 1, u32> right; - BitField<2, 1, u32> middle; - BitField<3, 1, u32> forward; - BitField<4, 1, u32> back; - }; -}; -static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size"); - -// This is nn::hid::MouseAttribute -struct MouseAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> transferable; - BitField<1, 1, u32> is_connected; - }; -}; -static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"); - -// This is nn::hid::detail::MouseState -struct MouseState { - s64 sampling_number{}; - s32 x{}; - s32 y{}; - s32 delta_x{}; - s32 delta_y{}; - // Axis Order in HW is switched for the wheel - s32 delta_wheel_y{}; - s32 delta_wheel_x{}; - MouseButton button{}; - MouseAttribute attribute{}; -}; -static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); - -struct UniquePadId { - u64 id; -}; -static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); - -} // namespace Core::HID diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp deleted file mode 100644 index a05716fd8..000000000 --- a/src/core/hid/input_converter.cpp +++ /dev/null @@ -1,436 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <random> - -#include "common/input.h" -#include "core/hid/input_converter.h" - -namespace Core::HID { - -Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) { - Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None}; - switch (callback.type) { - case Common::Input::InputType::Analog: - case Common::Input::InputType::Trigger: { - const auto value = TransformToTrigger(callback).analog.value; - battery = Common::Input::BatteryLevel::Empty; - if (value > 0.2f) { - battery = Common::Input::BatteryLevel::Critical; - } - if (value > 0.4f) { - battery = Common::Input::BatteryLevel::Low; - } - if (value > 0.6f) { - battery = Common::Input::BatteryLevel::Medium; - } - if (value > 0.8f) { - battery = Common::Input::BatteryLevel::Full; - } - if (value >= 0.95f) { - battery = Common::Input::BatteryLevel::Charging; - } - break; - } - case Common::Input::InputType::Button: - battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging - : Common::Input::BatteryLevel::Critical; - break; - case Common::Input::InputType::Battery: - battery = callback.battery_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type); - break; - } - - return battery; -} - -Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) { - Common::Input::ButtonStatus status{}; - switch (callback.type) { - case Common::Input::InputType::Analog: - status.value = TransformToTrigger(callback).pressed.value; - status.toggle = callback.analog_status.properties.toggle; - status.inverted = callback.analog_status.properties.inverted_button; - break; - case Common::Input::InputType::Trigger: - status.value = TransformToTrigger(callback).pressed.value; - break; - case Common::Input::InputType::Button: - status = callback.button_status; - break; - case Common::Input::InputType::Motion: - status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type); - break; - } - - if (status.inverted) { - status.value = !status.value; - } - - return status; -} - -Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) { - Common::Input::MotionStatus status{}; - switch (callback.type) { - case Common::Input::InputType::Button: { - Common::Input::AnalogProperties properties{ - .deadzone = 0.0f, - .range = 1.0f, - .offset = 0.0f, - }; - status.delta_timestamp = 1000; - status.force_update = true; - status.accel.x = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.accel.y = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.accel.z = { - .value = 0.0f, - .raw_value = -1.0f, - .properties = properties, - }; - status.gyro.x = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.gyro.y = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.gyro.z = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - if (TransformToButton(callback).value) { - std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution<s16> distribution(-5000, 5000); - status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - } - break; - } - case Common::Input::InputType::Motion: - status = callback.motion_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type); - break; - } - SanitizeAnalog(status.accel.x, false); - SanitizeAnalog(status.accel.y, false); - SanitizeAnalog(status.accel.z, false); - SanitizeAnalog(status.gyro.x, false); - SanitizeAnalog(status.gyro.y, false); - SanitizeAnalog(status.gyro.z, false); - - return status; -} - -Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) { - Common::Input::StickStatus status{}; - - switch (callback.type) { - case Common::Input::InputType::Stick: - status = callback.stick_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type); - break; - } - - SanitizeStick(status.x, status.y, true); - const auto& properties_x = status.x.properties; - const auto& properties_y = status.y.properties; - const float x = status.x.value; - const float y = status.y.value; - - // Set directional buttons - status.right = x > properties_x.threshold; - status.left = x < -properties_x.threshold; - status.up = y > properties_y.threshold; - status.down = y < -properties_y.threshold; - - return status; -} - -Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) { - Common::Input::TouchStatus status{}; - - switch (callback.type) { - case Common::Input::InputType::Touch: - status = callback.touch_status; - break; - case Common::Input::InputType::Stick: - status.x = callback.stick_status.x; - status.y = callback.stick_status.y; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type); - break; - } - - SanitizeAnalog(status.x, true); - SanitizeAnalog(status.y, true); - float& x = status.x.value; - float& y = status.y.value; - - // Adjust if value is inverted - x = status.x.properties.inverted ? 1.0f + x : x; - y = status.y.properties.inverted ? 1.0f + y : y; - - // clamp value - x = std::clamp(x, 0.0f, 1.0f); - y = std::clamp(y, 0.0f, 1.0f); - - if (status.pressed.inverted) { - status.pressed.value = !status.pressed.value; - } - - return status; -} - -Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) { - Common::Input::TriggerStatus status{}; - float& raw_value = status.analog.raw_value; - bool calculate_button_value = true; - - switch (callback.type) { - case Common::Input::InputType::Analog: - status.analog.properties = callback.analog_status.properties; - raw_value = callback.analog_status.raw_value; - break; - case Common::Input::InputType::Button: - status.analog.properties.range = 1.0f; - status.analog.properties.inverted = callback.button_status.inverted; - raw_value = callback.button_status.value ? 1.0f : 0.0f; - break; - case Common::Input::InputType::Trigger: - status = callback.trigger_status; - calculate_button_value = false; - break; - case Common::Input::InputType::Motion: - status.analog.properties.range = 1.0f; - raw_value = callback.motion_status.accel.x.raw_value; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type); - break; - } - - SanitizeAnalog(status.analog, true); - const auto& properties = status.analog.properties; - float& value = status.analog.value; - - // Set button status - if (calculate_button_value) { - status.pressed.value = value > properties.threshold; - } - - // Adjust if value is inverted - value = properties.inverted ? 1.0f + value : value; - - // clamp value - value = std::clamp(value, 0.0f, 1.0f); - - return status; -} - -Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) { - Common::Input::AnalogStatus status{}; - - switch (callback.type) { - case Common::Input::InputType::Analog: - status.properties = callback.analog_status.properties; - status.raw_value = callback.analog_status.raw_value; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type); - break; - } - - SanitizeAnalog(status, false); - - // Adjust if value is inverted - status.value = status.properties.inverted ? -status.value : status.value; - - return status; -} - -Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) { - Common::Input::CameraStatus camera{}; - switch (callback.type) { - case Common::Input::InputType::IrSensor: - camera = { - .format = callback.camera_status, - .data = callback.raw_data, - }; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); - break; - } - - return camera; -} - -Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { - Common::Input::NfcStatus nfc{}; - switch (callback.type) { - case Common::Input::InputType::Nfc: - return callback.nfc_status; - default: - LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); - break; - } - - return nfc; -} - -Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) { - switch (callback.type) { - case Common::Input::InputType::Color: - return callback.color_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type); - return {}; - break; - } -} - -void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { - const auto& properties = analog.properties; - float& raw_value = analog.raw_value; - float& value = analog.value; - - if (!std::isnormal(raw_value)) { - raw_value = 0; - } - - // Apply center offset - raw_value -= properties.offset; - - // Set initial values to be formatted - value = raw_value; - - // Calculate vector size - const float r = std::abs(value); - - // Return zero if value is smaller than the deadzone - if (r <= properties.deadzone || properties.deadzone == 1.0f) { - analog.value = 0; - return; - } - - // Adjust range of value - const float deadzone_factor = - 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone); - value = value * deadzone_factor / properties.range; - - // Invert direction if needed - if (properties.inverted) { - value = -value; - } - - // Clamp value - if (clamp_value) { - value = std::clamp(value, -1.0f, 1.0f); - } -} - -void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, - bool clamp_value) { - const auto& properties_x = analog_x.properties; - const auto& properties_y = analog_y.properties; - float& raw_x = analog_x.raw_value; - float& raw_y = analog_y.raw_value; - float& x = analog_x.value; - float& y = analog_y.value; - - if (!std::isnormal(raw_x)) { - raw_x = 0; - } - if (!std::isnormal(raw_y)) { - raw_y = 0; - } - - // Apply center offset - raw_x += properties_x.offset; - raw_y += properties_y.offset; - - // Apply X scale correction from offset - if (std::abs(properties_x.offset) < 0.75f) { - if (raw_x > 0) { - raw_x /= 1 + properties_x.offset; - } else { - raw_x /= 1 - properties_x.offset; - } - } - - // Apply Y scale correction from offset - if (std::abs(properties_y.offset) < 0.75f) { - if (raw_y > 0) { - raw_y /= 1 + properties_y.offset; - } else { - raw_y /= 1 - properties_y.offset; - } - } - - // Invert direction if needed - raw_x = properties_x.inverted ? -raw_x : raw_x; - raw_y = properties_y.inverted ? -raw_y : raw_y; - - // Set initial values to be formatted - x = raw_x; - y = raw_y; - - // Calculate vector size - float r = x * x + y * y; - r = std::sqrt(r); - - // TODO(German77): Use deadzone and range of both axis - - // Return zero if values are smaller than the deadzone - if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) { - x = 0; - y = 0; - return; - } - - // Adjust range of joystick - const float deadzone_factor = - 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone); - x = x * deadzone_factor / properties_x.range; - y = y * deadzone_factor / properties_x.range; - r = r * deadzone_factor / properties_x.range; - - // Normalize joystick - if (clamp_value && r > 1.0f) { - x /= r; - y /= r; - } -} - -} // namespace Core::HID diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h deleted file mode 100644 index c51c03e57..000000000 --- a/src/core/hid/input_converter.h +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace Common::Input { -struct CallbackStatus; -enum class BatteryLevel : u32; -using BatteryStatus = BatteryLevel; -struct AnalogStatus; -struct ButtonStatus; -struct MotionStatus; -struct StickStatus; -struct TouchStatus; -struct TriggerStatus; -}; // namespace Common::Input - -namespace Core::HID { - -/** - * Converts raw input data into a valid battery status. - * - * @param callback Supported callbacks: Analog, Battery, Trigger. - * @return A valid BatteryStatus object. - */ -Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid button status. Applies invert properties to the output. - * - * @param callback Supported callbacks: Analog, Button, Trigger. - * @return A valid TouchStatus object. - */ -Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid motion status. - * - * @param callback Supported callbacks: Motion. - * @return A valid TouchStatus object. - */ -Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert - * properties to the output. - * - * @param callback Supported callbacks: Stick. - * @return A valid StickStatus object. - */ -Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid touch status. - * - * @param callback Supported callbacks: Touch. - * @return A valid TouchStatus object. - */ -Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and - * invert properties to the output. Button status uses the threshold property if necessary. - * - * @param callback Supported callbacks: Analog, Button, Trigger. - * @return A valid TriggerStatus object. - */ -Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid analog status. Applies offset, deadzone, range and - * invert properties to the output. - * - * @param callback Supported callbacks: Analog. - * @return A valid AnalogStatus object. - */ -Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid camera status. - * - * @param callback Supported callbacks: Camera. - * @return A valid CameraObject object. - */ -Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid nfc status. - * - * @param callback Supported callbacks: Nfc. - * @return A valid data tag vector. - */ -Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid color status. - * - * @param callback Supported callbacks: Color. - * @return A valid Color object. - */ -Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw analog data into a valid analog value - * @param analog An analog object containing raw data and properties - * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. - */ -void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value); - -/** - * Converts raw stick data into a valid stick value - * @param analog_x raw analog data and properties for the x-axis - * @param analog_y raw analog data and properties for the y-axis - * @param clamp_value bool that determines if the value needs to be clamped into the unit circle. - */ -void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, - bool clamp_value); - -} // namespace Core::HID diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp deleted file mode 100644 index 072f38a68..000000000 --- a/src/core/hid/input_interpreter.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/hid/hid_types.h" -#include "core/hid/input_interpreter.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid_server.h" -#include "core/hle/service/hid/resource_manager.h" -#include "core/hle/service/sm/sm.h" - -InputInterpreter::InputInterpreter(Core::System& system) - : npad{system.ServiceManager() - .GetService<Service::HID::IHidServer>("hid") - ->GetResourceManager() - ->GetNpad()} { - ResetButtonStates(); -} - -InputInterpreter::~InputInterpreter() = default; - -void InputInterpreter::PollInput() { - if (npad == nullptr) { - return; - } - const auto button_state = npad->GetAndResetPressState(); - - previous_index = current_index; - current_index = (current_index + 1) % button_states.size(); - - button_states[current_index] = button_state; -} - -void InputInterpreter::ResetButtonStates() { - previous_index = 0; - current_index = 0; - - button_states[0] = Core::HID::NpadButton::All; - - for (std::size_t i = 1; i < button_states.size(); ++i) { - button_states[i] = Core::HID::NpadButton::None; - } -} - -bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const { - return True(button_states[current_index] & button); -} - -bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const { - const bool current_press = True(button_states[current_index] & button); - const bool previous_press = True(button_states[previous_index] & button); - - return current_press && !previous_press; -} - -bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const { - Core::HID::NpadButton held_buttons{button_states[0]}; - - for (std::size_t i = 1; i < button_states.size(); ++i) { - held_buttons &= button_states[i]; - } - - return True(held_buttons & button); -} diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h deleted file mode 100644 index 3569aac93..000000000 --- a/src/core/hid/input_interpreter.h +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_types.h" - -namespace Core { -class System; -} - -namespace Core::HID { -enum class NpadButton : u64; -} - -namespace Service::HID { -class NPad; -} - -/** - * The InputInterpreter class interfaces with HID to retrieve button press states. - * Input is intended to be polled every 50ms so that a button is considered to be - * held down after 400ms has elapsed since the initial button press and subsequent - * repeated presses occur every 50ms. - */ -class InputInterpreter { -public: - explicit InputInterpreter(Core::System& system); - virtual ~InputInterpreter(); - - /// Gets a button state from HID and inserts it into the array of button states. - void PollInput(); - - /// Resets all the button states to their defaults. - void ResetButtonStates(); - - /** - * Checks whether the button is pressed. - * - * @param button The button to check. - * - * @returns True when the button is pressed. - */ - [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const; - - /** - * Checks whether any of the buttons in the parameter list is pressed. - * - * @tparam HIDButton The buttons to check. - * - * @returns True when at least one of the buttons is pressed. - */ - template <Core::HID::NpadButton... T> - [[nodiscard]] bool IsAnyButtonPressed() { - return (IsButtonPressed(T) || ...); - } - - /** - * The specified button is considered to be pressed once - * if it is currently pressed and not pressed previously. - * - * @param button The button to check. - * - * @returns True when the button is pressed once. - */ - [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const; - - /** - * Checks whether any of the buttons in the parameter list is pressed once. - * - * @tparam T The buttons to check. - * - * @returns True when at least one of the buttons is pressed once. - */ - template <Core::HID::NpadButton... T> - [[nodiscard]] bool IsAnyButtonPressedOnce() const { - return (IsButtonPressedOnce(T) || ...); - } - - /** - * The specified button is considered to be held down if it is pressed in all 9 button states. - * - * @param button The button to check. - * - * @returns True when the button is held down. - */ - [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const; - - /** - * Checks whether any of the buttons in the parameter list is held down. - * - * @tparam T The buttons to check. - * - * @returns True when at least one of the buttons is held down. - */ - template <Core::HID::NpadButton... T> - [[nodiscard]] bool IsAnyButtonHeld() const { - return (IsButtonHeld(T) || ...); - } - -private: - std::shared_ptr<Service::HID::NPad> npad; - - /// Stores 9 consecutive button states polled from HID. - std::array<Core::HID::NpadButton, 9> button_states{}; - - std::size_t previous_index{}; - std::size_t current_index{}; -}; diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h deleted file mode 100644 index 0d1bfe53f..000000000 --- a/src/core/hid/irs_types.h +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hid/hid_types.h" - -namespace Core::IrSensor { - -// This is nn::irsensor::CameraAmbientNoiseLevel -enum class CameraAmbientNoiseLevel : u32 { - Low, - Medium, - High, - Unknown3, // This level can't be reached -}; - -// This is nn::irsensor::CameraLightTarget -enum class CameraLightTarget : u32 { - AllLeds, - BrightLeds, - DimLeds, - None, -}; - -// This is nn::irsensor::PackedCameraLightTarget -enum class PackedCameraLightTarget : u8 { - AllLeds, - BrightLeds, - DimLeds, - None, -}; - -// This is nn::irsensor::AdaptiveClusteringMode -enum class AdaptiveClusteringMode : u32 { - StaticFov, - DynamicFov, -}; - -// This is nn::irsensor::AdaptiveClusteringTargetDistance -enum class AdaptiveClusteringTargetDistance : u32 { - Near, - Middle, - Far, -}; - -// This is nn::irsensor::ImageTransferProcessorFormat -enum class ImageTransferProcessorFormat : u32 { - Size320x240, - Size160x120, - Size80x60, - Size40x30, - Size20x15, -}; - -// This is nn::irsensor::PackedImageTransferProcessorFormat -enum class PackedImageTransferProcessorFormat : u8 { - Size320x240, - Size160x120, - Size80x60, - Size40x30, - Size20x15, -}; - -// This is nn::irsensor::IrCameraStatus -enum class IrCameraStatus : u32 { - Available, - Unsupported, - Unconnected, -}; - -// This is nn::irsensor::IrCameraInternalStatus -enum class IrCameraInternalStatus : u32 { - Stopped, - FirmwareUpdateNeeded, - Unknown2, - Unknown3, - Unknown4, - FirmwareVersionRequested, - FirmwareVersionIsInvalid, - Ready, - Setting, -}; - -// This is nn::irsensor::detail::StatusManager::IrSensorMode -enum class IrSensorMode : u64 { - None, - MomentProcessor, - ClusteringProcessor, - ImageTransferProcessor, - PointingProcessorMarker, - TeraPluginProcessor, - IrLedProcessor, -}; - -// This is nn::irsensor::ImageProcessorStatus -enum ImageProcessorStatus : u32 { - Stopped, - Running, -}; - -// This is nn::irsensor::HandAnalysisMode -enum class HandAnalysisMode : u32 { - None, - Silhouette, - Image, - SilhoueteAndImage, - SilhuetteOnly, -}; - -// This is nn::irsensor::IrSensorFunctionLevel -enum class IrSensorFunctionLevel : u8 { - unknown0, - unknown1, - unknown2, - unknown3, - unknown4, -}; - -// This is nn::irsensor::MomentProcessorPreprocess -enum class MomentProcessorPreprocess : u32 { - Unknown0, - Unknown1, -}; - -// This is nn::irsensor::PackedMomentProcessorPreprocess -enum class PackedMomentProcessorPreprocess : u8 { - Unknown0, - Unknown1, -}; - -// This is nn::irsensor::PointingStatus -enum class PointingStatus : u32 { - Unknown0, - Unknown1, -}; - -struct IrsRect { - s16 x; - s16 y; - s16 width; - s16 height; -}; - -struct IrsCentroid { - f32 x; - f32 y; -}; - -struct CameraConfig { - u64 exposure_time; - CameraLightTarget light_target; - u32 gain; - bool is_negative_used; - INSERT_PADDING_BYTES(7); -}; -static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); - -struct PackedCameraConfig { - u64 exposure_time; - PackedCameraLightTarget light_target; - u8 gain; - bool is_negative_used; - INSERT_PADDING_BYTES(5); -}; -static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); - -// This is nn::irsensor::IrCameraHandle -struct IrCameraHandle { - u8 npad_id{}; - Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); - -// This is nn::irsensor::PackedMcuVersion -struct PackedMcuVersion { - u16 major; - u16 minor; -}; -static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); - -// This is nn::irsensor::PackedMomentProcessorConfig -struct PackedMomentProcessorConfig { - PackedCameraConfig camera_config; - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - PackedMomentProcessorPreprocess preprocess; - u8 preprocess_intensity_threshold; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, - "PackedMomentProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedClusteringProcessorConfig -struct PackedClusteringProcessorConfig { - PackedCameraConfig camera_config; - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - u32 pixel_count_min; - u32 pixel_count_max; - u8 object_intensity_min; - bool is_external_light_filter_enabled; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, - "PackedClusteringProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedImageTransferProcessorConfig -struct PackedImageTransferProcessorConfig { - PackedCameraConfig camera_config; - PackedMcuVersion required_mcu_version; - PackedImageTransferProcessorFormat format; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, - "PackedImageTransferProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedTeraPluginProcessorConfig -struct PackedTeraPluginProcessorConfig { - PackedMcuVersion required_mcu_version; - u8 mode; - u8 unknown_1; - u8 unknown_2; - u8 unknown_3; -}; -static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, - "PackedTeraPluginProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedPointingProcessorConfig -struct PackedPointingProcessorConfig { - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; -}; -static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, - "PackedPointingProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedFunctionLevel -struct PackedFunctionLevel { - IrSensorFunctionLevel function_level; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); - -// This is nn::irsensor::PackedImageTransferProcessorExConfig -struct PackedImageTransferProcessorExConfig { - PackedCameraConfig camera_config; - PackedMcuVersion required_mcu_version; - PackedImageTransferProcessorFormat origin_format; - PackedImageTransferProcessorFormat trimming_format; - u16 trimming_start_x; - u16 trimming_start_y; - bool is_external_light_filter_enabled; - INSERT_PADDING_BYTES(5); -}; -static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, - "PackedImageTransferProcessorExConfig is an invalid size"); - -// This is nn::irsensor::PackedIrLedProcessorConfig -struct PackedIrLedProcessorConfig { - PackedMcuVersion required_mcu_version; - u8 light_target; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, - "PackedIrLedProcessorConfig is an invalid size"); - -// This is nn::irsensor::HandAnalysisConfig -struct HandAnalysisConfig { - HandAnalysisMode mode; -}; -static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); - -// This is nn::irsensor::detail::ProcessorState contents are different for each processor -struct ProcessorState { - std::array<u8, 0xE20> processor_raw_data{}; -}; -static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); - -// This is nn::irsensor::detail::DeviceFormat -struct DeviceFormat { - Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected}; - Core::IrSensor::IrCameraInternalStatus camera_internal_status{ - Core::IrSensor::IrCameraInternalStatus::Ready}; - Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None}; - ProcessorState state{}; -}; -static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); - -// This is nn::irsensor::ImageTransferProcessorState -struct ImageTransferProcessorState { - u64 sampling_number; - Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; - INSERT_PADDING_BYTES(4); -}; -static_assert(sizeof(ImageTransferProcessorState) == 0x10, - "ImageTransferProcessorState is an invalid size"); - -} // namespace Core::IrSensor diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp deleted file mode 100644 index f56f2ae1d..000000000 --- a/src/core/hid/motion_input.cpp +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <cmath> - -#include "common/math_util.h" -#include "core/hid/motion_input.h" - -namespace Core::HID { - -MotionInput::MotionInput() { - // Initialize PID constants with default values - SetPID(0.3f, 0.005f, 0.0f); - SetGyroThreshold(ThresholdStandard); - ResetQuaternion(); - ResetRotations(); -} - -void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { - kp = new_kp; - ki = new_ki; - kd = new_kd; -} - -void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { - accel = acceleration; - - accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue); - accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue); - accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue); -} - -void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { - gyro = gyroscope - gyro_bias; - - gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue); - gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue); - gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue); - - // Auto adjust gyro_bias to minimize drift - if (!IsMoving(IsAtRestRelaxed)) { - gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f); - } - - // Adjust drift when calibration mode is enabled - if (calibration_mode) { - gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f); - StopCalibration(); - } - - if (gyro.Length() < gyro_threshold * user_gyro_threshold) { - gyro = {}; - } else { - only_accelerometer = false; - } -} - -void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) { - quat = quaternion; -} - -void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) { - const float cr = std::cos(euler_angles.x * 0.5f); - const float sr = std::sin(euler_angles.x * 0.5f); - const float cp = std::cos(euler_angles.y * 0.5f); - const float sp = std::sin(euler_angles.y * 0.5f); - const float cy = std::cos(euler_angles.z * 0.5f); - const float sy = std::sin(euler_angles.z * 0.5f); - - quat.w = cr * cp * cy + sr * sp * sy; - quat.xyz.x = sr * cp * cy - cr * sp * sy; - quat.xyz.y = cr * sp * cy + sr * cp * sy; - quat.xyz.z = cr * cp * sy - sr * sp * cy; -} - -void MotionInput::SetGyroBias(const Common::Vec3f& bias) { - gyro_bias = bias; -} - -void MotionInput::SetGyroThreshold(f32 threshold) { - gyro_threshold = threshold; -} - -void MotionInput::SetUserGyroThreshold(f32 threshold) { - user_gyro_threshold = threshold / ThresholdStandard; -} - -void MotionInput::EnableReset(bool reset) { - reset_enabled = reset; -} - -void MotionInput::ResetRotations() { - rotations = {}; -} - -void MotionInput::ResetQuaternion() { - quat = {{0.0f, 0.0f, -1.0f}, 0.0f}; -} - -bool MotionInput::IsMoving(f32 sensitivity) const { - return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f; -} - -bool MotionInput::IsCalibrated(f32 sensitivity) const { - return real_error.Length() < sensitivity; -} - -void MotionInput::UpdateRotation(u64 elapsed_time) { - const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; - if (sample_period > 0.1f) { - return; - } - rotations += gyro * sample_period; -} - -void MotionInput::Calibrate() { - calibration_mode = true; - calibration_counter = 0; -} - -void MotionInput::StopCalibration() { - if (calibration_counter++ > CalibrationSamples) { - calibration_mode = false; - ResetQuaternion(); - ResetRotations(); - } -} - -// Based on Madgwick's implementation of Mayhony's AHRS algorithm. -// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs -void MotionInput::UpdateOrientation(u64 elapsed_time) { - if (!IsCalibrated(0.1f)) { - ResetOrientation(); - } - // Short name local variable for readability - f32 q1 = quat.w; - f32 q2 = quat.xyz[0]; - f32 q3 = quat.xyz[1]; - f32 q4 = quat.xyz[2]; - const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; - - // Ignore invalid elapsed time - if (sample_period > 0.1f) { - return; - } - - const auto normal_accel = accel.Normalized(); - auto rad_gyro = gyro * Common::PI * 2; - const f32 swap = rad_gyro.x; - rad_gyro.x = rad_gyro.y; - rad_gyro.y = -swap; - rad_gyro.z = -rad_gyro.z; - - // Clear gyro values if there is no gyro present - if (only_accelerometer) { - rad_gyro.x = 0; - rad_gyro.y = 0; - rad_gyro.z = 0; - } - - // Ignore drift correction if acceleration is not reliable - if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) { - const f32 ax = -normal_accel.x; - const f32 ay = normal_accel.y; - const f32 az = -normal_accel.z; - - // Estimated direction of gravity - const f32 vx = 2.0f * (q2 * q4 - q1 * q3); - const f32 vy = 2.0f * (q1 * q2 + q3 * q4); - const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; - - // Error is cross product between estimated direction and measured direction of gravity - const Common::Vec3f new_real_error = { - az * vx - ax * vz, - ay * vz - az * vy, - ax * vy - ay * vx, - }; - - derivative_error = new_real_error - real_error; - real_error = new_real_error; - - // Prevent integral windup - if (ki != 0.0f && !IsCalibrated(0.05f)) { - integral_error += real_error; - } else { - integral_error = {}; - } - - // Apply feedback terms - if (!only_accelerometer) { - rad_gyro += kp * real_error; - rad_gyro += ki * integral_error; - rad_gyro += kd * derivative_error; - } else { - // Give more weight to accelerometer values to compensate for the lack of gyro - rad_gyro += 35.0f * kp * real_error; - rad_gyro += 10.0f * ki * integral_error; - rad_gyro += 10.0f * kd * derivative_error; - - // Emulate gyro values for games that need them - gyro.x = -rad_gyro.y; - gyro.y = rad_gyro.x; - gyro.z = -rad_gyro.z; - UpdateRotation(elapsed_time); - } - } - - const f32 gx = rad_gyro.y; - const f32 gy = rad_gyro.x; - const f32 gz = rad_gyro.z; - - // Integrate rate of change of quaternion - const f32 pa = q2; - const f32 pb = q3; - const f32 pc = q4; - q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period); - q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period); - q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); - q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); - - quat.w = q1; - quat.xyz[0] = q2; - quat.xyz[1] = q3; - quat.xyz[2] = q4; - quat = quat.Normalized(); -} - -std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const { - const Common::Quaternion<float> quad{ - .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w}, - .w = -quat.xyz[2], - }; - const std::array<float, 16> matrix4x4 = quad.ToMatrix(); - - return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]), - Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), - Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])}; -} - -Common::Vec3f MotionInput::GetAcceleration() const { - return accel; -} - -Common::Vec3f MotionInput::GetGyroscope() const { - return gyro; -} - -Common::Vec3f MotionInput::GetGyroBias() const { - return gyro_bias; -} - -Common::Quaternion<f32> MotionInput::GetQuaternion() const { - return quat; -} - -Common::Vec3f MotionInput::GetRotations() const { - return rotations; -} - -Common::Vec3f MotionInput::GetEulerAngles() const { - // roll (x-axis rotation) - const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z); - const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y); - - // pitch (y-axis rotation) - const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z)); - const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z)); - - // yaw (z-axis rotation) - const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y); - const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z); - - return { - std::atan2(sinr_cosp, cosr_cosp), - 2 * std::atan2(sinp, cosp) - Common::PI / 2, - std::atan2(siny_cosp, cosy_cosp), - }; -} - -void MotionInput::ResetOrientation() { - if (!reset_enabled || only_accelerometer) { - return; - } - if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) { - ++reset_counter; - if (reset_counter > 900) { - quat.w = 0; - quat.xyz[0] = 0; - quat.xyz[1] = 0; - quat.xyz[2] = -1; - SetOrientationFromAccelerometer(); - integral_error = {}; - reset_counter = 0; - } - } else { - reset_counter = 0; - } -} - -void MotionInput::SetOrientationFromAccelerometer() { - int iterations = 0; - const f32 sample_period = 0.015f; - - const auto normal_accel = accel.Normalized(); - - while (!IsCalibrated(0.01f) && ++iterations < 100) { - // Short name local variable for readability - f32 q1 = quat.w; - f32 q2 = quat.xyz[0]; - f32 q3 = quat.xyz[1]; - f32 q4 = quat.xyz[2]; - - Common::Vec3f rad_gyro; - const f32 ax = -normal_accel.x; - const f32 ay = normal_accel.y; - const f32 az = -normal_accel.z; - - // Estimated direction of gravity - const f32 vx = 2.0f * (q2 * q4 - q1 * q3); - const f32 vy = 2.0f * (q1 * q2 + q3 * q4); - const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; - - // Error is cross product between estimated direction and measured direction of gravity - const Common::Vec3f new_real_error = { - az * vx - ax * vz, - ay * vz - az * vy, - ax * vy - ay * vx, - }; - - derivative_error = new_real_error - real_error; - real_error = new_real_error; - - rad_gyro += 10.0f * kp * real_error; - rad_gyro += 5.0f * ki * integral_error; - rad_gyro += 10.0f * kd * derivative_error; - - const f32 gx = rad_gyro.y; - const f32 gy = rad_gyro.x; - const f32 gz = rad_gyro.z; - - // Integrate rate of change of quaternion - const f32 pa = q2; - const f32 pb = q3; - const f32 pc = q4; - q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period); - q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period); - q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); - q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); - - quat.w = q1; - quat.xyz[0] = q2; - quat.xyz[1] = q3; - quat.xyz[2] = q4; - quat = quat.Normalized(); - } -} -} // namespace Core::HID diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h deleted file mode 100644 index 11678983d..000000000 --- a/src/core/hid/motion_input.h +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "common/quaternion.h" -#include "common/vector_math.h" - -namespace Core::HID { - -class MotionInput { -public: - static constexpr float ThresholdLoose = 0.01f; - static constexpr float ThresholdStandard = 0.007f; - static constexpr float ThresholdThight = 0.002f; - - static constexpr float IsAtRestRelaxed = 0.05f; - static constexpr float IsAtRestLoose = 0.02f; - static constexpr float IsAtRestStandard = 0.01f; - static constexpr float IsAtRestThight = 0.005f; - - static constexpr float GyroMaxValue = 5.0f; - static constexpr float AccelMaxValue = 7.0f; - - static constexpr std::size_t CalibrationSamples = 300; - - explicit MotionInput(); - - MotionInput(const MotionInput&) = default; - MotionInput& operator=(const MotionInput&) = default; - - MotionInput(MotionInput&&) = default; - MotionInput& operator=(MotionInput&&) = default; - - void SetPID(f32 new_kp, f32 new_ki, f32 new_kd); - void SetAcceleration(const Common::Vec3f& acceleration); - void SetGyroscope(const Common::Vec3f& gyroscope); - void SetQuaternion(const Common::Quaternion<f32>& quaternion); - void SetEulerAngles(const Common::Vec3f& euler_angles); - void SetGyroBias(const Common::Vec3f& bias); - void SetGyroThreshold(f32 threshold); - - /// Applies a modifier on top of the normal gyro threshold - void SetUserGyroThreshold(f32 threshold); - - void EnableReset(bool reset); - void ResetRotations(); - void ResetQuaternion(); - - void UpdateRotation(u64 elapsed_time); - void UpdateOrientation(u64 elapsed_time); - - void Calibrate(); - - [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; - [[nodiscard]] Common::Vec3f GetAcceleration() const; - [[nodiscard]] Common::Vec3f GetGyroscope() const; - [[nodiscard]] Common::Vec3f GetGyroBias() const; - [[nodiscard]] Common::Vec3f GetRotations() const; - [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; - [[nodiscard]] Common::Vec3f GetEulerAngles() const; - - [[nodiscard]] bool IsMoving(f32 sensitivity) const; - [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; - -private: - void StopCalibration(); - void ResetOrientation(); - void SetOrientationFromAccelerometer(); - - // PID constants - f32 kp; - f32 ki; - f32 kd; - - // PID errors - Common::Vec3f real_error; - Common::Vec3f integral_error; - Common::Vec3f derivative_error; - - // Quaternion containing the device orientation - Common::Quaternion<f32> quat; - - // Number of full rotations in each axis - Common::Vec3f rotations; - - // Acceleration vector measurement in G force - Common::Vec3f accel; - - // Gyroscope vector measurement in radians/s. - Common::Vec3f gyro; - - // Vector to be subtracted from gyro measurements - Common::Vec3f gyro_bias; - - // Minimum gyro amplitude to detect if the device is moving - f32 gyro_threshold = 0.0f; - - // Multiplies gyro_threshold by this value - f32 user_gyro_threshold = 0.0f; - - // Number of invalid sequential data - u32 reset_counter = 0; - - // If the provided data is invalid the device will be autocalibrated - bool reset_enabled = true; - - // Use accelerometer values to calculate position - bool only_accelerometer = true; - - // When enabled it will aggressively adjust for gyro drift - bool calibration_mode = false; - - // Used to auto disable calibration mode - std::size_t calibration_counter = 0; -}; - -} // namespace Core::HID diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index ef3f61321..d2b7e9a66 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -81,12 +81,12 @@ enum class KMemoryState : u32 { ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped, - Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | - FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | - FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | + FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | + FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, - SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | - FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | + FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc | @@ -130,8 +130,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09); static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A); static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B); static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C); -static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D); -static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E); +static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x055C3C0D); +static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x045C380E); static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F); static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811); diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp index 58a1e7216..f08a6e448 100644 --- a/src/core/hle/kernel/k_memory_block_manager.cpp +++ b/src/core/hle/kernel/k_memory_block_manager.cpp @@ -28,14 +28,14 @@ Result KMemoryBlockManager::Initialize(KProcessAddress st, KProcessAddress nd, } void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, - HostUnmapCallback&& host_unmap_callback) { + BlockCallback&& block_callback) { // Erase every block until we have none left. auto it = m_memory_block_tree.begin(); while (it != m_memory_block_tree.end()) { KMemoryBlock* block = std::addressof(*it); it = m_memory_block_tree.erase(it); + block_callback(block->GetAddress(), block->GetSize()); slab_manager->Free(block); - host_unmap_callback(block->GetAddress(), block->GetSize()); } ASSERT(m_memory_block_tree.empty()); diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index cb7b6f430..377628504 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -85,11 +85,11 @@ public: public: KMemoryBlockManager(); - using HostUnmapCallback = std::function<void(Common::ProcessAddress, u64)>; + using BlockCallback = std::function<void(Common::ProcessAddress, u64)>; Result Initialize(KProcessAddress st, KProcessAddress nd, KMemoryBlockSlabManager* slab_manager); - void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback); + void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback); iterator end() { return m_memory_block_tree.end(); diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 8c1549559..1dd86fb3c 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -69,9 +69,14 @@ public: }; template <typename AddressType> -void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size) { +void InvalidateInstructionCache(KernelCore& kernel, KPageTableBase* table, AddressType addr, + u64 size) { // TODO: lock the process list for (auto& process : kernel.GetProcessList()) { + if (std::addressof(process->GetPageTable().GetBasePageTable()) != table) { + continue; + } + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { auto* interface = process->GetArmInterface(i); if (interface) { @@ -431,15 +436,43 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool m_memory_block_slab_manager)); } +Result KPageTableBase::FinalizeProcess() { + // Only process tables should be finalized. + ASSERT(!this->IsKernel()); + + // NOTE: Here Nintendo calls an unknown OnFinalize function. + // this->OnFinalize(); + + // NOTE: Here Nintendo calls a second unknown OnFinalize function. + // this->OnFinalize2(); + + // NOTE: Here Nintendo does a page table walk to discover heap pages to free. + // We will use the block manager finalization below to free them. + + R_SUCCEED(); +} + void KPageTableBase::Finalize() { - auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { - if (Settings::IsFastmemEnabled()) { + this->FinalizeProcess(); + + auto BlockCallback = [&](KProcessAddress addr, u64 size) { + if (m_impl->fastmem_arena) { m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); } + + // Get physical pages. + KPageGroup pg(m_kernel, m_block_info_manager); + this->MakePageGroup(pg, addr, size / PageSize); + + // Free the pages. + pg.CloseAndReset(); }; // Finalize memory blocks. - m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); + { + KScopedLightLock lk(m_general_lock); + m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(BlockCallback)); + } // Free any unsafe mapped memory. if (m_mapped_unsafe_physical_memory) { @@ -486,8 +519,8 @@ KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const { case Svc::MemoryState::Shared: case Svc::MemoryState::AliasCode: case Svc::MemoryState::AliasCodeData: - case Svc::MemoryState::Transfered: - case Svc::MemoryState::SharedTransfered: + case Svc::MemoryState::Transferred: + case Svc::MemoryState::SharedTransferred: case Svc::MemoryState::SharedCode: case Svc::MemoryState::GeneratedCode: case Svc::MemoryState::CodeOut: @@ -522,8 +555,8 @@ size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const { case Svc::MemoryState::Shared: case Svc::MemoryState::AliasCode: case Svc::MemoryState::AliasCodeData: - case Svc::MemoryState::Transfered: - case Svc::MemoryState::SharedTransfered: + case Svc::MemoryState::Transferred: + case Svc::MemoryState::SharedTransferred: case Svc::MemoryState::SharedCode: case Svc::MemoryState::GeneratedCode: case Svc::MemoryState::CodeOut: @@ -564,8 +597,8 @@ bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemorySt case Svc::MemoryState::AliasCodeData: case Svc::MemoryState::Stack: case Svc::MemoryState::ThreadLocal: - case Svc::MemoryState::Transfered: - case Svc::MemoryState::SharedTransfered: + case Svc::MemoryState::Transferred: + case Svc::MemoryState::SharedTransferred: case Svc::MemoryState::SharedCode: case Svc::MemoryState::GeneratedCode: case Svc::MemoryState::CodeOut: @@ -1274,7 +1307,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr bool reprotected_pages = false; SCOPE_EXIT({ if (reprotected_pages && any_code_pages) { - InvalidateInstructionCache(m_kernel, dst_address, size); + InvalidateInstructionCache(m_kernel, this, dst_address, size); } }); @@ -2008,7 +2041,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s for (const auto& block : pg) { StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize()); } - InvalidateInstructionCache(m_kernel, addr, size); + InvalidateInstructionCache(m_kernel, this, addr, size); } R_SUCCEED(); @@ -3249,7 +3282,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd R_TRY(PerformCopy()); // Invalidate the instruction cache, as this svc allows modifying executable pages. - InvalidateInstructionCache(m_kernel, dst_address, size); + InvalidateInstructionCache(m_kernel, this, dst_address, size); R_SUCCEED(); } diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h index 077cafc96..748419f86 100644 --- a/src/core/hle/kernel/k_page_table_base.h +++ b/src/core/hle/kernel/k_page_table_base.h @@ -241,6 +241,7 @@ public: KResourceLimit* resource_limit, Core::Memory::Memory& memory, KProcessAddress aslr_space_start); + Result FinalizeProcess(); void Finalize(); bool IsKernel() const { diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 068e71dff..0b08e877e 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -5,6 +5,7 @@ #include "common/scope_exit.h" #include "common/settings.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" @@ -171,6 +172,12 @@ void KProcess::Finalize() { m_resource_limit->Close(); } + // Clear expensive resources, as the destructor is not called for guest objects. + for (auto& interface : m_arm_interfaces) { + interface.reset(); + } + m_exclusive_monitor.reset(); + // Perform inherited finalization. KSynchronizationObject::Finalize(); } @@ -314,7 +321,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa // Ensure our memory is initialized. m_memory.SetCurrentPageTable(*this); - m_memory.SetGPUDirtyManagers(m_dirty_memory_managers); + m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager()); // Ensure we can insert the code region. R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, @@ -411,7 +418,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, // Ensure our memory is initialized. m_memory.SetCurrentPageTable(*this); - m_memory.SetGPUDirtyManagers(m_dirty_memory_managers); + m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager()); // Ensure we can insert the code region. R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), @@ -1135,8 +1142,7 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} KProcess::KProcess(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, - m_handle_table{kernel}, m_dirty_memory_managers{}, - m_exclusive_monitor{}, m_memory{kernel.System()} {} + m_handle_table{kernel}, m_exclusive_monitor{}, m_memory{kernel.System()} {} KProcess::~KProcess() = default; Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, @@ -1233,10 +1239,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); #ifdef HAS_NCE - if (this->IsApplication() && Settings::IsNceEnabled()) { + const auto& patch = code_set.PatchSegment(); + if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) { auto& buffer = m_kernel.System().DeviceMemory().buffer; const auto& code = code_set.CodeSegment(); - const auto& patch = code_set.PatchSegment(); buffer.Protect(GetInteger(base_addr + code.addr), code.size, Common::MemoryPermission::Read | Common::MemoryPermission::Execute); buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, @@ -1318,10 +1324,4 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT return true; } -void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { - for (auto& manager : m_dirty_memory_managers) { - manager.Gather(callback); - } -} - } // namespace Kernel diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 53c0e3316..ab1358a12 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -7,7 +7,6 @@ #include "core/arm/arm_interface.h" #include "core/file_sys/program_metadata.h" -#include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_capabilities.h" @@ -128,7 +127,6 @@ private: #ifdef HAS_NCE std::unordered_map<u64, u64> m_post_handlers{}; #endif - std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers; std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor; Core::Memory::Memory m_memory; @@ -511,8 +509,6 @@ public: return m_memory; } - void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); - Core::ExclusiveMonitor& GetExclusiveMonitor() const { return *m_exclusive_monitor; } diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 24394d222..8a360a839 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -543,7 +543,8 @@ void KThread::Unpin() { ASSERT(m_parent != nullptr); // Resume any threads that began waiting on us while we were pinned. - for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); ++it) { + for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); + it = m_pinned_waiter_list.erase(it)) { it->EndWait(ResultSuccess); } } @@ -1258,11 +1259,11 @@ ThreadState KThread::RequestTerminate() { // Change the thread's priority to be higher than any system thread's. this->IncreaseBasePriority(TerminatingThreadPriority); - // If the thread is runnable, send a termination interrupt to other cores. + // If the thread is runnable, send a termination interrupt to cores it may be running on. if (this->GetState() == ThreadState::Runnable) { - if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask() & - ~(1ULL << GetCurrentCoreId(m_kernel)); - core_mask != 0) { + // NOTE: We do not mask the "current core", because this code may not actually be + // executing from the thread representing the "current core". + if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) { Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask); } } diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 0e2e11743..cbb1b02bb 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -76,8 +76,8 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer // Map the memory. const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) - ? KMemoryState::Transfered - : KMemoryState::SharedTransfered; + ? KMemoryState::Transferred + : KMemoryState::SharedTransferred; R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( address, *m_page_group, state, KMemoryPermission::UserReadWrite)); @@ -96,8 +96,8 @@ Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { // Unmap the memory. const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) - ? KMemoryState::Transfered - : KMemoryState::SharedTransfered; + ? KMemoryState::Transferred + : KMemoryState::SharedTransferred; R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); // Mark ourselves as unmapped. diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1030f0c12..f3683cdcc 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -112,7 +112,14 @@ struct KernelCore::Impl { old_process->Close(); } - process_list.clear(); + { + std::scoped_lock lk{process_list_lock}; + for (auto* const process : process_list) { + process->Terminate(); + process->Close(); + } + process_list.clear(); + } next_object_id = 0; next_kernel_process_id = KProcess::InitialProcessIdMin; @@ -770,6 +777,7 @@ struct KernelCore::Impl { std::atomic<u64> next_thread_id{1}; // Lists all processes that exist in the current session. + std::mutex process_list_lock; std::vector<KProcess*> process_list; std::atomic<KProcess*> application_process{}; std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; @@ -869,9 +877,19 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() { } void KernelCore::AppendNewProcess(KProcess* process) { + process->Open(); + + std::scoped_lock lk{impl->process_list_lock}; impl->process_list.push_back(process); } +void KernelCore::RemoveProcess(KProcess* process) { + std::scoped_lock lk{impl->process_list_lock}; + if (std::erase(impl->process_list, process)) { + process->Close(); + } +} + void KernelCore::MakeApplicationProcess(KProcess* process) { impl->MakeApplicationProcess(process); } @@ -884,8 +902,15 @@ const KProcess* KernelCore::ApplicationProcess() const { return impl->application_process; } -const std::vector<KProcess*>& KernelCore::GetProcessList() const { - return impl->process_list; +std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() { + std::list<KScopedAutoObject<KProcess>> processes; + std::scoped_lock lk{impl->process_list_lock}; + + for (auto* const process : impl->process_list) { + processes.emplace_back(process); + } + + return processes; } Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5d4102145..8ea5bed1c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -5,6 +5,7 @@ #include <array> #include <functional> +#include <list> #include <memory> #include <string> #include <unordered_map> @@ -116,8 +117,9 @@ public: /// Retrieves a shared pointer to the system resource limit instance. KResourceLimit* GetSystemResourceLimit(); - /// Adds the given shared pointer to an internal list of active processes. + /// Adds/removes the given pointer to an internal list of active processes. void AppendNewProcess(KProcess* process); + void RemoveProcess(KProcess* process); /// Makes the given process the new application process. void MakeApplicationProcess(KProcess* process); @@ -129,7 +131,7 @@ public: const KProcess* ApplicationProcess() const; /// Retrieves the list of processes. - const std::vector<KProcess*>& GetProcessList() const; + std::list<KScopedAutoObject<KProcess>> GetProcessList(); /// Gets the sole instance of the global scheduler Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp index caa8bee9a..5c3e8829f 100644 --- a/src/core/hle/kernel/svc/svc_process.cpp +++ b/src/core/hle/kernel/svc/svc_process.cpp @@ -74,13 +74,15 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc } auto& memory = GetCurrentMemory(kernel); - const auto& process_list = kernel.GetProcessList(); + auto process_list = kernel.GetProcessList(); + auto it = process_list.begin(); + const auto num_processes = process_list.size(); const auto copy_amount = std::min(static_cast<std::size_t>(out_process_ids_size), num_processes); - for (std::size_t i = 0; i < copy_amount; ++i) { - memory.Write64(out_process_ids, process_list[i]->GetProcessId()); + for (std::size_t i = 0; i < copy_amount && it != process_list.end(); ++i, ++it) { + memory.Write64(out_process_ids, (*it)->GetProcessId()); out_process_ids += sizeof(u64); } diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 1f97121b3..671bca23f 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -90,7 +90,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add // Verify that the mapping is in range. R_UNLESS(GetCurrentProcess(system.Kernel()) .GetPageTable() - .CanContain(address, size, KMemoryState::Transfered), + .CanContain(address, size, KMemoryState::Transferred), ResultInvalidMemoryRegion); // Map the transfer memory. @@ -117,7 +117,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a // Verify that the mapping is in range. R_UNLESS(GetCurrentProcess(system.Kernel()) .GetPageTable() - .CanContain(address, size, KMemoryState::Transfered), + .CanContain(address, size, KMemoryState::Transferred), ResultInvalidMemoryRegion); // Unmap the transfer memory. diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 50de02e36..ab432ea78 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -27,8 +27,8 @@ enum class MemoryState : u32 { Ipc = 0x0A, Stack = 0x0B, ThreadLocal = 0x0C, - Transfered = 0x0D, - SharedTransfered = 0x0E, + Transferred = 0x0D, + SharedTransferred = 0x0E, SharedCode = 0x0F, Inaccessible = 0x10, NonSecureIpc = 0x11, diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 749f51f69..316370266 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -189,14 +189,14 @@ enum class ErrorModule : u32 { union Result { u32 raw; - BitField<0, 9, ErrorModule> module; - BitField<9, 13, u32> description; + using Module = BitField<0, 9, ErrorModule>; + using Description = BitField<9, 13, u32>; Result() = default; constexpr explicit Result(u32 raw_) : raw(raw_) {} constexpr Result(ErrorModule module_, u32 description_) - : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} + : raw(Module::FormatValue(module_) | Description::FormatValue(description_)) {} [[nodiscard]] constexpr bool IsSuccess() const { return raw == 0; @@ -211,7 +211,15 @@ union Result { } [[nodiscard]] constexpr u32 GetInnerValue() const { - return static_cast<u32>(module.Value()) | (description << module.bits); + return raw; + } + + [[nodiscard]] constexpr ErrorModule GetModule() const { + return Module::ExtractValue(raw); + } + + [[nodiscard]] constexpr u32 GetDescription() const { + return Description::ExtractValue(raw); } [[nodiscard]] constexpr bool Includes(Result result) const { @@ -274,8 +282,9 @@ public: } [[nodiscard]] constexpr bool Includes(Result other) const { - return code.module == other.module && code.description <= other.description && - other.description <= description_end; + return code.GetModule() == other.GetModule() && + code.GetDescription() <= other.GetDescription() && + other.GetDescription() <= description_end; } private: @@ -330,6 +339,16 @@ constexpr bool EvaluateResultFailure(const Result& r) { return R_FAILED(r); } +template <auto... R> +constexpr bool EvaluateAnyResultIncludes(const Result& r) { + return ((r == R) || ...); +} + +template <auto... R> +constexpr bool EvaluateResultNotIncluded(const Result& r) { + return !EvaluateAnyResultIncludes<R...>(r); +} + template <typename T> constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete; // Intentionally not defined @@ -371,6 +390,13 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ ON_RESULT_SUCCESS_2 +#define ON_RESULT_INCLUDED_2(...) \ + ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateAnyResultIncludes<__VA_ARGS__>) + +#define ON_RESULT_INCLUDED(...) \ + DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ + ON_RESULT_INCLUDED_2(__VA_ARGS__) + constexpr inline Result __TmpCurrentResultReference = ResultSuccess; /// Returns a result. diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 5542d6cbc..29a10ad13 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -11,6 +11,7 @@ #include "common/fs/path_util.h" #include "common/polyfill_ranges.h" #include "common/settings.h" +#include "common/string_util.h" #include "core/hle/service/acc/profile_manager.h" namespace Service::Account { @@ -61,9 +62,7 @@ ProfileManager::ProfileManager() { OpenUser(*GetUser(current)); } -ProfileManager::~ProfileManager() { - WriteUserSaveFile(); -} +ProfileManager::~ProfileManager() = default; /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// internal management of the users profiles @@ -113,6 +112,8 @@ Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) return ERROR_USER_ALREADY_EXISTS; } + is_save_needed = true; + return AddUser({ .user_uuid = uuid, .username = username, @@ -164,6 +165,22 @@ std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) return GetUserIndex(user.user_uuid); } +/// Returns the first user profile seen based on username (which does not enforce uniqueness) +std::optional<std::size_t> ProfileManager::GetUserIndex(const std::string& username) const { + const auto iter = + std::find_if(profiles.begin(), profiles.end(), [&username](const ProfileInfo& p) { + const std::string profile_username = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(p.username.data()), p.username.size()); + + return username.compare(profile_username) == 0; + }); + if (iter == profiles.end()) { + return std::nullopt; + } + + return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); +} + /// Returns the data structure used by the switch when GetProfileBase is called on acc:* bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { if (!index || index >= MAX_USERS) { @@ -326,6 +343,9 @@ bool ProfileManager::RemoveUser(UUID uuid) { profiles[*index] = ProfileInfo{}; std::stable_partition(profiles.begin(), profiles.end(), [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); + + is_save_needed = true; + return true; } @@ -340,6 +360,8 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { profile.username = profile_new.username; profile.creation_time = profile_new.timestamp; + is_save_needed = true; + return true; } @@ -348,6 +370,7 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& const auto index = GetUserIndex(uuid); if (index.has_value() && SetProfileBase(uuid, profile_new)) { profiles[*index].data = data_new; + is_save_needed = true; return true; } @@ -391,6 +414,10 @@ void ProfileManager::ParseUserSaveFile() { } void ProfileManager::WriteUserSaveFile() { + if (!is_save_needed) { + return; + } + ProfileDataRaw raw{}; for (std::size_t i = 0; i < MAX_USERS; ++i) { @@ -423,7 +450,10 @@ void ProfileManager::WriteUserSaveFile() { if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) { LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " "made in current session will be saved."); + return; } + + is_save_needed = false; } }; // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 900e32200..f94157300 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -70,6 +70,7 @@ public: std::optional<Common::UUID> GetUser(std::size_t index) const; std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const; std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; + std::optional<std::size_t> GetUserIndex(const std::string& username) const; bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; @@ -103,6 +104,7 @@ private: std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); bool RemoveProfileAtIndex(std::size_t index); + bool is_save_needed{}; std::array<ProfileInfo, MAX_USERS> profiles{}; std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{}; std::size_t user_count{}; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 97eb56ff0..a768bdc54 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -13,7 +13,6 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/savedata_factory.h" -#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" @@ -37,7 +36,7 @@ #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/hid/controllers/npad.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/nvnflinger/fb_share_buffer_manager.h" @@ -48,6 +47,8 @@ #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_results.h" #include "core/memory.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad.h" namespace Service::AM { @@ -2178,7 +2179,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) { attribute.type = FileSys::SaveDataType::SaveData; FileSys::VirtualDir save_data{}; - const auto res = system.GetFileSystemController().CreateSaveData( + const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); IPC::ResponseBuilder rb{ctx, 4}; @@ -2353,7 +2354,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) { "new_journal={:016X}", static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - system.GetFileSystemController().WriteSaveDataSize( + system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( type, system.GetApplicationProcessProgramID(), user_id, {new_normal_size, new_journal_size}); @@ -2378,7 +2379,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], user_id[0]); - const auto size = system.GetFileSystemController().ReadSaveDataSize( + const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( type, system.GetApplicationProcessProgramID(), user_id); IPC::ResponseBuilder rb{ctx, 6}; diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 3906c0fa4..c2ff444a6 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -5,13 +5,13 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/frontend/applets/cabinet.h" -#include "core/hid/hid_core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applet_cabinet.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/nfc/common/device.h" +#include "hid_core/hid_core.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp index 9840d2547..0e4d9cc39 100644 --- a/src/core/hle/service/am/applets/applet_controller.cpp +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -9,13 +9,13 @@ #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/controller.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applet_controller.h" -#include "core/hle/service/hid/controllers/npad.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index 5d17c353f..084bc138c 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -27,8 +27,8 @@ struct ErrorCode { static constexpr ErrorCode FromResult(Result result) { return { - .error_category{2000 + static_cast<u32>(result.module.Value())}, - .error_number{result.description.Value()}, + .error_category{2000 + static_cast<u32>(result.GetModule())}, + .error_number{result.GetDescription()}, }; } diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp index 50adc7c02..e83e931c5 100644 --- a/src/core/hle/service/am/applets/applet_mii_edit.cpp +++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp @@ -59,7 +59,7 @@ void MiiEdit::Initialize() { break; } - manager = system.ServiceManager().GetService<Mii::MiiDBModule>("mii:e")->GetMiiManager(); + manager = system.ServiceManager().GetService<Mii::IStaticService>("mii:e")->GetMiiManager(); if (manager == nullptr) { manager = std::make_shared<Mii::MiiManager>(); } diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index 369f9250f..673eed516 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h @@ -76,7 +76,7 @@ struct UiSettingsDisplayOptions { bool is_system_or_launcher; bool is_registration_permitted; bool show_skip_button; - bool aditional_select; + bool additional_select; bool show_user_selector; bool is_unqualified_user_selectable; }; diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index b0ea2b381..19057ad7b 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -9,13 +9,13 @@ #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.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/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/frontend/applets/web_browser.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" @@ -213,7 +213,7 @@ void ExtractSharedFonts(Core::System& system) { std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); + Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite); const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); @@ -333,7 +333,7 @@ void WebBrowser::ExtractOfflineRomFS() { const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); + Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite); FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); } diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h index 99fe18659..36adb2510 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.h +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -7,7 +7,7 @@ #include <optional> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" #include "core/hle/service/am/applets/applet_web_browser_types.h" #include "core/hle/service/am/applets/applets.h" diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 66dd64fd1..3101cf447 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -4,6 +4,8 @@ #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 { @@ -19,15 +21,15 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { {6, nullptr, "IsTargetConnected"}, {7, nullptr, "SetDefaultTarget"}, {8, nullptr, "GetDefaultTarget"}, - {9, nullptr, "GetAudioOutputMode"}, - {10, nullptr, "SetAudioOutputMode"}, + {9, &AudCtl::GetAudioOutputMode, "GetAudioOutputMode"}, + {10, &AudCtl::SetAudioOutputMode, "SetAudioOutputMode"}, {11, nullptr, "SetForceMutePolicy"}, {12, &AudCtl::GetForceMutePolicy, "GetForceMutePolicy"}, {13, &AudCtl::GetOutputModeSetting, "GetOutputModeSetting"}, - {14, nullptr, "SetOutputModeSetting"}, + {14, &AudCtl::SetOutputModeSetting, "SetOutputModeSetting"}, {15, nullptr, "SetOutputTarget"}, {16, nullptr, "SetInputTargetForceEnabled"}, - {17, nullptr, "SetHeadphoneOutputLevelMode"}, + {17, &AudCtl::SetHeadphoneOutputLevelMode, "SetHeadphoneOutputLevelMode"}, {18, &AudCtl::GetHeadphoneOutputLevelMode, "GetHeadphoneOutputLevelMode"}, {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"}, {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"}, @@ -40,7 +42,7 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { {27, nullptr, "SetVolumeMappingTableForDev"}, {28, nullptr, "GetAudioOutputChannelCountForPlayReport"}, {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, - {30, nullptr, "SetSpeakerAutoMuteEnabled"}, + {30, &AudCtl::SetSpeakerAutoMuteEnabled, "SetSpeakerAutoMuteEnabled"}, {31, &AudCtl::IsSpeakerAutoMuteEnabled, "IsSpeakerAutoMuteEnabled"}, {32, nullptr, "GetActiveOutputTarget"}, {33, nullptr, "GetTargetDeviceInfo"}, @@ -68,6 +70,9 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { // clang-format on RegisterHandlers(functions); + + m_set_sys = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); } AudCtl::~AudCtl() = default; @@ -96,6 +101,33 @@ void AudCtl::GetTargetVolumeMax(HLERequestContext& ctx) { 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"); @@ -106,13 +138,31 @@ void AudCtl::GetForceMutePolicy(HLERequestContext& ctx) { void AudCtl::GetOutputModeSetting(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto value = rp.Pop<u32>(); + const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()}; - LOG_WARNING(Audio, "(STUBBED) called, value={}", value); + LOG_WARNING(Audio, "(STUBBED) called, target={}", target); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(AudioOutputMode::PcmAuto); + 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) { @@ -123,14 +173,28 @@ void AudCtl::GetHeadphoneOutputLevelMode(HLERequestContext& ctx) { 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) { - const bool is_speaker_auto_mute_enabled = false; + 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(ResultSuccess); + rb.Push(result); rb.Push<u8>(is_speaker_auto_mute_enabled); } diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h index d57abb383..4c90ead70 100644 --- a/src/core/hle/service/audio/audctl.h +++ b/src/core/hle/service/audio/audctl.h @@ -9,6 +9,10 @@ namespace Core { class System; } +namespace Service::Set { +class ISystemSettingsServer; +} + namespace Service::Audio { class AudCtl final : public ServiceFramework<AudCtl> { @@ -17,14 +21,6 @@ public: ~AudCtl() override; private: - enum class AudioOutputMode { - Invalid, - Pcm1ch, - Pcm2ch, - Pcm6ch, - PcmAuto, - }; - enum class ForceMutePolicy { Disable, SpeakerMuteOnHeadphoneUnplugged, @@ -37,10 +33,18 @@ private: 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/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 56fee4591..de2aa6906 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -18,11 +18,11 @@ using namespace AudioCore::AudioIn; class IAudioIn final : public ServiceFramework<IAudioIn> { public: explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, - const std::string& device_name, const AudioInParameter& in_params, u32 handle, - u64 applet_resource_user_id) + const std::string& device_name, const AudioInParameter& in_params, + Kernel::KProcess* handle, u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, - impl{std::make_shared<In>(system_, manager, event, session_id)} { + process{handle}, impl{std::make_shared<In>(system_, manager, event, session_id)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, @@ -45,6 +45,8 @@ public: RegisterHandlers(functions); + process->Open(); + if (impl->GetSystem() .Initialize(device_name, in_params, handle, applet_resource_user_id) .IsError()) { @@ -55,6 +57,7 @@ public: ~IAudioIn() override { impl->Free(); service_context.CloseEvent(event); + process->Close(); } [[nodiscard]] std::shared_ptr<In> GetImpl() { @@ -196,6 +199,7 @@ private: KernelHelpers::ServiceContext service_context; Kernel::KEvent* event; + Kernel::KProcess* process; std::shared_ptr<AudioCore::AudioIn::In> impl; Common::ScratchBuffer<u64> released_buffer; }; @@ -267,6 +271,14 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) { auto device_name = Common::StringFromBuffer(device_name_data); auto handle{ctx.GetCopyHandle(0)}; + auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_Audio, "Failed to get process handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + std::scoped_lock l{impl->mutex}; auto link{impl->LinkToManager()}; if (link.IsError()) { @@ -287,8 +299,9 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, impl->num_free_sessions); - auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, - in_params, handle, applet_resource_user_id); + auto audio_in = + std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params, + process.GetPointerUnsafe(), applet_resource_user_id); impl->sessions[new_session_id] = audio_in->GetImpl(); impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; @@ -318,6 +331,14 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) { auto device_name = Common::StringFromBuffer(device_name_data); auto handle{ctx.GetCopyHandle(0)}; + auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_Audio, "Failed to get process handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + std::scoped_lock l{impl->mutex}; auto link{impl->LinkToManager()}; if (link.IsError()) { @@ -338,8 +359,9 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, impl->num_free_sessions); - auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, - in_params, handle, applet_resource_user_id); + auto audio_in = + std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params, + process.GetPointerUnsafe(), applet_resource_user_id); impl->sessions[new_session_id] = audio_in->GetImpl(); impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index ca683d72c..8cc7b69f4 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -26,9 +26,10 @@ class IAudioOut final : public ServiceFramework<IAudioOut> { public: explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id, const std::string& device_name, - const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) + const AudioOutParameter& in_params, Kernel::KProcess* handle, + u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, - event{service_context.CreateEvent("AudioOutEvent")}, + event{service_context.CreateEvent("AudioOutEvent")}, process{handle}, impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { // clang-format off @@ -50,11 +51,14 @@ public: }; // clang-format on RegisterHandlers(functions); + + process->Open(); } ~IAudioOut() override { impl->Free(); service_context.CloseEvent(event); + process->Close(); } [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { @@ -206,6 +210,7 @@ private: KernelHelpers::ServiceContext service_context; Kernel::KEvent* event; + Kernel::KProcess* process; std::shared_ptr<AudioCore::AudioOut::Out> impl; Common::ScratchBuffer<u64> released_buffer; }; @@ -257,6 +262,14 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) { auto device_name = Common::StringFromBuffer(device_name_data); auto handle{ctx.GetCopyHandle(0)}; + auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_Audio, "Failed to get process handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + auto link{impl->LinkToManager()}; if (link.IsError()) { LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); @@ -276,10 +289,11 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, impl->num_free_sessions); - auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, - in_params, handle, applet_resource_user_id); - result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle, - applet_resource_user_id); + auto audio_out = + std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, in_params, + process.GetPointerUnsafe(), applet_resource_user_id); + result = audio_out->GetImpl()->GetSystem().Initialize( + device_name, in_params, process.GetPointerUnsafe(), applet_resource_user_id); if (result.IsError()) { LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index bd4ca753b..10108abc0 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -35,10 +35,11 @@ public: explicit IAudioRenderer(Core::System& system_, Manager& manager_, AudioCore::AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, - u32 process_handle, u64 applet_resource_user_id, s32 session_id) + u32 process_handle, Kernel::KProcess& process_, + u64 applet_resource_user_id, s32 session_id) : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, - impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { + impl{std::make_unique<Renderer>(system_, manager, rendered_event)}, process{process_} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, @@ -59,13 +60,15 @@ public: // clang-format on RegisterHandlers(functions); - impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, + process.Open(); + impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, process, applet_resource_user_id, session_id); } ~IAudioRenderer() override { impl->Finalize(); service_context.CloseEvent(rendered_event); + process.Close(); } private: @@ -139,7 +142,8 @@ private: ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1); } } else { - LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); + LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", + result.GetDescription()); } IPC::ResponseBuilder rb{ctx, 2}; @@ -234,6 +238,7 @@ private: Kernel::KEvent* rendered_event; Manager& manager; std::unique_ptr<Renderer> impl; + Kernel::KProcess& process; Common::ScratchBuffer<u8> output_buffer; Common::ScratchBuffer<u8> performance_buffer; }; @@ -454,7 +459,7 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) { return; } - auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)}; + auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle).GetPointerUnsafe()}; auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)}; const auto session_id{impl->GetSessionId()}; @@ -471,7 +476,7 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(), - transfer_memory_size, process_handle, + transfer_memory_size, process_handle, *process, applet_resource_user_id, session_id); } @@ -521,7 +526,7 @@ void AudRenU::GetAudioDeviceService(HLERequestContext& ctx) { } void AudRenU::OpenAudioRendererForManualExecution(HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + LOG_ERROR(Service_Audio, "called. Implement me!"); } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx) { diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 205ed0702..aa36d29d5 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -8,7 +8,7 @@ #include <string> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index a6281913a..76d7bb139 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -8,7 +8,7 @@ #include "common/settings.h" #include "common/string_util.h" #include "core/core.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/bcat/bcat.h" diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index c65e32489..2dc23e674 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -283,7 +283,7 @@ public: {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, {18, nullptr, "DisconnectAudioDevice"}, {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, - {20, nullptr, "GetPairedAudioDevices"}, + {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"}, {21, nullptr, "RemoveAudioDevicePairing"}, {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} @@ -327,6 +327,13 @@ private: 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}; diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 9925720a3..69acb3a8b 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -202,14 +202,14 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) { } if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) { - if (in_result.description - 0x514 < 100) { + if (in_result.GetDescription() - 0x514 < 100) { return ResultInvalidFileData; } - if (in_result.description - 0x5dc < 100) { + if (in_result.GetDescription() - 0x5dc < 100) { return ResultInvalidFileData; } - if (in_result.description - 0x578 < 100) { + if (in_result.GetDescription() - 0x578 < 100) { if (in_result == ResultFileCountLimit) { return ResultUnknown22; } @@ -244,9 +244,10 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) { return ResultUnknown1024; } - if (in_result.module == ErrorModule::FS) { - if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) || - (((in_result.description - 3000) >> 3) < 0x271)) { + if (in_result.GetModule() == ErrorModule::FS) { + if ((in_result.GetDescription() >> 0xc < 0x7d) || + (in_result.GetDescription() - 1000 < 2000) || + (((in_result.GetDescription() - 3000) >> 3) < 0x271)) { // TODO: Translate FS error return in_result; } diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 96b225d5f..e3b8ecf3e 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -10,8 +10,10 @@ #include "core/core.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_result.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" namespace Service::Capture { @@ -85,7 +87,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu } Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, - ContentType contex_type, s64 start_posix_time, + ContentType content_type, s64 start_posix_time, s64 end_posix_time, u64 aruid) const { if (!is_mounted) { return ResultIsNotMounted; @@ -94,7 +96,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou std::vector<ApplicationAlbumEntry> album_entries; const auto start_date = ConvertToAlbumDateTime(start_posix_time); const auto end_date = ConvertToAlbumDateTime(end_posix_time); - const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid); + const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid); if (result.IsError()) { return result; @@ -113,14 +115,14 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou } Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, - ContentType contex_type, AlbumFileDateTime start_date, + ContentType content_type, AlbumFileDateTime start_date, AlbumFileDateTime end_date, u64 aruid) const { if (!is_mounted) { return ResultIsNotMounted; } for (auto& [file_id, path] : album_files) { - if (file_id.type != contex_type) { + if (file_id.type != content_type) { continue; } if (file_id.date > start_date) { @@ -139,7 +141,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en .hash{}, .datetime = file_id.date, .storage = file_id.storage, - .content = contex_type, + .content = content_type, .unknown = 1, }; out_entries.push_back(entry); @@ -239,10 +241,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) { const u64 title_id = system.GetApplicationProcessProgramID(); - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{}; + static_service->GetStandardUserSystemClock(user_clock); s64 posix_time{}; - Result result = user_clock.GetCurrentTime(system, posix_time); + auto result = user_clock->GetCurrentTime(posix_time); if (result.IsError()) { return result; @@ -257,10 +264,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, const AlbumFileId& file_id, std::span<const u8> image_data) { - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{}; + static_service->GetStandardUserSystemClock(user_clock); s64 posix_time{}; - Result result = user_clock.GetCurrentTime(system, posix_time); + auto result = user_clock->GetCurrentTime(posix_time); if (result.IsError()) { return result; @@ -455,19 +466,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const } AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { - Time::TimeZone::CalendarInfo calendar_date{}; - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{}; + static_service->GetTimeZoneService(timezone_service); - time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time); return { - .year = calendar_date.time.year, - .month = calendar_date.time.month, - .day = calendar_date.time.day, - .hour = calendar_date.time.hour, - .minute = calendar_date.time.minute, - .second = calendar_date.time.second, + .year = calendar_time.year, + .month = calendar_time.month, + .day = calendar_time.day, + .hour = calendar_time.hour, + .minute = calendar_time.minute, + .second = calendar_time.second, .unique_id = 0, }; } diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h index e20c70c7b..6fd34f589 100644 --- a/src/core/hle/service/caps/caps_manager.h +++ b/src/core/hle/service/caps/caps_manager.h @@ -45,10 +45,10 @@ public: Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, u8 flags) const; Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, - ContentType contex_type, s64 start_posix_time, s64 end_posix_time, + ContentType content_type, s64 start_posix_time, s64 end_posix_time, u64 aruid) const; Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, - ContentType contex_type, AlbumFileDateTime start_date, + ContentType content_type, AlbumFileDateTime start_date, AlbumFileDateTime end_date, u64 aruid) const; Result GetAutoSavingStorage(bool& out_is_autosaving) const; Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h index c65e5fb9a..179ae4840 100644 --- a/src/core/hle/service/caps/caps_result.h +++ b/src/core/hle/service/caps/caps_result.h @@ -12,7 +12,7 @@ constexpr Result ResultUnknown5(ErrorModule::Capture, 5); constexpr Result ResultUnknown6(ErrorModule::Capture, 6); constexpr Result ResultUnknown7(ErrorModule::Capture, 7); constexpr Result ResultOutOfRange(ErrorModule::Capture, 8); -constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12); +constexpr Result ResultInvalidTimestamp(ErrorModule::Capture, 12); constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13); constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14); constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21); diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h new file mode 100644 index 000000000..9eb10e816 --- /dev/null +++ b/src/core/hle/service/cmif_serialization.h @@ -0,0 +1,336 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/div_ceil.h" + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/service.h" + +namespace Service { + +// clang-format off +struct RequestLayout { + u32 copy_handle_count; + u32 move_handle_count; + u32 cmif_raw_data_size; + u32 domain_interface_count; +}; + +template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0> +constexpr u32 GetArgumentRawDataSize() { + if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { + return static_cast<u32>(DataOffset); + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>(); + } else { + return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>(); + } + } +} + +template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0> +constexpr u32 GetArgumentTypeCount() { + if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { + return static_cast<u32>(ArgCount); + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == DataType) { + return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>(); + } else { + return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>(); + } + } +} + +template <typename MethodArguments> +constexpr RequestLayout GetNonDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), + .domain_interface_count = 0, + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), + .domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(), + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetNonDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), + .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), + .domain_interface_count = 0, + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), + .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(), + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), + .domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetReplyInLayout(bool is_domain) { + return is_domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>(); +} + +template <typename MethodArguments> +constexpr RequestLayout GetReplyOutLayout(bool is_domain) { + return is_domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>(); +} + +using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>; + +template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> +void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { + return; + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All input interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) { + // TODO: abort parsing if PID is not provided? + // TODO: validate against raw data value? + std::get<ArgIndex>(args).pid = ctx.GetPID(); + } else { + std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize); + } + + return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) { + constexpr size_t ArgAlign = alignof(u32); + constexpr size_t ArgSize = sizeof(u32); + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + ASSERT(is_domain); + ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0); + + u32 value{}; + std::memcpy(&value, raw_data + ArgOffset, ArgSize); + std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); + + return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { + std::get<ArgIndex>(args) = ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)).GetPointerUnsafe(); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get<ArgIndex>(args), 0, BufferSize); + + std::span<const u8> buffer{}; + + ASSERT(ctx.CanReadBuffer(InBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + + std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size())); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) { + using ElementType = typename ArgType::Type; + + std::span<const u8> buffer{}; + + if (ctx.CanReadBuffer(InBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get<ArgIndex>(args) = std::span(ptr, size); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get<ArgIndex>(args), 0, BufferSize); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) { + using ElementType = typename ArgType::Type; + + // Set up scratch buffer. + auto& buffer = temp[OutBufferIndex]; + if (ctx.CanWriteBuffer(OutBufferIndex)) { + buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex)); + } else { + buffer.resize_destructive(0); + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get<ArgIndex>(args) = std::span(ptr, size); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else { + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } + } +} + +template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> +void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { + return; + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All output interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize); + + return WriteOutArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) { + if (is_domain) { + ctx.AddDomainObject(std::get<ArgIndex>(args)); + } else { + ctx.AddMoveInterface(std::get<ArgIndex>(args)); + } + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) { + ctx.AddCopyObject(std::get<ArgIndex>(args)); + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) { + ctx.AddMoveObject(std::get<ArgIndex>(args)); + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + ASSERT(ctx.CanWriteBuffer(OutBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); + } + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) { + auto& buffer = temp[OutBufferIndex]; + const size_t size = buffer.size(); + + if (ctx.CanWriteBuffer(OutBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(buffer.data(), size, OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(buffer.data(), size, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(buffer.data(), size, OutBufferIndex); + } + } + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else { + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } + } +} + +template <bool Domain, typename T, typename... A> +void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { + // Verify domain state. + if constexpr (!Domain) { + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session"); + } + const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; + + using MethodArguments = std::tuple<std::remove_reference_t<A>...>; + + OutTemporaryBuffers buffers{}; + auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>(); + + // Read inputs. + const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2; + ReadInArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers); + + // Call. + const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) { + return (t.*f)(args...); + }; + const Result res = std::apply(Callable, call_arguments); + + // Write result. + const RequestLayout layout = GetReplyOutLayout<MethodArguments>(is_domain); + IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count}; + rb.Push(res); + + // Write out arguments. + WriteOutArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers); +} +// clang-format on + +template <typename Self> +template <bool Domain, auto F> +inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) { + return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F); +} + +} // namespace Service diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h new file mode 100644 index 000000000..2610c49f3 --- /dev/null +++ b/src/core/hle/service/cmif_types.h @@ -0,0 +1,294 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hle_ipc.h" + +namespace Service { + +// clang-format off +template <typename T> +class Out { +public: + using Type = T; + + /* implicit */ Out(Type& t) : raw(&t) {} + ~Out() = default; + + Type* Get() const { + return raw; + } + + Type& operator*() { + return *raw; + } + +private: + Type* raw; +}; + +template <typename T> +using SharedPointer = std::shared_ptr<T>; + +struct ClientProcessId { + explicit operator bool() const { + return pid != 0; + } + + const u64& operator*() const { + return pid; + } + + u64 pid; +}; + +struct ProcessId { + explicit operator bool() const { + return pid != 0; + } + + const u64& operator*() const { + return pid; + } + + u64 pid; +}; + +using ClientAppletResourceUserId = ClientProcessId; +using AppletResourceUserId = ProcessId; + +template <typename T> +class InCopyHandle { +public: + using Type = T; + + /* implicit */ InCopyHandle(Type* t) : raw(t) {} + /* implicit */ InCopyHandle() : raw() {} + ~InCopyHandle() = default; + + InCopyHandle& operator=(Type* rhs) { + raw = rhs; + return *this; + } + + Type* Get() const { + return raw; + } + + Type& operator*() const { + return *raw; + } + + Type* operator->() const { + return raw; + } + + explicit operator bool() const { + return raw != nullptr; + } + +private: + Type* raw; +}; + +template <typename T> +class OutCopyHandle { +public: + using Type = T*; + + /* implicit */ OutCopyHandle(Type& t) : raw(&t) {} + ~OutCopyHandle() = default; + + Type* Get() const { + return raw; + } + + Type& operator*() { + return *raw; + } + +private: + Type* raw; +}; + +template <typename T> +class OutMoveHandle { +public: + using Type = T*; + + /* implicit */ OutMoveHandle(Type& t) : raw(&t) {} + ~OutMoveHandle() = default; + + Type* Get() const { + return raw; + } + + Type& operator*() { + return *raw; + } + +private: + Type* raw; +}; + +enum BufferAttr : int { + BufferAttr_In = (1U << 0), + BufferAttr_Out = (1U << 1), + BufferAttr_HipcMapAlias = (1U << 2), + BufferAttr_HipcPointer = (1U << 3), + BufferAttr_FixedSize = (1U << 4), + BufferAttr_HipcAutoSelect = (1U << 5), + BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6), + BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7), +}; + +template <typename T, int A> +struct Buffer : public std::span<T> { + static_assert(std::is_trivially_copyable_v<T>, "Buffer type must be trivially copyable"); + static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast<BufferAttr>(A); + using Type = T; + + /* implicit */ Buffer(const std::span<T>& rhs) : std::span<T>(rhs) {} + /* implicit */ Buffer() = default; + + Buffer& operator=(const std::span<T>& rhs) { + std::span<T>::operator=(rhs); + return *this; + } + + T& operator*() const { + return *this->data(); + } + + explicit operator bool() const { + return this->size() > 0; + } +}; + +template <BufferAttr A> +using InBuffer = Buffer<const u8, BufferAttr_In | A>; + +template <typename T, BufferAttr A> +using InArray = Buffer<T, BufferAttr_In | A>; + +template <BufferAttr A> +using OutBuffer = Buffer<u8, BufferAttr_Out | A>; + +template <typename T, BufferAttr A> +using OutArray = Buffer<T, BufferAttr_Out | A>; + +template <typename T, int A> +struct LargeData : public T { + static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable"); + static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast<BufferAttr>(A); + using Type = T; + + /* implicit */ LargeData(const T& rhs) : T(rhs) {} + /* implicit */ LargeData() = default; +}; + +template <typename T, BufferAttr A> +using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>; + +template <typename T, BufferAttr A> +using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>; + +template <typename T> +struct RemoveOut { + using Type = std::remove_reference_t<T>; +}; + +template <typename T> +struct RemoveOut<Out<T>> { + using Type = typename Out<T>::Type; +}; + +template <typename T> +struct RemoveOut<OutCopyHandle<T>> { + using Type = typename OutCopyHandle<T>::Type; +}; + +template <typename T> +struct RemoveOut<OutMoveHandle<T>> { + using Type = typename OutMoveHandle<T>::Type; +}; + +enum class ArgumentType { + InProcessId, + InData, + InInterface, + InCopyHandle, + OutData, + OutInterface, + OutCopyHandle, + OutMoveHandle, + InBuffer, + InLargeData, + OutBuffer, + OutLargeData, +}; + +template <typename T> +struct ArgumentTraits; + +template <> +struct ArgumentTraits<ClientProcessId> { + static constexpr ArgumentType Type = ArgumentType::InProcessId; +}; + +template <typename T> +struct ArgumentTraits<SharedPointer<T>> { + static constexpr ArgumentType Type = ArgumentType::InInterface; +}; + +template <typename T> +struct ArgumentTraits<InCopyHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::InCopyHandle; +}; + +template <typename T> +struct ArgumentTraits<Out<SharedPointer<T>>> { + static constexpr ArgumentType Type = ArgumentType::OutInterface; +}; + +template <typename T> +struct ArgumentTraits<Out<T>> { + static constexpr ArgumentType Type = ArgumentType::OutData; +}; + +template <typename T> +struct ArgumentTraits<OutCopyHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::OutCopyHandle; +}; + +template <typename T> +struct ArgumentTraits<OutMoveHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::OutMoveHandle; +}; + +template <typename T, int A> +struct ArgumentTraits<Buffer<T, A>> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer; +}; + +template <typename T, int A> +struct ArgumentTraits<LargeData<T, A>> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData; +}; + +template <typename T> +struct ArgumentTraits { + static constexpr ArgumentType Type = ArgumentType::InData; +}; +// clang-format on + +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 31da86074..dfcac1ffd 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -73,8 +73,8 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F "Program entry point: 0x{:16X}\n" "\n", Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, - 2000 + static_cast<u32>(error_code.module.Value()), - static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point); + 2000 + static_cast<u32>(error_code.GetModule()), + static_cast<u32>(error_code.GetDescription()), info.set_flags, info.program_entry_point); if (info.backtrace_size != 0x0) { crash_report += "Registers:\n"; for (size_t i = 0; i < info.registers.size(); i++) { diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 780f8c74d..ae230afc0 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -12,27 +12,24 @@ #include "core/file_sys/card_image.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/errors.h" -#include "core/file_sys/mode.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/sdmc_factory.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_ldr.h" -#include "core/hle/service/filesystem/fsp_pr.h" -#include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/fsp/fsp_ldr.h" +#include "core/hle/service/filesystem/fsp/fsp_pr.h" +#include "core/hle/service/filesystem/fsp/fsp_srv.h" +#include "core/hle/service/filesystem/romfs_controller.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/server_manager.h" #include "core/loader/loader.h" namespace Service::FileSystem { -// A default size for normal/journal save data size if application control metadata cannot be found. -// This should be large enough to satisfy even the most extreme requirements (~4.2GB) -constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; - static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, std::string_view dir_name_) { std::string dir_name(Common::FS::SanitizePath(dir_name_)); @@ -55,12 +52,12 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } - FileSys::EntryType entry_type{}; + FileSys::DirectoryEntryType entry_type{}; if (GetEntryType(&entry_type, path) == ResultSuccess) { - return FileSys::ERROR_PATH_ALREADY_EXISTS; + return FileSys::ResultPathAlreadyExists; } auto file = dir->CreateFile(Common::FS::GetFilename(path)); @@ -84,7 +81,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } if (!dir->DeleteFile(Common::FS::GetFilename(path))) { // TODO(DarkLordZach): Find a better error code for this @@ -155,12 +152,12 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { // Use more-optimized vfs implementation rename. if (src == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } if (dst && Common::FS::Exists(dst->GetFullPath())) { LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath()); - return FileSys::ERROR_PATH_ALREADY_EXISTS; + return FileSys::ResultPathAlreadyExists; } if (!src->Rename(Common::FS::GetFilename(dest_path))) { @@ -197,7 +194,7 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { // Use more-optimized vfs implementation rename. if (src == nullptr) - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; if (!src->Rename(Common::FS::GetFilename(dest_path))) { // TODO(DarkLordZach): Find a better error code for this return ResultUnknown; @@ -216,7 +213,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, } Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file, - const std::string& path_, FileSys::Mode mode) const { + const std::string& path_, + FileSys::OpenMode mode) const { const std::string path(Common::FS::SanitizePath(path_)); std::string_view npath = path; while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { @@ -225,10 +223,10 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file, auto file = backing->GetFileRelative(npath); if (file == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } - if (mode == FileSys::Mode::Append) { + if (mode == FileSys::OpenMode::AllowAppend) { *out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()); } else { *out_file = file; @@ -243,50 +241,50 @@ Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_direct auto dir = GetDirectoryRelativeWrapped(backing, path); if (dir == nullptr) { // TODO(DarkLordZach): Find a better error code for this - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } *out_directory = dir; return ResultSuccess; } -Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::EntryType* out_entry_type, +Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } auto filename = Common::FS::GetFilename(path); // TODO(Subv): Some games use the '/' path, find out what this means. if (filename.empty()) { - *out_entry_type = FileSys::EntryType::Directory; + *out_entry_type = FileSys::DirectoryEntryType::Directory; return ResultSuccess; } if (dir->GetFile(filename) != nullptr) { - *out_entry_type = FileSys::EntryType::File; + *out_entry_type = FileSys::DirectoryEntryType::File; return ResultSuccess; } if (dir->GetSubdirectory(filename) != nullptr) { - *out_entry_type = FileSys::EntryType::Directory; + *out_entry_type = FileSys::DirectoryEntryType::Directory; return ResultSuccess; } - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw( FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const { auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } - FileSys::EntryType entry_type; + FileSys::DirectoryEntryType entry_type; if (GetEntryType(&entry_type, path) != ResultSuccess) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } *out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path)); @@ -297,157 +295,77 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste FileSystemController::~FileSystemController() = default; -Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { - romfs_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered RomFS"); - return ResultSuccess; -} - -Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { - ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); - save_data_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered save data"); - return ResultSuccess; -} +Result FileSystemController::RegisterProcess( + ProcessId process_id, ProgramId program_id, + std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) { + std::scoped_lock lk{registration_lock}; -Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { - ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); - sdmc_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered SDMC"); - return ResultSuccess; -} + registrations.emplace(process_id, Registration{ + .program_id = program_id, + .romfs_factory = std::move(romfs_factory), + .save_data_factory = CreateSaveDataFactory(program_id), + }); -Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { - ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); - bis_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered BIS"); + LOG_DEBUG(Service_FS, "Registered for process {}", process_id); return ResultSuccess; } -void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { - LOG_TRACE(Service_FS, "Setting packed update for romfs"); - - if (romfs_factory == nullptr) - return; - - romfs_factory->SetPackedUpdate(std::move(update_raw)); -} - -FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const { - LOG_TRACE(Service_FS, "Opening RomFS for current process"); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID()); -} - -FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id, - FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenPatchedRomFS(title_id, type); -} - -FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex( - u64 title_id, u8 program_index, FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id, - program_index); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); -} - -FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", - title_id, storage_id, type); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->Open(title_id, storage_id, type); -} - -std::shared_ptr<FileSys::NCA> FileSystemController::OpenBaseNca( - u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { - return romfs_factory->GetEntry(title_id, storage_id, type); -} - -Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data, - FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const { - LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, - save_struct.DebugInfo()); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +Result FileSystemController::OpenProcess( + ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller, + std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) { + std::scoped_lock lk{registration_lock}; - auto save_data = save_data_factory->Create(space, save_struct); - if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + const auto it = registrations.find(process_id); + if (it == registrations.end()) { + return FileSys::ResultTargetNotFound; } - *out_save_data = save_data; + *out_program_id = it->second.program_id; + *out_save_data_controller = + std::make_shared<SaveDataController>(system, it->second.save_data_factory); + *out_romfs_controller = + std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id); return ResultSuccess; } -Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data, - FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& attribute) const { - LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space, - attribute.DebugInfo()); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) { + LOG_TRACE(Service_FS, "Setting packed update for romfs"); - auto save_data = save_data_factory->Open(space, attribute); - if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + std::scoped_lock lk{registration_lock}; + const auto it = registrations.find(process_id); + if (it == registrations.end()) { + return; } - *out_save_data = save_data; - return ResultSuccess; + it->second.romfs_factory->SetPackedUpdate(std::move(update_raw)); } -Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, - FileSys::SaveDataSpaceId space) const { - LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController() { + return std::make_shared<SaveDataController>(system, CreateSaveDataFactory(ProgramId{})); +} - auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space); - if (save_data_space == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory( + ProgramId program_id) { + using YuzuPath = Common::FS::YuzuPath; + const auto rw_mode = FileSys::OpenMode::ReadWrite; - *out_save_data_space = save_data_space; - return ResultSuccess; + auto vfs = system.GetFilesystem(); + const auto nand_directory = + vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); + return std::make_shared<FileSys::SaveDataFactory>(system, program_id, + std::move(nand_directory)); } Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { LOG_TRACE(Service_FS, "Opening SDMC"); if (sdmc_factory == nullptr) { - return FileSys::ERROR_SD_CARD_NOT_FOUND; + return FileSys::ResultPortSdCardNoDevice; } auto sdmc = sdmc_factory->Open(); if (sdmc == nullptr) { - return FileSys::ERROR_SD_CARD_NOT_FOUND; + return FileSys::ResultPortSdCardNoDevice; } *out_sdmc = sdmc; @@ -459,12 +377,12 @@ Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_parti LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id); if (bis_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } auto part = bis_factory->OpenPartition(id); if (part == nullptr) { - return FileSys::ERROR_INVALID_ARGUMENT; + return FileSys::ResultInvalidArgument; } *out_bis_partition = part; @@ -476,12 +394,12 @@ Result FileSystemController::OpenBISPartitionStorage( LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id); if (bis_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem()); if (part == nullptr) { - return FileSys::ERROR_INVALID_ARGUMENT; + return FileSys::ResultInvalidArgument; } *out_bis_partition_storage = part; @@ -540,48 +458,6 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const { return 0; } -FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type, - u64 title_id, u128 user_id) const { - if (save_data_factory == nullptr) { - return {0, 0}; - } - - const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id); - - if (value.normal == 0 && value.journal == 0) { - FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; - - FileSys::NACP nacp; - const auto res = system.GetAppLoader().ReadControlData(nacp); - - if (res != Loader::ResultStatus::Success) { - const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(), - system.GetFileSystemController(), - system.GetContentProvider()}; - const auto metadata = pm.GetControlMetadata(); - const auto& nacp_unique = metadata.first; - - if (nacp_unique != nullptr) { - new_size = {nacp_unique->GetDefaultNormalSaveSize(), - nacp_unique->GetDefaultJournalSaveSize()}; - } - } else { - new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()}; - } - - WriteSaveDataSize(type, title_id, user_id, new_size); - return new_size; - } - - return value; -} - -void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) const { - if (save_data_factory != nullptr) - save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); -} - void FileSystemController::SetGameCard(FileSys::VirtualFile file) { gamecard = std::make_unique<FileSys::XCI>(file); const auto dir = gamecard->ConcatenatedPseudoDirectory(); @@ -801,29 +677,24 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const { return bis_factory->GetBCATDirectory(title_id); } -void FileSystemController::SetAutoSaveDataCreation(bool enable) { - save_data_factory->SetAutoCreate(enable); -} - void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; - save_data_factory = nullptr; sdmc_factory = nullptr; } using YuzuPath = Common::FS::YuzuPath; const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir); const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents"; - const auto rw_mode = FileSys::Mode::ReadWrite; + const auto rw_mode = FileSys::OpenMode::ReadWrite; auto nand_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode); - auto load_directory = - vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); - auto sd_load_directory = - vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read); + auto load_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), + FileSys::OpenMode::Read); + auto sd_load_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), + FileSys::OpenMode::Read); auto dump_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); @@ -836,11 +707,6 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove bis_factory->GetUserNANDContents()); } - if (save_data_factory == nullptr) { - save_data_factory = - std::make_unique<FileSys::SaveDataFactory>(system, std::move(nand_directory)); - } - if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory), std::move(sd_load_directory)); @@ -849,12 +715,19 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove } } +void FileSystemController::Reset() { + std::scoped_lock lk{registration_lock}; + registrations.clear(); +} + void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); + const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); }; + server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system)); - server_manager->RegisterNamedService("fsp-srv", std::make_shared<FSP_SRV>(system)); + server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 276d264e1..718500385 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -4,9 +4,11 @@ #pragma once #include <memory> +#include <mutex> #include "common/common_types.h" -#include "core/file_sys/directory.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/fs_directory.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" namespace Core { @@ -26,7 +28,6 @@ class XCI; enum class BisPartitionId : u32; enum class ContentRecordType : u8; -enum class Mode : u32; enum class SaveDataSpaceId : u8; enum class SaveDataType : u8; enum class StorageId : u8; @@ -43,6 +44,9 @@ class ServiceManager; namespace FileSystem { +class RomFsController; +class SaveDataController; + enum class ContentStorageId : u32 { System, User, @@ -54,39 +58,24 @@ enum class ImageDirectoryId : u32 { SdCard, }; -enum class OpenDirectoryMode : u64 { - Directory = (1 << 0), - File = (1 << 1), - All = Directory | File -}; -DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); +using ProcessId = u64; +using ProgramId = u64; class FileSystemController { public: explicit FileSystemController(Core::System& system_); ~FileSystemController(); - Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); - Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); - Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); - Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); - - void SetPackedUpdate(FileSys::VirtualFile update_raw); - FileSys::VirtualFile OpenRomFSCurrentProcess() const; - FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const; - FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, - FileSys::ContentRecordType type) const; - FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const; - std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const; - - Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const; - Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const; - Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, - FileSys::SaveDataSpaceId space) const; + Result RegisterProcess(ProcessId process_id, ProgramId program_id, + std::shared_ptr<FileSys::RomFSFactory>&& factory); + Result OpenProcess(ProgramId* out_program_id, + std::shared_ptr<SaveDataController>* out_save_data_controller, + std::shared_ptr<RomFsController>* out_romfs_controller, + ProcessId process_id); + void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw); + + std::shared_ptr<SaveDataController> OpenSaveDataController(); + Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, FileSys::BisPartitionId id) const; @@ -96,11 +85,6 @@ public: u64 GetFreeSpaceSize(FileSys::StorageId id) const; u64 GetTotalSpaceSize(FileSys::StorageId id) const; - FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, - u128 user_id) const; - void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) const; - void SetGameCard(FileSys::VirtualFile file); FileSys::XCI* GetGameCard() const; @@ -133,15 +117,24 @@ public: FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; - void SetAutoSaveDataCreation(bool enable); - // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); + void Reset(); + private: - std::unique_ptr<FileSys::RomFSFactory> romfs_factory; - std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; + std::shared_ptr<FileSys::SaveDataFactory> CreateSaveDataFactory(ProgramId program_id); + + struct Registration { + ProgramId program_id; + std::shared_ptr<FileSys::RomFSFactory> romfs_factory; + std::shared_ptr<FileSys::SaveDataFactory> save_data_factory; + }; + + std::mutex registration_lock; + std::map<ProcessId, Registration> registrations; + std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; std::unique_ptr<FileSys::BISFactory> bis_factory; @@ -238,7 +231,7 @@ public: * @return Opened file, or error code */ Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path, - FileSys::Mode mode) const; + FileSys::OpenMode mode) const; /** * Open a directory specified by its path @@ -251,7 +244,7 @@ public: * Get the type of the specified path * @return The type of the specified path or error code */ - Result GetEntryType(FileSys::EntryType* out_entry_type, const std::string& path) const; + Result GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path) const; /** * Get the timestamp of the specified path diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp new file mode 100644 index 000000000..39690018b --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/savedata_factory.h" +#include "core/hle/service/filesystem/fsp/fs_i_directory.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +template <typename T> +static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries, + const std::vector<T>& new_data, FileSys::DirectoryEntryType type) { + entries.reserve(entries.size() + new_data.size()); + + for (const auto& new_entry : new_data) { + auto name = new_entry->GetName(); + + if (type == FileSys::DirectoryEntryType::File && + name == FileSys::GetSaveDataSizeFileName()) { + continue; + } + + entries.emplace_back(name, static_cast<s8>(type), + type == FileSys::DirectoryEntryType::Directory ? 0 + : new_entry->GetSize()); + } +} + +IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_, + FileSys::OpenDirectoryMode mode) + : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { + static const FunctionInfo functions[] = { + {0, &IDirectory::Read, "Read"}, + {1, &IDirectory::GetEntryCount, "GetEntryCount"}, + }; + RegisterHandlers(functions); + + // TODO(DarkLordZach): Verify that this is the correct behavior. + // Build entry index now to save time later. + if (True(mode & FileSys::OpenDirectoryMode::Directory)) { + BuildEntryIndex(entries, backend->GetSubdirectories(), + FileSys::DirectoryEntryType::Directory); + } + if (True(mode & FileSys::OpenDirectoryMode::File)) { + BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File); + } +} + +void IDirectory::Read(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called."); + + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>(); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index); + const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries); + const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(actual_entries); +} + +void IDirectory::GetEntryCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + u64 count = entries.size() - next_entry_index; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(count); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.h b/src/core/hle/service/filesystem/fsp/fs_i_directory.h new file mode 100644 index 000000000..793ecfcd7 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/service.h" + +namespace FileSys { +struct DirectoryEntry; +} + +namespace Service::FileSystem { + +class IDirectory final : public ServiceFramework<IDirectory> { +public: + explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, + FileSys::OpenDirectoryMode mode); + +private: + FileSys::VirtualDir backend; + std::vector<FileSys::DirectoryEntry> entries; + u64 next_entry_index = 0; + + void Read(HLERequestContext& ctx); + void GetEntryCount(HLERequestContext& ctx); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.cpp b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp new file mode 100644 index 000000000..9a18f6ec5 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/errors.h" +#include "core/hle/service/filesystem/fsp/fs_i_file.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_) + : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { + static const FunctionInfo functions[] = { + {0, &IFile::Read, "Read"}, + {1, &IFile::Write, "Write"}, + {2, &IFile::Flush, "Flush"}, + {3, &IFile::SetSize, "SetSize"}, + {4, &IFile::GetSize, "GetSize"}, + {5, nullptr, "OperateRange"}, + {6, nullptr, "OperateRangeWithBuffer"}, + }; + RegisterHandlers(functions); +} + +void IFile::Read(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 option = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length); + + // Error checking + if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidSize); + return; + } + if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidOffset); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output = backend->ReadBytes(length, offset); + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u64>(output.size())); +} + +void IFile::Write(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 option = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length); + + // Error checking + if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidSize); + return; + } + if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidOffset); + return; + } + + const auto data = ctx.ReadBuffer(); + + ASSERT_MSG(static_cast<s64>(data.size()) <= length, + "Attempting to write more data than requested (requested={:016X}, actual={:016X}).", + length, data.size()); + + // Write the data to the Storage backend + const auto write_size = + static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length)); + const std::size_t written = backend->Write(data.data(), write_size, offset); + + ASSERT_MSG(static_cast<s64>(written) == length, + "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length, + written); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFile::Flush(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + // Exists for SDK compatibiltity -- No need to flush file. + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFile::SetSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 size = rp.Pop<u64>(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + backend->Resize(size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFile::GetSize(HLERequestContext& ctx) { + const u64 size = backend->GetSize(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(size); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.h b/src/core/hle/service/filesystem/fsp/fs_i_file.h new file mode 100644 index 000000000..5e5430c67 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_file.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class IFile final : public ServiceFramework<IFile> { +public: + explicit IFile(Core::System& system_, FileSys::VirtualFile backend_); + +private: + FileSys::VirtualFile backend; + + void Read(HLERequestContext& ctx); + void Write(HLERequestContext& ctx); + void Flush(HLERequestContext& ctx); + void SetSize(HLERequestContext& ctx); + void GetSize(HLERequestContext& ctx); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp new file mode 100644 index 000000000..efa394dd1 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/string_util.h" +#include "core/hle/service/filesystem/fsp/fs_i_directory.h" +#include "core/hle/service/filesystem/fsp/fs_i_file.h" +#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) + : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( + size_)} { + static const FunctionInfo functions[] = { + {0, &IFileSystem::CreateFile, "CreateFile"}, + {1, &IFileSystem::DeleteFile, "DeleteFile"}, + {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, + {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, + {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, + {5, &IFileSystem::RenameFile, "RenameFile"}, + {6, nullptr, "RenameDirectory"}, + {7, &IFileSystem::GetEntryType, "GetEntryType"}, + {8, &IFileSystem::OpenFile, "OpenFile"}, + {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, + {10, &IFileSystem::Commit, "Commit"}, + {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, + {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, + {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, + {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, + {15, nullptr, "QueryEntry"}, + {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"}, + }; + RegisterHandlers(functions); +} + +void IFileSystem::CreateFile(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + const u64 file_mode = rp.Pop<u64>(); + const u32 file_size = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode, + file_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CreateFile(name, file_size)); +} + +void IFileSystem::DeleteFile(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. file={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteFile(name)); +} + +void IFileSystem::CreateDirectory(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. directory={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CreateDirectory(name)); +} + +void IFileSystem::DeleteDirectory(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. directory={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectory(name)); +} + +void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. directory={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectoryRecursively(name)); +} + +void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. Directory: {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CleanDirectoryRecursively(name)); +} + +void IFileSystem::RenameFile(HLERequestContext& ctx) { + const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0)); + const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1)); + + LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.RenameFile(src_name, dst_name)); +} + +void IFileSystem::OpenFile(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>()); + + LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode); + + FileSys::VirtualFile vfs_file{}; + auto result = backend.OpenFile(&vfs_file, name, mode); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + auto file = std::make_shared<IFile>(system, vfs_file); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IFile>(std::move(file)); +} + +void IFileSystem::OpenDirectory(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>(); + + LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode); + + FileSys::VirtualDir vfs_dir{}; + auto result = backend.OpenDirectory(&vfs_dir, name); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IDirectory>(std::move(directory)); +} + +void IFileSystem::GetEntryType(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. file={}", name); + + FileSys::DirectoryEntryType vfs_entry_type{}; + auto result = backend.GetEntryType(&vfs_entry_type, name); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(static_cast<u32>(vfs_entry_type)); +} + +void IFileSystem::Commit(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(size.get_free_size()); +} + +void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(size.get_total_size()); +} + +void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name); + + FileSys::FileTimeStampRaw vfs_timestamp{}; + auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(ResultSuccess); + rb.PushRaw(vfs_timestamp); +} + +void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + struct FileSystemAttribute { + u8 dir_entry_name_length_max_defined; + u8 file_entry_name_length_max_defined; + u8 dir_path_name_length_max_defined; + u8 file_path_name_length_max_defined; + INSERT_PADDING_BYTES_NOINIT(0x5); + u8 utf16_dir_entry_name_length_max_defined; + u8 utf16_file_entry_name_length_max_defined; + u8 utf16_dir_path_name_length_max_defined; + u8 utf16_file_path_name_length_max_defined; + INSERT_PADDING_BYTES_NOINIT(0x18); + s32 dir_entry_name_length_max; + s32 file_entry_name_length_max; + s32 dir_path_name_length_max; + s32 file_path_name_length_max; + INSERT_PADDING_WORDS_NOINIT(0x5); + s32 utf16_dir_entry_name_length_max; + s32 utf16_file_entry_name_length_max; + s32 utf16_dir_path_name_length_max; + s32 utf16_file_path_name_length_max; + INSERT_PADDING_WORDS_NOINIT(0x18); + INSERT_PADDING_WORDS_NOINIT(0x1); + }; + static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size"); + + FileSystemAttribute savedata_attribute{}; + savedata_attribute.dir_entry_name_length_max_defined = true; + savedata_attribute.file_entry_name_length_max_defined = true; + savedata_attribute.dir_entry_name_length_max = 0x40; + savedata_attribute.file_entry_name_length_max = 0x40; + + IPC::ResponseBuilder rb{ctx, 50}; + rb.Push(ResultSuccess); + rb.PushRaw(savedata_attribute); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h new file mode 100644 index 000000000..b06b3ef0e --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp/fsp_util.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class IFileSystem final : public ServiceFramework<IFileSystem> { +public: + explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_); + + void CreateFile(HLERequestContext& ctx); + void DeleteFile(HLERequestContext& ctx); + void CreateDirectory(HLERequestContext& ctx); + void DeleteDirectory(HLERequestContext& ctx); + void DeleteDirectoryRecursively(HLERequestContext& ctx); + void CleanDirectoryRecursively(HLERequestContext& ctx); + void RenameFile(HLERequestContext& ctx); + void OpenFile(HLERequestContext& ctx); + void OpenDirectory(HLERequestContext& ctx); + void GetEntryType(HLERequestContext& ctx); + void Commit(HLERequestContext& ctx); + void GetFreeSpaceSize(HLERequestContext& ctx); + void GetTotalSpaceSize(HLERequestContext& ctx); + void GetFileTimeStampRaw(HLERequestContext& ctx); + void GetFileSystemAttribute(HLERequestContext& ctx); + +private: + VfsDirectoryServiceWrapper backend; + SizeGetter size; +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp new file mode 100644 index 000000000..98223c1f9 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/errors.h" +#include "core/hle/service/filesystem/fsp/fs_i_storage.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_) + : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Read, "Read"}, + {1, nullptr, "Write"}, + {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, + {4, &IStorage::GetSize, "GetSize"}, + {5, nullptr, "OperateRange"}, + }; + RegisterHandlers(functions); +} + +void IStorage::Read(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + + // Error checking + if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidSize); + return; + } + if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidOffset); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output = backend->ReadBytes(length, offset); + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IStorage::GetSize(HLERequestContext& ctx) { + const u64 size = backend->GetSize(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(size); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.h b/src/core/hle/service/filesystem/fsp/fs_i_storage.h new file mode 100644 index 000000000..cb5bebcc9 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class IStorage final : public ServiceFramework<IStorage> { +public: + explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_); + +private: + FileSys::VirtualFile backend; + + void Read(HLERequestContext& ctx); + void GetSize(HLERequestContext& ctx); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp new file mode 100644 index 000000000..8ee733f47 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/filesystem/fsp/fsp_ldr.h" + +namespace Service::FileSystem { + +FSP_LDR::FSP_LDR(Core::System& system_) : ServiceFramework{system_, "fsp:ldr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "OpenCodeFileSystem"}, + {1, nullptr, "IsArchivedProgram"}, + {2, nullptr, "SetCurrentProcess"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +FSP_LDR::~FSP_LDR() = default; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp/fsp_ldr.h index 358739a87..358739a87 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.h +++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.h diff --git a/src/core/hle/service/filesystem/fsp/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp new file mode 100644 index 000000000..7c03ebaea --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/filesystem/fsp/fsp_pr.h" + +namespace Service::FileSystem { + +FSP_PR::FSP_PR(Core::System& system_) : ServiceFramework{system_, "fsp:pr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterProgram"}, + {1, nullptr, "UnregisterProgram"}, + {2, nullptr, "SetCurrentProcess"}, + {256, nullptr, "SetEnabledProgramVerification"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +FSP_PR::~FSP_PR() = default; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp/fsp_pr.h index bd4e0a730..bd4e0a730 100644 --- a/src/core/hle/service/filesystem/fsp_pr.h +++ b/src/core/hle/service/filesystem/fsp/fsp_pr.h diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp new file mode 100644 index 000000000..2be72b021 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -0,0 +1,727 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <cinttypes> +#include <cstring> +#include <iterator> +#include <string> +#include <utility> +#include <vector> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/fs_directory.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/savedata_factory.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/result.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h" +#include "core/hle/service/filesystem/fsp/fs_i_storage.h" +#include "core/hle/service/filesystem/fsp/fsp_srv.h" +#include "core/hle/service/filesystem/romfs_controller.h" +#include "core/hle/service/filesystem/save_data_controller.h" +#include "core/hle/service/hle_ipc.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/reporter.h" + +namespace Service::FileSystem { +enum class FileSystemType : u8 { + Invalid0 = 0, + Invalid1 = 1, + Logo = 2, + ContentControl = 3, + ContentManual = 4, + ContentMeta = 5, + ContentData = 6, + ApplicationPackage = 7, +}; + +class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { +public: + explicit ISaveDataInfoReader(Core::System& system_, + std::shared_ptr<SaveDataController> save_data_controller_, + FileSys::SaveDataSpaceId space) + : ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{ + save_data_controller_} { + static const FunctionInfo functions[] = { + {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, + }; + RegisterHandlers(functions); + + FindAllSaves(space); + } + + void ReadSaveDataInfo(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferNumElements<SaveDataInfo>(); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index); + const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries); + const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(actual_entries); + } + +private: + static u64 stoull_be(std::string_view str) { + if (str.size() != 16) + return 0; + + const auto bytes = Common::HexStringToArray<0x8>(str); + u64 out{}; + std::memcpy(&out, bytes.data(), sizeof(u64)); + + return Common::swap64(out); + } + + void FindAllSaves(FileSys::SaveDataSpaceId space) { + FileSys::VirtualDir save_root{}; + const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space); + + if (result != ResultSuccess || save_root == nullptr) { + LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); + return; + } + + for (const auto& type : save_root->GetSubdirectories()) { + if (type->GetName() == "save") { + for (const auto& save_id : type->GetSubdirectories()) { + for (const auto& user_id : save_id->GetSubdirectories()) { + const auto save_id_numeric = stoull_be(save_id->GetName()); + auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + if (save_id_numeric != 0) { + // System Save Data + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::SystemSaveData, + {}, + user_id_numeric, + save_id_numeric, + 0, + user_id->GetSize(), + {}, + {}, + }); + + continue; + } + + for (const auto& title_id : user_id->GetSubdirectories()) { + const auto device = + std::all_of(user_id_numeric.begin(), user_id_numeric.end(), + [](u8 val) { return val == 0; }); + info.emplace_back(SaveDataInfo{ + 0, + space, + device ? FileSys::SaveDataType::DeviceSaveData + : FileSys::SaveDataType::SaveData, + {}, + user_id_numeric, + save_id_numeric, + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + {}, + }); + } + } + } + } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { + // Temporary Storage + for (const auto& user_id : type->GetSubdirectories()) { + for (const auto& title_id : user_id->GetSubdirectories()) { + if (!title_id->GetFiles().empty() || + !title_id->GetSubdirectories().empty()) { + auto user_id_numeric = + Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::TemporaryStorage, + {}, + user_id_numeric, + stoull_be(type->GetName()), + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + {}, + }); + } + } + } + } + } + } + + struct SaveDataInfo { + u64_le save_id_unknown; + FileSys::SaveDataSpaceId space; + FileSys::SaveDataType type; + INSERT_PADDING_BYTES(0x6); + std::array<u8, 0x10> user_id; + u64_le save_id; + u64_le title_id; + u64_le save_image_size; + u16_le index; + FileSys::SaveDataRank rank; + INSERT_PADDING_BYTES(0x25); + }; + static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); + + ProcessId process_id = 0; + std::shared_ptr<SaveDataController> save_data_controller; + std::vector<SaveDataInfo> info; + u64 next_entry_index = 0; +}; + +FSP_SRV::FSP_SRV(Core::System& system_) + : ServiceFramework{system_, "fsp-srv"}, fsc{system.GetFileSystemController()}, + content_provider{system.GetContentProvider()}, reporter{system.GetReporter()} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "OpenFileSystem"}, + {1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"}, + {2, nullptr, "OpenDataFileSystemByCurrentProcess"}, + {7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"}, + {8, nullptr, "OpenFileSystemWithId"}, + {9, nullptr, "OpenDataFileSystemByApplicationId"}, + {11, nullptr, "OpenBisFileSystem"}, + {12, nullptr, "OpenBisStorage"}, + {13, nullptr, "InvalidateBisCache"}, + {17, nullptr, "OpenHostFileSystem"}, + {18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"}, + {19, nullptr, "FormatSdCardFileSystem"}, + {21, nullptr, "DeleteSaveDataFileSystem"}, + {22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"}, + {23, &FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId, "CreateSaveDataFileSystemBySystemSaveDataId"}, + {24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"}, + {25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"}, + {26, nullptr, "FormatSdCardDryRun"}, + {27, nullptr, "IsExFatSupported"}, + {28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"}, + {30, nullptr, "OpenGameCardStorage"}, + {31, nullptr, "OpenGameCardFileSystem"}, + {32, nullptr, "ExtendSaveDataFileSystem"}, + {33, nullptr, "DeleteCacheStorage"}, + {34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"}, + {35, nullptr, "CreateSaveDataFileSystemByHashSalt"}, + {36, nullptr, "OpenHostFileSystemWithOption"}, + {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"}, + {52, &FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId, "OpenSaveDataFileSystemBySystemSaveDataId"}, + {53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"}, + {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"}, + {58, nullptr, "ReadSaveDataFileSystemExtraData"}, + {59, nullptr, "WriteSaveDataFileSystemExtraData"}, + {60, nullptr, "OpenSaveDataInfoReader"}, + {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, + {62, &FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage, "OpenSaveDataInfoReaderOnlyCacheStorage"}, + {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, + {65, nullptr, "UpdateSaveDataMacForDebug"}, + {66, nullptr, "WriteSaveDataFileSystemExtraData2"}, + {67, nullptr, "FindSaveDataWithFilter"}, + {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, + {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, + {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, + {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, + {80, nullptr, "OpenSaveDataMetaFile"}, + {81, nullptr, "OpenSaveDataTransferManager"}, + {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, + {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, + {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"}, + {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"}, + {86, nullptr, "OpenSaveDataMover"}, + {87, nullptr, "OpenSaveDataTransferManagerForRepair"}, + {100, nullptr, "OpenImageDirectoryFileSystem"}, + {101, nullptr, "OpenBaseFileSystem"}, + {102, nullptr, "FormatBaseFileSystem"}, + {110, nullptr, "OpenContentStorageFileSystem"}, + {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, + {130, nullptr, "OpenCustomStorageFileSystem"}, + {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, + {201, nullptr, "OpenDataStorageByProgramId"}, + {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, + {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, + {204, nullptr, "OpenDataFileSystemByProgramIndex"}, + {205, &FSP_SRV::OpenDataStorageWithProgramIndex, "OpenDataStorageWithProgramIndex"}, + {206, nullptr, "OpenDataStorageByPath"}, + {400, nullptr, "OpenDeviceOperator"}, + {500, nullptr, "OpenSdCardDetectionEventNotifier"}, + {501, nullptr, "OpenGameCardDetectionEventNotifier"}, + {510, nullptr, "OpenSystemDataUpdateEventNotifier"}, + {511, nullptr, "NotifySystemDataUpdateEvent"}, + {520, nullptr, "SimulateGameCardDetectionEvent"}, + {600, nullptr, "SetCurrentPosixTime"}, + {601, nullptr, "QuerySaveDataTotalSize"}, + {602, nullptr, "VerifySaveDataFileSystem"}, + {603, nullptr, "CorruptSaveDataFileSystem"}, + {604, nullptr, "CreatePaddingFile"}, + {605, nullptr, "DeleteAllPaddingFiles"}, + {606, nullptr, "GetRightsId"}, + {607, nullptr, "RegisterExternalKey"}, + {608, nullptr, "UnregisterAllExternalKey"}, + {609, nullptr, "GetRightsIdByPath"}, + {610, nullptr, "GetRightsIdAndKeyGenerationByPath"}, + {611, nullptr, "SetCurrentPosixTimeWithTimeDifference"}, + {612, nullptr, "GetFreeSpaceSizeForSaveData"}, + {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"}, + {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, + {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, + {616, nullptr, "GetSaveDataCommitId"}, + {617, nullptr, "UnregisterExternalKey"}, + {620, nullptr, "SetSdCardEncryptionSeed"}, + {630, nullptr, "SetSdCardAccessibility"}, + {631, nullptr, "IsSdCardAccessible"}, + {640, nullptr, "IsSignedSystemPartitionOnSdCardValid"}, + {700, nullptr, "OpenAccessFailureResolver"}, + {701, nullptr, "GetAccessFailureDetectionEvent"}, + {702, nullptr, "IsAccessFailureDetected"}, + {710, nullptr, "ResolveAccessFailure"}, + {720, nullptr, "AbandonAccessFailure"}, + {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, + {810, nullptr, "RegisterProgramIndexMapInfo"}, + {1000, nullptr, "SetBisRootForHost"}, + {1001, nullptr, "SetSaveDataSize"}, + {1002, nullptr, "SetSaveDataRootPath"}, + {1003, &FSP_SRV::DisableAutoSaveDataCreation, "DisableAutoSaveDataCreation"}, + {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"}, + {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, + {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"}, + {1007, nullptr, "RegisterUpdatePartition"}, + {1008, nullptr, "OpenRegisteredUpdatePartition"}, + {1009, nullptr, "GetAndClearMemoryReportInfo"}, + {1010, nullptr, "SetDataStorageRedirectTarget"}, + {1011, &FSP_SRV::GetProgramIndexForAccessLog, "GetProgramIndexForAccessLog"}, + {1012, nullptr, "GetFsStackUsage"}, + {1013, nullptr, "UnsetSaveDataRootPath"}, + {1014, nullptr, "OutputMultiProgramTagAccessLog"}, + {1016, nullptr, "FlushAccessLogOnSdCard"}, + {1017, nullptr, "OutputApplicationInfoAccessLog"}, + {1018, nullptr, "SetDebugOption"}, + {1019, nullptr, "UnsetDebugOption"}, + {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, + {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, + {1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"}, + {1300, nullptr, "OpenBisWiper"}, + }; + // clang-format on + RegisterHandlers(functions); + + if (Settings::values.enable_fs_access_log) { + access_log_mode = AccessLogMode::SdCard; + } +} + +FSP_SRV::~FSP_SRV() = default; + +void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { + current_process_id = ctx.GetPID(); + + LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); + + const auto res = + fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto type = rp.PopRaw<FileSystemType>(); + const auto title_id = rp.PopRaw<u64>(); + LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}", type, title_id); + + IPC::ResponseBuilder rb{ctx, 2, 0, 0}; + rb.Push(ResultUnknown); +} + +void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + FileSys::VirtualDir sdmc_dir{}; + fsc.OpenSDMC(&sdmc_dir); + + auto filesystem = std::make_shared<IFileSystem>( + system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + +void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); + [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); + u128 uid = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), + uid[1], uid[0]); + + FileSys::VirtualDir save_data_dir{}; + save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, + save_struct); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); + [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); + + LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); + + FileSys::VirtualDir save_data_dir{}; + save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, + save_struct); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + struct Parameters { + FileSys::SaveDataSpaceId space_id; + FileSys::SaveDataAttribute attribute; + }; + + const auto parameters = rp.PopRaw<Parameters>(); + + LOG_INFO(Service_FS, "called."); + + FileSys::VirtualDir dir{}; + auto result = + save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2, 0, 0}; + rb.Push(FileSys::ResultTargetNotFound); + return; + } + + FileSys::StorageId id{}; + switch (parameters.space_id) { + case FileSys::SaveDataSpaceId::NandUser: + id = FileSys::StorageId::NandUser; + break; + case FileSys::SaveDataSpaceId::SdCardSystem: + case FileSys::SaveDataSpaceId::SdCardUser: + id = FileSys::StorageId::SdCard; + break; + case FileSys::SaveDataSpaceId::NandSystem: + id = FileSys::StorageId::NandSystem; + break; + case FileSys::SaveDataSpaceId::TemporaryStorage: + case FileSys::SaveDataSpaceId::ProperSystem: + case FileSys::SaveDataSpaceId::SafeMode: + ASSERT(false); + } + + auto filesystem = + std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + +void FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); + OpenSaveDataFileSystem(ctx); +} + +void FSP_SRV::OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); + OpenSaveDataFileSystem(ctx); +} + +void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>(); + LOG_INFO(Service_FS, "called, space={}", space); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISaveDataInfoReader>( + std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space)); +} + +void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISaveDataInfoReader>(system, save_data_controller, + FileSys::SaveDataSpaceId::TemporaryStorage); +} + +void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + struct Parameters { + FileSys::SaveDataSpaceId space_id; + FileSys::SaveDataAttribute attribute; + }; + + const auto parameters = rp.PopRaw<Parameters>(); + // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData + constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None); + + LOG_WARNING(Service_FS, + "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n" + "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n" + "attribute.type={}, attribute.rank={}, attribute.index={}", + flags, parameters.space_id, parameters.attribute.title_id, + parameters.attribute.user_id[1], parameters.attribute.user_id[0], + parameters.attribute.save_id, parameters.attribute.type, parameters.attribute.rank, + parameters.attribute.index); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(flags); +} + +void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + if (!romfs) { + auto current_romfs = romfs_controller->OpenRomFSCurrentProcess(); + if (!current_romfs) { + // TODO (bunnei): Find the right error code to use here + LOG_CRITICAL(Service_FS, "no file system interface available!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + romfs = current_romfs; + } + + auto storage = std::make_shared<IStorage>(system, romfs); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IStorage>(std::move(storage)); +} + +void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto storage_id = rp.PopRaw<FileSys::StorageId>(); + const auto unknown = rp.PopRaw<u32>(); + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", + storage_id, unknown, title_id); + + auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); + + if (!data) { + const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + + if (archive != nullptr) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(std::make_shared<IStorage>(system, archive)); + return; + } + + // TODO(DarkLordZach): Find the right error code to use here + LOG_ERROR(Service_FS, + "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id, + storage_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + const FileSys::PatchManager pm{title_id, fsc, content_provider}; + + auto base = + romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); + auto storage = std::make_shared<IStorage>( + system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IStorage>(std::move(storage)); +} + +void FSP_SRV::OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto storage_id = rp.PopRaw<FileSys::StorageId>(); + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultTargetNotFound); +} + +void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto program_index = rp.PopRaw<u8>(); + + LOG_DEBUG(Service_FS, "called, program_index={}", program_index); + + auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex( + program_id, program_index, FileSys::ContentRecordType::Program); + + if (!patched_romfs) { + // TODO: Find the right error code to use here + LOG_ERROR(Service_FS, "could not open storage with program_index={}", program_index); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto storage = std::make_shared<IStorage>(system, std::move(patched_romfs)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IStorage>(std::move(storage)); +} + +void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + save_data_controller->SetAutoCreate(false); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void FSP_SRV::SetGlobalAccessLogMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + access_log_mode = rp.PopEnum<AccessLogMode>(); + + LOG_DEBUG(Service_FS, "called, access_log_mode={}", access_log_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void FSP_SRV::GetGlobalAccessLogMode(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(access_log_mode); +} + +void FSP_SRV::OutputAccessLogToSdCard(HLERequestContext& ctx) { + const auto raw = ctx.ReadBufferCopy(); + auto log = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(raw.data()), raw.size()); + + LOG_DEBUG(Service_FS, "called"); + + reporter.SaveFSAccessLog(log); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(AccessLogVersion::Latest); + rb.Push(access_log_program_index); +} + +void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index{rp.Pop<s32>()}; + + LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(s64{0}); + rb.Push(s64{0}); +} + +class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> { +public: + explicit IMultiCommitManager(Core::System& system_) + : ServiceFramework{system_, "IMultiCommitManager"} { + static const FunctionInfo functions[] = { + {1, &IMultiCommitManager::Add, "Add"}, + {2, &IMultiCommitManager::Commit, "Commit"}, + }; + RegisterHandlers(functions); + } + +private: + FileSys::VirtualFile backend; + + void Add(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Commit(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + +void FSP_SRV::OpenMultiCommitManager(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>(system)); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h new file mode 100644 index 000000000..26980af99 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h @@ -0,0 +1,78 @@ +// 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 Core { +class Reporter; +} + +namespace FileSys { +class ContentProvider; +class FileSystemBackend; +} // namespace FileSys + +namespace Service::FileSystem { + +class RomFsController; +class SaveDataController; + +enum class AccessLogVersion : u32 { + V7_0_0 = 2, + + Latest = V7_0_0, +}; + +enum class AccessLogMode : u32 { + None, + Log, + SdCard, +}; + +class FSP_SRV final : public ServiceFramework<FSP_SRV> { +public: + explicit FSP_SRV(Core::System& system_); + ~FSP_SRV() override; + +private: + void SetCurrentProcess(HLERequestContext& ctx); + void OpenFileSystemWithPatch(HLERequestContext& ctx); + void OpenSdCardFileSystem(HLERequestContext& ctx); + void CreateSaveDataFileSystem(HLERequestContext& ctx); + void CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx); + void OpenSaveDataFileSystem(HLERequestContext& ctx); + void OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx); + void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx); + void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx); + void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx); + void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx); + void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx); + void OpenDataStorageByCurrentProcess(HLERequestContext& ctx); + void OpenDataStorageByDataId(HLERequestContext& ctx); + void OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx); + void OpenDataStorageWithProgramIndex(HLERequestContext& ctx); + void DisableAutoSaveDataCreation(HLERequestContext& ctx); + void SetGlobalAccessLogMode(HLERequestContext& ctx); + void GetGlobalAccessLogMode(HLERequestContext& ctx); + void OutputAccessLogToSdCard(HLERequestContext& ctx); + void GetProgramIndexForAccessLog(HLERequestContext& ctx); + void OpenMultiCommitManager(HLERequestContext& ctx); + void GetCacheStorageSize(HLERequestContext& ctx); + + FileSystemController& fsc; + const FileSys::ContentProvider& content_provider; + const Core::Reporter& reporter; + + FileSys::VirtualFile romfs; + u64 current_process_id = 0; + u32 access_log_program_index = 0; + AccessLogMode access_log_mode = AccessLogMode::None; + u64 program_id = 0; + std::shared_ptr<SaveDataController> save_data_controller; + std::shared_ptr<RomFsController> romfs_controller; +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fsp_util.h b/src/core/hle/service/filesystem/fsp/fsp_util.h new file mode 100644 index 000000000..253f866db --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fsp_util.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/filesystem/filesystem.h" + +namespace Service::FileSystem { + +struct SizeGetter { + std::function<u64()> get_free_size; + std::function<u64()> get_total_size; + + static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { + return { + [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, + [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, + }; + } +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp deleted file mode 100644 index 1e3366e71..000000000 --- a/src/core/hle/service/filesystem/fsp_ldr.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/filesystem/fsp_ldr.h" - -namespace Service::FileSystem { - -FSP_LDR::FSP_LDR(Core::System& system_) : ServiceFramework{system_, "fsp:ldr"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "OpenCodeFileSystem"}, - {1, nullptr, "IsArchivedProgram"}, - {2, nullptr, "SetCurrentProcess"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -FSP_LDR::~FSP_LDR() = default; - -} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp deleted file mode 100644 index 4ffc31977..000000000 --- a/src/core/hle/service/filesystem/fsp_pr.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/filesystem/fsp_pr.h" - -namespace Service::FileSystem { - -FSP_PR::FSP_PR(Core::System& system_) : ServiceFramework{system_, "fsp:pr"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "RegisterProgram"}, - {1, nullptr, "UnregisterProgram"}, - {2, nullptr, "SetCurrentProcess"}, - {256, nullptr, "SetEnabledProgramVerification"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -FSP_PR::~FSP_PR() = default; - -} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp deleted file mode 100644 index 82ecc1b90..000000000 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ /dev/null @@ -1,1250 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <cinttypes> -#include <cstring> -#include <iterator> -#include <string> -#include <utility> -#include <vector> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/hex_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/file_sys/directory.h" -#include "core/file_sys/errors.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/romfs_factory.h" -#include "core/file_sys/savedata_factory.h" -#include "core/file_sys/system_archive/system_archive.h" -#include "core/file_sys/vfs.h" -#include "core/hle/result.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_srv.h" -#include "core/hle/service/hle_ipc.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/reporter.h" - -namespace Service::FileSystem { - -struct SizeGetter { - std::function<u64()> get_free_size; - std::function<u64()> get_total_size; - - static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { - return { - [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, - [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, - }; - } -}; - -enum class FileSystemType : u8 { - Invalid0 = 0, - Invalid1 = 1, - Logo = 2, - ContentControl = 3, - ContentManual = 4, - ContentMeta = 5, - ContentData = 6, - ApplicationPackage = 7, -}; - -class IStorage final : public ServiceFramework<IStorage> { -public: - explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { - static const FunctionInfo functions[] = { - {0, &IStorage::Read, "Read"}, - {1, nullptr, "Write"}, - {2, nullptr, "Flush"}, - {3, nullptr, "SetSize"}, - {4, &IStorage::GetSize, "GetSize"}, - {5, nullptr, "OperateRange"}, - }; - RegisterHandlers(functions); - } - -private: - FileSys::VirtualFile backend; - - void Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const s64 offset = rp.Pop<s64>(); - const s64 length = rp.Pop<s64>(); - - LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); - - // Error checking - if (length < 0) { - LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_SIZE); - return; - } - if (offset < 0) { - LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_OFFSET); - return; - } - - // Read the data from the Storage backend - std::vector<u8> output = backend->ReadBytes(length, offset); - // Write the data to memory - ctx.WriteBuffer(output); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetSize(HLERequestContext& ctx) { - const u64 size = backend->GetSize(); - LOG_DEBUG(Service_FS, "called, size={}", size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(size); - } -}; - -class IFile final : public ServiceFramework<IFile> { -public: - explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { - static const FunctionInfo functions[] = { - {0, &IFile::Read, "Read"}, - {1, &IFile::Write, "Write"}, - {2, &IFile::Flush, "Flush"}, - {3, &IFile::SetSize, "SetSize"}, - {4, &IFile::GetSize, "GetSize"}, - {5, nullptr, "OperateRange"}, - {6, nullptr, "OperateRangeWithBuffer"}, - }; - RegisterHandlers(functions); - } - -private: - FileSys::VirtualFile backend; - - void Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 option = rp.Pop<u64>(); - const s64 offset = rp.Pop<s64>(); - const s64 length = rp.Pop<s64>(); - - LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, - length); - - // Error checking - if (length < 0) { - LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_SIZE); - return; - } - if (offset < 0) { - LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_OFFSET); - return; - } - - // Read the data from the Storage backend - std::vector<u8> output = backend->ReadBytes(length, offset); - - // Write the data to memory - ctx.WriteBuffer(output); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u64>(output.size())); - } - - void Write(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 option = rp.Pop<u64>(); - const s64 offset = rp.Pop<s64>(); - const s64 length = rp.Pop<s64>(); - - LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, - length); - - // Error checking - if (length < 0) { - LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_SIZE); - return; - } - if (offset < 0) { - LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_OFFSET); - return; - } - - const auto data = ctx.ReadBuffer(); - - ASSERT_MSG( - static_cast<s64>(data.size()) <= length, - "Attempting to write more data than requested (requested={:016X}, actual={:016X}).", - length, data.size()); - - // Write the data to the Storage backend - const auto write_size = - static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length)); - const std::size_t written = backend->Write(data.data(), write_size, offset); - - ASSERT_MSG(static_cast<s64>(written) == length, - "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length, - written); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Flush(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - // Exists for SDK compatibiltity -- No need to flush file. - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void SetSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 size = rp.Pop<u64>(); - LOG_DEBUG(Service_FS, "called, size={}", size); - - backend->Resize(size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetSize(HLERequestContext& ctx) { - const u64 size = backend->GetSize(); - LOG_DEBUG(Service_FS, "called, size={}", size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(size); - } -}; - -template <typename T> -static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, - FileSys::EntryType type) { - entries.reserve(entries.size() + new_data.size()); - - for (const auto& new_entry : new_data) { - auto name = new_entry->GetName(); - - if (type == FileSys::EntryType::File && name == FileSys::GetSaveDataSizeFileName()) { - continue; - } - - entries.emplace_back(name, type, - type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize()); - } -} - -class IDirectory final : public ServiceFramework<IDirectory> { -public: - explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, OpenDirectoryMode mode) - : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { - static const FunctionInfo functions[] = { - {0, &IDirectory::Read, "Read"}, - {1, &IDirectory::GetEntryCount, "GetEntryCount"}, - }; - RegisterHandlers(functions); - - // TODO(DarkLordZach): Verify that this is the correct behavior. - // Build entry index now to save time later. - if (True(mode & OpenDirectoryMode::Directory)) { - BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory); - } - if (True(mode & OpenDirectoryMode::File)) { - BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File); - } - } - -private: - FileSys::VirtualDir backend; - std::vector<FileSys::Entry> entries; - u64 next_entry_index = 0; - - void Read(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called."); - - // Calculate how many entries we can fit in the output buffer - const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>(); - - // Cap at total number of entries. - const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); - - // Determine data start and end - const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index); - const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries); - const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); - - next_entry_index += actual_entries; - - // Write the data to memory - ctx.WriteBuffer(begin, range_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(actual_entries); - } - - void GetEntryCount(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - u64 count = entries.size() - next_entry_index; - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(count); - } -}; - -class IFileSystem final : public ServiceFramework<IFileSystem> { -public: - explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) - : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( - size_)} { - static const FunctionInfo functions[] = { - {0, &IFileSystem::CreateFile, "CreateFile"}, - {1, &IFileSystem::DeleteFile, "DeleteFile"}, - {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, - {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, - {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, - {5, &IFileSystem::RenameFile, "RenameFile"}, - {6, nullptr, "RenameDirectory"}, - {7, &IFileSystem::GetEntryType, "GetEntryType"}, - {8, &IFileSystem::OpenFile, "OpenFile"}, - {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, - {10, &IFileSystem::Commit, "Commit"}, - {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, - {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, - {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, - {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, - {15, nullptr, "QueryEntry"}, - {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"}, - }; - RegisterHandlers(functions); - } - - void CreateFile(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - const u64 file_mode = rp.Pop<u64>(); - const u32 file_size = rp.Pop<u32>(); - - LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode, - file_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.CreateFile(name, file_size)); - } - - void DeleteFile(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. file={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.DeleteFile(name)); - } - - void CreateDirectory(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. directory={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.CreateDirectory(name)); - } - - void DeleteDirectory(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. directory={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.DeleteDirectory(name)); - } - - void DeleteDirectoryRecursively(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. directory={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.DeleteDirectoryRecursively(name)); - } - - void CleanDirectoryRecursively(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. Directory: {}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.CleanDirectoryRecursively(name)); - } - - void RenameFile(HLERequestContext& ctx) { - const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0)); - const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1)); - - LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.RenameFile(src_name, dst_name)); - } - - void OpenFile(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); - - LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode); - - FileSys::VirtualFile vfs_file{}; - auto result = backend.OpenFile(&vfs_file, name, mode); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - auto file = std::make_shared<IFile>(system, vfs_file); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IFile>(std::move(file)); - } - - void OpenDirectory(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - const auto mode = rp.PopRaw<OpenDirectoryMode>(); - - LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode); - - FileSys::VirtualDir vfs_dir{}; - auto result = backend.OpenDirectory(&vfs_dir, name); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDirectory>(std::move(directory)); - } - - void GetEntryType(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. file={}", name); - - FileSys::EntryType vfs_entry_type{}; - auto result = backend.GetEntryType(&vfs_entry_type, name); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(vfs_entry_type)); - } - - void Commit(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetFreeSpaceSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(size.get_free_size()); - } - - void GetTotalSpaceSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(size.get_total_size()); - } - - void GetFileTimeStampRaw(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name); - - FileSys::FileTimeStampRaw vfs_timestamp{}; - auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(ResultSuccess); - rb.PushRaw(vfs_timestamp); - } - - void GetFileSystemAttribute(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - struct FileSystemAttribute { - u8 dir_entry_name_length_max_defined; - u8 file_entry_name_length_max_defined; - u8 dir_path_name_length_max_defined; - u8 file_path_name_length_max_defined; - INSERT_PADDING_BYTES_NOINIT(0x5); - u8 utf16_dir_entry_name_length_max_defined; - u8 utf16_file_entry_name_length_max_defined; - u8 utf16_dir_path_name_length_max_defined; - u8 utf16_file_path_name_length_max_defined; - INSERT_PADDING_BYTES_NOINIT(0x18); - s32 dir_entry_name_length_max; - s32 file_entry_name_length_max; - s32 dir_path_name_length_max; - s32 file_path_name_length_max; - INSERT_PADDING_WORDS_NOINIT(0x5); - s32 utf16_dir_entry_name_length_max; - s32 utf16_file_entry_name_length_max; - s32 utf16_dir_path_name_length_max; - s32 utf16_file_path_name_length_max; - INSERT_PADDING_WORDS_NOINIT(0x18); - INSERT_PADDING_WORDS_NOINIT(0x1); - }; - static_assert(sizeof(FileSystemAttribute) == 0xc0, - "FileSystemAttribute has incorrect size"); - - FileSystemAttribute savedata_attribute{}; - savedata_attribute.dir_entry_name_length_max_defined = true; - savedata_attribute.file_entry_name_length_max_defined = true; - savedata_attribute.dir_entry_name_length_max = 0x40; - savedata_attribute.file_entry_name_length_max = 0x40; - - IPC::ResponseBuilder rb{ctx, 50}; - rb.Push(ResultSuccess); - rb.PushRaw(savedata_attribute); - } - -private: - VfsDirectoryServiceWrapper backend; - SizeGetter size; -}; - -class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { -public: - explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space, - FileSystemController& fsc_) - : ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} { - static const FunctionInfo functions[] = { - {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, - }; - RegisterHandlers(functions); - - FindAllSaves(space); - } - - void ReadSaveDataInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - // Calculate how many entries we can fit in the output buffer - const u64 count_entries = ctx.GetWriteBufferNumElements<SaveDataInfo>(); - - // Cap at total number of entries. - const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); - - // Determine data start and end - const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index); - const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries); - const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); - - next_entry_index += actual_entries; - - // Write the data to memory - ctx.WriteBuffer(begin, range_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(actual_entries); - } - -private: - static u64 stoull_be(std::string_view str) { - if (str.size() != 16) - return 0; - - const auto bytes = Common::HexStringToArray<0x8>(str); - u64 out{}; - std::memcpy(&out, bytes.data(), sizeof(u64)); - - return Common::swap64(out); - } - - void FindAllSaves(FileSys::SaveDataSpaceId space) { - FileSys::VirtualDir save_root{}; - const auto result = fsc.OpenSaveDataSpace(&save_root, space); - - if (result != ResultSuccess || save_root == nullptr) { - LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); - return; - } - - for (const auto& type : save_root->GetSubdirectories()) { - if (type->GetName() == "save") { - for (const auto& save_id : type->GetSubdirectories()) { - for (const auto& user_id : save_id->GetSubdirectories()) { - const auto save_id_numeric = stoull_be(save_id->GetName()); - auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); - std::reverse(user_id_numeric.begin(), user_id_numeric.end()); - - if (save_id_numeric != 0) { - // System Save Data - info.emplace_back(SaveDataInfo{ - 0, - space, - FileSys::SaveDataType::SystemSaveData, - {}, - user_id_numeric, - save_id_numeric, - 0, - user_id->GetSize(), - {}, - {}, - }); - - continue; - } - - for (const auto& title_id : user_id->GetSubdirectories()) { - const auto device = - std::all_of(user_id_numeric.begin(), user_id_numeric.end(), - [](u8 val) { return val == 0; }); - info.emplace_back(SaveDataInfo{ - 0, - space, - device ? FileSys::SaveDataType::DeviceSaveData - : FileSys::SaveDataType::SaveData, - {}, - user_id_numeric, - save_id_numeric, - stoull_be(title_id->GetName()), - title_id->GetSize(), - {}, - {}, - }); - } - } - } - } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { - // Temporary Storage - for (const auto& user_id : type->GetSubdirectories()) { - for (const auto& title_id : user_id->GetSubdirectories()) { - if (!title_id->GetFiles().empty() || - !title_id->GetSubdirectories().empty()) { - auto user_id_numeric = - Common::HexStringToArray<0x10>(user_id->GetName()); - std::reverse(user_id_numeric.begin(), user_id_numeric.end()); - - info.emplace_back(SaveDataInfo{ - 0, - space, - FileSys::SaveDataType::TemporaryStorage, - {}, - user_id_numeric, - stoull_be(type->GetName()), - stoull_be(title_id->GetName()), - title_id->GetSize(), - {}, - {}, - }); - } - } - } - } - } - } - - struct SaveDataInfo { - u64_le save_id_unknown; - FileSys::SaveDataSpaceId space; - FileSys::SaveDataType type; - INSERT_PADDING_BYTES(0x6); - std::array<u8, 0x10> user_id; - u64_le save_id; - u64_le title_id; - u64_le save_image_size; - u16_le index; - FileSys::SaveDataRank rank; - INSERT_PADDING_BYTES(0x25); - }; - static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); - - FileSystemController& fsc; - std::vector<SaveDataInfo> info; - u64 next_entry_index = 0; -}; - -FSP_SRV::FSP_SRV(Core::System& system_) - : ServiceFramework{system_, "fsp-srv"}, fsc{system.GetFileSystemController()}, - content_provider{system.GetContentProvider()}, reporter{system.GetReporter()} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "OpenFileSystem"}, - {1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"}, - {2, nullptr, "OpenDataFileSystemByCurrentProcess"}, - {7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"}, - {8, nullptr, "OpenFileSystemWithId"}, - {9, nullptr, "OpenDataFileSystemByApplicationId"}, - {11, nullptr, "OpenBisFileSystem"}, - {12, nullptr, "OpenBisStorage"}, - {13, nullptr, "InvalidateBisCache"}, - {17, nullptr, "OpenHostFileSystem"}, - {18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"}, - {19, nullptr, "FormatSdCardFileSystem"}, - {21, nullptr, "DeleteSaveDataFileSystem"}, - {22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"}, - {23, &FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId, "CreateSaveDataFileSystemBySystemSaveDataId"}, - {24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"}, - {25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"}, - {26, nullptr, "FormatSdCardDryRun"}, - {27, nullptr, "IsExFatSupported"}, - {28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"}, - {30, nullptr, "OpenGameCardStorage"}, - {31, nullptr, "OpenGameCardFileSystem"}, - {32, nullptr, "ExtendSaveDataFileSystem"}, - {33, nullptr, "DeleteCacheStorage"}, - {34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"}, - {35, nullptr, "CreateSaveDataFileSystemByHashSalt"}, - {36, nullptr, "OpenHostFileSystemWithOption"}, - {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"}, - {52, &FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId, "OpenSaveDataFileSystemBySystemSaveDataId"}, - {53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"}, - {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"}, - {58, nullptr, "ReadSaveDataFileSystemExtraData"}, - {59, nullptr, "WriteSaveDataFileSystemExtraData"}, - {60, nullptr, "OpenSaveDataInfoReader"}, - {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, - {62, &FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage, "OpenSaveDataInfoReaderOnlyCacheStorage"}, - {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, - {65, nullptr, "UpdateSaveDataMacForDebug"}, - {66, nullptr, "WriteSaveDataFileSystemExtraData2"}, - {67, nullptr, "FindSaveDataWithFilter"}, - {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, - {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, - {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, - {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, - {80, nullptr, "OpenSaveDataMetaFile"}, - {81, nullptr, "OpenSaveDataTransferManager"}, - {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, - {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, - {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"}, - {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"}, - {86, nullptr, "OpenSaveDataMover"}, - {87, nullptr, "OpenSaveDataTransferManagerForRepair"}, - {100, nullptr, "OpenImageDirectoryFileSystem"}, - {101, nullptr, "OpenBaseFileSystem"}, - {102, nullptr, "FormatBaseFileSystem"}, - {110, nullptr, "OpenContentStorageFileSystem"}, - {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, - {130, nullptr, "OpenCustomStorageFileSystem"}, - {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, - {201, nullptr, "OpenDataStorageByProgramId"}, - {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, - {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, - {204, nullptr, "OpenDataFileSystemByProgramIndex"}, - {205, &FSP_SRV::OpenDataStorageWithProgramIndex, "OpenDataStorageWithProgramIndex"}, - {206, nullptr, "OpenDataStorageByPath"}, - {400, nullptr, "OpenDeviceOperator"}, - {500, nullptr, "OpenSdCardDetectionEventNotifier"}, - {501, nullptr, "OpenGameCardDetectionEventNotifier"}, - {510, nullptr, "OpenSystemDataUpdateEventNotifier"}, - {511, nullptr, "NotifySystemDataUpdateEvent"}, - {520, nullptr, "SimulateGameCardDetectionEvent"}, - {600, nullptr, "SetCurrentPosixTime"}, - {601, nullptr, "QuerySaveDataTotalSize"}, - {602, nullptr, "VerifySaveDataFileSystem"}, - {603, nullptr, "CorruptSaveDataFileSystem"}, - {604, nullptr, "CreatePaddingFile"}, - {605, nullptr, "DeleteAllPaddingFiles"}, - {606, nullptr, "GetRightsId"}, - {607, nullptr, "RegisterExternalKey"}, - {608, nullptr, "UnregisterAllExternalKey"}, - {609, nullptr, "GetRightsIdByPath"}, - {610, nullptr, "GetRightsIdAndKeyGenerationByPath"}, - {611, nullptr, "SetCurrentPosixTimeWithTimeDifference"}, - {612, nullptr, "GetFreeSpaceSizeForSaveData"}, - {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"}, - {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, - {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, - {616, nullptr, "GetSaveDataCommitId"}, - {617, nullptr, "UnregisterExternalKey"}, - {620, nullptr, "SetSdCardEncryptionSeed"}, - {630, nullptr, "SetSdCardAccessibility"}, - {631, nullptr, "IsSdCardAccessible"}, - {640, nullptr, "IsSignedSystemPartitionOnSdCardValid"}, - {700, nullptr, "OpenAccessFailureResolver"}, - {701, nullptr, "GetAccessFailureDetectionEvent"}, - {702, nullptr, "IsAccessFailureDetected"}, - {710, nullptr, "ResolveAccessFailure"}, - {720, nullptr, "AbandonAccessFailure"}, - {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, - {810, nullptr, "RegisterProgramIndexMapInfo"}, - {1000, nullptr, "SetBisRootForHost"}, - {1001, nullptr, "SetSaveDataSize"}, - {1002, nullptr, "SetSaveDataRootPath"}, - {1003, &FSP_SRV::DisableAutoSaveDataCreation, "DisableAutoSaveDataCreation"}, - {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"}, - {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, - {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"}, - {1007, nullptr, "RegisterUpdatePartition"}, - {1008, nullptr, "OpenRegisteredUpdatePartition"}, - {1009, nullptr, "GetAndClearMemoryReportInfo"}, - {1010, nullptr, "SetDataStorageRedirectTarget"}, - {1011, &FSP_SRV::GetProgramIndexForAccessLog, "GetProgramIndexForAccessLog"}, - {1012, nullptr, "GetFsStackUsage"}, - {1013, nullptr, "UnsetSaveDataRootPath"}, - {1014, nullptr, "OutputMultiProgramTagAccessLog"}, - {1016, nullptr, "FlushAccessLogOnSdCard"}, - {1017, nullptr, "OutputApplicationInfoAccessLog"}, - {1018, nullptr, "SetDebugOption"}, - {1019, nullptr, "UnsetDebugOption"}, - {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, - {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, - {1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"}, - {1300, nullptr, "OpenBisWiper"}, - }; - // clang-format on - RegisterHandlers(functions); - - if (Settings::values.enable_fs_access_log) { - access_log_mode = AccessLogMode::SdCard; - } - - // This should be true on creation - fsc.SetAutoSaveDataCreation(true); -} - -FSP_SRV::~FSP_SRV() = default; - -void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - current_process_id = rp.Pop<u64>(); - - LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto type = rp.PopRaw<FileSystemType>(); - const auto title_id = rp.PopRaw<u64>(); - LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}", type, title_id); - - IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(ResultUnknown); -} - -void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - FileSys::VirtualDir sdmc_dir{}; - fsc.OpenSDMC(&sdmc_dir); - - auto filesystem = std::make_shared<IFileSystem>( - system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); -} - -void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); - [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); - u128 uid = rp.PopRaw<u128>(); - - LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), - uid[1], uid[0]); - - FileSys::VirtualDir save_data_dir{}; - fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); - [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); - - LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); - - FileSys::VirtualDir save_data_dir{}; - fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - struct Parameters { - FileSys::SaveDataSpaceId space_id; - FileSys::SaveDataAttribute attribute; - }; - - const auto parameters = rp.PopRaw<Parameters>(); - - LOG_INFO(Service_FS, "called."); - - FileSys::VirtualDir dir{}; - auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); - return; - } - - FileSys::StorageId id{}; - switch (parameters.space_id) { - case FileSys::SaveDataSpaceId::NandUser: - id = FileSys::StorageId::NandUser; - break; - case FileSys::SaveDataSpaceId::SdCardSystem: - case FileSys::SaveDataSpaceId::SdCardUser: - id = FileSys::StorageId::SdCard; - break; - case FileSys::SaveDataSpaceId::NandSystem: - id = FileSys::StorageId::NandSystem; - break; - case FileSys::SaveDataSpaceId::TemporaryStorage: - case FileSys::SaveDataSpaceId::ProperSystem: - case FileSys::SaveDataSpaceId::SafeMode: - ASSERT(false); - } - - auto filesystem = - std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id)); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); -} - -void FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); - OpenSaveDataFileSystem(ctx); -} - -void FSP_SRV::OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); - OpenSaveDataFileSystem(ctx); -} - -void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>(); - LOG_INFO(Service_FS, "called, space={}", space); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISaveDataInfoReader>( - std::make_shared<ISaveDataInfoReader>(system, space, fsc)); -} - -void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage, - fsc); -} - -void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called."); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - struct Parameters { - FileSys::SaveDataSpaceId space_id; - FileSys::SaveDataAttribute attribute; - }; - - const auto parameters = rp.PopRaw<Parameters>(); - // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData - constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None); - - LOG_WARNING(Service_FS, - "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n" - "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n" - "attribute.type={}, attribute.rank={}, attribute.index={}", - flags, parameters.space_id, parameters.attribute.title_id, - parameters.attribute.user_id[1], parameters.attribute.user_id[0], - parameters.attribute.save_id, parameters.attribute.type, parameters.attribute.rank, - parameters.attribute.index); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(flags); -} - -void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - if (!romfs) { - auto current_romfs = fsc.OpenRomFSCurrentProcess(); - if (!current_romfs) { - // TODO (bunnei): Find the right error code to use here - LOG_CRITICAL(Service_FS, "no file system interface available!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - romfs = current_romfs; - } - - auto storage = std::make_shared<IStorage>(system, romfs); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>(std::move(storage)); -} - -void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto storage_id = rp.PopRaw<FileSys::StorageId>(); - const auto unknown = rp.PopRaw<u32>(); - const auto title_id = rp.PopRaw<u64>(); - - LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", - storage_id, unknown, title_id); - - auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); - - if (!data) { - const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); - - if (archive != nullptr) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(std::make_shared<IStorage>(system, archive)); - return; - } - - // TODO(DarkLordZach): Find the right error code to use here - LOG_ERROR(Service_FS, - "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id, - storage_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - const FileSys::PatchManager pm{title_id, fsc, content_provider}; - - auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); - auto storage = std::make_shared<IStorage>( - system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>(std::move(storage)); -} - -void FSP_SRV::OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto storage_id = rp.PopRaw<FileSys::StorageId>(); - const auto title_id = rp.PopRaw<u64>(); - - LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); -} - -void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto program_index = rp.PopRaw<u8>(); - - LOG_DEBUG(Service_FS, "called, program_index={}", program_index); - - auto patched_romfs = - fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, - FileSys::ContentRecordType::Program); - - if (!patched_romfs) { - // TODO: Find the right error code to use here - LOG_ERROR(Service_FS, "could not open storage with program_index={}", program_index); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto storage = std::make_shared<IStorage>(system, std::move(patched_romfs)); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>(std::move(storage)); -} - -void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - fsc.SetAutoSaveDataCreation(false); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::SetGlobalAccessLogMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - access_log_mode = rp.PopEnum<AccessLogMode>(); - - LOG_DEBUG(Service_FS, "called, access_log_mode={}", access_log_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::GetGlobalAccessLogMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(access_log_mode); -} - -void FSP_SRV::OutputAccessLogToSdCard(HLERequestContext& ctx) { - const auto raw = ctx.ReadBufferCopy(); - auto log = Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(raw.data()), raw.size()); - - LOG_DEBUG(Service_FS, "called"); - - reporter.SaveFSAccessLog(log); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(AccessLogVersion::Latest); - rb.Push(access_log_program_index); -} - -void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto index{rp.Pop<s32>()}; - - LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(s64{0}); - rb.Push(s64{0}); -} - -class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> { -public: - explicit IMultiCommitManager(Core::System& system_) - : ServiceFramework{system_, "IMultiCommitManager"} { - static const FunctionInfo functions[] = { - {1, &IMultiCommitManager::Add, "Add"}, - {2, &IMultiCommitManager::Commit, "Commit"}, - }; - RegisterHandlers(functions); - } - -private: - FileSys::VirtualFile backend; - - void Add(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Commit(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } -}; - -void FSP_SRV::OpenMultiCommitManager(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>(system)); -} - -} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h deleted file mode 100644 index 280bc9867..000000000 --- a/src/core/hle/service/filesystem/fsp_srv.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 <memory> -#include "core/hle/service/service.h" - -namespace Core { -class Reporter; -} - -namespace FileSys { -class ContentProvider; -class FileSystemBackend; -} // namespace FileSys - -namespace Service::FileSystem { - -enum class AccessLogVersion : u32 { - V7_0_0 = 2, - - Latest = V7_0_0, -}; - -enum class AccessLogMode : u32 { - None, - Log, - SdCard, -}; - -class FSP_SRV final : public ServiceFramework<FSP_SRV> { -public: - explicit FSP_SRV(Core::System& system_); - ~FSP_SRV() override; - -private: - void SetCurrentProcess(HLERequestContext& ctx); - void OpenFileSystemWithPatch(HLERequestContext& ctx); - void OpenSdCardFileSystem(HLERequestContext& ctx); - void CreateSaveDataFileSystem(HLERequestContext& ctx); - void CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx); - void OpenSaveDataFileSystem(HLERequestContext& ctx); - void OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx); - void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx); - void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx); - void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx); - void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx); - void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx); - void OpenDataStorageByCurrentProcess(HLERequestContext& ctx); - void OpenDataStorageByDataId(HLERequestContext& ctx); - void OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx); - void OpenDataStorageWithProgramIndex(HLERequestContext& ctx); - void DisableAutoSaveDataCreation(HLERequestContext& ctx); - void SetGlobalAccessLogMode(HLERequestContext& ctx); - void GetGlobalAccessLogMode(HLERequestContext& ctx); - void OutputAccessLogToSdCard(HLERequestContext& ctx); - void GetProgramIndexForAccessLog(HLERequestContext& ctx); - void OpenMultiCommitManager(HLERequestContext& ctx); - void GetCacheStorageSize(HLERequestContext& ctx); - - FileSystemController& fsc; - const FileSys::ContentProvider& content_provider; - const Core::Reporter& reporter; - - FileSys::VirtualFile romfs; - u64 current_process_id = 0; - u32 access_log_program_index = 0; - AccessLogMode access_log_mode = AccessLogMode::None; -}; - -} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/romfs_controller.cpp b/src/core/hle/service/filesystem/romfs_controller.cpp new file mode 100644 index 000000000..19c9cec72 --- /dev/null +++ b/src/core/hle/service/filesystem/romfs_controller.cpp @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/filesystem/romfs_controller.h" + +namespace Service::FileSystem { + +RomFsController::RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_) + : factory{std::move(factory_)}, program_id{program_id_} {} +RomFsController::~RomFsController() = default; + +FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() { + return factory->OpenCurrentProcess(program_id); +} + +FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id, + FileSys::ContentRecordType type) { + return factory->OpenPatchedRomFS(title_id, type); +} + +FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex( + u64 title_id, u8 program_index, FileSys::ContentRecordType type) { + return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); +} + +FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type) { + return factory->Open(title_id, storage_id, type); +} + +std::shared_ptr<FileSys::NCA> RomFsController::OpenBaseNca(u64 title_id, + FileSys::StorageId storage_id, + FileSys::ContentRecordType type) { + return factory->GetEntry(title_id, storage_id, type); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h new file mode 100644 index 000000000..3c3ead344 --- /dev/null +++ b/src/core/hle/service/filesystem/romfs_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/file_sys/nca_metadata.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/vfs/vfs_types.h" + +namespace Service::FileSystem { + +class RomFsController { +public: + explicit RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_); + ~RomFsController(); + + FileSys::VirtualFile OpenRomFSCurrentProcess(); + FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type); + FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, + FileSys::ContentRecordType type); + FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type); + std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type); + +private: + const std::shared_ptr<FileSys::RomFSFactory> factory; + const u64 program_id; +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp new file mode 100644 index 000000000..03e45f7f9 --- /dev/null +++ b/src/core/hle/service/filesystem/save_data_controller.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/patch_manager.h" +#include "core/hle/service/filesystem/save_data_controller.h" +#include "core/loader/loader.h" + +namespace Service::FileSystem { + +namespace { + +// A default size for normal/journal save data size if application control metadata cannot be found. +// This should be large enough to satisfy even the most extreme requirements (~4.2GB) +constexpr u64 SufficientSaveDataSize = 0xF0000000; + +FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) { + const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto metadata = pm.GetControlMetadata(); + const auto& nacp = metadata.first; + + if (nacp != nullptr) { + return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()}; + } + + return {SufficientSaveDataSize, SufficientSaveDataSize}; +} + +} // namespace + +SaveDataController::SaveDataController(Core::System& system_, + std::shared_ptr<FileSys::SaveDataFactory> factory_) + : system{system_}, factory{std::move(factory_)} {} +SaveDataController::~SaveDataController() = default; + +Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data, + FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute) { + LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, + attribute.DebugInfo()); + + auto save_data = factory->Create(space, attribute); + if (save_data == nullptr) { + return FileSys::ResultTargetNotFound; + } + + *out_save_data = save_data; + return ResultSuccess; +} + +Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data, + FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute) { + auto save_data = factory->Open(space, attribute); + if (save_data == nullptr) { + return FileSys::ResultTargetNotFound; + } + + *out_save_data = save_data; + return ResultSuccess; +} + +Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, + FileSys::SaveDataSpaceId space) { + auto save_data_space = factory->GetSaveDataSpaceDirectory(space); + if (save_data_space == nullptr) { + return FileSys::ResultTargetNotFound; + } + + *out_save_data_space = save_data_space; + return ResultSuccess; +} + +FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, + u128 user_id) { + const auto value = factory->ReadSaveDataSize(type, title_id, user_id); + + if (value.normal == 0 && value.journal == 0) { + const auto size = GetDefaultSaveDataSize(system, title_id); + factory->WriteSaveDataSize(type, title_id, user_id, size); + return size; + } + + return value; +} + +void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) { + factory->WriteSaveDataSize(type, title_id, user_id, new_value); +} + +void SaveDataController::SetAutoCreate(bool state) { + factory->SetAutoCreate(state); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h new file mode 100644 index 000000000..dc9d713df --- /dev/null +++ b/src/core/hle/service/filesystem/save_data_controller.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/savedata_factory.h" +#include "core/file_sys/vfs/vfs_types.h" + +namespace Service::FileSystem { + +class SaveDataController { +public: + explicit SaveDataController(Core::System& system, + std::shared_ptr<FileSys::SaveDataFactory> factory_); + ~SaveDataController(); + + Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute); + Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute); + Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, + FileSys::SaveDataSpaceId space); + + FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); + void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value); + void SetAutoCreate(bool state); + +private: + Core::System& system; + const std::shared_ptr<FileSys::SaveDataFactory> factory; +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 0507b14e7..aeb849efa 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -131,7 +131,7 @@ private: u8 is_favorite; u8 same_app; u8 same_app_played; - u8 arbitary_app_played; + u8 arbitrary_app_played; u64 group_id; }; static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index 6f1151b03..1254b6d49 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -15,9 +15,10 @@ namespace Service::Glue { namespace { -std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) { - const auto& list = system.Kernel().GetProcessList(); - const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { +std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id) { + auto list = system.Kernel().GetProcessList(); + + const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) { return process->GetProcessId() == process_id; }); diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index 993c3d21d..10376bfac 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -8,6 +8,9 @@ #include "core/hle/service/glue/ectx.h" #include "core/hle/service/glue/glue.h" #include "core/hle/service/glue/notif.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/common.h" #include "core/hle/service/server_manager.h" namespace Service::Glue { @@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) { // Notification Services for application server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); + // Time + auto time = std::make_shared<Time::TimeManager>(system); + + server_manager->RegisterNamedService( + "time:u", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u")); + server_manager->RegisterNamedService( + "time:a", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a")); + server_manager->RegisterNamedService( + "time:r", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r")); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp new file mode 100644 index 000000000..f549ed00a --- /dev/null +++ b/src/core/hle/service/glue/time/alarm_worker.cpp @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/alarm_worker.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { + +AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource) + : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{ + steady_clock_resource} {} + +AlarmWorker::~AlarmWorker() { + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + + m_ctx.CloseEvent(m_timer_event); +} + +void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) { + m_time_m = std::move(time_m); + + m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent"); + m_timer_timing_event = Core::Timing::CreateEvent( + "Glue:AlarmWorker::AlarmTimer", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_event->Signal(); + return std::nullopt; + }); + + AttachToClosestAlarmEvent(); +} + +bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, + s64& out_time) { + bool is_valid{}; + Service::PSC::Time::AlarmInfo alarm_info{}; + s64 closest_time{}; + + auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time); + ASSERT(res == ResultSuccess); + + if (is_valid) { + out_alarm_info = alarm_info; + out_time = closest_time; + } + + return is_valid; +} + +void AlarmWorker::OnPowerStateChanged() { + Service::PSC::Time::AlarmInfo closest_alarm_info{}; + s64 closest_time{}; + if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) { + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + m_timer_event->Clear(); + return; + } + + if (closest_alarm_info.alert_time <= closest_time) { + m_time_m->CheckAndSignalAlarms(); + } else { + auto next_time{closest_alarm_info.alert_time - closest_time}; + + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + m_timer_event->Clear(); + + m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time), + m_timer_timing_event); + } +} + +Result AlarmWorker::AttachToClosestAlarmEvent() { + m_time_m->GetClosestAlarmUpdatedEvent(&m_event); + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h new file mode 100644 index 000000000..f269cffdb --- /dev/null +++ b/src/core/hle/service/glue/time/alarm_worker.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class ServiceManager; +} + +namespace Service::Glue::Time { +class StandardSteadyClockResource; + +class AlarmWorker { +public: + explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource); + ~AlarmWorker(); + + void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m); + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + Kernel::KEvent& GetTimerEvent() { + return *m_timer_event; + } + + void OnPowerStateChanged(); + +private: + bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time); + Result AttachToClosestAlarmEvent(); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + + Kernel::KEvent* m_event{}; + Kernel::KEvent* m_timer_event{}; + std::shared_ptr<Core::Timing::EventType> m_timer_timing_event; + StandardSteadyClockResource& m_steady_clock_resource; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp new file mode 100644 index 000000000..5a6309549 --- /dev/null +++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::Glue::Time { + +void FileTimestampWorker::SetFilesystemPosixTime() { + s64 time{}; + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + + if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess && + m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) == + ResultSuccess) { + // TODO IFileSystemProxy::SetCurrentPosixTime + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h new file mode 100644 index 000000000..5f8b9b049 --- /dev/null +++ b/src/core/hle/service/glue/time/file_timestamp_worker.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_types.h" + +namespace Service::PSC::Time { +class SystemClock; +class TimeZoneService; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { + +class FileTimestampWorker { +public: + FileTimestampWorker() = default; + + void SetFilesystemPosixTime(); + + std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{}; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{}; + bool m_initialized{}; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp new file mode 100644 index 000000000..b56762941 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.cpp @@ -0,0 +1,277 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" + +#include "common/settings.h" +#include "common/time_zone.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/time_zone_binary.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#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; +} + +s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) { + constexpr auto is_leap = [](s32 year) -> bool { + return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); + }; + constexpr std::array<s32, 12> MonthStartDayOfYear{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + }; + + s16 month_s16{calendar.month}; + s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) + + ((month_s16 * 43) >> 9))}; + s8 month_index{static_cast<s8>(calendar.month - 12 * month)}; + if (month_index == 0) { + month_index = 12; + } + s32 year{(month + calendar.year) - !month_index}; + s32 v8{year >= 0 ? year : year + 3}; + + s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1]; + days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365; + + if (month_index <= 2 && is_leap(year)) { + days_since_epoch--; + } + auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll + + calendar.second}; + return epoch_s - 62135683200ll; +} + +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"), + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + }; + return CalendarTimeToEpoch(calendar); +} + +Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) { + auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); + + Service::PSC::Time::LocationName configured_name{}; + std::memcpy(configured_name.name.data(), configured_zone.data(), + std::min(configured_name.name.size(), configured_zone.size())); + + if (!IsTimeZoneBinaryValid(configured_name)) { + configured_zone = Common::TimeZone::FindSystemTimeZone(); + configured_name = {}; + std::memcpy(configured_name.name.data(), configured_zone.data(), + std::min(configured_name.name.size(), configured_zone.size())); + } + + ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!", + configured_name.name.data()); + + return configured_name; +} + +} // namespace + +TimeManager::TimeManager(Core::System& system) + : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource, + m_file_timestamp_worker} { + m_time_m = + system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); + + auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm); + ASSERT(res == ResultSuccess); + + m_set_sys = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); + + res = MountTimeZoneBinary(system); + ASSERT(res == ResultSuccess); + + m_worker.Initialize(m_time_sm, m_set_sys); + + res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone); + ASSERT(res == ResultSuccess); + + res = SetupStandardSteadyClockCore(); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SystemClockContext 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 + // first boot to update it, but I haven't been able to find that point (likely via ntc's auto + // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for + // first boot, grab the current real seconds. + auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)}; + if (user_clock_context == Service::PSC::Time::SystemClockContext{}) { + m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time); + } + + res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SystemClockContext 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")}; + 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}; + + res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns); + ASSERT(res == ResultSuccess); + + bool 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); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point, + is_automatic_correction_enabled); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupEphemeralNetworkSystemClockCore(); + ASSERT(res == ResultSuccess); + + res = SetupTimeZoneServiceCore(); + ASSERT(res == ResultSuccess); + + s64 rtc_time_s{}; + res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s); + ASSERT(res == ResultSuccess); + + // TODO system report "launch" + // "rtc_reset" = m_steady_clock_resource.m_rtc_reset + // "rtc_value" = rtc_time_s + + m_worker.StartThread(); + + m_file_timestamp_worker.m_initialized = true; + + s64 system_clock_time{}; + if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) == + ResultSuccess) { + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo calendar_additional{}; + if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule( + calendar_time, calendar_additional, system_clock_time) == ResultSuccess) { + // TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time, + // calendar_additional.ut_offset) + } + } +} + +Result TimeManager::SetupStandardSteadyClockCore() { + Common::UUID 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); + ASSERT(res == ResultSuccess); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + 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")}; + 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}; + + auto reset_detected = m_steady_clock_resource.GetResetDetected(); + if (reset_detected) { + external_clock_source_id = {}; + } + + Common::UUID clock_source_id{}; + m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id); + + if (clock_source_id != external_clock_source_id) { + m_set_sys->SetExternalSteadyClockSourceId(clock_source_id); + } + + res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(), + external_steady_clock_internal_offset_ns, + standard_steady_clock_test_offset_ns, + reset_detected); + ASSERT(res == ResultSuccess); + R_SUCCEED(); +} + +Result TimeManager::SetupTimeZoneServiceCore() { + Service::PSC::Time::LocationName name{}; + auto res = m_set_sys->GetDeviceTimeZoneLocationName(name); + ASSERT(res == ResultSuccess); + + auto configured_zone = GetTimeZoneString(name); + + if (configured_zone.name != name.name) { + m_set_sys->SetDeviceTimeZoneLocationName(configured_zone); + name = configured_zone; + + std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; + m_time_sm->GetStandardLocalSystemClock(local_clock); + Service::PSC::Time::SystemClockContext context{}; + local_clock->GetSystemClockContext(context); + m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point); + } + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point); + ASSERT(res == ResultSuccess); + + auto location_count = GetTimeZoneCount(); + Service::PSC::Time::RuleVersion rule_version{}; + GetTimeZoneVersion(rule_version); + + std::span<const u8> rule_buffer{}; + size_t rule_size{}; + res = GetTimeZoneRule(rule_buffer, rule_size, name); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count, + rule_buffer); + ASSERT(res == ResultSuccess); + + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h new file mode 100644 index 000000000..1de93f8f9 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> +#include <string> + +#include "common/common_types.h" +#include "core/file_sys/vfs/vfs_types.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/glue/time/worker.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class ServiceManager; +class StaticService; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class TimeManager { +public: + explicit TimeManager(Core::System& system); + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{}; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{}; + StandardSteadyClockResource m_steady_clock_resource; + FileTimestampWorker m_file_timestamp_worker; + TimeWorker m_worker; + +private: + Result SetupStandardSteadyClockCore(); + Result SetupTimeZoneServiceCore(); +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp new file mode 100644 index 000000000..7470fb225 --- /dev/null +++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/glue/time/pm_state_change_handler.h" + +namespace Service::Glue::Time { + +PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker) + : m_alarm_worker{alarm_worker} { + // TODO Initialize IPmModule, dependent on Rtc and Fs +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h new file mode 100644 index 000000000..27d9f7872 --- /dev/null +++ b/src/core/hle/service/glue/time/pm_state_change_handler.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::Glue::Time { +class AlarmWorker; + +class PmStateChangeHandler { +public: + explicit PmStateChangeHandler(AlarmWorker& alarm_worker); + + AlarmWorker& m_alarm_worker; + s32 m_priority{}; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp new file mode 100644 index 000000000..5ebaa33e0 --- /dev/null +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/psc/time/errors.h" + +namespace Service::Glue::Time { +namespace { +[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001; +[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001; + +Result GetTimeInSeconds(Core::System& system, s64& out_time_s) { + out_time_s = std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + if (Settings::values.custom_rtc_enabled) { + out_time_s += Settings::values.custom_rtc_offset.GetValue(); + } + R_SUCCEED(); +} +} // namespace + +StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {} + +void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id, + Common::UUID* external_source_id) { + constexpr size_t NUM_TRIES{20}; + + size_t i{0}; + Result res{ResultSuccess}; + for (; i < NUM_TRIES; i++) { + res = SetCurrentTime(); + if (res == ResultSuccess) { + break; + } + Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(1)) + .count()); + } + + if (i < NUM_TRIES) { + m_set_time_result = ResultSuccess; + if (*external_source_id != Service::PSC::Time::ClockSourceId{}) { + m_clock_source_id = *external_source_id; + } else { + m_clock_source_id = Common::UUID::MakeRandom(); + } + } else { + m_set_time_result = res; + auto ticks{m_system.CoreTiming().GetClockTicks()}; + m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count(); + m_clock_source_id = Common::UUID::MakeRandom(); + } + + if (out_source_id) { + *out_source_id = m_clock_source_id; + } +} + +bool StandardSteadyClockResource::GetResetDetected() { + // TODO: + // call Rtc::GetRtcResetDetected(Max77620RtcSession) + // if detected: + // SetSys::SetExternalSteadyClockSourceId(invalid_id) + // Rtc::ClearRtcResetDetected(Max77620RtcSession) + // set m_rtc_reset to result + // Instead, only set reset to true if we're booting for the first time. + m_rtc_reset = false; + return m_rtc_reset; +} + +Result StandardSteadyClockResource::SetCurrentTime() { + auto start_tick{m_system.CoreTiming().GetClockTicks()}; + + s64 rtc_time_s{}; + // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession)) + R_TRY(GetTimeInSeconds(m_system, rtc_time_s)); + + auto end_tick{m_system.CoreTiming().GetClockTicks()}; + auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)}; + // Why is this here? + R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 boot_time{rtc_time_s * one_second_ns - + Service::PSC::Time::ConvertToTimeSpan(end_tick).count()}; + + std::scoped_lock l{m_mutex}; + m_time = boot_time; + R_SUCCEED(); +} + +Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) { + // TODO + // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession) + R_RETURN(GetTimeInSeconds(m_system, out_time)); +} + +void StandardSteadyClockResource::UpdateTime() { + constexpr size_t NUM_TRIES{3}; + + size_t i{0}; + Result res{ResultSuccess}; + for (; i < NUM_TRIES; i++) { + res = SetCurrentTime(); + if (res == ResultSuccess) { + break; + } + Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(1)) + .count()); + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h new file mode 100644 index 000000000..978d6b63b --- /dev/null +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Glue::Time { +class StandardSteadyClockResource { +public: + StandardSteadyClockResource(Core::System& system); + + void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id); + + s64 GetTime() const { + return m_time; + } + + bool GetResetDetected(); + Result SetCurrentTime(); + Result GetRtcTimeInSeconds(s64& out_time); + void UpdateTime(); + +private: + Core::System& m_system; + + std::mutex m_mutex; + Service::PSC::Time::ClockSourceId m_clock_source_id{}; + s64 m_time{}; + Result m_set_time_result; + bool m_rtc_reset; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp new file mode 100644 index 000000000..63b7d91da --- /dev/null +++ b/src/core/hle/service/glue/time/static.cpp @@ -0,0 +1,448 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/errors.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#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, + std::shared_ptr<TimeManager> time, const char* name) + : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m}, + m_setup_info{setup_info}, m_time_sm{time->m_time_sm}, + m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{ + time->m_steady_clock_resource} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"}, + {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"}, + {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"}, + {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"}, + {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"}, + {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"}, + {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); + + m_set_sys = + m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); + + if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location && + !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsAdmin(m_wrapped_service); + } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && + !m_setup_info.can_write_timezone_device_location && + !m_setup_info.can_write_steady_clock && + !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsUser(m_wrapped_service); + } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && + !m_setup_info.can_write_timezone_device_location && + m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsRepair(m_wrapped_service); + } else { + UNREACHABLE(); + } + + auto res = m_wrapped_service->GetTimeZoneService(m_time_zone); + ASSERT(res == ResultSuccess); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SteadyClock> service{}; + auto res = GetStandardSteadyClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<TimeZoneService> service{}; + auto res = GetTimeZoneService(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KSharedMemory* shared_memory{}; + auto res = GetSharedMemoryNativeHandle(&shared_memory); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(shared_memory); +} + +void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto offset_ns{rp.Pop<s64>()}; + + auto res = SetStandardSteadyClockInternalOffset(offset_ns); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 rtc_value{}; + auto res = GetStandardSteadyClockRtcValue(rtc_value); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(rtc_value); +} + +void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_enabled{}; + auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop<bool>()}; + + auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s32 initial_year{}; + auto res = GetStandardUserSystemClockInitialYear(initial_year); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(initial_year); +} + +void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_sufficient{}; + auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_sufficient); +} + +void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, + 2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum<Service::PSC::Time::TimeType>()}; + + Service::PSC::Time::ClockSnapshot snapshot{}; + auto res = GetClockSnapshot(snapshot, type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()}; + [[maybe_unused]] auto alignment{rp.Pop<u32>()}; + auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + + Service::PSC::Time::ClockSnapshot snapshot{}; + auto res = + GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::ClockSnapshot a{}; + Service::PSC::Time::ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + + s64 difference{}; + auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(difference); +} + +void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::ClockSnapshot a{}; + Service::PSC::Time::ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + + s64 time{}; + auto res = CalculateSpanBetween(time, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result StaticService::GetStandardUserSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service)); +} + +Result StaticService::GetStandardNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service)); +} + +Result StaticService::GetStandardSteadyClock( + std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service)); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) { + out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker, + m_setup_info.can_write_timezone_device_location, + m_time_zone); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service)); +} + +Result StaticService::GetEphemeralNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service)); +} + +Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) { + R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory)); +} + +Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) { + R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied); + + R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset( + offset_ns / + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count())); +} + +Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) { + R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value)); +} + +Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( + bool& out_automatic_correction) { + R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled( + out_automatic_correction)); +} + +Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction) { + R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled( + automatic_correction)); +} + +Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) { + out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year"); + R_SUCCEED(); +} + +Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) { + R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient)); +} + +Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + out_time_point)); +} + +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( + s64& out_time, Service::PSC::Time::SystemClockContext& context) { + R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); +} + +Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::TimeType type) { + R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type)); +} + +Result StaticService::GetClockSnapshotFromSystemClockContext( + Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) { + R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context, + network_context, type)); +} + +Result StaticService::CalculateStandardUserSystemClockDifferenceByUser( + s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) { + R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b)); +} + +Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b) { + R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b)); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h new file mode 100644 index 000000000..75fe4e2cd --- /dev/null +++ b/src/core/hle/service/glue/time/static.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/time_zone.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class StaticService; +class SystemClock; +class SteadyClock; +class TimeZoneService; +class ServiceManager; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class FileTimestampWorker; +class StandardSteadyClockResource; + +class StaticService final : public ServiceFramework<StaticService> { +public: + explicit StaticService(Core::System& system, + Service::PSC::Time::StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetStandardNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service); + Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service); + Result GetStandardLocalSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetEphemeralNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory); + Result SetStandardSteadyClockInternalOffset(s64 offset); + Result GetStandardSteadyClockRtcValue(s64& out_rtc_value); + Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction); + Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction); + Result GetStandardUserSystemClockInitialYear(s32& out_year); + Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Result CalculateMonotonicSystemClockBaseTimePoint( + s64& out_time, Service::PSC::Time::SystemClockContext& context); + Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::TimeType type); + Result GetClockSnapshotFromSystemClockContext( + Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type); + Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, + Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b); + Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b); + +private: + Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, + Service::PSC::Time::TimeType type); + + void Handle_GetStandardUserSystemClock(HLERequestContext& ctx); + void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetStandardSteadyClock(HLERequestContext& ctx); + void Handle_GetTimeZoneService(HLERequestContext& ctx); + void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx); + void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx); + void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx); + void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); + void Handle_GetClockSnapshot(HLERequestContext& ctx); + void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); + void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); + void Handle_CalculateSpanBetween(HLERequestContext& ctx); + + Core::System& m_system; + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service; + + Service::PSC::Time::StaticServiceSetupInfo m_setup_info; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone; + FileTimestampWorker& m_file_timestamp_worker; + StandardSteadyClockResource& m_standard_steady_clock_resource; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp new file mode 100644 index 000000000..503c327dd --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -0,0 +1,377 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/time_zone.h" +#include "core/hle/service/glue/time/time_zone_binary.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { +static std::mutex g_list_mutex; +static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{}; +} // namespace + +TimeZoneService::TimeZoneService( + Core::System& system_, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service) + : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, + m_can_write_timezone_device_location{can_write_timezone_device_location}, + m_file_timestamp_worker{file_timestamp_worker}, + m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"}, + {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"}, + {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"}, + {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, + {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"}, + {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"}, + {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"}, + {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"}, + {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"}, + {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"}, + {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, + }; + // clang-format on + RegisterHandlers(functions); + + g_list_nodes.clear(); + m_set_sys = + m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); +} + +TimeZoneService::~TimeZoneService() = default; + +void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::LocationName name{}; + auto res = GetDeviceLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::LocationName>(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; + + auto res = SetDeviceLocation(name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 count{}; + auto res = GetTotalLocationNameCount(count); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto index{rp.Pop<u32>()}; + + auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)}; + + std::vector<Service::PSC::Time::LocationName> names{}; + u32 count{}; + auto res = LoadLocationNameList(count, names, max_names, index); + + ctx.WriteBuffer(names); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; + + Tz::Rule rule{}; + auto res = LoadTimeZoneRule(rule, name); + + ctx.WriteBuffer(rule); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::RuleVersion rule_version{}; + auto res = GetTimeZoneRuleVersion(rule_version); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version); +} + +void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::LocationName name{}; + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) + + (sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::LocationName>(name); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = SetDeviceLocationNameWithTimeZoneRule(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Service::PSC::Time::ResultNotImplemented); +} + +void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetDeviceLocationNameOperationEventReadableHandle(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + Tz::Rule rule{}; + std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule)); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTime(calendar_time, additional_info, time, rule); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) + + (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time); + rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + LOG_DEBUG(Service_Time, "called. time={}", time); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) + + (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time); + rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()}; + + LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}", + calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute, + calendar.second); + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTime(count, times, times_count, calendar, rule); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()}; + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +// =============================== Implementations =========================== + +Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) { + R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); +} + +Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) { + R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); + R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound); + + std::scoped_lock l{m_mutex}; + + std::span<const u8> binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, location_name)) + + R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary)); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + Service::PSC::Time::LocationName name{}; + R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name)); + + m_set_sys->SetDeviceTimeZoneLocationName(name); + m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point); + + std::scoped_lock m{g_list_mutex}; + for (auto& operation_event : g_list_nodes) { + operation_event.m_event->Signal(); + } + R_SUCCEED(); +} + +Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) { + R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count)); +} + +Result TimeZoneService::LoadLocationNameList( + u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names, + u32 index) { + std::scoped_lock l{m_mutex}; + R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index)); +} + +Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule, + Service::PSC::Time::LocationName& name) { + std::scoped_lock l{m_mutex}; + std::span<const u8> binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, name)) + R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) { + R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version)); +} + +Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point, + Service::PSC::Time::LocationName& location_name) { + R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name)); +} + +Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() { + R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); + R_RETURN(Service::PSC::Time::ResultNotImplemented); +} + +Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle( + Kernel::KEvent** out_event) { + if (!operation_event_initialized) { + operation_event_initialized = false; + + m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event); + m_operation_event.m_event = + m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent"); + operation_event_initialized = true; + std::scoped_lock l{m_mutex}; + g_list_nodes.push_back(m_operation_event); + } + + *out_event = m_operation_event.m_event; + R_SUCCEED(); +} + +Result TimeZoneService::ToCalendarTime( + Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) { + R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZoneService::ToCalendarTimeWithMyRule( + Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) { + R_RETURN( + m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); +} + +Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, + Tz::Rule& rule) { + R_RETURN( + m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule)); +} + +Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time) { + R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count, + calendar_time)); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h new file mode 100644 index 000000000..3c8ae4bf8 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <mutex> +#include <span> +#include <vector> + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Tz { +struct Rule; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class TimeZoneService; +} + +namespace Service::Glue::Time { +class FileTimestampWorker; + +class TimeZoneService final : public ServiceFramework<TimeZoneService> { +public: + explicit TimeZoneService( + Core::System& system, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service); + + ~TimeZoneService() override; + + Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name); + Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name); + Result GetTotalLocationNameCount(u32& out_count); + Result LoadLocationNameList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index); + Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name); + Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version); + Result GetDeviceLocationNameAndUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point, + Service::PSC::Time::LocationName& location_name); + Result SetDeviceLocationNameWithTimeZoneRule(); + Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event); + Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, + s64 time); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time); + +private: + void Handle_GetDeviceLocationName(HLERequestContext& ctx); + void Handle_SetDeviceLocationName(HLERequestContext& ctx); + void Handle_GetTotalLocationNameCount(HLERequestContext& ctx); + void Handle_LoadLocationNameList(HLERequestContext& ctx); + void Handle_LoadTimeZoneRule(HLERequestContext& ctx); + void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx); + void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx); + void Handle_ParseTimeZoneBinary(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx); + void Handle_ToCalendarTime(HLERequestContext& ctx); + void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx); + void Handle_ToPosixTime(HLERequestContext& ctx); + void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx); + + Core::System& m_system; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + bool m_can_write_timezone_device_location; + FileTimestampWorker& m_file_timestamp_worker; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service; + std::mutex m_mutex; + bool operation_event_initialized{}; + Service::PSC::Time::OperationEvent m_operation_event; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp new file mode 100644 index 000000000..d33f784c0 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -0,0 +1,221 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#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/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/glue/time/time_zone_binary.h" + +namespace Service::Glue::Time { +namespace { +constexpr u64 TimeZoneBinaryId = 0x10000000000080E; + +static FileSys::VirtualDir g_time_zone_binary_romfs{}; +static Result g_time_zone_binary_mount_result{ResultUnknown}; +static std::vector<u8> g_time_zone_scratch_space(0x2800, 0); + +Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size, + std::string_view path) { + R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result); + + auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; + R_UNLESS(vfs_file, ResultUnknown); + + auto file_size{vfs_file->GetSize()}; + R_UNLESS(file_size > 0, ResultUnknown); + + R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed); + + out_read_size = vfs_file->Read(out_buffer.data(), file_size); + R_UNLESS(out_read_size > 0, ResultUnknown); + + R_SUCCEED(); +} +} // namespace + +void ResetTimeZoneBinary() { + g_time_zone_binary_romfs = {}; + g_time_zone_binary_mount_result = ResultUnknown; + g_time_zone_scratch_space.clear(); + g_time_zone_scratch_space.resize(0x2800, 0); +} + +Result MountTimeZoneBinary(Core::System& system) { + ResetTimeZoneBinary(); + + auto& fsc{system.GetFileSystemController()}; + std::unique_ptr<FileSys::NCA> nca{}; + + auto* bis_system = fsc.GetSystemNANDContents(); + + R_UNLESS(bis_system, ResultUnknown); + + nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data); + + if (nca) { + g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS()); + } + + if (g_time_zone_binary_romfs) { + // Validate that the romfs is readable, using invalid firmware keys can cause this to get + // set but the files to be garbage. In that case, we want to hit the next path and + // synthesise them instead. + Service::PSC::Time::LocationName name{"Etc/GMT"}; + if (!IsTimeZoneBinaryValid(name)) { + ResetTimeZoneBinary(); + } + } + + if (!g_time_zone_binary_romfs) { + g_time_zone_binary_romfs = FileSys::ExtractRomFS( + FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId)); + } + + R_UNLESS(g_time_zone_binary_romfs, ResultUnknown); + + g_time_zone_binary_mount_result = ResultSuccess; + R_SUCCEED(); +} + +void GetTimeZoneBinaryListPath(std::string& out_path) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary"); + out_path = "/binaryList.txt"; +} + +void GetTimeZoneBinaryVersionPath(std::string& out_path) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary"); + out_path = "/version.txt"; +} + +void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name); + out_path = fmt::format("/zoneinfo/{}", name.name.data()); +} + +bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { + std::string path{}; + GetTimeZoneZonePath(path, name); + + auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; + if (!vfs_file) { + LOG_INFO(Service_Time, "Could not find timezone file {}", path); + return false; + } + return vfs_file->GetSize() != 0; +} + +u32 GetTimeZoneCount() { + std::string path{}; + GetTimeZoneBinaryListPath(path); + + size_t bytes_read{}; + if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) { + return 0; + } + if (bytes_read == 0) { + return 0; + } + + auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read); + u32 count{}; + for (auto chr : chars) { + if (chr == '\n') { + count++; + } + } + return count; +} + +Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) { + std::string path{}; + GetTimeZoneBinaryVersionPath(path); + + auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version), + sizeof(Service::PSC::Time::RuleVersion))}; + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(), + path)); + + rule_version_buffer[bytes_read] = 0; + R_SUCCEED(); +} + +Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, + Service::PSC::Time::LocationName& name) { + std::string path{}; + GetTimeZoneZonePath(path, name); + + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, + g_time_zone_scratch_space.size(), path)); + + out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read); + out_rule_size = bytes_read; + R_SUCCEED(); +} + +Result GetTimeZoneLocationList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index) { + std::string path{}; + GetTimeZoneBinaryListPath(path); + + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, + g_time_zone_scratch_space.size(), path)); + + out_count = 0; + R_SUCCEED_IF(bytes_read == 0); + + Service::PSC::Time::LocationName current_name{}; + size_t current_name_len{}; + std::span<const u8> chars{g_time_zone_scratch_space}; + u32 name_count{}; + + for (auto chr : chars) { + if (chr == '\r') { + continue; + } + + if (chr == '\n') { + if (name_count >= index) { + out_names.push_back(current_name); + out_count++; + if (out_count >= max_names) { + break; + } + } + name_count++; + current_name_len = 0; + current_name = {}; + continue; + } + + if (chr == '\0') { + break; + } + + R_UNLESS(current_name_len <= current_name.name.size() - 2, + Service::PSC::Time::ResultFailed); + + current_name.name[current_name_len++] = chr; + } + + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h new file mode 100644 index 000000000..2cad6b458 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <span> +#include <string> +#include <string_view> + +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Glue::Time { + +void ResetTimeZoneBinary(); +Result MountTimeZoneBinary(Core::System& system); +void GetTimeZoneBinaryListPath(std::string& out_path); +void GetTimeZoneBinaryVersionPath(std::string& out_path); +void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name); +bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name); +u32 GetTimeZoneCount(); +Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version); +Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, + Service::PSC::Time::LocationName& name); +Result GetTimeZoneLocationList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index); + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp new file mode 100644 index 000000000..ea0e49b90 --- /dev/null +++ b/src/core/hle/service/glue/time/worker.cpp @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/glue/time/worker.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { + +bool g_ig_report_network_clock_context_set{}; +Service::PSC::Time::SystemClockContext g_report_network_clock_context{}; +bool g_ig_report_ephemeral_clock_context_set{}; +Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{}; + +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 + +TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, + FileTimestampWorker& file_timestamp_worker) + : m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")}, + m_steady_clock_resource{steady_clock_resource}, + m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent( + "Glue:58:SteadyClockTimerEvent")}, + m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")}, + m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} { + g_ig_report_network_clock_context_set = false; + g_report_network_clock_context = {}; + g_ig_report_ephemeral_clock_context_set = false; + g_report_ephemeral_clock_context = {}; + + m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( + "Time::SteadyClockEvent", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_steady_clock->Signal(); + return std::nullopt; + }); + + m_timer_file_system_timing_event = Core::Timing::CreateEvent( + "Time::SteadyClockEvent", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_file_system->Signal(); + return std::nullopt; + }); +} + +TimeWorker::~TimeWorker() { + m_local_clock_event->Signal(); + m_network_clock_event->Signal(); + m_ephemeral_clock_event->Signal(); + std::this_thread::sleep_for(std::chrono::milliseconds(16)); + + m_thread.request_stop(); + m_event->Signal(); + m_thread.join(); + + m_ctx.CloseEvent(m_event); + m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); + m_ctx.CloseEvent(m_timer_steady_clock); + m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event); + m_ctx.CloseEvent(m_timer_file_system); +} + +void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm, + std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) { + m_set_sys = std::move(set_sys); + m_time_m = + m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); + m_time_sm = std::move(time_sm); + + m_alarm_worker.Initialize(m_time_m); + + auto steady_clock_interval_m = GetSettingsItemValue<s32>( + m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes"); + + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns}; + + m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), + std::chrono::nanoseconds(steady_clock_interval_ns), + m_timer_steady_clock_timing_event); + + auto fs_notify_time_s = + GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds"); + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns}; + + m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), + std::chrono::nanoseconds(fs_notify_time_ns), + m_timer_file_system_timing_event); + + auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock); + ASSERT(res == ResultSuccess); + res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock); + ASSERT(res == ResultSuccess); + res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock); + ASSERT(res == ResultSuccess); + res = + m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + &m_standard_user_auto_correct_clock_event); + ASSERT(res == ResultSuccess); +} + +void TimeWorker::StartThread() { + m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this)); +} + +void TimeWorker::ThreadFunc(std::stop_token stop_token) { + Common::SetCurrentThreadName("TimeWorker"); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); + + enum class EventType { + Exit = 0, + IpmModuleService_GetEvent = 1, + PowerStateChange = 2, + SignalAlarms = 3, + UpdateLocalSystemClock = 4, + UpdateNetworkSystemClock = 5, + UpdateEphemeralSystemClock = 6, + UpdateSteadyClock = 7, + UpdateFileTimestamp = 8, + AutoCorrect = 9, + Max = 10, + }; + + s32 num_objs{}; + std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{}; + std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{}; + + const auto AddWaiter{ + [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) { + // Open a new reference to the object. + synchronization_object->Open(); + + // Insert into the list. + wait_indices[num_objs] = type; + wait_objs[num_objs++] = synchronization_object; + }}; + + while (!stop_token.stop_requested()) { + SCOPE_EXIT({ + for (s32 i = 0; i < num_objs; i++) { + wait_objs[i]->Close(); + } + }); + + num_objs = {}; + wait_objs = {}; + if (m_pm_state_change_handler.m_priority != 0) { + AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); + // TODO + // AddWaiter(gIPmModuleService::GetEvent(), 1); + AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange); + } else { + AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); + // TODO + // AddWaiter(gIPmModuleService::GetEvent(), 1); + AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange); + AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); + AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock); + AddWaiter(&m_network_clock_event->GetReadableEvent(), + EventType::UpdateNetworkSystemClock); + AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(), + EventType::UpdateEphemeralSystemClock); + AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); + AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); + AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(), + EventType::AutoCorrect); + } + + s32 out_index{-1}; + Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), + num_objs, -1); + ASSERT(out_index >= 0 && out_index < num_objs); + + if (stop_token.stop_requested()) { + return; + } + + switch (wait_indices[out_index]) { + case EventType::Exit: + return; + + case EventType::IpmModuleService_GetEvent: + // TODO + // IPmModuleService::GetEvent() + // clear the event + // Handle power state change event + break; + + case EventType::PowerStateChange: + m_alarm_worker.GetEvent().Clear(); + if (m_pm_state_change_handler.m_priority <= 1) { + m_alarm_worker.OnPowerStateChanged(); + } + break; + + case EventType::SignalAlarms: + m_alarm_worker.GetTimerEvent().Clear(); + m_time_m->CheckAndSignalAlarms(); + break; + + case EventType::UpdateLocalSystemClock: { + m_local_clock_event->Clear(); + + Service::PSC::Time::SystemClockContext context{}; + auto res = m_local_clock->GetSystemClockContext(context); + ASSERT(res == ResultSuccess); + + m_set_sys->SetUserSystemClockContext(context); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + } break; + + case EventType::UpdateNetworkSystemClock: { + m_network_clock_event->Clear(); + Service::PSC::Time::SystemClockContext context{}; + auto res = m_network_clock->GetSystemClockContext(context); + ASSERT(res == ResultSuccess); + m_set_sys->SetNetworkSystemClockContext(context); + + s64 time{}; + if (m_network_clock->GetCurrentTime(time) != ResultSuccess) { + break; + } + + [[maybe_unused]] auto offset_before{ + g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0}; + // TODO system report "standard_netclock_operation" + // "clock_time" = time + // "context_offset_before" = offset_before + // "context_offset_after" = context.offset + g_report_network_clock_context = context; + if (!g_ig_report_network_clock_context_set) { + g_ig_report_network_clock_context_set = true; + } + + m_file_timestamp_worker.SetFilesystemPosixTime(); + } break; + + case EventType::UpdateEphemeralSystemClock: { + m_ephemeral_clock_event->Clear(); + + Service::PSC::Time::SystemClockContext context{}; + auto res = m_ephemeral_clock->GetSystemClockContext(context); + if (res != ResultSuccess) { + break; + } + + s64 time{}; + res = m_ephemeral_clock->GetCurrentTime(time); + if (res != ResultSuccess) { + break; + } + + [[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set + ? g_report_ephemeral_clock_context.offset + : 0}; + // TODO system report "ephemeral_netclock_operation" + // "clock_time" = time + // "context_offset_before" = offset_before + // "context_offset_after" = context.offset + g_report_ephemeral_clock_context = context; + if (!g_ig_report_ephemeral_clock_context_set) { + g_ig_report_ephemeral_clock_context_set = true; + } + } break; + + case EventType::UpdateSteadyClock: + m_timer_steady_clock->Clear(); + + m_steady_clock_resource.UpdateTime(); + m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime()); + break; + + case EventType::UpdateFileTimestamp: + m_timer_file_system->Clear(); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + break; + + case EventType::AutoCorrect: { + m_standard_user_auto_correct_clock_event->Clear(); + + bool automatic_correction{}; + auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( + automatic_correction); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + ASSERT(res == ResultSuccess); + + m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + } break; + + default: + UNREACHABLE(); + break; + } + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h new file mode 100644 index 000000000..adbbe6b6d --- /dev/null +++ b/src/core/hle/service/glue/time/worker.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/glue/time/alarm_worker.h" +#include "core/hle/service/glue/time/pm_state_change_handler.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class StaticService; +class SystemClock; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class FileTimestampWorker; +class StandardSteadyClockResource; + +class TimeWorker { +public: + explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, + FileTimestampWorker& file_timestamp_worker); + ~TimeWorker(); + + void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm, + std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys); + + void StartThread(); + +private: + void ThreadFunc(std::stop_token stop_token); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + std::jthread m_thread; + Kernel::KEvent* m_event{}; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; + std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock; + std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock; + std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock; + StandardSteadyClockResource& m_steady_clock_resource; + FileTimestampWorker& m_file_timestamp_worker; + Kernel::KEvent* m_local_clock_event{}; + Kernel::KEvent* m_network_clock_event{}; + Kernel::KEvent* m_ephemeral_clock_event{}; + Kernel::KEvent* m_standard_user_auto_correct_clock_event{}; + Kernel::KEvent* m_timer_steady_clock{}; + std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event; + Kernel::KEvent* m_timer_file_system{}; + std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event; + AlarmWorker m_alarm_worker; + PmStateChangeHandler m_pm_state_change_handler; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/hid/controllers/applet_resource.cpp b/src/core/hle/service/hid/controllers/applet_resource.cpp deleted file mode 100644 index b4ff663c2..000000000 --- a/src/core/hle/service/hid/controllers/applet_resource.cpp +++ /dev/null @@ -1,329 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/core.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" -#include "core/hle/service/hid/errors.h" - -namespace Service::HID { - -AppletResource::AppletResource(Core::System& system_) : system{system_} {} - -AppletResource::~AppletResource() = default; - -Result AppletResource::CreateAppletResource(u64 aruid) { - const u64 index = GetIndexFromAruid(aruid); - - if (index >= AruidIndexMax) { - return ResultAruidNotRegistered; - } - - if (data[index].flag.is_assigned) { - return ResultAruidAlreadyRegistered; - } - - auto& shared_memory = shared_memory_holder[index]; - if (!shared_memory.IsMapped()) { - const Result result = shared_memory.Initialize(system); - if (result.IsError()) { - return result; - } - if (shared_memory.GetAddress() == nullptr) { - shared_memory.Finalize(); - return ResultSharedMemoryNotInitialized; - } - } - - auto* shared_memory_format = shared_memory.GetAddress(); - if (shared_memory_format != nullptr) { - shared_memory_format->Initialize(); - } - - data[index].shared_memory_format = shared_memory_format; - data[index].flag.is_assigned.Assign(true); - // TODO: InitializeSixAxisControllerConfig(false); - active_aruid = aruid; - return ResultSuccess; -} - -Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input) { - const u64 index = GetIndexFromAruid(aruid); - - if (index < AruidIndexMax) { - return ResultAruidAlreadyRegistered; - } - - std::size_t data_index = AruidIndexMax; - for (std::size_t i = 0; i < AruidIndexMax; i++) { - if (!data[i].flag.is_initialized) { - data_index = i; - break; - } - } - - if (data_index == AruidIndexMax) { - return ResultAruidNoAvailableEntries; - } - - AruidData& aruid_data = data[data_index]; - - aruid_data.aruid = aruid; - aruid_data.flag.is_initialized.Assign(true); - if (enable_input) { - aruid_data.flag.enable_pad_input.Assign(true); - aruid_data.flag.enable_six_axis_sensor.Assign(true); - aruid_data.flag.bit_18.Assign(true); - aruid_data.flag.enable_touchscreen.Assign(true); - } - - data_index = AruidIndexMax; - for (std::size_t i = 0; i < AruidIndexMax; i++) { - if (registration_list.flag[i] == RegistrationStatus::Initialized) { - if (registration_list.aruid[i] != aruid) { - continue; - } - data_index = i; - break; - } - if (registration_list.flag[i] == RegistrationStatus::None) { - data_index = i; - break; - } - } - - if (data_index == AruidIndexMax) { - return ResultSuccess; - } - - registration_list.flag[data_index] = RegistrationStatus::Initialized; - registration_list.aruid[data_index] = aruid; - - return ResultSuccess; -} - -void AppletResource::UnregisterAppletResourceUserId(u64 aruid) { - u64 index = GetIndexFromAruid(aruid); - - if (index < AruidIndexMax) { - if (data[index].flag.is_assigned) { - data[index].shared_memory_format = nullptr; - data[index].flag.is_assigned.Assign(false); - } - } - - index = GetIndexFromAruid(aruid); - if (index < AruidIndexMax) { - DestroySevenSixAxisTransferMemory(); - data[index].flag.raw = 0; - data[index].aruid = 0; - - index = GetIndexFromAruid(aruid); - if (index < AruidIndexMax) { - registration_list.flag[index] = RegistrationStatus::PendingDelete; - } - } -} - -void AppletResource::FreeAppletResourceId(u64 aruid) { - u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - auto& aruid_data = data[index]; - if (aruid_data.flag.is_assigned) { - aruid_data.shared_memory_format = nullptr; - aruid_data.flag.is_assigned.Assign(false); - } -} - -u64 AppletResource::GetActiveAruid() { - return active_aruid; -} - -Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { - u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return ResultAruidNotRegistered; - } - - *out_handle = shared_memory_holder[index].GetHandle(); - return ResultSuccess; -} - -Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, - u64 aruid) { - u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return ResultAruidNotRegistered; - } - - *out_shared_memory_format = data[index].shared_memory_format; - return ResultSuccess; -} - -AruidData* AppletResource::GetAruidData(u64 aruid) { - const u64 aruid_index = GetIndexFromAruid(aruid); - if (aruid_index == AruidIndexMax) { - return nullptr; - } - return &data[aruid_index]; -} - -AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) { - return &data[aruid_index]; -} - -bool AppletResource::IsVibrationAruidActive(u64 aruid) const { - return aruid == 0 || aruid == active_vibration_aruid; -} - -u64 AppletResource::GetIndexFromAruid(u64 aruid) { - for (std::size_t i = 0; i < AruidIndexMax; i++) { - if (registration_list.flag[i] == RegistrationStatus::Initialized && - registration_list.aruid[i] == aruid) { - return i; - } - } - return AruidIndexMax; -} - -Result AppletResource::DestroySevenSixAxisTransferMemory() { - // TODO - return ResultSuccess; -} - -void AppletResource::EnableInput(u64 aruid, bool is_enabled) { - const u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - data[index].flag.enable_pad_input.Assign(is_enabled); - data[index].flag.enable_touchscreen.Assign(is_enabled); -} - -void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) { - const u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - data[index].flag.enable_six_axis_sensor.Assign(is_enabled); -} - -void AppletResource::EnablePadInput(u64 aruid, bool is_enabled) { - const u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - data[index].flag.enable_pad_input.Assign(is_enabled); -} - -void AppletResource::EnableTouchScreen(u64 aruid, bool is_enabled) { - const u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - data[index].flag.enable_touchscreen.Assign(is_enabled); -} - -void AppletResource::SetIsPalmaConnectable(u64 aruid, bool is_connectable) { - const u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - data[index].flag.is_palma_connectable.Assign(is_connectable); -} - -void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) { - const u64 index = GetIndexFromAruid(aruid); - if (index >= AruidIndexMax) { - return; - } - - data[index].flag.enable_palma_boost_mode.Assign(is_enabled); -} - -Result AppletResource::RegisterCoreAppletResource() { - if (ref_counter == std::numeric_limits<s32>::max() - 1) { - return ResultAppletResourceOverflow; - } - if (ref_counter == 0) { - const u64 index = GetIndexFromAruid(0); - if (index < AruidIndexMax) { - return ResultAruidAlreadyRegistered; - } - - std::size_t data_index = AruidIndexMax; - for (std::size_t i = 0; i < AruidIndexMax; i++) { - if (!data[i].flag.is_initialized) { - data_index = i; - break; - } - } - - if (data_index == AruidIndexMax) { - return ResultAruidNoAvailableEntries; - } - - AruidData& aruid_data = data[data_index]; - - aruid_data.aruid = 0; - aruid_data.flag.is_initialized.Assign(true); - aruid_data.flag.enable_pad_input.Assign(true); - aruid_data.flag.enable_six_axis_sensor.Assign(true); - aruid_data.flag.bit_18.Assign(true); - aruid_data.flag.enable_touchscreen.Assign(true); - - data_index = AruidIndexMax; - for (std::size_t i = 0; i < AruidIndexMax; i++) { - if (registration_list.flag[i] == RegistrationStatus::Initialized) { - if (registration_list.aruid[i] != 0) { - continue; - } - data_index = i; - break; - } - if (registration_list.flag[i] == RegistrationStatus::None) { - data_index = i; - break; - } - } - - Result result = ResultSuccess; - - if (data_index == AruidIndexMax) { - result = CreateAppletResource(0); - } else { - registration_list.flag[data_index] = RegistrationStatus::Initialized; - registration_list.aruid[data_index] = 0; - } - - if (result.IsError()) { - UnregisterAppletResourceUserId(0); - return result; - } - } - ref_counter++; - return ResultSuccess; -} - -Result AppletResource::UnregisterCoreAppletResource() { - if (ref_counter == 0) { - return ResultAppletResourceNotInitialized; - } - - if (--ref_counter == 0) { - UnregisterAppletResourceUserId(0); - } - - return ResultSuccess; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/applet_resource.h b/src/core/hle/service/hid/controllers/applet_resource.h deleted file mode 100644 index 52cc4cf42..000000000 --- a/src/core/hle/service/hid/controllers/applet_resource.h +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include <mutex> - -#include "common/bit_field.h" -#include "common/common_types.h" -#include "core/hle/result.h" -#include "core/hle/service/hid/controllers/shared_memory_holder.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KSharedMemory; -} - -namespace Service::HID { -struct SharedMemoryFormat; -class AppletResource; -class NPadResource; - -static constexpr std::size_t AruidIndexMax = 0x20; - -enum class RegistrationStatus : u32 { - None, - Initialized, - PendingDelete, -}; - -struct DataStatusFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> is_initialized; - BitField<1, 1, u32> is_assigned; - BitField<16, 1, u32> enable_pad_input; - BitField<17, 1, u32> enable_six_axis_sensor; - BitField<18, 1, u32> bit_18; - BitField<19, 1, u32> is_palma_connectable; - BitField<20, 1, u32> enable_palma_boost_mode; - BitField<21, 1, u32> enable_touchscreen; - }; -}; - -struct AruidRegisterList { - std::array<RegistrationStatus, AruidIndexMax> flag{}; - std::array<u64, AruidIndexMax> aruid{}; -}; -static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size"); - -struct AruidData { - DataStatusFlag flag{}; - u64 aruid{}; - SharedMemoryFormat* shared_memory_format{nullptr}; -}; - -struct HandheldConfig { - bool is_handheld_hid_enabled; - bool is_force_handheld; - bool is_joycon_rail_enabled; - bool is_force_handheld_style_vibration; -}; -static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size"); - -struct AppletResourceHolder { - std::shared_ptr<AppletResource> applet_resource{nullptr}; - std::recursive_mutex* shared_mutex{nullptr}; - NPadResource* shared_npad_resource{nullptr}; - std::shared_ptr<HandheldConfig> handheld_config{nullptr}; - long* handle_1; -}; - -class AppletResource { -public: - explicit AppletResource(Core::System& system_); - ~AppletResource(); - - Result CreateAppletResource(u64 aruid); - - Result RegisterAppletResourceUserId(u64 aruid, bool enable_input); - void UnregisterAppletResourceUserId(u64 aruid); - - void FreeAppletResourceId(u64 aruid); - - u64 GetActiveAruid(); - Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid); - Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid); - AruidData* GetAruidData(u64 aruid); - AruidData* GetAruidDataByIndex(std::size_t aruid_index); - - bool IsVibrationAruidActive(u64 aruid) const; - - u64 GetIndexFromAruid(u64 aruid); - - Result DestroySevenSixAxisTransferMemory(); - - void EnableInput(u64 aruid, bool is_enabled); - void EnableSixAxisSensor(u64 aruid, bool is_enabled); - void EnablePadInput(u64 aruid, bool is_enabled); - void EnableTouchScreen(u64 aruid, bool is_enabled); - void SetIsPalmaConnectable(u64 aruid, bool is_connectable); - void EnablePalmaBoostMode(u64 aruid, bool is_enabled); - - Result RegisterCoreAppletResource(); - Result UnregisterCoreAppletResource(); - -private: - u64 active_aruid{}; - AruidRegisterList registration_list{}; - std::array<AruidData, AruidIndexMax> data{}; - std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{}; - s32 ref_counter{}; - u64 active_vibration_aruid; - - Core::System& system; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/capture_button.cpp b/src/core/hle/service/hid/controllers/capture_button.cpp deleted file mode 100644 index 8b486fcb5..000000000 --- a/src/core/hle/service/hid/controllers/capture_button.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/capture_button.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} - -CaptureButton::~CaptureButton() = default; - -void CaptureButton::OnInit() {} - -void CaptureButton::OnRelease() {} - -void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - auto& header = data->shared_memory_format->capture_button.header; - header.timestamp = core_timing.GetGlobalTimeNs().count(); - header.total_entry_count = 17; - header.entry_count = 0; - header.last_entry_index = 0; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/capture_button.h b/src/core/hle/service/hid/controllers/capture_button.h deleted file mode 100644 index dcc4715c5..000000000 --- a/src/core/hle/service/hid/controllers/capture_button.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Service::HID { - -class CaptureButton final : public ControllerBase { -public: - explicit CaptureButton(Core::HID::HIDCore& hid_core_); - ~CaptureButton() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp deleted file mode 100644 index 8eba2c292..000000000 --- a/src/core/hle/service/hid/controllers/console_six_axis.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hid/emulated_console.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/console_six_axis.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { - console = hid_core.GetEmulatedConsole(); -} - -ConsoleSixAxis::~ConsoleSixAxis() = default; - -void ConsoleSixAxis::OnInit() {} - -void ConsoleSixAxis::OnRelease() {} - -void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - ConsoleSixAxisSensorSharedMemoryFormat& shared_memory = data->shared_memory_format->console; - - if (!IsControllerActivated()) { - return; - } - - const auto motion_status = console->GetMotion(); - - shared_memory.sampling_number++; - shared_memory.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; - shared_memory.verticalization_error = motion_status.verticalization_error; - shared_memory.gyro_bias = motion_status.gyro_bias; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h deleted file mode 100644 index e3351f83c..000000000 --- a/src/core/hle/service/hid/controllers/console_six_axis.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Core::HID { -class EmulatedConsole; -} // namespace Core::HID - -namespace Service::HID { -class ConsoleSixAxis final : public ControllerBase { -public: - explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_); - ~ConsoleSixAxis() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - Core::HID::EmulatedConsole* console = nullptr; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp deleted file mode 100644 index 2083ccfad..000000000 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Service::HID { - -ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} -ControllerBase::~ControllerBase() = default; - -Result ControllerBase::Activate() { - if (is_activated) { - return ResultSuccess; - } - is_activated = true; - OnInit(); - return ResultSuccess; -} - -Result ControllerBase::Activate(u64 aruid) { - return Activate(); -} - -void ControllerBase::DeactivateController() { - if (is_activated) { - OnRelease(); - } - is_activated = false; -} - -bool ControllerBase::IsControllerActivated() const { - return is_activated; -} - -void ControllerBase::SetAppletResource(std::shared_ptr<AppletResource> resource) { - applet_resource = resource; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h deleted file mode 100644 index 759ae0053..000000000 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "common/common_types.h" -#include "core/hle/result.h" -#include "core/hle/service/hid/controllers/applet_resource.h" - -namespace Core::Timing { -class CoreTiming; -} - -namespace Core::HID { -class HIDCore; -} // namespace Core::HID - -namespace Service::HID { -class ControllerBase { -public: - explicit ControllerBase(Core::HID::HIDCore& hid_core_); - virtual ~ControllerBase(); - - // Called when the controller is initialized - virtual void OnInit() = 0; - - // When the controller is released - virtual void OnRelease() = 0; - - // When the controller is requesting an update for the shared memory - virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0; - - // When the controller is requesting a motion update for the shared memory - virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} - - Result Activate(); - Result Activate(u64 aruid); - - void DeactivateController(); - - bool IsControllerActivated() const; - - void SetAppletResource(std::shared_ptr<AppletResource> resource); - -protected: - bool is_activated{false}; - std::shared_ptr<AppletResource> applet_resource{nullptr}; - - Core::HID::HIDCore& hid_core; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_mouse.cpp b/src/core/hle/service/hid/controllers/debug_mouse.cpp deleted file mode 100644 index f2f1a27f8..000000000 --- a/src/core/hle/service/hid/controllers/debug_mouse.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/frontend/emu_window.h" -#include "core/hid/emulated_devices.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/debug_mouse.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { - emulated_devices = hid_core.GetEmulatedDevices(); -} - -DebugMouse::~DebugMouse() = default; - -void DebugMouse::OnInit() {} -void DebugMouse::OnRelease() {} - -void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_mouse; - - if (!IsControllerActivated()) { - shared_memory.mouse_lifo.buffer_count = 0; - shared_memory.mouse_lifo.buffer_tail = 0; - return; - } - - next_state = {}; - - const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state; - next_state.sampling_number = last_entry.sampling_number + 1; - - if (Settings::values.mouse_enabled) { - const auto& mouse_button_state = emulated_devices->GetMouseButtons(); - const auto& mouse_position_state = emulated_devices->GetMousePosition(); - const auto& mouse_wheel_state = emulated_devices->GetMouseWheel(); - next_state.attribute.is_connected.Assign(1); - next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width); - next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height); - next_state.delta_x = next_state.x - last_entry.x; - next_state.delta_y = next_state.y - last_entry.y; - next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x; - next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y; - - last_mouse_wheel_state = mouse_wheel_state; - next_state.button = mouse_button_state; - } - - shared_memory.mouse_lifo.WriteNextEntry(next_state); -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_mouse.h b/src/core/hle/service/hid/controllers/debug_mouse.h deleted file mode 100644 index ec939fa9f..000000000 --- a/src/core/hle/service/hid/controllers/debug_mouse.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Core::HID { -class EmulatedDevices; -struct MouseState; -struct AnalogStickState; -} // namespace Core::HID - -namespace Service::HID { -class DebugMouse final : public ControllerBase { -public: - explicit DebugMouse(Core::HID::HIDCore& hid_core_); - ~DebugMouse() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - Core::HID::MouseState next_state{}; - Core::HID::AnalogStickState last_mouse_wheel_state{}; - Core::HID::EmulatedDevices* emulated_devices = nullptr; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp deleted file mode 100644 index 1811cf620..000000000 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/debug_pad.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { - controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); -} - -DebugPad::~DebugPad() = default; - -void DebugPad::OnInit() {} - -void DebugPad::OnRelease() {} - -void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - DebugPadSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_pad; - - if (!IsControllerActivated()) { - shared_memory.debug_pad_lifo.buffer_count = 0; - shared_memory.debug_pad_lifo.buffer_tail = 0; - return; - } - - const auto& last_entry = shared_memory.debug_pad_lifo.ReadCurrentEntry().state; - next_state.sampling_number = last_entry.sampling_number + 1; - - if (Settings::values.debug_pad_enabled) { - next_state.attribute.connected.Assign(1); - - const auto& button_state = controller->GetDebugPadButtons(); - const auto& stick_state = controller->GetSticks(); - - next_state.pad_state = button_state; - next_state.l_stick = stick_state.left; - next_state.r_stick = stick_state.right; - } - - shared_memory.debug_pad_lifo.WriteNextEntry(next_state); -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h deleted file mode 100644 index dd00b2402..000000000 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/types/debug_pad_types.h" - -namespace Core::HID { -class HIDCore; -} - -namespace Core::Timing { -class CoreTiming; -} - -namespace Service::HID { -class DebugPad final : public ControllerBase { -public: - explicit DebugPad(Core::HID::HIDCore& hid_core_); - ~DebugPad() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - DebugPadState next_state{}; - Core::HID::EmulatedController* controller = nullptr; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/digitizer.cpp b/src/core/hle/service/hid/controllers/digitizer.cpp deleted file mode 100644 index c01580fd6..000000000 --- a/src/core/hle/service/hid/controllers/digitizer.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/digitizer.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} - -Digitizer::~Digitizer() = default; - -void Digitizer::OnInit() {} - -void Digitizer::OnRelease() {} - -void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - auto& header = data->shared_memory_format->digitizer.header; - header.timestamp = core_timing.GetGlobalTimeNs().count(); - header.total_entry_count = 17; - header.entry_count = 0; - header.last_entry_index = 0; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/digitizer.h b/src/core/hle/service/hid/controllers/digitizer.h deleted file mode 100644 index d81f814c3..000000000 --- a/src/core/hle/service/hid/controllers/digitizer.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Service::HID { - -class Digitizer final : public ControllerBase { -public: - explicit Digitizer(Core::HID::HIDCore& hid_core_); - ~Digitizer() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp deleted file mode 100644 index 6e686fe65..000000000 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/math_util.h" -#include "common/settings.h" -#include "core/frontend/emu_window.h" -#include "core/hid/emulated_console.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/gesture.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { -// HW is around 700, value is set to 400 to make it easier to trigger with mouse -constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s -constexpr f32 angle_threshold = 0.015f; // Threshold in radians -constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels -constexpr f32 press_delay = 0.5f; // Time in seconds -constexpr f32 double_tap_delay = 0.35f; // Time in seconds - -constexpr f32 Square(s32 num) { - return static_cast<f32>(num * num); -} - -Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { - console = hid_core.GetEmulatedConsole(); -} -Gesture::~Gesture() = default; - -void Gesture::OnInit() { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - shared_memory = &data->shared_memory_format->gesture; - shared_memory->gesture_lifo.buffer_count = 0; - shared_memory->gesture_lifo.buffer_tail = 0; - force_update = true; -} - -void Gesture::OnRelease() {} - -void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - shared_memory = &data->shared_memory_format->gesture; - - if (!IsControllerActivated()) { - shared_memory->gesture_lifo.buffer_count = 0; - shared_memory->gesture_lifo.buffer_tail = 0; - return; - } - - ReadTouchInput(); - - GestureProperties gesture = GetGestureProperties(); - f32 time_difference = - static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / - (1000 * 1000 * 1000); - - // Only update if necessary - if (!ShouldUpdateGesture(gesture, time_difference)) { - return; - } - - last_update_timestamp = shared_memory->gesture_lifo.timestamp; - UpdateGestureSharedMemory(gesture, time_difference); -} - -void Gesture::ReadTouchInput() { - if (!Settings::values.touchscreen.enabled) { - fingers = {}; - return; - } - - const auto touch_status = console->GetTouch(); - for (std::size_t id = 0; id < fingers.size(); ++id) { - fingers[id] = touch_status[id]; - } -} - -bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - if (force_update) { - force_update = false; - return true; - } - - // Update if coordinates change - for (size_t id = 0; id < MAX_POINTS; id++) { - if (gesture.points[id] != last_gesture.points[id]) { - return true; - } - } - - // Update on press and hold event after 0.5 seconds - if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && - time_difference > press_delay) { - return enable_press_and_tap; - } - - return false; -} - -void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { - GestureType type = GestureType::Idle; - GestureAttribute attributes{}; - - const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; - - // Reset next state to default - next_state.sampling_number = last_entry.sampling_number + 1; - next_state.delta = {}; - next_state.vel_x = 0; - next_state.vel_y = 0; - next_state.direction = GestureDirection::None; - next_state.rotation_angle = 0; - next_state.scale = 0; - - if (gesture.active_points > 0) { - if (last_gesture.active_points == 0) { - NewGesture(gesture, type, attributes); - } else { - UpdateExistingGesture(gesture, type, time_difference); - } - } else { - EndGesture(gesture, last_gesture, type, attributes, time_difference); - } - - // Apply attributes - next_state.detection_count = gesture.detection_count; - next_state.type = type; - next_state.attributes = attributes; - next_state.pos = gesture.mid_point; - next_state.point_count = static_cast<s32>(gesture.active_points); - next_state.points = gesture.points; - last_gesture = gesture; - - shared_memory->gesture_lifo.WriteNextEntry(next_state); -} - -void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, - GestureAttribute& attributes) { - const auto& last_entry = GetLastGestureEntry(); - - gesture.detection_count++; - type = GestureType::Touch; - - // New touch after cancel is not considered new - if (last_entry.type != GestureType::Cancel) { - attributes.is_new_touch.Assign(1); - enable_press_and_tap = true; - } -} - -void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, - f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - - // Promote to pan type if touch moved - for (size_t id = 0; id < MAX_POINTS; id++) { - if (gesture.points[id] != last_gesture.points[id]) { - type = GestureType::Pan; - break; - } - } - - // Number of fingers changed cancel the last event and clear data - if (gesture.active_points != last_gesture.active_points) { - type = GestureType::Cancel; - enable_press_and_tap = false; - gesture.active_points = 0; - gesture.mid_point = {}; - gesture.points.fill({}); - return; - } - - // Calculate extra parameters of panning - if (type == GestureType::Pan) { - UpdatePanEvent(gesture, last_gesture, type, time_difference); - return; - } - - // Promote to press type - if (last_entry.type == GestureType::Touch) { - type = GestureType::Press; - } -} - -void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - - if (last_gesture_props.active_points != 0) { - switch (last_entry.type) { - case GestureType::Touch: - if (enable_press_and_tap) { - SetTapEvent(gesture, last_gesture_props, type, attributes); - return; - } - type = GestureType::Cancel; - force_update = true; - break; - case GestureType::Press: - case GestureType::Tap: - case GestureType::Swipe: - case GestureType::Pinch: - case GestureType::Rotate: - type = GestureType::Complete; - force_update = true; - break; - case GestureType::Pan: - EndPanEvent(gesture, last_gesture_props, type, time_difference); - break; - default: - break; - } - return; - } - if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { - gesture.detection_count++; - } -} - -void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes) { - type = GestureType::Tap; - gesture = last_gesture_props; - force_update = true; - f32 tap_time_difference = - static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); - last_tap_timestamp = last_update_timestamp; - if (tap_time_difference < double_tap_delay) { - attributes.is_double_tap.Assign(1); - } -} - -void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - - next_state.delta = gesture.mid_point - last_entry.pos; - next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; - next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; - last_pan_time_difference = time_difference; - - // Promote to pinch type - if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > - pinch_threshold) { - type = GestureType::Pinch; - next_state.scale = gesture.average_distance / last_gesture_props.average_distance; - } - - const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / - (1 + (gesture.angle * last_gesture_props.angle))); - // Promote to rotate type - if (std::abs(angle_between_two_lines) > angle_threshold) { - type = GestureType::Rotate; - next_state.scale = 0; - next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; - } -} - -void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - next_state.vel_x = - static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); - next_state.vel_y = - static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); - const f32 curr_vel = - std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); - - // Set swipe event with parameters - if (curr_vel > swipe_threshold) { - SetSwipeEvent(gesture, last_gesture_props, type); - return; - } - - // End panning without swipe - type = GestureType::Complete; - next_state.vel_x = 0; - next_state.vel_y = 0; - force_update = true; -} - -void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type) { - const auto& last_entry = GetLastGestureEntry(); - - type = GestureType::Swipe; - gesture = last_gesture_props; - force_update = true; - next_state.delta = last_entry.delta; - - if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { - if (next_state.delta.x > 0) { - next_state.direction = GestureDirection::Right; - return; - } - next_state.direction = GestureDirection::Left; - return; - } - if (next_state.delta.y > 0) { - next_state.direction = GestureDirection::Down; - return; - } - next_state.direction = GestureDirection::Up; -} - -const GestureState& Gesture::GetLastGestureEntry() const { - return shared_memory->gesture_lifo.ReadCurrentEntry().state; -} - -GestureProperties Gesture::GetGestureProperties() { - GestureProperties gesture; - std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; - const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), - [](const auto& finger) { return finger.pressed; }); - gesture.active_points = - static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); - - for (size_t id = 0; id < gesture.active_points; ++id) { - const auto& [active_x, active_y] = active_fingers[id].position; - gesture.points[id] = { - .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), - .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), - }; - - // Hack: There is no touch in docked but games still allow it - if (Settings::IsDockedMode()) { - gesture.points[id] = { - .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), - .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), - }; - } - - gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); - gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); - } - - for (size_t id = 0; id < gesture.active_points; ++id) { - const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + - Square(gesture.mid_point.y - gesture.points[id].y)); - gesture.average_distance += distance / static_cast<f32>(gesture.active_points); - } - - gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), - static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); - - gesture.detection_count = last_gesture.detection_count; - - return gesture; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h deleted file mode 100644 index 78da1552a..000000000 --- a/src/core/hle/service/hid/controllers/gesture.h +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_types.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/types/touch_types.h" - -namespace Core::HID { -class EmulatedConsole; -} - -namespace Service::HID { -struct GestureSharedMemoryFormat; - -class Gesture final : public ControllerBase { -public: - explicit Gesture(Core::HID::HIDCore& hid_core_); - ~Gesture() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - // Reads input from all available input engines - void ReadTouchInput(); - - // Returns true if gesture state needs to be updated - bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); - - // Updates the shared memory to the next state - void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); - - // Initializes new gesture - void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); - - // Updates existing gesture state - void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); - - // Terminates exiting gesture - void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes, f32 time_difference); - - // Set current event to a tap event - void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes); - - // Calculates and set the extra parameters related to a pan event - void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference); - - // Terminates the pan event - void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference); - - // Set current event to a swipe event - void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type); - - // Retrieves the last gesture entry, as indicated by shared memory indices. - [[nodiscard]] const GestureState& GetLastGestureEntry() const; - - // Returns the average distance, angle and middle point of the active fingers - GestureProperties GetGestureProperties(); - - GestureState next_state{}; - GestureSharedMemoryFormat* shared_memory; - Core::HID::EmulatedConsole* console = nullptr; - - std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; - GestureProperties last_gesture{}; - s64 last_update_timestamp{}; - s64 last_tap_timestamp{}; - f32 last_pan_time_difference{}; - bool force_update{false}; - bool enable_press_and_tap{false}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/home_button.cpp b/src/core/hle/service/hid/controllers/home_button.cpp deleted file mode 100644 index 71dd9bc08..000000000 --- a/src/core/hle/service/hid/controllers/home_button.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/home_button.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} - -HomeButton::~HomeButton() = default; - -void HomeButton::OnInit() {} - -void HomeButton::OnRelease() {} - -void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - auto& header = data->shared_memory_format->home_button.header; - header.timestamp = core_timing.GetGlobalTimeNs().count(); - header.total_entry_count = 17; - header.entry_count = 0; - header.last_entry_index = 0; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/home_button.h b/src/core/hle/service/hid/controllers/home_button.h deleted file mode 100644 index e91c2aa5d..000000000 --- a/src/core/hle/service/hid/controllers/home_button.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Service::HID { - -class HomeButton final : public ControllerBase { -public: - explicit HomeButton(Core::HID::HIDCore& hid_core_); - ~HomeButton() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp deleted file mode 100644 index c72b3e5ce..000000000 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "core/core_timing.h" -#include "core/hid/emulated_devices.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/keyboard.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { - emulated_devices = hid_core.GetEmulatedDevices(); -} - -Keyboard::~Keyboard() = default; - -void Keyboard::OnInit() {} - -void Keyboard::OnRelease() {} - -void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - KeyboardSharedMemoryFormat& shared_memory = data->shared_memory_format->keyboard; - - if (!IsControllerActivated()) { - shared_memory.keyboard_lifo.buffer_count = 0; - shared_memory.keyboard_lifo.buffer_tail = 0; - return; - } - - const auto& last_entry = shared_memory.keyboard_lifo.ReadCurrentEntry().state; - next_state.sampling_number = last_entry.sampling_number + 1; - - if (Settings::values.keyboard_enabled) { - const auto& keyboard_state = emulated_devices->GetKeyboard(); - const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier(); - - next_state.key = keyboard_state; - next_state.modifier = keyboard_modifier_state; - next_state.attribute.is_connected.Assign(1); - } - - shared_memory.keyboard_lifo.WriteNextEntry(next_state); -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h deleted file mode 100644 index e8ca326c6..000000000 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/types/keyboard_types.h" - -namespace Service::HID { -class Keyboard final : public ControllerBase { -public: - explicit Keyboard(Core::HID::HIDCore& hid_core_); - ~Keyboard() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - KeyboardState next_state{}; - Core::HID::EmulatedDevices* emulated_devices = nullptr; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp deleted file mode 100644 index 58deafbc5..000000000 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/frontend/emu_window.h" -#include "core/hid/emulated_devices.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/mouse.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { - emulated_devices = hid_core.GetEmulatedDevices(); -} - -Mouse::~Mouse() = default; - -void Mouse::OnInit() {} -void Mouse::OnRelease() {} - -void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->mouse; - - if (!IsControllerActivated()) { - shared_memory.mouse_lifo.buffer_count = 0; - shared_memory.mouse_lifo.buffer_tail = 0; - return; - } - - next_state = {}; - - const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state; - next_state.sampling_number = last_entry.sampling_number + 1; - - if (Settings::values.mouse_enabled) { - const auto& mouse_button_state = emulated_devices->GetMouseButtons(); - const auto& mouse_position_state = emulated_devices->GetMousePosition(); - const auto& mouse_wheel_state = emulated_devices->GetMouseWheel(); - next_state.attribute.is_connected.Assign(1); - next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width); - next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height); - next_state.delta_x = next_state.x - last_entry.x; - next_state.delta_y = next_state.y - last_entry.y; - next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x; - next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y; - - last_mouse_wheel_state = mouse_wheel_state; - next_state.button = mouse_button_state; - } - - shared_memory.mouse_lifo.WriteNextEntry(next_state); -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h deleted file mode 100644 index cefad956c..000000000 --- a/src/core/hle/service/hid/controllers/mouse.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Core::HID { -class EmulatedDevices; -struct MouseState; -struct AnalogStickState; -} // namespace Core::HID - -namespace Service::HID { -class Mouse final : public ControllerBase { -public: - explicit Mouse(Core::HID::HIDCore& hid_core_); - ~Mouse() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - Core::HID::MouseState next_state{}; - Core::HID::AnalogStickState last_mouse_wheel_state{}; - Core::HID::EmulatedDevices* emulated_devices = nullptr; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp deleted file mode 100644 index c7aa606bc..000000000 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ /dev/null @@ -1,1346 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <array> -#include <chrono> -#include <cstring> - -#include "common/assert.h" -#include "common/bit_field.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" -#include "core/hle/service/hid/errors.h" -#include "core/hle/service/hid/hid_util.h" -#include "core/hle/service/kernel_helpers.h" - -namespace Service::HID { -constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{ - Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3, - Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6, - Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other, - Core::HID::NpadIdType::Handheld, -}; - -NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) - : ControllerBase{hid_core_}, service_context{service_context_} { - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - controller.device = hid_core.GetEmulatedControllerByIndex(i); - controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = - Core::HID::DEFAULT_VIBRATION_VALUE; - controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value = - Core::HID::DEFAULT_VIBRATION_VALUE; - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this, - i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); }, - .is_npad_service = true, - }; - controller.callback_key = controller.device->SetCallback(engine_callback); - } -} - -NPad::~NPad() { - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - controller.device->DeleteCallback(controller.callback_key); - } - OnRelease(); -} - -void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { - if (type == Core::HID::ControllerTriggerType::All) { - ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); - ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); - return; - } - if (controller_idx >= controller_data.size()) { - return; - } - - auto& controller = controller_data[controller_idx]; - const auto is_connected = controller.device->IsConnected(); - const auto npad_type = controller.device->GetNpadStyleIndex(); - const auto npad_id = controller.device->GetNpadIdType(); - switch (type) { - case Core::HID::ControllerTriggerType::Connected: - case Core::HID::ControllerTriggerType::Disconnected: - if (is_connected == controller.is_connected) { - return; - } - UpdateControllerAt(npad_type, npad_id, is_connected); - break; - case Core::HID::ControllerTriggerType::Battery: { - if (!controller.device->IsConnected()) { - return; - } - auto* shared_memory = controller.shared_memory; - const auto& battery_level = controller.device->GetBattery(); - shared_memory->battery_level_dual = battery_level.dual.battery_level; - shared_memory->battery_level_left = battery_level.left.battery_level; - shared_memory->battery_level_right = battery_level.right.battery_level; - break; - } - default: - break; - } -} - -void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { - auto& controller = GetControllerFromNpadIdType(npad_id); - if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { - return; - } - LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); - const auto controller_type = controller.device->GetNpadStyleIndex(); - const auto& body_colors = controller.device->GetColors(); - const auto& battery_level = controller.device->GetBattery(); - auto* shared_memory = controller.shared_memory; - if (controller_type == Core::HID::NpadStyleIndex::None) { - controller.styleset_changed_event->Signal(); - return; - } - - // Reset memory values - shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; - shared_memory->device_type.raw = 0; - shared_memory->system_properties.raw = 0; - shared_memory->joycon_color.attribute = ColorAttribute::NoController; - shared_memory->joycon_color.attribute = ColorAttribute::NoController; - shared_memory->fullkey_color = {}; - shared_memory->joycon_color.left = {}; - shared_memory->joycon_color.right = {}; - shared_memory->battery_level_dual = {}; - shared_memory->battery_level_left = {}; - shared_memory->battery_level_right = {}; - - switch (controller_type) { - case Core::HID::NpadStyleIndex::None: - ASSERT(false); - break; - case Core::HID::NpadStyleIndex::ProController: - shared_memory->fullkey_color.attribute = ColorAttribute::Ok; - shared_memory->fullkey_color.fullkey = body_colors.fullkey; - shared_memory->battery_level_dual = battery_level.dual.battery_level; - shared_memory->style_tag.fullkey.Assign(1); - shared_memory->device_type.fullkey.Assign(1); - shared_memory->system_properties.is_vertical.Assign(1); - shared_memory->system_properties.use_plus.Assign(1); - shared_memory->system_properties.use_minus.Assign(1); - shared_memory->system_properties.is_charging_joy_dual.Assign( - battery_level.dual.is_charging); - shared_memory->applet_footer_type = AppletFooterUiType::SwitchProController; - shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); - break; - case Core::HID::NpadStyleIndex::Handheld: - shared_memory->fullkey_color.attribute = ColorAttribute::Ok; - shared_memory->joycon_color.attribute = ColorAttribute::Ok; - shared_memory->fullkey_color.fullkey = body_colors.fullkey; - shared_memory->joycon_color.left = body_colors.left; - shared_memory->joycon_color.right = body_colors.right; - shared_memory->style_tag.handheld.Assign(1); - shared_memory->device_type.handheld_left.Assign(1); - shared_memory->device_type.handheld_right.Assign(1); - shared_memory->system_properties.is_vertical.Assign(1); - shared_memory->system_properties.use_plus.Assign(1); - shared_memory->system_properties.use_minus.Assign(1); - shared_memory->system_properties.use_directional_buttons.Assign(1); - shared_memory->system_properties.is_charging_joy_dual.Assign( - battery_level.left.is_charging); - shared_memory->system_properties.is_charging_joy_left.Assign( - battery_level.left.is_charging); - shared_memory->system_properties.is_charging_joy_right.Assign( - battery_level.right.is_charging); - shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; - shared_memory->applet_footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; - shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); - break; - case Core::HID::NpadStyleIndex::JoyconDual: - shared_memory->fullkey_color.attribute = ColorAttribute::Ok; - shared_memory->joycon_color.attribute = ColorAttribute::Ok; - shared_memory->style_tag.joycon_dual.Assign(1); - if (controller.is_dual_left_connected) { - shared_memory->joycon_color.left = body_colors.left; - shared_memory->battery_level_left = battery_level.left.battery_level; - shared_memory->device_type.joycon_left.Assign(1); - shared_memory->system_properties.use_minus.Assign(1); - shared_memory->system_properties.is_charging_joy_left.Assign( - battery_level.left.is_charging); - shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); - } - if (controller.is_dual_right_connected) { - shared_memory->joycon_color.right = body_colors.right; - shared_memory->battery_level_right = battery_level.right.battery_level; - shared_memory->device_type.joycon_right.Assign(1); - shared_memory->system_properties.use_plus.Assign(1); - shared_memory->system_properties.is_charging_joy_right.Assign( - battery_level.right.is_charging); - shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); - } - shared_memory->system_properties.use_directional_buttons.Assign(1); - shared_memory->system_properties.is_vertical.Assign(1); - shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; - - if (controller.is_dual_left_connected && controller.is_dual_right_connected) { - shared_memory->applet_footer_type = AppletFooterUiType::JoyDual; - shared_memory->fullkey_color.fullkey = body_colors.left; - shared_memory->battery_level_dual = battery_level.left.battery_level; - shared_memory->system_properties.is_charging_joy_dual.Assign( - battery_level.left.is_charging); - } else if (controller.is_dual_left_connected) { - shared_memory->applet_footer_type = AppletFooterUiType::JoyDualLeftOnly; - shared_memory->fullkey_color.fullkey = body_colors.left; - shared_memory->battery_level_dual = battery_level.left.battery_level; - shared_memory->system_properties.is_charging_joy_dual.Assign( - battery_level.left.is_charging); - } else { - shared_memory->applet_footer_type = AppletFooterUiType::JoyDualRightOnly; - shared_memory->fullkey_color.fullkey = body_colors.right; - shared_memory->battery_level_dual = battery_level.right.battery_level; - shared_memory->system_properties.is_charging_joy_dual.Assign( - battery_level.right.is_charging); - } - break; - case Core::HID::NpadStyleIndex::JoyconLeft: - shared_memory->fullkey_color.attribute = ColorAttribute::Ok; - shared_memory->fullkey_color.fullkey = body_colors.left; - shared_memory->joycon_color.attribute = ColorAttribute::Ok; - shared_memory->joycon_color.left = body_colors.left; - shared_memory->battery_level_dual = battery_level.left.battery_level; - shared_memory->style_tag.joycon_left.Assign(1); - shared_memory->device_type.joycon_left.Assign(1); - shared_memory->system_properties.is_horizontal.Assign(1); - shared_memory->system_properties.use_minus.Assign(1); - shared_memory->system_properties.is_charging_joy_left.Assign( - battery_level.left.is_charging); - shared_memory->applet_footer_type = AppletFooterUiType::JoyLeftHorizontal; - shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); - break; - case Core::HID::NpadStyleIndex::JoyconRight: - shared_memory->fullkey_color.attribute = ColorAttribute::Ok; - shared_memory->fullkey_color.fullkey = body_colors.right; - shared_memory->joycon_color.attribute = ColorAttribute::Ok; - shared_memory->joycon_color.right = body_colors.right; - shared_memory->battery_level_right = battery_level.right.battery_level; - shared_memory->style_tag.joycon_right.Assign(1); - shared_memory->device_type.joycon_right.Assign(1); - shared_memory->system_properties.is_horizontal.Assign(1); - shared_memory->system_properties.use_plus.Assign(1); - shared_memory->system_properties.is_charging_joy_right.Assign( - battery_level.right.is_charging); - shared_memory->applet_footer_type = AppletFooterUiType::JoyRightHorizontal; - shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); - break; - case Core::HID::NpadStyleIndex::GameCube: - shared_memory->style_tag.gamecube.Assign(1); - shared_memory->device_type.fullkey.Assign(1); - shared_memory->system_properties.is_vertical.Assign(1); - shared_memory->system_properties.use_plus.Assign(1); - break; - case Core::HID::NpadStyleIndex::Pokeball: - shared_memory->style_tag.palma.Assign(1); - shared_memory->device_type.palma.Assign(1); - shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); - break; - case Core::HID::NpadStyleIndex::NES: - shared_memory->style_tag.lark.Assign(1); - shared_memory->device_type.fullkey.Assign(1); - break; - case Core::HID::NpadStyleIndex::SNES: - shared_memory->style_tag.lucia.Assign(1); - shared_memory->device_type.fullkey.Assign(1); - shared_memory->applet_footer_type = AppletFooterUiType::Lucia; - break; - case Core::HID::NpadStyleIndex::N64: - shared_memory->style_tag.lagoon.Assign(1); - shared_memory->device_type.fullkey.Assign(1); - shared_memory->applet_footer_type = AppletFooterUiType::Lagon; - break; - case Core::HID::NpadStyleIndex::SegaGenesis: - shared_memory->style_tag.lager.Assign(1); - shared_memory->device_type.fullkey.Assign(1); - break; - default: - break; - } - - controller.is_connected = true; - controller.device->Connect(); - controller.device->SetLedPattern(); - if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) { - if (controller.is_dual_left_connected) { - controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex, - Common::Input::PollingMode::Active); - } - if (controller.is_dual_right_connected) { - controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - } - } else { - controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, - Common::Input::PollingMode::Active); - } - - SignalStyleSetChangedEvent(npad_id); - WriteEmptyEntry(controller.shared_memory); - hid_core.SetLastActiveController(npad_id); -} - -void NPad::OnInit() { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - if (!IsControllerActivated()) { - return; - } - - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state; - controller.styleset_changed_event = - service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); - } - - supported_npad_id_types.resize(npad_id_list.size()); - std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), - npad_id_list.size() * sizeof(Core::HID::NpadIdType)); - - // Prefill controller buffers - for (auto& controller : controller_data) { - auto* npad = controller.shared_memory; - npad->fullkey_color = { - .attribute = ColorAttribute::NoController, - .fullkey = {}, - }; - npad->joycon_color = { - .attribute = ColorAttribute::NoController, - .left = {}, - .right = {}, - }; - // HW seems to initialize the first 19 entries - for (std::size_t i = 0; i < 19; ++i) { - WriteEmptyEntry(npad); - } - } -} - -void NPad::WriteEmptyEntry(NpadInternalState* npad) { - NPadGenericState dummy_pad_state{}; - NpadGcTriggerState dummy_gc_state{}; - dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; - npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; - npad->handheld_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; - npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; - npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; - npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; - npad->palma_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; - npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); - dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; - npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); -} - -void NPad::OnRelease() { - is_controller_initialized = false; - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - if (controller.styleset_changed_event) { - service_context.CloseEvent(controller.styleset_changed_event); - } - for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { - VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {}); - } - } -} - -void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { - std::scoped_lock lock{mutex}; - auto& controller = GetControllerFromNpadIdType(npad_id); - const auto controller_type = controller.device->GetNpadStyleIndex(); - - if (!controller.device->IsConnected() && controller.is_connected) { - DisconnectNpad(npad_id); - return; - } - if (!controller.device->IsConnected()) { - return; - } - if (controller.device->IsConnected() && !controller.is_connected) { - InitNewlyAddedController(npad_id); - } - - // This function is unique to yuzu for the turbo buttons and motion to work properly - controller.device->StatusUpdate(); - - auto& pad_entry = controller.npad_pad_state; - auto& trigger_entry = controller.npad_trigger_state; - const auto button_state = controller.device->GetNpadButtons(); - const auto stick_state = controller.device->GetSticks(); - - using btn = Core::HID::NpadButton; - pad_entry.npad_buttons.raw = btn::None; - if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) { - constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R | - btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp | - btn::StickRRight | btn::StickRDown; - pad_entry.npad_buttons.raw = button_state.raw & right_button_mask; - pad_entry.r_stick = stick_state.right; - } - - if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) { - constexpr btn left_button_mask = - btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL | - btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown; - pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask; - pad_entry.l_stick = stick_state.left; - } - - if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft || - controller_type == Core::HID::NpadStyleIndex::JoyconDual) { - pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); - pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); - } - - if (controller_type == Core::HID::NpadStyleIndex::JoyconRight || - controller_type == Core::HID::NpadStyleIndex::JoyconDual) { - pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); - pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); - } - - if (controller_type == Core::HID::NpadStyleIndex::GameCube) { - const auto& trigger_state = controller.device->GetTriggers(); - trigger_entry.l_analog = trigger_state.left; - trigger_entry.r_analog = trigger_state.right; - pad_entry.npad_buttons.zl.Assign(false); - pad_entry.npad_buttons.zr.Assign(button_state.r); - pad_entry.npad_buttons.l.Assign(button_state.zl); - pad_entry.npad_buttons.r.Assign(button_state.zr); - } - - if (pad_entry.npad_buttons.raw != Core::HID::NpadButton::None) { - hid_core.SetLastActiveController(npad_id); - } -} - -void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - if (!IsControllerActivated()) { - return; - } - - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state; - auto* npad = controller.shared_memory; - - const auto& controller_type = controller.device->GetNpadStyleIndex(); - - if (controller_type == Core::HID::NpadStyleIndex::None || - !controller.device->IsConnected()) { - continue; - } - - RequestPadStateUpdate(controller.device->GetNpadIdType()); - auto& pad_state = controller.npad_pad_state; - auto& libnx_state = controller.npad_libnx_state; - auto& trigger_state = controller.npad_trigger_state; - - // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate - // any controllers. - libnx_state.connection_status.raw = 0; - libnx_state.connection_status.is_connected.Assign(1); - switch (controller_type) { - case Core::HID::NpadStyleIndex::None: - ASSERT(false); - break; - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::NES: - case Core::HID::NpadStyleIndex::SNES: - case Core::HID::NpadStyleIndex::N64: - case Core::HID::NpadStyleIndex::SegaGenesis: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - pad_state.connection_status.is_wired.Assign(1); - - libnx_state.connection_status.is_wired.Assign(1); - pad_state.sampling_number = - npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->fullkey_lifo.WriteNextEntry(pad_state); - break; - case Core::HID::NpadStyleIndex::Handheld: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - pad_state.connection_status.is_wired.Assign(1); - pad_state.connection_status.is_left_connected.Assign(1); - pad_state.connection_status.is_right_connected.Assign(1); - pad_state.connection_status.is_left_wired.Assign(1); - pad_state.connection_status.is_right_wired.Assign(1); - - libnx_state.connection_status.is_wired.Assign(1); - libnx_state.connection_status.is_left_connected.Assign(1); - libnx_state.connection_status.is_right_connected.Assign(1); - libnx_state.connection_status.is_left_wired.Assign(1); - libnx_state.connection_status.is_right_wired.Assign(1); - pad_state.sampling_number = - npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->handheld_lifo.WriteNextEntry(pad_state); - break; - case Core::HID::NpadStyleIndex::JoyconDual: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - if (controller.is_dual_left_connected) { - pad_state.connection_status.is_left_connected.Assign(1); - libnx_state.connection_status.is_left_connected.Assign(1); - } - if (controller.is_dual_right_connected) { - pad_state.connection_status.is_right_connected.Assign(1); - libnx_state.connection_status.is_right_connected.Assign(1); - } - - pad_state.sampling_number = - npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->joy_dual_lifo.WriteNextEntry(pad_state); - break; - case Core::HID::NpadStyleIndex::JoyconLeft: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - pad_state.connection_status.is_left_connected.Assign(1); - - libnx_state.connection_status.is_left_connected.Assign(1); - pad_state.sampling_number = - npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->joy_left_lifo.WriteNextEntry(pad_state); - break; - case Core::HID::NpadStyleIndex::JoyconRight: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - pad_state.connection_status.is_right_connected.Assign(1); - - libnx_state.connection_status.is_right_connected.Assign(1); - pad_state.sampling_number = - npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->joy_right_lifo.WriteNextEntry(pad_state); - break; - case Core::HID::NpadStyleIndex::GameCube: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - pad_state.connection_status.is_wired.Assign(1); - - libnx_state.connection_status.is_wired.Assign(1); - pad_state.sampling_number = - npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; - trigger_state.sampling_number = - npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->fullkey_lifo.WriteNextEntry(pad_state); - npad->gc_trigger_lifo.WriteNextEntry(trigger_state); - break; - case Core::HID::NpadStyleIndex::Pokeball: - pad_state.connection_status.raw = 0; - pad_state.connection_status.is_connected.Assign(1); - pad_state.sampling_number = - npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad->palma_lifo.WriteNextEntry(pad_state); - break; - default: - break; - } - - libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; - libnx_state.l_stick = pad_state.l_stick; - libnx_state.r_stick = pad_state.r_stick; - npad->system_ext_lifo.WriteNextEntry(pad_state); - - press_state |= static_cast<u64>(pad_state.npad_buttons.raw); - } -} - -void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { - hid_core.SetSupportedStyleTag(style_set); - - if (is_controller_initialized) { - return; - } - - // Once SetSupportedStyleSet is called controllers are fully initialized - is_controller_initialized = true; -} - -Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const { - if (!is_controller_initialized) { - return {Core::HID::NpadStyleSet::None}; - } - return hid_core.GetSupportedStyleTag(); -} - -Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { - constexpr std::size_t max_number_npad_ids = 0xa; - const auto length = data.size(); - ASSERT(length > 0 && (length % sizeof(u32)) == 0); - const std::size_t elements = length / sizeof(u32); - - if (elements > max_number_npad_ids) { - return InvalidArraySize; - } - - supported_npad_id_types.clear(); - supported_npad_id_types.resize(elements); - std::memcpy(supported_npad_id_types.data(), data.data(), length); - return ResultSuccess; -} - -void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { - const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); - ASSERT(max_length <= copy_amount); - std::memcpy(data, supported_npad_id_types.data(), copy_amount); -} - -std::size_t NPad::GetSupportedNpadIdTypesSize() const { - return supported_npad_id_types.size(); -} - -void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { - if (joy_hold_type != NpadJoyHoldType::Horizontal && - joy_hold_type != NpadJoyHoldType::Vertical) { - LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", - joy_hold_type); - return; - } - hold_type = joy_hold_type; -} - -NpadJoyHoldType NPad::GetHoldType() const { - return hold_type; -} - -void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { - if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { - ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); - return; - } - - handheld_activation_mode = activation_mode; -} - -NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { - return handheld_activation_mode; -} - -void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { - communication_mode = communication_mode_; -} - -NpadCommunicationMode NPad::GetNpadCommunicationMode() const { - return communication_mode; -} - -bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, - NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return false; - } - - auto& controller = GetControllerFromNpadIdType(npad_id); - if (controller.shared_memory->assignment_mode != assignment_mode) { - controller.shared_memory->assignment_mode = assignment_mode; - } - - if (!controller.device->IsConnected()) { - return false; - } - - if (assignment_mode == NpadJoyAssignmentMode::Dual) { - if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) { - DisconnectNpad(npad_id); - controller.is_dual_left_connected = true; - controller.is_dual_right_connected = false; - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); - return false; - } - if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { - DisconnectNpad(npad_id); - controller.is_dual_left_connected = false; - controller.is_dual_right_connected = true; - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); - return false; - } - return false; - } - - // This is for NpadJoyAssignmentMode::Single - - // Only JoyconDual get affected by this function - if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { - return false; - } - - if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { - DisconnectNpad(npad_id); - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); - return false; - } - if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { - DisconnectNpad(npad_id); - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); - return false; - } - - // We have two controllers connected to the same npad_id we need to split them - new_npad_id = hid_core.GetFirstDisconnectedNpadId(); - auto& controller_2 = GetControllerFromNpadIdType(new_npad_id); - DisconnectNpad(npad_id); - if (npad_device_type == NpadJoyDeviceType::Left) { - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); - controller_2.is_dual_left_connected = false; - controller_2.is_dual_right_connected = true; - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); - } else { - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); - controller_2.is_dual_left_connected = true; - controller_2.is_dual_right_connected = false; - UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); - } - return true; -} - -bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, - const Core::HID::VibrationValue& vibration_value) { - auto& controller = GetControllerFromNpadIdType(npad_id); - if (!controller.device->IsConnected()) { - return false; - } - - if (!controller.device->IsVibrationEnabled(device_index)) { - if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || - controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { - // Send an empty vibration to stop any vibrations. - Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f}; - controller.device->SetVibration(device_index, vibration); - // Then reset the vibration value to its default value. - controller.vibration[device_index].latest_vibration_value = - Core::HID::DEFAULT_VIBRATION_VALUE; - } - - return false; - } - - if (!Settings::values.enable_accurate_vibrations.GetValue()) { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::steady_clock; - - const auto now = steady_clock::now(); - - // Filter out non-zero vibrations that are within 15ms of each other. - if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && - duration_cast<milliseconds>( - now - controller.vibration[device_index].last_vibration_timepoint) < - milliseconds(15)) { - return false; - } - - controller.vibration[device_index].last_vibration_timepoint = now; - } - - Core::HID::VibrationValue vibration{ - vibration_value.low_amplitude, vibration_value.low_frequency, - vibration_value.high_amplitude, vibration_value.high_frequency}; - return controller.device->SetVibration(device_index, vibration); -} - -void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, - const Core::HID::VibrationValue& vibration_value) { - if (IsVibrationHandleValid(vibration_device_handle).IsError()) { - return; - } - - if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { - return; - } - - auto& controller = GetControllerFromHandle(vibration_device_handle); - const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); - - if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) { - return; - } - - if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { - ASSERT_MSG(false, "DeviceIndex should never be None!"); - return; - } - - // Some games try to send mismatched parameters in the device handle, block these. - if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft && - (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight || - vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) || - (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight && - (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft || - vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) { - return; - } - - // Filter out vibrations with equivalent values to reduce unnecessary state changes. - if (vibration_value.low_amplitude == - controller.vibration[device_index].latest_vibration_value.low_amplitude && - vibration_value.high_amplitude == - controller.vibration[device_index].latest_vibration_value.high_amplitude) { - return; - } - - if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index, - vibration_value)) { - controller.vibration[device_index].latest_vibration_value = vibration_value; - } -} - -void NPad::VibrateControllers( - std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, - std::span<const Core::HID::VibrationValue> vibration_values) { - if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { - return; - } - - ASSERT_OR_EXECUTE_MSG( - vibration_device_handles.size() == vibration_values.size(), { return; }, - "The amount of device handles does not match with the amount of vibration values," - "this is undefined behavior!"); - - for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { - VibrateController(vibration_device_handles[i], vibration_values[i]); - } -} - -Core::HID::VibrationValue NPad::GetLastVibration( - const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (IsVibrationHandleValid(vibration_device_handle).IsError()) { - return {}; - } - - const auto& controller = GetControllerFromHandle(vibration_device_handle); - const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); - return controller.vibration[device_index].latest_vibration_value; -} - -void NPad::InitializeVibrationDevice( - const Core::HID::VibrationDeviceHandle& vibration_device_handle) { - if (IsVibrationHandleValid(vibration_device_handle).IsError()) { - return; - } - - const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id); - const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); - InitializeVibrationDeviceAtIndex(npad_index, device_index); -} - -void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, - std::size_t device_index) { - auto& controller = GetControllerFromNpadIdType(npad_id); - if (!Settings::values.vibration_enabled.GetValue()) { - controller.vibration[device_index].device_mounted = false; - return; - } - - controller.vibration[device_index].device_mounted = - controller.device->IsVibrationEnabled(device_index); -} - -void NPad::SetPermitVibrationSession(bool permit_vibration_session) { - permit_vibration_session_enabled = permit_vibration_session; -} - -bool NPad::IsVibrationDeviceMounted( - const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (IsVibrationHandleValid(vibration_device_handle).IsError()) { - return false; - } - - const auto& controller = GetControllerFromHandle(vibration_device_handle); - const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); - return controller.vibration[device_index].device_mounted; -} - -Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - // Fallback to player 1 - const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1); - return controller.styleset_changed_event->GetReadableEvent(); - } - - const auto& controller = GetControllerFromNpadIdType(npad_id); - return controller.styleset_changed_event->GetReadableEvent(); -} - -void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { - const auto& controller = GetControllerFromNpadIdType(npad_id); - controller.styleset_changed_event->Signal(); -} - -void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { - UpdateControllerAt(controller, npad_id, true); -} - -void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, - bool connected) { - auto& controller = GetControllerFromNpadIdType(npad_id); - if (!connected) { - DisconnectNpad(npad_id); - return; - } - - controller.device->SetNpadStyleIndex(type); - InitNewlyAddedController(npad_id); -} - -Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return InvalidNpadId; - } - - LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); - auto& controller = GetControllerFromNpadIdType(npad_id); - for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { - // Send an empty vibration to stop any vibrations. - VibrateControllerAtIndex(npad_id, device_idx, {}); - controller.vibration[device_idx].device_mounted = false; - } - - auto* shared_memory = controller.shared_memory; - // Don't reset shared_memory->assignment_mode this value is persistent - shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out - shared_memory->device_type.raw = 0; - shared_memory->system_properties.raw = 0; - shared_memory->button_properties.raw = 0; - shared_memory->sixaxis_fullkey_properties.raw = 0; - shared_memory->sixaxis_handheld_properties.raw = 0; - shared_memory->sixaxis_dual_left_properties.raw = 0; - shared_memory->sixaxis_dual_right_properties.raw = 0; - shared_memory->sixaxis_left_properties.raw = 0; - shared_memory->sixaxis_right_properties.raw = 0; - shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty; - shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty; - shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty; - shared_memory->fullkey_color = { - .attribute = ColorAttribute::NoController, - .fullkey = {}, - }; - shared_memory->joycon_color = { - .attribute = ColorAttribute::NoController, - .left = {}, - .right = {}, - }; - shared_memory->applet_footer_type = AppletFooterUiType::None; - - controller.is_dual_left_connected = true; - controller.is_dual_right_connected = true; - controller.is_connected = false; - controller.device->Disconnect(); - SignalStyleSetChangedEvent(npad_id); - WriteEmptyEntry(shared_memory); - return ResultSuccess; -} - -Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); - is_firmware_available = sixaxis_properties.is_firmware_update_available != 0; - return ResultSuccess; -} - -Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); - sixaxis_properties.is_newly_assigned.Assign(0); - - return ResultSuccess; -} - -Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { - if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, - npad_id_2); - return InvalidNpadId; - } - auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); - auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); - auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); - auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); - - // Simplify this code by converting dualjoycon with only a side connected to single joycons - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) { - if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { - controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft; - } - if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { - controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight; - } - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { - if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { - controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft; - } - if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { - controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight; - } - } - - // Invalid merge errors - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual || - controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { - return NpadIsDualJoycon; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) { - return NpadIsSameType; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { - return NpadIsSameType; - } - - // These exceptions are handled as if they where dual joycon - if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft && - controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) { - return NpadIsDualJoycon; - } - if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft && - controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) { - return NpadIsDualJoycon; - } - - // Disconnect the joycons and connect them as dual joycon at the first index. - DisconnectNpad(npad_id_1); - DisconnectNpad(npad_id_2); - controller_1.is_dual_left_connected = true; - controller_1.is_dual_right_connected = true; - AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); - return ResultSuccess; -} - -void NPad::StartLRAssignmentMode() { - // Nothing internally is used for lr assignment mode. Since we have the ability to set the - // controller types from boot, it doesn't really matter about showing a selection screen - is_in_lr_assignment_mode = true; -} - -void NPad::StopLRAssignmentMode() { - is_in_lr_assignment_mode = false; -} - -Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { - if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, - npad_id_2); - return InvalidNpadId; - } - if (npad_id_1 == Core::HID::NpadIdType::Handheld || - npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || - npad_id_2 == Core::HID::NpadIdType::Other) { - return ResultSuccess; - } - const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; - const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; - const auto type_index_1 = controller_1->GetNpadStyleIndex(); - const auto type_index_2 = controller_2->GetNpadStyleIndex(); - const auto is_connected_1 = controller_1->IsConnected(); - const auto is_connected_2 = controller_2->IsConnected(); - - if (!IsControllerSupported(type_index_1) && is_connected_1) { - return NpadNotConnected; - } - if (!IsControllerSupported(type_index_2) && is_connected_2) { - return NpadNotConnected; - } - - UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); - UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); - - return ResultSuccess; -} - -Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return InvalidNpadId; - } - const auto& controller = GetControllerFromNpadIdType(npad_id).device; - pattern = controller->GetLedPattern(); - return ResultSuccess; -} - -Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, - bool& is_valid) const { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return InvalidNpadId; - } - const auto& controller = GetControllerFromNpadIdType(npad_id); - is_valid = controller.unintended_home_button_input_protection; - return ResultSuccess; -} - -Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, - Core::HID::NpadIdType npad_id) { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return InvalidNpadId; - } - auto& controller = GetControllerFromNpadIdType(npad_id); - controller.unintended_home_button_input_protection = is_protection_enabled; - return ResultSuccess; -} - -void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { - analog_stick_use_center_clamp = use_center_clamp; -} - -void NPad::ClearAllConnectedControllers() { - for (auto& controller : controller_data) { - if (controller.device->IsConnected() && - controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { - controller.device->Disconnect(); - controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); - } - } -} - -void NPad::DisconnectAllConnectedControllers() { - for (auto& controller : controller_data) { - controller.device->Disconnect(); - } -} - -void NPad::ConnectAllDisconnectedControllers() { - for (auto& controller : controller_data) { - if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && - !controller.device->IsConnected()) { - controller.device->Connect(); - } - } -} - -void NPad::ClearAllControllers() { - for (auto& controller : controller_data) { - controller.device->Disconnect(); - controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); - } -} - -Core::HID::NpadButton NPad::GetAndResetPressState() { - return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); -} - -void NPad::ApplyNpadSystemCommonPolicy() { - Core::HID::NpadStyleTag styletag{}; - styletag.fullkey.Assign(1); - styletag.handheld.Assign(1); - styletag.joycon_dual.Assign(1); - styletag.system_ext.Assign(1); - styletag.system.Assign(1); - SetSupportedStyleSet(styletag); - - SetNpadHandheldActivationMode(NpadHandheldActivationMode::Dual); - - supported_npad_id_types.clear(); - supported_npad_id_types.resize(10); - supported_npad_id_types[0] = Core::HID::NpadIdType::Player1; - supported_npad_id_types[1] = Core::HID::NpadIdType::Player2; - supported_npad_id_types[2] = Core::HID::NpadIdType::Player3; - supported_npad_id_types[3] = Core::HID::NpadIdType::Player4; - supported_npad_id_types[4] = Core::HID::NpadIdType::Player5; - supported_npad_id_types[5] = Core::HID::NpadIdType::Player6; - supported_npad_id_types[6] = Core::HID::NpadIdType::Player7; - supported_npad_id_types[7] = Core::HID::NpadIdType::Player8; - supported_npad_id_types[8] = Core::HID::NpadIdType::Other; - supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; -} - -bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { - if (controller == Core::HID::NpadStyleIndex::Handheld) { - const bool support_handheld = - std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), - Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end(); - // Handheld is not even a supported type, lets stop here - if (!support_handheld) { - return false; - } - // Handheld shouldn't be supported in docked mode - if (Settings::IsDockedMode()) { - return false; - } - - return true; - } - - if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), - [](Core::HID::NpadIdType npad_id) { - return npad_id <= Core::HID::NpadIdType::Player8; - })) { - Core::HID::NpadStyleTag style = GetSupportedStyleSet(); - switch (controller) { - case Core::HID::NpadStyleIndex::ProController: - return style.fullkey.As<bool>(); - case Core::HID::NpadStyleIndex::JoyconDual: - return style.joycon_dual.As<bool>(); - case Core::HID::NpadStyleIndex::JoyconLeft: - return style.joycon_left.As<bool>(); - case Core::HID::NpadStyleIndex::JoyconRight: - return style.joycon_right.As<bool>(); - case Core::HID::NpadStyleIndex::GameCube: - return style.gamecube.As<bool>(); - case Core::HID::NpadStyleIndex::Pokeball: - return style.palma.As<bool>(); - case Core::HID::NpadStyleIndex::NES: - return style.lark.As<bool>(); - case Core::HID::NpadStyleIndex::SNES: - return style.lucia.As<bool>(); - case Core::HID::NpadStyleIndex::N64: - return style.lagoon.As<bool>(); - case Core::HID::NpadStyleIndex::SegaGenesis: - return style.lager.As<bool>(); - default: - return false; - } - } - - return false; -} - -NPad::NpadControllerData& NPad::GetControllerFromHandle( - const Core::HID::VibrationDeviceHandle& device_handle) { - const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); - return GetControllerFromNpadIdType(npad_id); -} - -const NPad::NpadControllerData& NPad::GetControllerFromHandle( - const Core::HID::VibrationDeviceHandle& device_handle) const { - const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); - return GetControllerFromNpadIdType(npad_id); -} - -NPad::NpadControllerData& NPad::GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) { - const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); - return GetControllerFromNpadIdType(npad_id); -} - -const NPad::NpadControllerData& NPad::GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) const { - const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); - return GetControllerFromNpadIdType(npad_id); -} - -NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - npad_id = Core::HID::NpadIdType::Player1; - } - const auto npad_index = NpadIdTypeToIndex(npad_id); - return controller_data[npad_index]; -} - -const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( - Core::HID::NpadIdType npad_id) const { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - npad_id = Core::HID::NpadIdType::Player1; - } - const auto npad_index = NpadIdTypeToIndex(npad_id); - return controller_data[npad_index]; -} - -Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) { - auto& controller = GetControllerFromHandle(sixaxis_handle); - switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: - return controller.shared_memory->sixaxis_fullkey_properties; - case Core::HID::NpadStyleIndex::Handheld: - return controller.shared_memory->sixaxis_handheld_properties; - case Core::HID::NpadStyleIndex::JoyconDual: - if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { - return controller.shared_memory->sixaxis_dual_left_properties; - } - return controller.shared_memory->sixaxis_dual_right_properties; - case Core::HID::NpadStyleIndex::JoyconLeft: - return controller.shared_memory->sixaxis_left_properties; - case Core::HID::NpadStyleIndex::JoyconRight: - return controller.shared_memory->sixaxis_right_properties; - default: - return controller.shared_memory->sixaxis_fullkey_properties; - } -} - -const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { - const auto& controller = GetControllerFromHandle(sixaxis_handle); - switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: - return controller.shared_memory->sixaxis_fullkey_properties; - case Core::HID::NpadStyleIndex::Handheld: - return controller.shared_memory->sixaxis_handheld_properties; - case Core::HID::NpadStyleIndex::JoyconDual: - if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { - return controller.shared_memory->sixaxis_dual_left_properties; - } - return controller.shared_memory->sixaxis_dual_right_properties; - case Core::HID::NpadStyleIndex::JoyconLeft: - return controller.shared_memory->sixaxis_left_properties; - case Core::HID::NpadStyleIndex::JoyconRight: - return controller.shared_memory->sixaxis_right_properties; - default: - return controller.shared_memory->sixaxis_fullkey_properties; - } -} - -AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { - const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; - - return { - .ui_variant = 0, - .footer = shared_memory->applet_footer_type, - }; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h deleted file mode 100644 index 80cfcb2bb..000000000 --- a/src/core/hle/service/hid/controllers/npad.h +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <atomic> -#include <mutex> -#include <span> - -#include "common/common_types.h" -#include "core/hid/hid_types.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/types/npad_types.h" - -namespace Core::HID { -class EmulatedController; -enum class ControllerTriggerType; -} // namespace Core::HID - -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Service::KernelHelpers { -class ServiceContext; -} // namespace Service::KernelHelpers - -union Result; - -namespace Service::HID { -class AppletResource; -struct NpadInternalState; -struct NpadSixAxisSensorLifo; -struct NpadSharedMemoryFormat; - -class NPad final : public ControllerBase { -public: - explicit NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_); - ~NPad() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - - void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); - Core::HID::NpadStyleTag GetSupportedStyleSet() const; - - Result SetSupportedNpadIdTypes(std::span<const u8> data); - void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); - std::size_t GetSupportedNpadIdTypesSize() const; - - void SetHoldType(NpadJoyHoldType joy_hold_type); - NpadJoyHoldType GetHoldType() const; - - void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); - NpadHandheldActivationMode GetNpadHandheldActivationMode() const; - - void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); - NpadCommunicationMode GetNpadCommunicationMode() const; - - bool SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, - NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode); - - bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, - const Core::HID::VibrationValue& vibration_value); - - void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, - const Core::HID::VibrationValue& vibration_value); - - void VibrateControllers( - std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, - std::span<const Core::HID::VibrationValue> vibration_values); - - Core::HID::VibrationValue GetLastVibration( - const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; - - void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle); - - void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index); - - void SetPermitVibrationSession(bool permit_vibration_session); - - bool IsVibrationDeviceMounted( - const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; - - Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id); - void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const; - - // Adds a new controller at an index. - void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id); - // Adds a new controller at an index with connection status. - void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, - bool connected); - - Result DisconnectNpad(Core::HID::NpadIdType npad_id); - - Result IsFirmwareUpdateAvailableForSixAxisSensor( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; - Result ResetIsSixAxisSensorDeviceNewlyAssigned( - const Core::HID::SixAxisSensorHandle& sixaxis_handle); - - Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; - Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, - bool& is_enabled) const; - Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, - Core::HID::NpadIdType npad_id); - void SetAnalogStickUseCenterClamp(bool use_center_clamp); - void ClearAllConnectedControllers(); - void DisconnectAllConnectedControllers(); - void ConnectAllDisconnectedControllers(); - void ClearAllControllers(); - - Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2); - void StartLRAssignmentMode(); - void StopLRAssignmentMode(); - Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); - - // Logical OR for all buttons presses on all controllers - // Specifically for cheat engine and other features. - Core::HID::NpadButton GetAndResetPressState(); - - void ApplyNpadSystemCommonPolicy(); - - AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); - -private: - struct VibrationData { - bool device_mounted{}; - Core::HID::VibrationValue latest_vibration_value{}; - std::chrono::steady_clock::time_point last_vibration_timepoint{}; - }; - - struct NpadControllerData { - Kernel::KEvent* styleset_changed_event{}; - NpadInternalState* shared_memory = nullptr; - Core::HID::EmulatedController* device = nullptr; - - std::array<VibrationData, 2> vibration{}; - bool unintended_home_button_input_protection{}; - bool is_connected{}; - - // Dual joycons can have only one side connected - bool is_dual_left_connected{true}; - bool is_dual_right_connected{true}; - - // Current pad state - NPadGenericState npad_pad_state{}; - NPadGenericState npad_libnx_state{}; - NpadGcTriggerState npad_trigger_state{}; - int callback_key{}; - }; - - void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); - void InitNewlyAddedController(Core::HID::NpadIdType npad_id); - bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const; - void RequestPadStateUpdate(Core::HID::NpadIdType npad_id); - void WriteEmptyEntry(NpadInternalState* npad); - - NpadControllerData& GetControllerFromHandle( - const Core::HID::VibrationDeviceHandle& device_handle); - const NpadControllerData& GetControllerFromHandle( - const Core::HID::VibrationDeviceHandle& device_handle) const; - NpadControllerData& GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle); - const NpadControllerData& GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) const; - NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); - const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; - - Core::HID::SixAxisSensorProperties& GetSixaxisProperties( - const Core::HID::SixAxisSensorHandle& device_handle); - const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( - const Core::HID::SixAxisSensorHandle& device_handle) const; - - std::atomic<u64> press_state{}; - - std::array<NpadControllerData, NpadCount> controller_data{}; - KernelHelpers::ServiceContext& service_context; - std::mutex mutex; - std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; - NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical}; - NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; - NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; - bool permit_vibration_session_enabled{false}; - bool analog_stick_use_center_clamp{false}; - bool is_in_lr_assignment_mode{false}; - bool is_controller_initialized{false}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp deleted file mode 100644 index aa0454b5e..000000000 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/hid/controllers/palma.h" -#include "core/hle/service/kernel_helpers.h" - -namespace Service::HID { - -Palma::Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) - : ControllerBase{hid_core_}, service_context{service_context_} { - controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); - operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); -} - -Palma::~Palma() { - service_context.CloseEvent(operation_complete_event); -}; - -void Palma::OnInit() {} - -void Palma::OnRelease() {} - -void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!IsControllerActivated()) { - return; - } -} - -Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, - PalmaConnectionHandle& handle) { - active_handle.npad_id = npad_id; - handle = active_handle; - return ResultSuccess; -} - -Result Palma::InitializePalma(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - Activate(); - return ResultSuccess; -} - -Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent( - const PalmaConnectionHandle& handle) const { - if (handle.npad_id != active_handle.npad_id) { - LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); - } - return operation_complete_event->GetReadableEvent(); -} - -Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, - PalmaOperationType& operation_type, - PalmaOperationData& data) const { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation_type = operation.operation; - data = operation.data; - return ResultSuccess; -} - -Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::PlayActivity; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - fr_mode = fr_mode_; - return ResultSuccess; -} - -Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::ReadStep; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - return ResultSuccess; -} - -Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - return ResultSuccess; -} - -void Palma::ReadPalmaApplicationSection() {} - -void Palma::WritePalmaApplicationSection() {} - -Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::ReadUniqueCode; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::SetUniqueCodeInvalid; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -void Palma::WritePalmaActivityEntry() {} - -Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, - Common::ProcessAddress t_mem, u64 size) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::WriteWaveEntry; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, - s32 database_id_version_) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - database_id_version = database_id_version_; - operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; - operation.result = PalmaResultSuccess; - operation.data[0] = {}; - operation_complete_event->Signal(); - return ResultSuccess; -} - -Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; - operation.result = PalmaResultSuccess; - operation.data = {}; - operation.data[0] = static_cast<u8>(database_id_version); - operation_complete_event->Signal(); - return ResultSuccess; -} - -void Palma::SuspendPalmaFeature() {} - -Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - return operation.result; -} -void Palma::ReadPalmaPlayLog() {} - -void Palma::ResetPalmaPlayLog() {} - -void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { - // If true controllers are able to be paired - is_connectable = is_all_connectable; -} - -void Palma::SetIsPalmaPairedConnectable() {} - -Result Palma::PairPalma(const PalmaConnectionHandle& handle) { - if (handle.npad_id != active_handle.npad_id) { - return InvalidPalmaHandle; - } - // TODO: Do something - return ResultSuccess; -} - -void Palma::SetPalmaBoostMode(bool boost_mode) {} - -void Palma::CancelWritePalmaWaveEntry() {} - -void Palma::EnablePalmaBoostMode() {} - -void Palma::GetPalmaBluetoothAddress() {} - -void Palma::SetDisallowedPalmaConnection() {} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h deleted file mode 100644 index 73884230d..000000000 --- a/src/core/hle/service/hid/controllers/palma.h +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include "common/common_funcs.h" -#include "common/typed_address.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/errors.h" - -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Service::KernelHelpers { -class ServiceContext; -} - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::HID { -class Palma final : public ControllerBase { -public: - using PalmaOperationData = std::array<u8, 0x140>; - - // This is nn::hid::PalmaOperationType - enum class PalmaOperationType { - PlayActivity, - SetFrModeType, - ReadStep, - EnableStep, - ResetStep, - ReadApplicationSection, - WriteApplicationSection, - ReadUniqueCode, - SetUniqueCodeInvalid, - WriteActivityEntry, - WriteRgbLedPatternEntry, - WriteWaveEntry, - ReadDataBaseIdentificationVersion, - WriteDataBaseIdentificationVersion, - SuspendFeature, - ReadPlayLog, - ResetPlayLog, - }; - - // This is nn::hid::PalmaWaveSet - enum class PalmaWaveSet : u64 { - Small, - Medium, - Large, - }; - - // This is nn::hid::PalmaFrModeType - enum class PalmaFrModeType : u64 { - Off, - B01, - B02, - B03, - Downloaded, - }; - - // This is nn::hid::PalmaFeature - enum class PalmaFeature : u64 { - FrMode, - RumbleFeedback, - Step, - MuteSwitch, - }; - - // This is nn::hid::PalmaOperationInfo - struct PalmaOperationInfo { - PalmaOperationType operation{}; - Result result{PalmaResultSuccess}; - PalmaOperationData data{}; - }; - static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); - - // This is nn::hid::PalmaActivityEntry - struct PalmaActivityEntry { - u32 rgb_led_pattern_index; - INSERT_PADDING_BYTES(2); - PalmaWaveSet wave_set; - u32 wave_index; - INSERT_PADDING_BYTES(12); - }; - static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); - - struct PalmaConnectionHandle { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_BYTES(4); // Unknown - }; - static_assert(sizeof(PalmaConnectionHandle) == 0x8, - "PalmaConnectionHandle has incorrect size."); - - explicit Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_); - ~Palma() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - - Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); - Result InitializePalma(const PalmaConnectionHandle& handle); - Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( - const PalmaConnectionHandle& handle) const; - Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, - PalmaOperationType& operation_type, - PalmaOperationData& data) const; - Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); - Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); - Result ReadPalmaStep(const PalmaConnectionHandle& handle); - Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); - Result ResetPalmaStep(const PalmaConnectionHandle& handle); - Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); - Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); - Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); - Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, - Common::ProcessAddress t_mem, u64 size); - Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, - s32 database_id_version_); - Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); - Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; - void SetIsPalmaAllConnectable(bool is_all_connectable); - Result PairPalma(const PalmaConnectionHandle& handle); - void SetPalmaBoostMode(bool boost_mode); - -private: - void ReadPalmaApplicationSection(); - void WritePalmaApplicationSection(); - void WritePalmaActivityEntry(); - void SuspendPalmaFeature(); - void ReadPalmaPlayLog(); - void ResetPalmaPlayLog(); - void SetIsPalmaPairedConnectable(); - void CancelWritePalmaWaveEntry(); - void EnablePalmaBoostMode(); - void GetPalmaBluetoothAddress(); - void SetDisallowedPalmaConnection(); - - bool is_connectable{}; - s32 database_id_version{}; - PalmaOperationInfo operation{}; - PalmaFrModeType fr_mode{}; - PalmaConnectionHandle active_handle{}; - - Core::HID::EmulatedController* controller; - - Kernel::KEvent* operation_complete_event; - KernelHelpers::ServiceContext& service_context; -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/seven_six_axis.cpp b/src/core/hle/service/hid/controllers/seven_six_axis.cpp deleted file mode 100644 index 495568484..000000000 --- a/src/core/hle/service/hid/controllers/seven_six_axis.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include <cstring> -#include "common/common_types.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/frontend/emu_window.h" -#include "core/hid/emulated_console.h" -#include "core/hid/emulated_devices.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/seven_six_axis.h" -#include "core/memory.h" - -namespace Service::HID { -SevenSixAxis::SevenSixAxis(Core::System& system_) - : ControllerBase{system_.HIDCore()}, system{system_} { - console = hid_core.GetEmulatedConsole(); -} - -SevenSixAxis::~SevenSixAxis() = default; - -void SevenSixAxis::OnInit() {} -void SevenSixAxis::OnRelease() {} - -void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!IsControllerActivated() || transfer_memory == 0) { - seven_sixaxis_lifo.buffer_count = 0; - seven_sixaxis_lifo.buffer_tail = 0; - return; - } - - const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state; - next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1; - - const auto motion_status = console->GetMotion(); - last_global_timestamp = core_timing.GetGlobalTimeNs().count(); - - // This value increments every time the switch goes to sleep - next_seven_sixaxis_state.unknown = 1; - next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp; - next_seven_sixaxis_state.accel = motion_status.accel; - next_seven_sixaxis_state.gyro = motion_status.gyro; - next_seven_sixaxis_state.quaternion = { - { - motion_status.quaternion.xyz.y, - motion_status.quaternion.xyz.x, - -motion_status.quaternion.w, - }, - -motion_status.quaternion.xyz.z, - }; - - seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); - system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, - sizeof(seven_sixaxis_lifo)); -} - -void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { - transfer_memory = t_mem; -} - -void SevenSixAxis::ResetTimestamp() { - last_saved_timestamp = last_global_timestamp; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/seven_six_axis.h b/src/core/hle/service/hid/controllers/seven_six_axis.h deleted file mode 100644 index 40e3f5d12..000000000 --- a/src/core/hle/service/hid/controllers/seven_six_axis.h +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "common/quaternion.h" -#include "common/typed_address.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/ring_lifo.h" - -namespace Core { -class System; -} // namespace Core - -namespace Core::HID { -class EmulatedConsole; -} // namespace Core::HID - -namespace Service::HID { -class SevenSixAxis final : public ControllerBase { -public: - explicit SevenSixAxis(Core::System& system_); - ~SevenSixAxis() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - - // Called on InitializeSevenSixAxisSensor - void SetTransferMemoryAddress(Common::ProcessAddress t_mem); - - // Called on ResetSevenSixAxisSensorTimestamp - void ResetTimestamp(); - -private: - struct SevenSixAxisState { - INSERT_PADDING_WORDS(2); // unused - u64 timestamp{}; - u64 sampling_number{}; - u64 unknown{}; - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Quaternion<f32> quaternion{}; - }; - static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); - - Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; - static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); - - u64 last_saved_timestamp{}; - u64 last_global_timestamp{}; - - SevenSixAxisState next_seven_sixaxis_state{}; - Common::ProcessAddress transfer_memory{}; - Core::HID::EmulatedConsole* console = nullptr; - - Core::System& system; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp deleted file mode 100644 index 0bc5169c6..000000000 --- a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/core.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/shared_memory_holder.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" -#include "core/hle/service/hid/errors.h" - -namespace Service::HID { -SharedMemoryHolder::SharedMemoryHolder() {} - -SharedMemoryHolder::~SharedMemoryHolder() { - Finalize(); -} - -Result SharedMemoryHolder::Initialize(Core::System& system) { - shared_memory = Kernel::KSharedMemory::Create(system.Kernel()); - const Result result = shared_memory->Initialize( - system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None, - Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat)); - if (result.IsError()) { - return result; - } - Kernel::KSharedMemory::Register(system.Kernel(), shared_memory); - - is_created = true; - is_mapped = true; - address = std::construct_at(reinterpret_cast<SharedMemoryFormat*>(shared_memory->GetPointer())); - return ResultSuccess; -} - -void SharedMemoryHolder::Finalize() { - if (address != nullptr) { - shared_memory->Close(); - } - is_created = false; - is_mapped = false; - address = nullptr; -} - -bool SharedMemoryHolder::IsMapped() { - return is_mapped; -} - -SharedMemoryFormat* SharedMemoryHolder::GetAddress() { - return address; -} - -Kernel::KSharedMemory* SharedMemoryHolder::GetHandle() { - return shared_memory; -} -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.h b/src/core/hle/service/hid/controllers/shared_memory_holder.h deleted file mode 100644 index 943407c00..000000000 --- a/src/core/hle/service/hid/controllers/shared_memory_holder.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "core/hle/result.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KSharedMemory; -} - -namespace Service::HID { -struct SharedMemoryFormat; - -// This is nn::hid::detail::SharedMemoryHolder -class SharedMemoryHolder { -public: - SharedMemoryHolder(); - ~SharedMemoryHolder(); - - Result Initialize(Core::System& system); - void Finalize(); - - bool IsMapped(); - SharedMemoryFormat* GetAddress(); - Kernel::KSharedMemory* GetHandle(); - -private: - bool is_owner{}; - bool is_created{}; - bool is_mapped{}; - INSERT_PADDING_BYTES(0x5); - Kernel::KSharedMemory* shared_memory; - INSERT_PADDING_BYTES(0x38); - SharedMemoryFormat* address = nullptr; -}; -// Correct size is 0x50 bytes -static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size"); - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp deleted file mode 100644 index a5a67dea6..000000000 --- a/src/core/hle/service/hid/controllers/six_axis.cpp +++ /dev/null @@ -1,420 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "common/common_types.h" -#include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/six_axis.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" -#include "core/hle/service/hid/errors.h" -#include "core/hle/service/hid/hid_util.h" - -namespace Service::HID { - -SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_) - : ControllerBase{hid_core_}, npad{npad_} { - for (std::size_t i = 0; i < controller_data.size(); ++i) { - auto& controller = controller_data[i]; - controller.device = hid_core.GetEmulatedControllerByIndex(i); - } -} - -SixAxis::~SixAxis() = default; - -void SixAxis::OnInit() {} -void SixAxis::OnRelease() {} - -void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - if (!IsControllerActivated()) { - return; - } - - for (std::size_t i = 0; i < controller_data.size(); ++i) { - NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; - auto& controller = controller_data[i]; - const auto& controller_type = controller.device->GetNpadStyleIndex(); - - if (controller_type == Core::HID::NpadStyleIndex::None || - !controller.device->IsConnected()) { - continue; - } - - const auto& motion_state = controller.device->GetMotions(); - auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; - auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; - auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; - auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; - auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; - auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; - - auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; - auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; - auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; - auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; - auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; - auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; - - // Clear previous state - sixaxis_fullkey_state = {}; - sixaxis_handheld_state = {}; - sixaxis_dual_left_state = {}; - sixaxis_dual_right_state = {}; - sixaxis_left_lifo_state = {}; - sixaxis_right_lifo_state = {}; - - if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { - controller.sixaxis_at_rest = true; - for (std::size_t e = 0; e < motion_state.size(); ++e) { - controller.sixaxis_at_rest = - controller.sixaxis_at_rest && motion_state[e].is_at_rest; - } - } - - const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, - const Core::HID::ControllerMotion& hid_state) { - using namespace std::literals::chrono_literals; - static constexpr Core::HID::SixAxisSensorState default_motion_state = { - .delta_time = std::chrono::nanoseconds(5ms).count(), - .accel = {0, 0, -1.0f}, - .orientation = - { - Common::Vec3f{1.0f, 0, 0}, - Common::Vec3f{0, 1.0f, 0}, - Common::Vec3f{0, 0, 1.0f}, - }, - .attribute = {1}, - }; - if (!controller.sixaxis_sensor_enabled) { - state = default_motion_state; - return; - } - if (!Settings::values.motion_enabled.GetValue()) { - state = default_motion_state; - return; - } - state.attribute.is_connected.Assign(1); - state.delta_time = std::chrono::nanoseconds(5ms).count(); - state.accel = hid_state.accel; - state.gyro = hid_state.gyro; - state.rotation = hid_state.rotation; - state.orientation = hid_state.orientation; - }; - - switch (controller_type) { - case Core::HID::NpadStyleIndex::None: - ASSERT(false); - break; - case Core::HID::NpadStyleIndex::ProController: - set_motion_state(sixaxis_fullkey_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::Handheld: - set_motion_state(sixaxis_handheld_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::JoyconDual: - set_motion_state(sixaxis_dual_left_state, motion_state[0]); - set_motion_state(sixaxis_dual_right_state, motion_state[1]); - break; - case Core::HID::NpadStyleIndex::JoyconLeft: - set_motion_state(sixaxis_left_lifo_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::JoyconRight: - set_motion_state(sixaxis_right_lifo_state, motion_state[1]); - break; - case Core::HID::NpadStyleIndex::Pokeball: - using namespace std::literals::chrono_literals; - set_motion_state(sixaxis_fullkey_state, motion_state[0]); - sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); - break; - default: - break; - } - - sixaxis_fullkey_state.sampling_number = - sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_handheld_state.sampling_number = - sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_dual_left_state.sampling_number = - sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_dual_right_state.sampling_number = - sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_left_lifo_state.sampling_number = - sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_right_lifo_state.sampling_number = - sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - - if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { - // This buffer only is updated on handheld on HW - sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); - } else { - // Handheld doesn't update this buffer on HW - sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); - } - - sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); - sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); - sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); - sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); - } -} - -Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode drift_mode) { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - auto& controller = GetControllerFromHandle(sixaxis_handle); - sixaxis.gyroscope_zero_drift_mode = drift_mode; - controller.device->SetGyroscopeZeroDriftMode(drift_mode); - - return ResultSuccess; -} - -Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode& drift_mode) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - drift_mode = sixaxis.gyroscope_zero_drift_mode; - - return ResultSuccess; -} - -Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_at_rest) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& controller = GetControllerFromHandle(sixaxis_handle); - is_at_rest = controller.sixaxis_at_rest; - return ResultSuccess; -} - -Result SixAxis::LoadSixAxisSensorCalibrationParameter( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorCalibrationParameter& calibration) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - // TODO: Request this data to the controller. On error return 0xd8ca - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - calibration = sixaxis.calibration; - return ResultSuccess; -} - -Result SixAxis::GetSixAxisSensorIcInformation( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorIcInformation& ic_information) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - // TODO: Request this data to the controller. On error return 0xd8ca - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - ic_information = sixaxis.ic_information; - return ResultSuccess; -} - -Result SixAxis::EnableSixAxisSensorUnalteredPassthrough( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - sixaxis.unaltered_passtrough = is_enabled; - return ResultSuccess; -} - -Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - is_enabled = sixaxis.unaltered_passtrough; - return ResultSuccess; -} - -Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool sixaxis_status) { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_sensor_enabled = sixaxis_status; - return ResultSuccess; -} - -Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_fusion_enabled) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - is_fusion_enabled = sixaxis.is_fusion_enabled; - - return ResultSuccess; -} -Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool is_fusion_enabled) { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - sixaxis.is_fusion_enabled = is_fusion_enabled; - - return ResultSuccess; -} - -Result SixAxis::SetSixAxisFusionParameters( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto param1 = sixaxis_fusion_parameters.parameter1; - if (param1 < 0.0f || param1 > 1.0f) { - return InvalidSixAxisFusionRange; - } - - auto& sixaxis = GetSixaxisState(sixaxis_handle); - sixaxis.fusion = sixaxis_fusion_parameters; - - return ResultSuccess; -} - -Result SixAxis::GetSixAxisFusionParameters( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters& parameters) const { - const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); - if (is_valid.IsError()) { - LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); - return is_valid; - } - - const auto& sixaxis = GetSixaxisState(sixaxis_handle); - parameters = sixaxis.fusion; - - return ResultSuccess; -} - -SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) { - auto& controller = GetControllerFromHandle(sixaxis_handle); - switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: - return controller.sixaxis_fullkey; - case Core::HID::NpadStyleIndex::Handheld: - return controller.sixaxis_handheld; - case Core::HID::NpadStyleIndex::JoyconDual: - if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { - return controller.sixaxis_dual_left; - } - return controller.sixaxis_dual_right; - case Core::HID::NpadStyleIndex::JoyconLeft: - return controller.sixaxis_left; - case Core::HID::NpadStyleIndex::JoyconRight: - return controller.sixaxis_right; - default: - return controller.sixaxis_unknown; - } -} - -const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( - const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { - const auto& controller = GetControllerFromHandle(sixaxis_handle); - switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: - return controller.sixaxis_fullkey; - case Core::HID::NpadStyleIndex::Handheld: - return controller.sixaxis_handheld; - case Core::HID::NpadStyleIndex::JoyconDual: - if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { - return controller.sixaxis_dual_left; - } - return controller.sixaxis_dual_right; - case Core::HID::NpadStyleIndex::JoyconLeft: - return controller.sixaxis_left; - case Core::HID::NpadStyleIndex::JoyconRight: - return controller.sixaxis_right; - default: - return controller.sixaxis_unknown; - } -} - -SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) { - const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); - return GetControllerFromNpadIdType(npad_id); -} - -const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) const { - const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); - return GetControllerFromNpadIdType(npad_id); -} - -SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - npad_id = Core::HID::NpadIdType::Player1; - } - const auto npad_index = NpadIdTypeToIndex(npad_id); - return controller_data[npad_index]; -} - -const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType( - Core::HID::NpadIdType npad_id) const { - if (!IsNpadIdValid(npad_id)) { - LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - npad_id = Core::HID::NpadIdType::Player1; - } - const auto npad_index = NpadIdTypeToIndex(npad_id); - return controller_data[npad_index]; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/six_axis.h b/src/core/hle/service/hid/controllers/six_axis.h deleted file mode 100644 index 4c4f5dc7b..000000000 --- a/src/core/hle/service/hid/controllers/six_axis.h +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hid/hid_types.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/ring_lifo.h" - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::HID { -class NPad; - -class SixAxis final : public ControllerBase { -public: - explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_); - ~SixAxis() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - - Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode drift_mode); - Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::GyroscopeZeroDriftMode& drift_mode) const; - Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_at_rest) const; - Result EnableSixAxisSensorUnalteredPassthrough( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); - Result IsSixAxisSensorUnalteredPassthroughEnabled( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; - Result LoadSixAxisSensorCalibrationParameter( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorCalibrationParameter& calibration) const; - Result GetSixAxisSensorIcInformation( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorIcInformation& ic_information) const; - Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool sixaxis_status); - Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool& is_fusion_enabled) const; - Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - bool is_fusion_enabled); - Result SetSixAxisFusionParameters( - const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); - Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters& parameters) const; - -private: - static constexpr std::size_t NPAD_COUNT = 10; - - struct SixaxisParameters { - bool is_fusion_enabled{true}; - bool unaltered_passtrough{false}; - Core::HID::SixAxisSensorFusionParameters fusion{}; - Core::HID::SixAxisSensorCalibrationParameter calibration{}; - Core::HID::SixAxisSensorIcInformation ic_information{}; - Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ - Core::HID::GyroscopeZeroDriftMode::Standard}; - }; - - struct NpadControllerData { - Core::HID::EmulatedController* device = nullptr; - - // Motion parameters - bool sixaxis_at_rest{true}; - bool sixaxis_sensor_enabled{true}; - SixaxisParameters sixaxis_fullkey{}; - SixaxisParameters sixaxis_handheld{}; - SixaxisParameters sixaxis_dual_left{}; - SixaxisParameters sixaxis_dual_right{}; - SixaxisParameters sixaxis_left{}; - SixaxisParameters sixaxis_right{}; - SixaxisParameters sixaxis_unknown{}; - - // Current pad state - Core::HID::SixAxisSensorState sixaxis_fullkey_state{}; - Core::HID::SixAxisSensorState sixaxis_handheld_state{}; - Core::HID::SixAxisSensorState sixaxis_dual_left_state{}; - Core::HID::SixAxisSensorState sixaxis_dual_right_state{}; - Core::HID::SixAxisSensorState sixaxis_left_lifo_state{}; - Core::HID::SixAxisSensorState sixaxis_right_lifo_state{}; - int callback_key{}; - }; - - SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); - const SixaxisParameters& GetSixaxisState( - const Core::HID::SixAxisSensorHandle& device_handle) const; - - NpadControllerData& GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle); - const NpadControllerData& GetControllerFromHandle( - const Core::HID::SixAxisSensorHandle& device_handle) const; - NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); - const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; - - std::shared_ptr<NPad> npad; - std::array<NpadControllerData, NPAD_COUNT> controller_data{}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/sleep_button.cpp b/src/core/hle/service/hid/controllers/sleep_button.cpp deleted file mode 100644 index 978dc4c1f..000000000 --- a/src/core/hle/service/hid/controllers/sleep_button.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/sleep_button.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} - -SleepButton::~SleepButton() = default; - -void SleepButton::OnInit() {} - -void SleepButton::OnRelease() {} - -void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - auto& header = data->shared_memory_format->capture_button.header; - header.timestamp = core_timing.GetGlobalTimeNs().count(); - header.total_entry_count = 17; - header.entry_count = 0; - header.last_entry_index = 0; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/sleep_button.h b/src/core/hle/service/hid/controllers/sleep_button.h deleted file mode 100644 index 59964bf63..000000000 --- a/src/core/hle/service/hid/controllers/sleep_button.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Service::HID { - -class SleepButton final : public ControllerBase { -public: - explicit SleepButton(Core::HID::HIDCore& hid_core_); - ~SleepButton() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp deleted file mode 100644 index 291dc707e..000000000 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include "common/common_types.h" -#include "common/settings.h" -#include "core/core_timing.h" -#include "core/frontend/emu_window.h" -#include "core/hid/emulated_console.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" - -namespace Service::HID { - -TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) - : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), - touchscreen_height(Layout::ScreenUndocked::Height) { - console = hid_core.GetEmulatedConsole(); -} - -TouchScreen::~TouchScreen() = default; - -void TouchScreen::OnInit() {} - -void TouchScreen::OnRelease() {} - -void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; - shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); - - if (!IsControllerActivated()) { - shared_memory.touch_screen_lifo.buffer_count = 0; - shared_memory.touch_screen_lifo.buffer_tail = 0; - return; - } - - const auto touch_status = console->GetTouch(); - for (std::size_t id = 0; id < MAX_FINGERS; id++) { - const auto& current_touch = touch_status[id]; - auto& finger = fingers[id]; - finger.id = current_touch.id; - - if (finger.attribute.start_touch) { - finger.attribute.raw = 0; - continue; - } - - if (finger.attribute.end_touch) { - finger.attribute.raw = 0; - finger.pressed = false; - continue; - } - - if (!finger.pressed && current_touch.pressed) { - // Ignore all touch fingers if disabled - if (!Settings::values.touchscreen.enabled) { - continue; - } - - finger.attribute.start_touch.Assign(1); - finger.pressed = true; - finger.position = current_touch.position; - continue; - } - - if (finger.pressed && !current_touch.pressed) { - finger.attribute.raw = 0; - finger.attribute.end_touch.Assign(1); - continue; - } - - // Only update position if touch is not on a special frame - finger.position = current_touch.position; - } - - std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; - const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), - [](const auto& finger) { return finger.pressed; }); - const auto active_fingers_count = - static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); - - const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); - const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state; - - next_state.sampling_number = last_entry.sampling_number + 1; - next_state.entry_count = static_cast<s32>(active_fingers_count); - - for (std::size_t id = 0; id < MAX_FINGERS; ++id) { - auto& touch_entry = next_state.states[id]; - if (id < active_fingers_count) { - const auto& [active_x, active_y] = active_fingers[id].position; - touch_entry.position = { - .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), - .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), - }; - touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; - touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; - touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; - touch_entry.delta_time = timestamp - active_fingers[id].last_touch; - fingers[active_fingers[id].id].last_touch = timestamp; - touch_entry.finger = active_fingers[id].id; - touch_entry.attribute.raw = active_fingers[id].attribute.raw; - } else { - // Clear touch entry - touch_entry.attribute.raw = 0; - touch_entry.position = {}; - touch_entry.diameter_x = 0; - touch_entry.diameter_y = 0; - touch_entry.rotation_angle = 0; - touch_entry.delta_time = 0; - touch_entry.finger = 0; - } - } - - shared_memory.touch_screen_lifo.WriteNextEntry(next_state); -} - -void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { - touchscreen_width = width; - touchscreen_height = height; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h deleted file mode 100644 index 945d359be..000000000 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "core/hid/hid_types.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/types/touch_types.h" - -namespace Core::HID { -class EmulatedConsole; -} // namespace Core::HID - -namespace Service::HID { -struct TouchScreenSharedMemoryFormat; - -class TouchScreen final : public ControllerBase { -public: - explicit TouchScreen(Core::HID::HIDCore& hid_core_); - ~TouchScreen() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - - void SetTouchscreenDimensions(u32 width, u32 height); - -private: - TouchScreenState next_state{}; - Core::HID::EmulatedConsole* console = nullptr; - - std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; - u32 touchscreen_width; - u32 touchscreen_height; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/debug_pad_types.h b/src/core/hle/service/hid/controllers/types/debug_pad_types.h deleted file mode 100644 index a96171b62..000000000 --- a/src/core/hle/service/hid/controllers/types/debug_pad_types.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_types.h" -#include "core/hid/hid_types.h" - -namespace Service::HID { - -// This is nn::hid::DebugPadAttribute -struct DebugPadAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> connected; - }; -}; -static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size"); - -// This is nn::hid::DebugPadState -struct DebugPadState { - s64 sampling_number{}; - DebugPadAttribute attribute{}; - Core::HID::DebugPadButton pad_state{}; - Core::HID::AnalogStickState r_stick{}; - Core::HID::AnalogStickState l_stick{}; -}; -static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state"); - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/gesture_types.h b/src/core/hle/service/hid/controllers/types/gesture_types.h deleted file mode 100644 index b4f034cd3..000000000 --- a/src/core/hle/service/hid/controllers/types/gesture_types.h +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include "common/bit_field.h" -#include "common/common_types.h" -#include "common/point.h" - -namespace Service::HID { -static constexpr size_t MAX_FINGERS = 16; -static constexpr size_t MAX_POINTS = 4; - -// This is nn::hid::GestureType -enum class GestureType : u32 { - Idle, // Nothing touching the screen - Complete, // Set at the end of a touch event - Cancel, // Set when the number of fingers change - Touch, // A finger just touched the screen - Press, // Set if last type is touch and the finger hasn't moved - Tap, // Fast press then release - Pan, // All points moving together across the screen - Swipe, // Fast press movement and release of a single point - Pinch, // All points moving away/closer to the midpoint - Rotate, // All points rotating from the midpoint -}; - -// This is nn::hid::GestureDirection -enum class GestureDirection : u32 { - None, - Left, - Up, - Right, - Down, -}; - -// This is nn::hid::GestureAttribute -struct GestureAttribute { - union { - u32 raw{}; - - BitField<4, 1, u32> is_new_touch; - BitField<8, 1, u32> is_double_tap; - }; -}; -static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); - -// This is nn::hid::GestureState -struct GestureState { - s64 sampling_number{}; - s64 detection_count{}; - GestureType type{GestureType::Idle}; - GestureDirection direction{GestureDirection::None}; - Common::Point<s32> pos{}; - Common::Point<s32> delta{}; - f32 vel_x{}; - f32 vel_y{}; - GestureAttribute attributes{}; - f32 scale{}; - f32 rotation_angle{}; - s32 point_count{}; - std::array<Common::Point<s32>, 4> points{}; -}; -static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); - -struct GestureProperties { - std::array<Common::Point<s32>, MAX_POINTS> points{}; - std::size_t active_points{}; - Common::Point<s32> mid_point{}; - s64 detection_count{}; - u64 delta_time{}; - f32 average_distance{}; - f32 angle{}; -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/keyboard_types.h b/src/core/hle/service/hid/controllers/types/keyboard_types.h deleted file mode 100644 index f44a536b9..000000000 --- a/src/core/hle/service/hid/controllers/types/keyboard_types.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hid/hid_types.h" - -namespace Service::HID { - -// This is nn::hid::detail::KeyboardState -struct KeyboardState { - s64 sampling_number{}; - Core::HID::KeyboardModifier modifier{}; - Core::HID::KeyboardAttribute attribute{}; - Core::HID::KeyboardKey key{}; -}; -static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size"); - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/mouse_types.h b/src/core/hle/service/hid/controllers/types/mouse_types.h deleted file mode 100644 index 8bd6e167c..000000000 --- a/src/core/hle/service/hid/controllers/types/mouse_types.h +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" - -namespace Service::HID {} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/npad_types.h b/src/core/hle/service/hid/controllers/types/npad_types.h deleted file mode 100644 index a5ce2562b..000000000 --- a/src/core/hle/service/hid/controllers/types/npad_types.h +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hid/hid_types.h" - -namespace Service::HID { -static constexpr std::size_t NpadCount = 10; - -// This is nn::hid::NpadJoyHoldType -enum class NpadJoyHoldType : u64 { - Vertical = 0, - Horizontal = 1, -}; - -// This is nn::hid::NpadJoyAssignmentMode -enum class NpadJoyAssignmentMode : u32 { - Dual = 0, - Single = 1, -}; - -// This is nn::hid::NpadJoyDeviceType -enum class NpadJoyDeviceType : s64 { - Left = 0, - Right = 1, -}; - -// This is nn::hid::NpadHandheldActivationMode -enum class NpadHandheldActivationMode : u64 { - Dual = 0, - Single = 1, - None = 2, - MaxActivationMode = 3, -}; - -// This is nn::hid::system::AppletFooterUiAttributesSet -struct AppletFooterUiAttributes { - INSERT_PADDING_BYTES(0x4); -}; - -// This is nn::hid::system::AppletFooterUiType -enum class AppletFooterUiType : u8 { - None = 0, - HandheldNone = 1, - HandheldJoyConLeftOnly = 2, - HandheldJoyConRightOnly = 3, - HandheldJoyConLeftJoyConRight = 4, - JoyDual = 5, - JoyDualLeftOnly = 6, - JoyDualRightOnly = 7, - JoyLeftHorizontal = 8, - JoyLeftVertical = 9, - JoyRightHorizontal = 10, - JoyRightVertical = 11, - SwitchProController = 12, - CompatibleProController = 13, - CompatibleJoyCon = 14, - LarkHvc1 = 15, - LarkHvc2 = 16, - LarkNesLeft = 17, - LarkNesRight = 18, - Lucia = 19, - Verification = 20, - Lagon = 21, -}; - -using AppletFooterUiVariant = u8; - -// This is "nn::hid::system::AppletDetailedUiType". -struct AppletDetailedUiType { - AppletFooterUiVariant ui_variant; - INSERT_PADDING_BYTES(0x2); - AppletFooterUiType footer; -}; -static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size"); -// This is nn::hid::NpadCommunicationMode -enum class NpadCommunicationMode : u64 { - Mode_5ms = 0, - Mode_10ms = 1, - Mode_15ms = 2, - Default = 3, -}; - -enum class NpadRevision : u32 { - Revision0 = 0, - Revision1 = 1, - Revision2 = 2, - Revision3 = 3, -}; - -// This is nn::hid::detail::ColorAttribute -enum class ColorAttribute : u32 { - Ok = 0, - ReadError = 1, - NoController = 2, -}; -static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size"); - -// This is nn::hid::detail::NpadFullKeyColorState -struct NpadFullKeyColorState { - ColorAttribute attribute{ColorAttribute::NoController}; - Core::HID::NpadControllerColor fullkey{}; -}; -static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); - -// This is nn::hid::detail::NpadJoyColorState -struct NpadJoyColorState { - ColorAttribute attribute{ColorAttribute::NoController}; - Core::HID::NpadControllerColor left{}; - Core::HID::NpadControllerColor right{}; -}; -static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); - -// This is nn::hid::NpadAttribute -struct NpadAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> is_connected; - BitField<1, 1, u32> is_wired; - BitField<2, 1, u32> is_left_connected; - BitField<3, 1, u32> is_left_wired; - BitField<4, 1, u32> is_right_connected; - BitField<5, 1, u32> is_right_wired; - }; -}; -static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size"); - -// This is nn::hid::NpadFullKeyState -// This is nn::hid::NpadHandheldState -// This is nn::hid::NpadJoyDualState -// This is nn::hid::NpadJoyLeftState -// This is nn::hid::NpadJoyRightState -// This is nn::hid::NpadPalmaState -// This is nn::hid::NpadSystemExtState -struct NPadGenericState { - s64_le sampling_number{}; - Core::HID::NpadButtonState npad_buttons{}; - Core::HID::AnalogStickState l_stick{}; - Core::HID::AnalogStickState r_stick{}; - NpadAttribute connection_status{}; - INSERT_PADDING_BYTES(4); // Reserved -}; -static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); - -// This is nn::hid::server::NpadGcTriggerState -struct NpadGcTriggerState { - s64 sampling_number{}; - s32 l_analog{}; - s32 r_analog{}; -}; -static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); - -// This is nn::hid::NpadSystemProperties -struct NPadSystemProperties { - union { - s64 raw{}; - BitField<0, 1, s64> is_charging_joy_dual; - BitField<1, 1, s64> is_charging_joy_left; - BitField<2, 1, s64> is_charging_joy_right; - BitField<3, 1, s64> is_powered_joy_dual; - BitField<4, 1, s64> is_powered_joy_left; - BitField<5, 1, s64> is_powered_joy_right; - BitField<9, 1, s64> is_system_unsupported_button; - BitField<10, 1, s64> is_system_ext_unsupported_button; - BitField<11, 1, s64> is_vertical; - BitField<12, 1, s64> is_horizontal; - BitField<13, 1, s64> use_plus; - BitField<14, 1, s64> use_minus; - BitField<15, 1, s64> use_directional_buttons; - }; -}; -static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); - -// This is nn::hid::NpadSystemButtonProperties -struct NpadSystemButtonProperties { - union { - s32 raw{}; - BitField<0, 1, s32> is_home_button_protection_enabled; - }; -}; -static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); - -// This is nn::hid::system::DeviceType -struct DeviceType { - union { - u32 raw{}; - BitField<0, 1, s32> fullkey; - BitField<1, 1, s32> debug_pad; - BitField<2, 1, s32> handheld_left; - BitField<3, 1, s32> handheld_right; - BitField<4, 1, s32> joycon_left; - BitField<5, 1, s32> joycon_right; - BitField<6, 1, s32> palma; - BitField<7, 1, s32> lark_hvc_left; - BitField<8, 1, s32> lark_hvc_right; - BitField<9, 1, s32> lark_nes_left; - BitField<10, 1, s32> lark_nes_right; - BitField<11, 1, s32> handheld_lark_hvc_left; - BitField<12, 1, s32> handheld_lark_hvc_right; - BitField<13, 1, s32> handheld_lark_nes_left; - BitField<14, 1, s32> handheld_lark_nes_right; - BitField<15, 1, s32> lucia; - BitField<16, 1, s32> lagon; - BitField<17, 1, s32> lager; - BitField<31, 1, s32> system; - }; -}; - -// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl -struct NfcXcdDeviceHandleStateImpl { - u64 handle{}; - bool is_available{}; - bool is_activated{}; - INSERT_PADDING_BYTES(0x6); // Reserved - u64 sampling_number{}; -}; -static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, - "NfcXcdDeviceHandleStateImpl is an invalid size"); - -// This is nn::hid::NpadLarkType -enum class NpadLarkType : u32 { - Invalid, - H1, - H2, - NL, - NR, -}; - -// This is nn::hid::NpadLuciaType -enum class NpadLuciaType : u32 { - Invalid, - J, - E, - U, -}; - -// This is nn::hid::NpadLagonType -enum class NpadLagonType : u32 { - Invalid, -}; - -// This is nn::hid::NpadLagerType -enum class NpadLagerType : u32 { - Invalid, - J, - E, - U, -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/shared_memory_format.h b/src/core/hle/service/hid/controllers/types/shared_memory_format.h deleted file mode 100644 index 2986c113e..000000000 --- a/src/core/hle/service/hid/controllers/types/shared_memory_format.h +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/vector_math.h" -#include "core/hid/hid_types.h" -#include "core/hle/service/hid//controllers/types/debug_pad_types.h" -#include "core/hle/service/hid//controllers/types/keyboard_types.h" -#include "core/hle/service/hid//controllers/types/mouse_types.h" -#include "core/hle/service/hid//controllers/types/npad_types.h" -#include "core/hle/service/hid//controllers/types/touch_types.h" -#include "core/hle/service/hid/ring_lifo.h" - -namespace Service::HID { -static const std::size_t HidEntryCount = 17; - -struct CommonHeader { - s64 timestamp{}; - s64 total_entry_count{}; - s64 last_entry_index{}; - s64 entry_count{}; -}; -static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); - -// This is nn::hid::detail::DebugPadSharedMemoryFormat -struct DebugPadSharedMemoryFormat { - // This is nn::hid::detail::DebugPadLifo - Lifo<DebugPadState, HidEntryCount> debug_pad_lifo{}; - static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); - INSERT_PADDING_WORDS(0x4E); -}; -static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400, - "DebugPadSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::TouchScreenSharedMemoryFormat -struct TouchScreenSharedMemoryFormat { - // This is nn::hid::detail::TouchScreenLifo - Lifo<TouchScreenState, HidEntryCount> touch_screen_lifo{}; - static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); - INSERT_PADDING_WORDS(0xF2); -}; -static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000, - "TouchScreenSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::MouseSharedMemoryFormat -struct MouseSharedMemoryFormat { - // This is nn::hid::detail::MouseLifo - Lifo<Core::HID::MouseState, HidEntryCount> mouse_lifo{}; - static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); - INSERT_PADDING_WORDS(0x2C); -}; -static_assert(sizeof(MouseSharedMemoryFormat) == 0x400, - "MouseSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::KeyboardSharedMemoryFormat -struct KeyboardSharedMemoryFormat { - // This is nn::hid::detail::KeyboardLifo - Lifo<KeyboardState, HidEntryCount> keyboard_lifo{}; - static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); - INSERT_PADDING_WORDS(0xA); -}; -static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400, - "KeyboardSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::DigitizerSharedMemoryFormat -struct DigitizerSharedMemoryFormat { - CommonHeader header; - INSERT_PADDING_BYTES(0xFE0); -}; -static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000, - "DigitizerSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::HomeButtonSharedMemoryFormat -struct HomeButtonSharedMemoryFormat { - CommonHeader header; - INSERT_PADDING_BYTES(0x1E0); -}; -static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200, - "HomeButtonSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::SleepButtonSharedMemoryFormat -struct SleepButtonSharedMemoryFormat { - CommonHeader header; - INSERT_PADDING_BYTES(0x1E0); -}; -static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200, - "SleepButtonSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::CaptureButtonSharedMemoryFormat -struct CaptureButtonSharedMemoryFormat { - CommonHeader header; - INSERT_PADDING_BYTES(0x1E0); -}; -static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200, - "CaptureButtonSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::InputDetectorSharedMemoryFormat -struct InputDetectorSharedMemoryFormat { - CommonHeader header; - INSERT_PADDING_BYTES(0x7E0); -}; -static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800, - "InputDetectorSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::UniquePadSharedMemoryFormat -struct UniquePadSharedMemoryFormat { - CommonHeader header; - INSERT_PADDING_BYTES(0x3FE0); -}; -static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000, - "UniquePadSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::NpadSixAxisSensorLifo -struct NpadSixAxisSensorLifo { - Lifo<Core::HID::SixAxisSensorState, HidEntryCount> lifo; -}; - -// This is nn::hid::detail::NpadInternalState -struct NpadInternalState { - Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; - NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; - NpadFullKeyColorState fullkey_color{}; - NpadJoyColorState joycon_color{}; - Lifo<NPadGenericState, HidEntryCount> fullkey_lifo{}; - Lifo<NPadGenericState, HidEntryCount> handheld_lifo{}; - Lifo<NPadGenericState, HidEntryCount> joy_dual_lifo{}; - Lifo<NPadGenericState, HidEntryCount> joy_left_lifo{}; - Lifo<NPadGenericState, HidEntryCount> joy_right_lifo{}; - Lifo<NPadGenericState, HidEntryCount> palma_lifo{}; - Lifo<NPadGenericState, HidEntryCount> system_ext_lifo{}; - NpadSixAxisSensorLifo sixaxis_fullkey_lifo{}; - NpadSixAxisSensorLifo sixaxis_handheld_lifo{}; - NpadSixAxisSensorLifo sixaxis_dual_left_lifo{}; - NpadSixAxisSensorLifo sixaxis_dual_right_lifo{}; - NpadSixAxisSensorLifo sixaxis_left_lifo{}; - NpadSixAxisSensorLifo sixaxis_right_lifo{}; - DeviceType device_type{}; - INSERT_PADDING_BYTES(0x4); // Reserved - NPadSystemProperties system_properties{}; - NpadSystemButtonProperties button_properties{}; - Core::HID::NpadBatteryLevel battery_level_dual{}; - Core::HID::NpadBatteryLevel battery_level_left{}; - Core::HID::NpadBatteryLevel battery_level_right{}; - AppletFooterUiAttributes applet_footer_attributes{}; - AppletFooterUiType applet_footer_type{AppletFooterUiType::None}; - INSERT_PADDING_BYTES(0x5B); // Reserved - INSERT_PADDING_BYTES(0x20); // Unknown - Lifo<NpadGcTriggerState, HidEntryCount> gc_trigger_lifo{}; - NpadLarkType lark_type_l_and_main{}; - NpadLarkType lark_type_r{}; - NpadLuciaType lucia_type{}; - NpadLagerType lager_type{}; - Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties; - Core::HID::SixAxisSensorProperties sixaxis_handheld_properties; - Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties; - Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties; - Core::HID::SixAxisSensorProperties sixaxis_left_properties; - Core::HID::SixAxisSensorProperties sixaxis_right_properties; -}; -static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size"); - -// This is nn::hid::detail::NpadSharedMemoryEntry -struct NpadSharedMemoryEntry { - NpadInternalState internal_state; - INSERT_PADDING_BYTES(0xC08); -}; -static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size"); - -// This is nn::hid::detail::NpadSharedMemoryFormat -struct NpadSharedMemoryFormat { - std::array<NpadSharedMemoryEntry, NpadCount> npad_entry; -}; -static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000, - "NpadSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::GestureSharedMemoryFormat -struct GestureSharedMemoryFormat { - // This is nn::hid::detail::GestureLifo - Lifo<GestureState, HidEntryCount> gesture_lifo{}; - static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); - INSERT_PADDING_WORDS(0x3E); -}; -static_assert(sizeof(GestureSharedMemoryFormat) == 0x800, - "GestureSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat -struct ConsoleSixAxisSensorSharedMemoryFormat { - u64 sampling_number{}; - bool is_seven_six_axis_sensor_at_rest{}; - INSERT_PADDING_BYTES(3); // padding - f32 verticalization_error{}; - Common::Vec3f gyro_bias{}; - INSERT_PADDING_BYTES(4); // padding -}; -static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20, - "ConsoleSixAxisSensorSharedMemoryFormat is an invalid size"); - -// This is nn::hid::detail::SharedMemoryFormat -struct SharedMemoryFormat { - void Initialize() {} - - DebugPadSharedMemoryFormat debug_pad; - TouchScreenSharedMemoryFormat touch_screen; - MouseSharedMemoryFormat mouse; - KeyboardSharedMemoryFormat keyboard; - DigitizerSharedMemoryFormat digitizer; - HomeButtonSharedMemoryFormat home_button; - SleepButtonSharedMemoryFormat sleep_button; - CaptureButtonSharedMemoryFormat capture_button; - InputDetectorSharedMemoryFormat input_detector; - UniquePadSharedMemoryFormat unique_pad; - NpadSharedMemoryFormat npad; - GestureSharedMemoryFormat gesture; - ConsoleSixAxisSensorSharedMemoryFormat console; - INSERT_PADDING_BYTES(0x19E0); - MouseSharedMemoryFormat debug_mouse; - INSERT_PADDING_BYTES(0x2000); -}; -static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00, - "sleep_button has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000, - "capture_button has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200, - "input_detector has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset"); -static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset"); -static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size"); - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/types/touch_types.h b/src/core/hle/service/hid/controllers/types/touch_types.h deleted file mode 100644 index efeaa796d..000000000 --- a/src/core/hle/service/hid/controllers/types/touch_types.h +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> - -#include <array> -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/point.h" -#include "core/hid/hid_types.h" - -namespace Service::HID { -static constexpr std::size_t MAX_FINGERS = 16; -static constexpr size_t MAX_POINTS = 4; - -// This is nn::hid::GestureType -enum class GestureType : u32 { - Idle, // Nothing touching the screen - Complete, // Set at the end of a touch event - Cancel, // Set when the number of fingers change - Touch, // A finger just touched the screen - Press, // Set if last type is touch and the finger hasn't moved - Tap, // Fast press then release - Pan, // All points moving together across the screen - Swipe, // Fast press movement and release of a single point - Pinch, // All points moving away/closer to the midpoint - Rotate, // All points rotating from the midpoint -}; - -// This is nn::hid::GestureDirection -enum class GestureDirection : u32 { - None, - Left, - Up, - Right, - Down, -}; - -// This is nn::hid::GestureAttribute -struct GestureAttribute { - union { - u32 raw{}; - - BitField<4, 1, u32> is_new_touch; - BitField<8, 1, u32> is_double_tap; - }; -}; -static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); - -// This is nn::hid::GestureState -struct GestureState { - s64 sampling_number{}; - s64 detection_count{}; - GestureType type{GestureType::Idle}; - GestureDirection direction{GestureDirection::None}; - Common::Point<s32> pos{}; - Common::Point<s32> delta{}; - f32 vel_x{}; - f32 vel_y{}; - GestureAttribute attributes{}; - f32 scale{}; - f32 rotation_angle{}; - s32 point_count{}; - std::array<Common::Point<s32>, 4> points{}; -}; -static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); - -struct GestureProperties { - std::array<Common::Point<s32>, MAX_POINTS> points{}; - std::size_t active_points{}; - Common::Point<s32> mid_point{}; - s64 detection_count{}; - u64 delta_time{}; - f32 average_distance{}; - f32 angle{}; -}; - -// This is nn::hid::TouchScreenState -struct TouchScreenState { - s64 sampling_number{}; - s32 entry_count{}; - INSERT_PADDING_BYTES(4); // Reserved - std::array<Core::HID::TouchState, MAX_FINGERS> states{}; -}; -static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/unique_pad.cpp b/src/core/hle/service/hid/controllers/unique_pad.cpp deleted file mode 100644 index 8230501a5..000000000 --- a/src/core/hle/service/hid/controllers/unique_pad.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core_timing.h" -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" -#include "core/hle/service/hid/controllers/unique_pad.h" - -namespace Service::HID { - -UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} - -UniquePad::~UniquePad() = default; - -void UniquePad::OnInit() {} - -void UniquePad::OnRelease() {} - -void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr) { - return; - } - - auto& header = data->shared_memory_format->capture_button.header; - header.timestamp = core_timing.GetGlobalTimeNs().count(); - header.total_entry_count = 17; - header.entry_count = 0; - header.last_entry_index = 0; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/unique_pad.h b/src/core/hle/service/hid/controllers/unique_pad.h deleted file mode 100644 index 966368264..000000000 --- a/src/core/hle/service/hid/controllers/unique_pad.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hid/controllers/controller_base.h" - -namespace Service::HID { - -class UniquePad final : public ControllerBase { -public: - explicit UniquePad(Core::HID::HIDCore& hid_core_); - ~UniquePad() override; - - // Called when the controller is initialized - void OnInit() override; - - // When the controller is released - void OnRelease() override; - - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h deleted file mode 100644 index 6dc976fe1..000000000 --- a/src/core/hle/service/hid/errors.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/result.h" - -namespace Service::HID { - -constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; -constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; -constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; -constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; -constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; -constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; -constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; -constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; -constexpr Result NpadIsSameType{ErrorModule::HID, 602}; -constexpr Result InvalidNpadId{ErrorModule::HID, 709}; -constexpr Result NpadNotConnected{ErrorModule::HID, 710}; -constexpr Result InvalidArraySize{ErrorModule::HID, 715}; - -constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041}; -constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042}; -constexpr Result ResultSharedMemoryNotInitialized{ErrorModule::HID, 1043}; -constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044}; -constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046}; -constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047}; - -constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; - -} // namespace Service::HID - -namespace Service::IRS { - -constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78}; -constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204}; - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index afbcb019f..5b28be577 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -5,35 +5,35 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_debug_server.h" -#include "core/hle/service/hid/hid_firmware_settings.h" #include "core/hle/service/hid/hid_server.h" #include "core/hle/service/hid/hid_system_server.h" #include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/irs.h" -#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/hid/xcd.h" #include "core/hle/service/server_manager.h" +#include "hid_core/resource_manager.h" +#include "hid_core/resources/hid_firmware_settings.h" namespace Service::HID { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system); + std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system); std::shared_ptr<HidFirmwareSettings> firmware_settings = - std::make_shared<HidFirmwareSettings>(); + std::make_shared<HidFirmwareSettings>(system); - // TODO: Remove this hack until this service is emulated properly. - const auto process_list = system.Kernel().GetProcessList(); - if (!process_list.empty()) { - resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true); - } + // TODO: Remove this hack when am is emulated properly. + resource_manager->Initialize(); + resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(), + true); + resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true); server_manager->RegisterNamedService( - "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings)); + "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService( - "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager)); + "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager)); server_manager->RegisterNamedService( - "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager)); + "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp index 6294f3dfb..f2a767d37 100644 --- a/src/core/hle/service/hid/hid_debug_server.cpp +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/hid/hid_debug_server.h" -#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/ipc_helpers.h" +#include "hid_core/resource_manager.h" namespace Service::HID { diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp deleted file mode 100644 index 59bd6825c..000000000 --- a/src/core/hle/service/hid/hid_firmware_settings.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/hid/hid_firmware_settings.h" - -namespace Service::HID { - -HidFirmwareSettings::HidFirmwareSettings() { - LoadSettings(true); -} - -void HidFirmwareSettings::Reload() { - LoadSettings(true); -} - -void HidFirmwareSettings::LoadSettings(bool reload_config) { - if (is_initalized && !reload_config) { - return; - } - - // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values - - is_debug_pad_enabled = true; - is_device_managed = true; - is_touch_i2c_managed = is_device_managed; - is_future_devices_emulated = false; - is_mcu_hardware_error_emulated = false; - is_rail_enabled = true; - is_firmware_update_failure_emulated = false; - is_firmware_update_failure = {}; - is_ble_disabled = false; - is_dscale_disabled = false; - is_handheld_forced = true; - features_per_id_disabled = {}; - is_touch_firmware_auto_update_disabled = false; - is_initalized = true; -} - -bool HidFirmwareSettings::IsDebugPadEnabled() { - LoadSettings(false); - return is_debug_pad_enabled; -} - -bool HidFirmwareSettings::IsDeviceManaged() { - LoadSettings(false); - return is_device_managed; -} - -bool HidFirmwareSettings::IsEmulateFutureDevice() { - LoadSettings(false); - return is_future_devices_emulated; -} - -bool HidFirmwareSettings::IsTouchI2cManaged() { - LoadSettings(false); - return is_touch_i2c_managed; -} - -bool HidFirmwareSettings::IsHandheldForced() { - LoadSettings(false); - return is_handheld_forced; -} - -bool HidFirmwareSettings::IsRailEnabled() { - LoadSettings(false); - return is_rail_enabled; -} - -bool HidFirmwareSettings::IsHardwareErrorEmulated() { - LoadSettings(false); - return is_mcu_hardware_error_emulated; -} - -bool HidFirmwareSettings::IsBleDisabled() { - LoadSettings(false); - return is_ble_disabled; -} - -bool HidFirmwareSettings::IsDscaleDisabled() { - LoadSettings(false); - return is_dscale_disabled; -} - -bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() { - LoadSettings(false); - return is_touch_firmware_auto_update_disabled; -} - -HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() { - LoadSettings(false); - return is_firmware_update_failure; -} - -HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() { - LoadSettings(false); - return features_per_id_disabled; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h deleted file mode 100644 index 6c10c440b..000000000 --- a/src/core/hle/service/hid/hid_firmware_settings.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" - -namespace Service::HID { - -/// Loads firmware config from nn::settings::fwdbg -class HidFirmwareSettings { -public: - using FirmwareSetting = std::array<u8, 4>; - using FeaturesPerId = std::array<bool, 0xA8>; - - HidFirmwareSettings(); - - void Reload(); - void LoadSettings(bool reload_config); - - bool IsDebugPadEnabled(); - bool IsDeviceManaged(); - bool IsEmulateFutureDevice(); - bool IsTouchI2cManaged(); - bool IsHandheldForced(); - bool IsRailEnabled(); - bool IsHardwareErrorEmulated(); - bool IsBleDisabled(); - bool IsDscaleDisabled(); - bool IsTouchAutoUpdateDisabled(); - - FirmwareSetting GetFirmwareUpdateFailure(); - FeaturesPerId FeaturesDisabledPerId(); - -private: - bool is_initalized{}; - - // Debug settings - bool is_debug_pad_enabled{}; - bool is_device_managed{}; - bool is_touch_i2c_managed{}; - bool is_future_devices_emulated{}; - bool is_mcu_hardware_error_emulated{}; - bool is_rail_enabled{}; - bool is_firmware_update_failure_emulated{}; - bool is_ble_disabled{}; - bool is_dscale_disabled{}; - bool is_handheld_forced{}; - bool is_touch_firmware_auto_update_disabled{}; - FirmwareSetting is_firmware_update_failure{}; - FeaturesPerId features_per_id_disabled{}; -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 3174672af..09c47b5e3 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -5,30 +5,33 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/settings.h" -#include "core/hid/hid_core.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/hid/errors.h" -#include "core/hle/service/hid/hid_firmware_settings.h" #include "core/hle/service/hid/hid_server.h" -#include "core/hle/service/hid/hid_util.h" -#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" - -#include "core/hle/service/hid/controllers/console_six_axis.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/debug_pad.h" -#include "core/hle/service/hid/controllers/gesture.h" -#include "core/hle/service/hid/controllers/keyboard.h" -#include "core/hle/service/hid/controllers/mouse.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/palma.h" -#include "core/hle/service/hid/controllers/seven_six_axis.h" -#include "core/hle/service/hid/controllers/six_axis.h" -#include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/hle/service/hid/controllers/types/npad_types.h" +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resource_manager.h" +#include "hid_core/resources/hid_firmware_settings.h" + +#include "hid_core/resources/controller_base.h" +#include "hid_core/resources/debug_pad/debug_pad.h" +#include "hid_core/resources/keyboard/keyboard.h" +#include "hid_core/resources/mouse/mouse.h" +#include "hid_core/resources/npad/npad.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/palma/palma.h" +#include "hid_core/resources/six_axis/console_six_axis.h" +#include "hid_core/resources/six_axis/seven_six_axis.h" +#include "hid_core/resources/six_axis/six_axis.h" +#include "hid_core/resources/touch_screen/gesture.h" +#include "hid_core/resources/touch_screen/touch_screen.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" +#include "hid_core/resources/vibration/vibration_device.h" namespace Service::HID { @@ -39,7 +42,7 @@ public: : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) { // clang-format off static const FunctionInfo functions[] = { - {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, + {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"}, }; // clang-format on @@ -47,22 +50,49 @@ public: } private: - void InitializeVibrationDevice(HLERequestContext& ctx) { + void ActivateVibrationDevice(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; - if (resource_manager != nullptr && resource_manager->GetNpad()) { - resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle); - } - LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", vibration_device_handle.npad_type, vibration_device_handle.npad_id, vibration_device_handle.device_index); + const auto result = ActivateVibrationDeviceImpl(vibration_device_handle); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } + Result ActivateVibrationDeviceImpl(const Core::HID::VibrationDeviceHandle& handle) { + std::scoped_lock lock{mutex}; + + const Result is_valid = IsVibrationHandleValid(handle); + if (is_valid.IsError()) { + return is_valid; + } + + for (std::size_t i = 0; i < list_size; i++) { + if (handle.device_index == vibration_device_list[i].device_index && + handle.npad_id == vibration_device_list[i].npad_id && + handle.npad_type == vibration_device_list[i].npad_type) { + return ResultSuccess; + } + } + if (list_size == vibration_device_list.size()) { + return ResultVibrationDeviceIndexOutOfRange; + } + const Result result = resource_manager->GetVibrationDevice(handle)->Activate(); + if (result.IsError()) { + return result; + } + vibration_device_list[list_size++] = handle; + return ResultSuccess; + } + + mutable std::mutex mutex; + std::size_t list_size{}; + std::array<Core::HID::VibrationDeviceHandle, 0x100> vibration_device_list{}; std::shared_ptr<ResourceManager> resource_manager; }; @@ -154,7 +184,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"}, {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, - {212, nullptr, "SendVibrationValueInBool"}, + {212, &IHidServer::SendVibrationValueInBool, "SendVibrationValueInBool"}, {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, @@ -785,8 +815,8 @@ void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ct bool is_firmware_available{}; auto controller = GetResourceManager()->GetNpad(); - controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, - is_firmware_available); + controller->IsFirmwareUpdateAvailableForSixAxisSensor( + parameters.applet_resource_user_id, parameters.sixaxis_handle, is_firmware_available); LOG_WARNING( Service_HID, @@ -924,8 +954,8 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) const auto parameters{rp.PopRaw<Parameters>()}; auto controller = GetResourceManager()->GetNpad(); - const auto result = - controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); + const auto result = controller->ResetIsSixAxisSensorDeviceNewlyAssigned( + parameters.applet_resource_user_id, parameters.sixaxis_handle); LOG_WARNING( Service_HID, @@ -970,7 +1000,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) { void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Core::HID::NpadStyleSet supported_styleset; + Core::HID::NpadStyleSet supported_style_set; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; }; @@ -978,13 +1008,25 @@ void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset}); + LOG_DEBUG(Service_HID, "called, supported_style_set={}, applet_resource_user_id={}", + parameters.supported_style_set, parameters.applet_resource_user_id); + + const auto npad = GetResourceManager()->GetNpad(); + const Result result = npad->SetSupportedNpadStyleSet(parameters.applet_resource_user_id, + parameters.supported_style_set); - LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", - parameters.supported_styleset, parameters.applet_resource_user_id); + if (result.IsSuccess()) { + Core::HID::NpadStyleTag style_tag{parameters.supported_style_set}; + const auto revision = npad->GetRevision(parameters.applet_resource_user_id); + + if (style_tag.palma != 0 && revision < NpadRevision::Revision3) { + // GetResourceManager()->GetPalma()->EnableBoostMode(parameters.applet_resource_user_id, + // true); + } + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { @@ -993,19 +1035,31 @@ void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + Core::HID::NpadStyleSet supported_style_set{}; + const auto npad = GetResourceManager()->GetNpad(); + const auto result = + npad->GetSupportedNpadStyleSet(applet_resource_user_id, supported_style_set); + IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw); + rb.Push(result); + rb.PushEnum(supported_style_set); } void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - - const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer()); + const auto buffer = ctx.ReadBuffer(); + const std::size_t elements = ctx.GetReadBufferNumElements<Core::HID::NpadIdType>(); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + std::vector<Core::HID::NpadIdType> supported_npad_list(elements); + memcpy(supported_npad_list.data(), buffer.data(), buffer.size()); + + const auto npad = GetResourceManager()->GetNpad(); + const Result result = + npad->SetSupportedNpadIdType(applet_resource_user_id, supported_npad_list); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -1018,7 +1072,7 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) { auto npad = GetResourceManager()->GetNpad(); - // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); + npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); const Result result = npad->Activate(applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1052,13 +1106,13 @@ void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); - // Games expect this event to be signaled after calling this function - GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id); + Kernel::KReadableEvent* style_set_update_event; + const auto result = GetResourceManager()->GetNpad()->AcquireNpadStyleSetUpdateEventHandle( + parameters.applet_resource_user_id, &style_set_update_event, parameters.npad_id); IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects( - GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id)); + rb.Push(result); + rb.PushCopyObjects(style_set_update_event); } void IHidServer::DisconnectNpad(HLERequestContext& ctx) { @@ -1073,7 +1127,7 @@ void IHidServer::DisconnectNpad(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto controller = GetResourceManager()->GetNpad(); - controller->DisconnectNpad(parameters.npad_id); + controller->DisconnectNpad(parameters.applet_resource_user_id, parameters.npad_id); LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -1113,7 +1167,7 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { auto npad = GetResourceManager()->GetNpad(); - // TODO: npad->SetRevision(applet_resource_user_id, revision); + npad->SetRevision(parameters.applet_resource_user_id, parameters.revision); const auto result = npad->Activate(parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -1125,13 +1179,19 @@ void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop<u64>()}; const auto hold_type{rp.PopEnum<NpadJoyHoldType>()}; - GetResourceManager()->GetNpad()->SetHoldType(hold_type); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", applet_resource_user_id, hold_type); + if (hold_type != NpadJoyHoldType::Horizontal && hold_type != NpadJoyHoldType::Vertical) { + // This should crash console + ASSERT_MSG(false, "Invalid npad joy hold type"); + } + + const auto npad = GetResourceManager()->GetNpad(); + const auto result = npad->SetNpadJoyHoldType(applet_resource_user_id, hold_type); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { @@ -1140,9 +1200,13 @@ void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + NpadJoyHoldType hold_type{}; + const auto npad = GetResourceManager()->GetNpad(); + const auto result = npad->GetNpadJoyHoldType(applet_resource_user_id, hold_type); + IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType()); + rb.Push(result); + rb.PushEnum(hold_type); } void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { @@ -1158,8 +1222,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) Core::HID::NpadIdType new_npad_id{}; auto controller = GetResourceManager()->GetNpad(); - controller->SetNpadMode(new_npad_id, parameters.npad_id, NpadJoyDeviceType::Left, - NpadJoyAssignmentMode::Single); + controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id, + NpadJoyDeviceType::Left, NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -1182,8 +1246,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { Core::HID::NpadIdType new_npad_id{}; auto controller = GetResourceManager()->GetNpad(); - controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, - NpadJoyAssignmentMode::Single); + controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id, + parameters.npad_joy_device_type, NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id, @@ -1206,7 +1270,8 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { Core::HID::NpadIdType new_npad_id{}; auto controller = GetResourceManager()->GetNpad(); - controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NpadJoyAssignmentMode::Dual); + controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id, {}, + NpadJoyAssignmentMode::Dual); LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); // Spams a lot when controller applet is open @@ -1222,7 +1287,8 @@ void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop<u64>()}; auto controller = GetResourceManager()->GetNpad(); - const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + const auto result = + controller->MergeSingleJoyAsDualJoy(applet_resource_user_id, npad_id_1, npad_id_2); LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); @@ -1235,10 +1301,10 @@ void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - GetResourceManager()->GetNpad()->StartLRAssignmentMode(); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + GetResourceManager()->GetNpad()->StartLrAssignmentMode(applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -1247,10 +1313,10 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - GetResourceManager()->GetNpad()->StopLRAssignmentMode(); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + GetResourceManager()->GetNpad()->StopLrAssignmentMode(applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -1260,13 +1326,23 @@ void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop<u64>()}; const auto activation_mode{rp.PopEnum<NpadHandheldActivationMode>()}; - GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", applet_resource_user_id, activation_mode); + if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { + // Console should crash here + ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + const auto npad = GetResourceManager()->GetNpad(); + const auto result = + npad->SetNpadHandheldActivationMode(applet_resource_user_id, activation_mode); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { @@ -1275,9 +1351,14 @@ void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + NpadHandheldActivationMode activation_mode{}; + const auto npad = GetResourceManager()->GetNpad(); + const auto result = + npad->GetNpadHandheldActivationMode(applet_resource_user_id, activation_mode); + IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode()); + rb.Push(result); + rb.PushEnum(activation_mode); } void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { @@ -1286,12 +1367,12 @@ void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - auto controller = GetResourceManager()->GetNpad(); - const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2); - LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); + const auto npad = GetResourceManager()->GetNpad(); + const auto result = npad->SwapNpadAssignment(applet_resource_user_id, npad_id_1, npad_id_2); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -1307,13 +1388,19 @@ void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& const auto parameters{rp.PopRaw<Parameters>()}; - bool is_enabled = false; - auto controller = GetResourceManager()->GetNpad(); - const auto result = - controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", - parameters.npad_id, parameters.applet_resource_user_id); + if (!IsNpadIdValid(parameters.npad_id)) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultInvalidNpadId); + return; + } + + bool is_enabled{}; + const auto npad = GetResourceManager()->GetNpad(); + const auto result = npad->IsUnintendedHomeButtonInputProtectionEnabled( + is_enabled, parameters.applet_resource_user_id, parameters.npad_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(result); @@ -1332,14 +1419,19 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct const auto parameters{rp.PopRaw<Parameters>()}; - auto controller = GetResourceManager()->GetNpad(); - const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled( - parameters.is_enabled, parameters.npad_id); - - LOG_DEBUG(Service_HID, - "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); + if (!IsNpadIdValid(parameters.npad_id)) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultInvalidNpadId); + return; + } + + const auto npad = GetResourceManager()->GetNpad(); + const auto result = npad->EnableUnintendedHomeButtonInputProtection( + parameters.applet_resource_user_id, parameters.npad_id, parameters.is_enabled); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -1359,8 +1451,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext Core::HID::NpadIdType new_npad_id{}; auto controller = GetResourceManager()->GetNpad(); const auto is_reassigned = - controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, - NpadJoyAssignmentMode::Single); + controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id, + parameters.npad_joy_device_type, NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id, @@ -1375,7 +1467,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - bool analog_stick_use_center_clamp; + bool use_center_clamp; INSERT_PADDING_BYTES_NOINIT(7); u64 applet_resource_user_id; }; @@ -1383,12 +1475,11 @@ void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp( - parameters.analog_stick_use_center_clamp); + LOG_INFO(Service_HID, "called, use_center_clamp={}, applet_resource_user_id={}", + parameters.use_center_clamp, parameters.applet_resource_user_id); - LOG_WARNING(Service_HID, - "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", - parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); + GetResourceManager()->GetNpad()->SetNpadAnalogStickUseCenterClamp( + parameters.applet_resource_user_id, parameters.use_center_clamp); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1406,81 +1497,39 @@ void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - LOG_WARNING(Service_HID, - "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", - parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); + LOG_INFO(Service_HID, "called, npad_styleset={}, applet_resource_user_id={}, button={}", + parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); + + const auto result = GetResourceManager()->GetNpad()->SetNpadCaptureButtonAssignment( + parameters.applet_resource_user_id, parameters.npad_styleset, parameters.button); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + const auto result = + GetResourceManager()->GetNpad()->ClearNpadCaptureButtonAssignment(applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; - const auto controller = GetResourceManager()->GetNpad(); - - Core::HID::VibrationDeviceInfo vibration_device_info; - bool check_device_index = false; - - switch (vibration_device_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Handheld: - case Core::HID::NpadStyleIndex::JoyconDual: - case Core::HID::NpadStyleIndex::JoyconLeft: - case Core::HID::NpadStyleIndex::JoyconRight: - vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; - check_device_index = true; - break; - case Core::HID::NpadStyleIndex::GameCube: - vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; - break; - case Core::HID::NpadStyleIndex::N64: - vibration_device_info.type = Core::HID::VibrationDeviceType::N64; - break; - default: - vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; - break; - } - - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - if (check_device_index) { - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - ASSERT_MSG(false, "DeviceIndex should never be None!"); - break; - } - } - - LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", - vibration_device_info.type, vibration_device_info.position); - const auto result = IsVibrationHandleValid(vibration_device_handle); - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } + Core::HID::VibrationDeviceInfo vibration_device_info{}; + const auto result = GetResourceManager()->GetVibrationDeviceInfo(vibration_device_info, + vibration_device_handle); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); + rb.Push(result); rb.PushRaw(vibration_device_info); } @@ -1496,15 +1545,16 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, - parameters.vibration_value); - LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.vibration_device_handle.npad_type, parameters.vibration_device_handle.npad_id, parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + GetResourceManager()->SendVibrationValue(parameters.applet_resource_user_id, + parameters.vibration_device_handle, + parameters.vibration_value); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -1526,10 +1576,28 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) { parameters.vibration_device_handle.npad_id, parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + bool has_active_aruid{}; + NpadVibrationDevice* device{nullptr}; + Core::HID::VibrationValue vibration_value{}; + Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id, + has_active_aruid); + + if (result.IsSuccess() && has_active_aruid) { + result = IsVibrationHandleValid(parameters.vibration_device_handle); + } + if (result.IsSuccess() && has_active_aruid) { + device = GetResourceManager()->GetNSVibrationDevice(parameters.vibration_device_handle); + } + if (device != nullptr) { + result = device->GetActualVibrationValue(vibration_value); + } + if (result.IsError()) { + vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE; + } + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); - rb.PushRaw( - GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle)); + rb.PushRaw(vibration_value); } void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { @@ -1544,25 +1612,27 @@ void IHidServer::PermitVibration(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop<bool>()}; - // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value - // by converting it to a bool - Settings::values.vibration_enabled.SetValue(can_vibrate); - LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume( + can_vibrate ? 1.0f : 0.0f); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called"); - // nnSDK checks if a float is greater than zero. We return the bool we stored earlier - const auto is_enabled = Settings::values.vibration_enabled.GetValue(); + f32 master_volume{}; + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume( + master_volume); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_enabled); + rb.Push(result); + rb.Push(master_volume > 0.0f); } void IHidServer::SendVibrationValues(HLERequestContext& ctx) { @@ -1580,12 +1650,22 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) { auto vibration_values = std::span( reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); - GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + Result result = ResultSuccess; + if (handle_count != vibration_count) { + result = ResultVibrationArraySizeMismatch; + } + + for (std::size_t i = 0; i < handle_count; i++) { + if (result.IsSuccess()) { + result = GetResourceManager()->SendVibrationValue( + applet_resource_user_id, vibration_device_handles[i], vibration_values[i]); + } + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { @@ -1600,43 +1680,6 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - /** - * Note: This uses yuzu-specific behavior such that the StopHard command produces - * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, - * in order to differentiate between Stop and StopHard commands. - * This is done to reuse the controller vibration functions made for regular controllers. - */ - const auto vibration_value = [parameters] { - switch (parameters.gc_erm_command) { - case Core::HID::VibrationGcErmCommand::Stop: - return Core::HID::VibrationValue{ - .low_amplitude = 0.0f, - .low_frequency = 160.0f, - .high_amplitude = 0.0f, - .high_frequency = 320.0f, - }; - case Core::HID::VibrationGcErmCommand::Start: - return Core::HID::VibrationValue{ - .low_amplitude = 1.0f, - .low_frequency = 160.0f, - .high_amplitude = 1.0f, - .high_frequency = 320.0f, - }; - case Core::HID::VibrationGcErmCommand::StopHard: - return Core::HID::VibrationValue{ - .low_amplitude = 0.0f, - .low_frequency = 0.0f, - .high_amplitude = 0.0f, - .high_frequency = 0.0f, - }; - default: - return Core::HID::DEFAULT_VIBRATION_VALUE; - } - }(); - - GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, - vibration_value); - LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " "gc_erm_command={}", @@ -1645,8 +1688,23 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, parameters.gc_erm_command); + bool has_active_aruid{}; + NpadGcVibrationDevice* gc_device{nullptr}; + Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id, + has_active_aruid); + + if (result.IsSuccess() && has_active_aruid) { + result = IsVibrationHandleValid(parameters.vibration_device_handle); + } + if (result.IsSuccess() && has_active_aruid) { + gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle); + } + if (gc_device != nullptr) { + result = gc_device->SendVibrationGcErmCommand(parameters.gc_erm_command); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { @@ -1659,33 +1717,31 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - const auto last_vibration = - GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle); - - const auto gc_erm_command = [last_vibration] { - if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { - return Core::HID::VibrationGcErmCommand::Start; - } - - /** - * Note: This uses yuzu-specific behavior such that the StopHard command produces - * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function - * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. - * This is done to reuse the controller vibration functions made for regular controllers. - */ - if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { - return Core::HID::VibrationGcErmCommand::StopHard; - } - - return Core::HID::VibrationGcErmCommand::Stop; - }(); - LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.vibration_device_handle.npad_type, parameters.vibration_device_handle.npad_id, parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + bool has_active_aruid{}; + NpadGcVibrationDevice* gc_device{nullptr}; + Core::HID::VibrationGcErmCommand gc_erm_command{}; + Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id, + has_active_aruid); + + if (result.IsSuccess() && has_active_aruid) { + result = IsVibrationHandleValid(parameters.vibration_device_handle); + } + if (result.IsSuccess() && has_active_aruid) { + gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle); + } + if (gc_device != nullptr) { + result = gc_device->GetActualVibrationGcErmCommand(gc_erm_command); + } + if (result.IsError()) { + gc_erm_command = Core::HID::VibrationGcErmCommand::Stop; + } + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushEnum(gc_erm_command); @@ -1695,21 +1751,24 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - GetResourceManager()->GetNpad()->SetPermitVibrationSession(true); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession( + applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { - GetResourceManager()->GetNpad()->SetPermitVibrationSession(false); - LOG_DEBUG(Service_HID, "called"); + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession(); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { @@ -1729,10 +1788,61 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { parameters.vibration_device_handle.npad_id, parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + bool is_mounted{}; + NpadVibrationBase* device{nullptr}; + Result result = IsVibrationHandleValid(parameters.vibration_device_handle); + + if (result.IsSuccess()) { + device = GetResourceManager()->GetVibrationDevice(parameters.vibration_device_handle); + } + + if (device != nullptr) { + is_mounted = device->IsVibrationMounted(); + } + IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted( - parameters.vibration_device_handle)); + rb.Push(result); + rb.Push(is_mounted); +} + +void IHidServer::SendVibrationValueInBool(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + bool is_vibrating; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " + "is_vibrating={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, + parameters.is_vibrating); + + bool has_active_aruid{}; + NpadN64VibrationDevice* n64_device{nullptr}; + Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id, + has_active_aruid); + + if (result.IsSuccess() && has_active_aruid) { + result = IsVibrationHandleValid(parameters.vibration_device_handle); + } + if (result.IsSuccess() && has_active_aruid) { + n64_device = + GetResourceManager()->GetN64VibrationDevice(parameters.vibration_device_handle); + } + if (n64_device != nullptr) { + result = n64_device->SendValueInBool(parameters.is_vibrating); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { @@ -2315,10 +2425,10 @@ void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop<u64>()}; const auto communication_mode{rp.PopEnum<NpadCommunicationMode>()}; - GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, communication_mode={}", + applet_resource_user_id, communication_mode); - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", - applet_resource_user_id, communication_mode); + // This function has been stubbed since 2.0.0+ IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -2326,12 +2436,15 @@ void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + // This function has been stubbed since 2.0.0+ IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode()); + rb.PushEnum(NpadCommunicationMode::Default); } void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h index cc7c4ebdd..3a2e0a230 100644 --- a/src/core/hle/service/hid/hid_server.h +++ b/src/core/hle/service/hid/hid_server.h @@ -97,6 +97,7 @@ private: void BeginPermitVibrationSession(HLERequestContext& ctx); void EndPermitVibrationSession(HLERequestContext& ctx); void IsVibrationDeviceMounted(HLERequestContext& ctx); + void SendVibrationValueInBool(HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); void StartConsoleSixAxisSensor(HLERequestContext& ctx); void StopConsoleSixAxisSensor(HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 5cc88c4a1..d1ec42edc 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -1,21 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/palma.h" -#include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/hle/service/hid/controllers/types/npad_types.h" -#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid_system_server.h" -#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_types.h" +#include "hid_core/hid_result.h" +#include "hid_core/resource_manager.h" +#include "hid_core/resources/hid_firmware_settings.h" +#include "hid_core/resources/npad/npad.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/palma/palma.h" +#include "hid_core/resources/touch_screen/touch_screen.h" namespace Service::HID { -IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) +IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings) : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, - resource_manager{resource} { + resource_manager{resource}, firmware_settings{settings} { // clang-format off static const FunctionInfo functions[] = { {31, nullptr, "SendKeyboardLockKeyEvent"}, @@ -25,7 +28,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {131, nullptr, "ActivateSleepButton"}, {141, nullptr, "AcquireCaptureButtonEventHandle"}, {151, nullptr, "ActivateCaptureButton"}, - {161, nullptr, "GetPlatformConfig"}, + {161, &IHidSystemServer::GetPlatformConfig, "GetPlatformConfig"}, {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, {211, nullptr, "GetNpadsWithNfc"}, {212, nullptr, "AcquireNfcActivateEventHandle"}, @@ -47,7 +50,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"}, {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"}, - {313, nullptr, "GetNpadCaptureButtonAssignment"}, + {313, &IHidSystemServer::GetNpadCaptureButtonAssignment, "GetNpadCaptureButtonAssignment"}, {314, nullptr, "GetAppletFooterUiType"}, {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"}, {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"}, @@ -55,8 +58,8 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {318, &IHidSystemServer::HasBattery, "HasBattery"}, {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"}, {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, - {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"}, - {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, + {322, &IHidSystemServer::SetNpadSystemExtStateEnabled, "SetNpadSystemExtStateEnabled"}, + {323, nullptr, "GetLastActiveUniquePad"}, {324, nullptr, "GetUniquePadButtonSet"}, {325, nullptr, "GetUniquePadColor"}, {326, nullptr, "GetUniquePadAppletDetailedUiType"}, @@ -68,21 +71,21 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"}, {502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"}, {503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"}, - {504, nullptr, "SetAruidValidForVibration"}, + {504, &IHidSystemServer::SetAruidValidForVibration, "SetAruidValidForVibration"}, {505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"}, {506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"}, {507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"}, - {510, nullptr, "SetVibrationMasterVolume"}, - {511, nullptr, "GetVibrationMasterVolume"}, - {512, nullptr, "BeginPermitVibrationSession"}, - {513, nullptr, "EndPermitVibrationSession"}, + {510, &IHidSystemServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"}, + {511, &IHidSystemServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"}, + {512, &IHidSystemServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, + {513, &IHidSystemServer::EndPermitVibrationSession, "EndPermitVibrationSession"}, {514, nullptr, "Unknown514"}, {520, nullptr, "EnableHandheldHids"}, {521, nullptr, "DisableHandheldHids"}, {522, nullptr, "SetJoyConRailEnabled"}, - {523, nullptr, "IsJoyConRailEnabled"}, + {523, &IHidSystemServer::IsJoyConRailEnabled, "IsJoyConRailEnabled"}, {524, nullptr, "IsHandheldHidsEnabled"}, - {525, nullptr, "IsJoyConAttachedOnAllRail"}, + {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"}, {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, {541, nullptr, "GetPlayReportControllerUsages"}, {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, @@ -123,7 +126,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {831, nullptr, "SetNotificationLedPatternWithTimeout"}, {832, nullptr, "PrepareHidsForNotificationWake"}, {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, - {851, nullptr, "EnableUsbFullKeyController"}, + {851, &IHidSystemServer::EnableUsbFullKeyController, "EnableUsbFullKeyController"}, {852, nullptr, "IsUsbConnected"}, {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"}, {900, nullptr, "ActivateInputDetector"}, @@ -132,7 +135,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1001, nullptr, "GetFirmwareVersion"}, {1002, nullptr, "GetAvailableFirmwareVersion"}, {1003, nullptr, "IsFirmwareUpdateAvailable"}, - {1004, nullptr, "CheckFirmwareUpdateRequired"}, + {1004, &IHidSystemServer::CheckFirmwareUpdateRequired, "CheckFirmwareUpdateRequired"}, {1005, nullptr, "StartFirmwareUpdate"}, {1006, nullptr, "AbortFirmwareUpdate"}, {1007, nullptr, "GetFirmwareUpdateState"}, @@ -145,10 +148,10 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, {1100, nullptr, "GetHidbusSystemServiceObject"}, - {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, - {1130, nullptr, "InitializeUsbFirmwareUpdate"}, - {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, - {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, + {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"}, + {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"}, + {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"}, + {1132, &IHidSystemServer::CheckUsbFirmwareUpdateRequired, "CheckUsbFirmwareUpdateRequired"}, {1133, nullptr, "StartUsbFirmwareUpdate"}, {1134, nullptr, "GetUsbFirmwareUpdateState"}, {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, @@ -157,7 +160,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, {1154, nullptr, "IsFirmwareAvailableForNotification"}, - {1155, nullptr, "SetForceHandheldStyleVibration"}, + {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"}, {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, {1157, nullptr, "CancelConnectionTrigger"}, {1200, nullptr, "IsButtonConfigSupported"}, @@ -197,7 +200,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1268, nullptr, "DeleteButtonConfigStorageFull"}, {1269, nullptr, "DeleteButtonConfigStorageLeft"}, {1270, nullptr, "DeleteButtonConfigStorageRight"}, - {1271, nullptr, "IsUsingCustomButtonConfig"}, + {1271, &IHidSystemServer::IsUsingCustomButtonConfig, "IsUsingCustomButtonConfig"}, {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, {1274, nullptr, "SetDefaultButtonConfig"}, @@ -239,41 +242,70 @@ IHidSystemServer::~IHidSystemServer() { service_context.CloseEvent(unique_pad_connection_event); }; +void IHidSystemServer::GetPlatformConfig(HLERequestContext& ctx) { + const auto platform_config = firmware_settings->GetPlatformConfig(); + + LOG_INFO(Service_HID, "called, platform_config={}", platform_config.raw); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(platform_config); +} + void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); + GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running + Core::HID::NpadIdType npad_id{}; + const Result result = GetResourceManager()->GetNpad()->GetLastActiveNpad(npad_id); + + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(system.HIDCore().GetLastActiveController()); + rb.Push(result); + rb.PushEnum(npad_id); } void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); + GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -298,28 +330,53 @@ void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) { void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_INFO(Service_HID, "(STUBBED) called"); + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - Core::HID::NpadStyleSet supported_styleset = - GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; + Core::HID::NpadStyleSet supported_styleset{}; + const auto& npad = GetResourceManager()->GetNpad(); + const Result result = + npad->GetMaskedSupportedNpadStyleSet(applet_resource_user_id, supported_styleset); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); + rb.Push(result); rb.PushEnum(supported_styleset); } void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_INFO(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - Core::HID::NpadStyleSet supported_styleset = - GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; + const auto& npad = GetResourceManager()->GetNpad(); + const auto result = + npad->SetSupportedNpadStyleSet(applet_resource_user_id, Core::HID::NpadStyleSet::All); - IPC::ResponseBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidSystemServer::GetNpadCaptureButtonAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto capture_button_list_size{ctx.GetWriteBufferNumElements<Core::HID::NpadButton>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + std::vector<Core::HID::NpadButton> capture_button_list(capture_button_list_size); + const auto& npad = GetResourceManager()->GetNpad(); + const u64 list_size = + npad->GetNpadCaptureButtonAssignment(capture_button_list, applet_resource_user_id); + + if (list_size != 0) { + ctx.WriteBuffer(capture_button_list); + } + + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushEnum(supported_styleset); + rb.Push(list_size); } void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) { @@ -414,13 +471,25 @@ void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { rb.Push(static_cast<u32>(unique_pads.size())); } -void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) { +void IHidSystemServer::SetNpadSystemExtStateEnabled(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - LOG_WARNING(Service_HID, "(STUBBED) called"); + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.applet_resource_user_id); + + const auto result = GetResourceManager()->GetNpad()->SetNpadSystemExtStateEnabled( + parameters.applet_resource_user_id, parameters.is_enabled); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -477,7 +546,28 @@ void IHidSystemServer::EnableAppletToGetInput(HLERequestContext& ctx) { parameters.is_enabled, parameters.applet_resource_user_id); GetResourceManager()->EnableInput(parameters.applet_resource_user_id, parameters.is_enabled); - // GetResourceManager()->GetNpad()->EnableInput(parameters.applet_resource_user_id); + GetResourceManager()->GetNpad()->EnableAppletToGetInput(parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::SetAruidValidForVibration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.applet_resource_user_id); + + GetResourceManager()->SetAruidValidForVibration(parameters.applet_resource_user_id, + parameters.is_enabled); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -519,7 +609,7 @@ void IHidSystemServer::EnableAppletToGetPadInput(HLERequestContext& ctx) { parameters.is_enabled, parameters.applet_resource_user_id); GetResourceManager()->EnablePadInput(parameters.applet_resource_user_id, parameters.is_enabled); - // GetResourceManager()->GetNpad()->EnableInput(parameters.applet_resource_user_id); + GetResourceManager()->GetNpad()->EnableAppletToGetInput(parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -546,6 +636,77 @@ void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IHidSystemServer::SetVibrationMasterVolume(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto master_volume{rp.Pop<f32>()}; + + LOG_INFO(Service_HID, "called, volume={}", master_volume); + + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume( + master_volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidSystemServer::GetVibrationMasterVolume(HLERequestContext& ctx) { + f32 master_volume{}; + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume( + master_volume); + + LOG_INFO(Service_HID, "called, volume={}", master_volume); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(master_volume); +} + +void IHidSystemServer::BeginPermitVibrationSession(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession( + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) { + LOG_INFO(Service_HID, "called"); + + const auto result = + GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidSystemServer::IsJoyConRailEnabled(HLERequestContext& ctx) { + const bool is_attached = true; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_attached={}", is_attached); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); +} + +void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) { + const bool is_attached = true; + + LOG_DEBUG(Service_HID, "(STUBBED) called, is_attached={}", is_attached); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); +} + void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { LOG_INFO(Service_AM, "(STUBBED) called"); @@ -589,7 +750,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& } void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -614,6 +775,16 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { rb.Push(is_enabled); } +void IHidSystemServer::EnableUsbFullKeyController(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_enabled{rp.Pop<bool>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { const bool button_pressed = false; @@ -632,6 +803,41 @@ void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IHidSystemServer::CheckFirmwareUpdateRequired(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::InitializeUsbFirmwareUpdate(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called"); @@ -656,6 +862,29 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx rb.PushRaw(touchscreen_config); } +void IHidSystemServer::SetForceHandheldStyleVibration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_forced{rp.Pop<bool>()}; + + LOG_INFO(Service_HID, "called, is_forced={}", is_forced); + + GetResourceManager()->SetForceHandheldStyleVibration(is_forced); + GetResourceManager()->GetNpad()->UpdateHandheldAbstractState(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) { + const bool is_enabled = false; + + LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_enabled); +} + std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() { resource_manager->Initialize(); return resource_manager; diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index 1e623dfc2..4ab4d3931 100644 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -16,13 +16,16 @@ class KEvent; namespace Service::HID { class ResourceManager; +class HidFirmwareSettings; class IHidSystemServer final : public ServiceFramework<IHidSystemServer> { public: - explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); + explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings); ~IHidSystemServer() override; private: + void GetPlatformConfig(HLERequestContext& ctx); void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); @@ -31,19 +34,27 @@ private: void GetNpadFullKeyGripColor(HLERequestContext& ctx); void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx); void SetSupportedNpadStyleSetAll(HLERequestContext& ctx); + void GetNpadCaptureButtonAssignment(HLERequestContext& ctx); void GetAppletDetailedUiType(HLERequestContext& ctx); void GetNpadInterfaceType(HLERequestContext& ctx); void GetNpadLeftRightInterfaceType(HLERequestContext& ctx); void HasBattery(HLERequestContext& ctx); void HasLeftRightBattery(HLERequestContext& ctx); void GetUniquePadsFromNpad(HLERequestContext& ctx); - void GetIrSensorState(HLERequestContext& ctx); + void SetNpadSystemExtStateEnabled(HLERequestContext& ctx); void RegisterAppletResourceUserId(HLERequestContext& ctx); void UnregisterAppletResourceUserId(HLERequestContext& ctx); void EnableAppletToGetInput(HLERequestContext& ctx); + void SetAruidValidForVibration(HLERequestContext& ctx); void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx); void EnableAppletToGetPadInput(HLERequestContext& ctx); void EnableAppletToGetTouchScreen(HLERequestContext& ctx); + void SetVibrationMasterVolume(HLERequestContext& ctx); + void GetVibrationMasterVolume(HLERequestContext& ctx); + void BeginPermitVibrationSession(HLERequestContext& ctx); + void EndPermitVibrationSession(HLERequestContext& ctx); + void IsJoyConRailEnabled(HLERequestContext& ctx); + void IsJoyConAttachedOnAllRail(HLERequestContext& ctx); void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); void GetRegisteredDevices(HLERequestContext& ctx); @@ -51,10 +62,18 @@ private: void GetUniquePadIds(HLERequestContext& ctx); void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void EnableUsbFullKeyController(HLERequestContext& ctx); void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); void InitializeFirmwareUpdate(HLERequestContext& ctx); + void CheckFirmwareUpdateRequired(HLERequestContext& ctx); + void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx); + void InitializeUsbFirmwareUpdate(HLERequestContext& ctx); + void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); + void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); + void SetForceHandheldStyleVibration(HLERequestContext& ctx); + void IsUsingCustomButtonConfig(HLERequestContext& ctx); std::shared_ptr<ResourceManager> GetResourceManager(); @@ -64,6 +83,7 @@ private: Kernel::KEvent* unique_pad_connection_event; KernelHelpers::ServiceContext service_context; std::shared_ptr<ResourceManager> resource_manager; + std::shared_ptr<HidFirmwareSettings> firmware_settings; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h deleted file mode 100644 index b87cc10e3..000000000 --- a/src/core/hle/service/hid/hid_util.h +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "core/hid/hid_types.h" -#include "core/hle/service/hid/errors.h" - -namespace Service::HID { - -constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) { - switch (npad_id) { - case Core::HID::NpadIdType::Player1: - case Core::HID::NpadIdType::Player2: - case Core::HID::NpadIdType::Player3: - case Core::HID::NpadIdType::Player4: - case Core::HID::NpadIdType::Player5: - case Core::HID::NpadIdType::Player6: - case Core::HID::NpadIdType::Player7: - case Core::HID::NpadIdType::Player8: - case Core::HID::NpadIdType::Other: - case Core::HID::NpadIdType::Handheld: - return true; - default: - return false; - } -} - -constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) { - const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id)); - const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; - - if (!npad_id) { - return InvalidNpadId; - } - if (!device_index) { - return NpadDeviceIndexOutOfRange; - } - - return ResultSuccess; -} - -constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { - switch (handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Handheld: - case Core::HID::NpadStyleIndex::JoyconDual: - case Core::HID::NpadStyleIndex::JoyconLeft: - case Core::HID::NpadStyleIndex::JoyconRight: - case Core::HID::NpadStyleIndex::GameCube: - case Core::HID::NpadStyleIndex::N64: - case Core::HID::NpadStyleIndex::SystemExt: - case Core::HID::NpadStyleIndex::System: - // These support vibration - break; - default: - return VibrationInvalidStyleIndex; - } - - if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) { - return VibrationInvalidNpadId; - } - - if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) { - return VibrationDeviceIndexOutOfRange; - } - - return ResultSuccess; -} - -/// Converts a Core::HID::NpadIdType to an array index. -constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) { - switch (npad_id_type) { - case Core::HID::NpadIdType::Player1: - return 0; - case Core::HID::NpadIdType::Player2: - return 1; - case Core::HID::NpadIdType::Player3: - return 2; - case Core::HID::NpadIdType::Player4: - return 3; - case Core::HID::NpadIdType::Player5: - return 4; - case Core::HID::NpadIdType::Player6: - return 5; - case Core::HID::NpadIdType::Player7: - return 6; - case Core::HID::NpadIdType::Player8: - return 7; - case Core::HID::NpadIdType::Handheld: - return 8; - case Core::HID::NpadIdType::Other: - return 9; - default: - return 8; - } -} - -/// Converts an array index to a Core::HID::NpadIdType -constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) { - switch (index) { - case 0: - return Core::HID::NpadIdType::Player1; - case 1: - return Core::HID::NpadIdType::Player2; - case 2: - return Core::HID::NpadIdType::Player3; - case 3: - return Core::HID::NpadIdType::Player4; - case 4: - return Core::HID::NpadIdType::Player5; - case 5: - return Core::HID::NpadIdType::Player6; - case 6: - return Core::HID::NpadIdType::Player7; - case 7: - return Core::HID::NpadIdType::Player8; - case 8: - return Core::HID::NpadIdType::Handheld; - case 9: - return Core::HID::NpadIdType::Other; - default: - return Core::HID::NpadIdType::Invalid; - } -} - -constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) { - switch (index) { - case 0: - return Core::HID::NpadStyleSet::Fullkey; - case 1: - return Core::HID::NpadStyleSet::Handheld; - case 2: - return Core::HID::NpadStyleSet::JoyDual; - case 3: - return Core::HID::NpadStyleSet::JoyLeft; - case 4: - return Core::HID::NpadStyleSet::JoyRight; - case 5: - return Core::HID::NpadStyleSet::Palma; - default: - return Core::HID::NpadStyleSet::None; - } -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index ffa7e144d..c903ee8b8 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp @@ -5,18 +5,18 @@ #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/hid/hidbus.h" -#include "core/hle/service/hid/hidbus/ringcon.h" -#include "core/hle/service/hid/hidbus/starlink.h" -#include "core/hle/service/hid/hidbus/stubbed.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/service.h" #include "core/memory.h" +#include "hid_core/hid_types.h" +#include "hid_core/hidbus/ringcon.h" +#include "hid_core/hidbus/starlink.h" +#include "hid_core/hidbus/stubbed.h" namespace Service::HID { // (15ms, 66Hz) @@ -67,7 +67,7 @@ HidBus::~HidBus() { void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { if (is_hidbus_enabled) { for (std::size_t i = 0; i < devices.size(); ++i) { - if (!devices[i].is_device_initializated) { + if (!devices[i].is_device_initialized) { continue; } auto& device = devices[i].device; @@ -213,7 +213,7 @@ void HidBus::Initialize(HLERequestContext& ctx) { if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { MakeDevice<RingController>(bus_handle_); - devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].is_device_initialized = true; devices[device_index.value()].device->ActivateDevice(); cur_entry.is_in_focus = true; cur_entry.is_connected = true; @@ -222,7 +222,7 @@ void HidBus::Initialize(HLERequestContext& ctx) { cur_entry.is_polling_mode = false; } else { MakeDevice<HidbusStubbed>(bus_handle_); - devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].is_device_initialized = true; cur_entry.is_in_focus = true; cur_entry.is_connected = false; cur_entry.is_connected_result = ResultSuccess; @@ -261,7 +261,7 @@ void HidBus::Finalize(HLERequestContext& ctx) { const auto entry_index = devices[device_index.value()].handle.internal_index; auto& cur_entry = hidbus_status.entries[entry_index]; auto& device = devices[device_index.value()].device; - devices[device_index.value()].is_device_initializated = false; + devices[device_index.value()].is_device_initialized = false; device->DeactivateDevice(); cur_entry.is_in_focus = true; diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h index 85a1df133..03d9f6863 100644 --- a/src/core/hle/service/hid/hidbus.h +++ b/src/core/hle/service/hid/hidbus.h @@ -5,9 +5,9 @@ #include <functional> -#include "core/hle/service/hid/hidbus/hidbus_base.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" +#include "hid_core/hidbus/hidbus_base.h" namespace Core::Timing { struct EventType; @@ -89,7 +89,7 @@ private: static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size"); struct HidbusDevice { - bool is_device_initializated{}; + bool is_device_initialized{}; BusHandle handle{}; std::unique_ptr<HidbusBase> device{nullptr}; }; diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp deleted file mode 100644 index 8c44f93e8..000000000 --- a/src/core/hle/service/hid/hidbus/hidbus_base.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hid/hid_core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/hid/hidbus/hidbus_base.h" -#include "core/hle/service/kernel_helpers.h" - -namespace Service::HID { - -HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_) - : system(system_), service_context(service_context_) { - send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); -} - -HidbusBase::~HidbusBase() { - service_context.CloseEvent(send_command_async_event); -}; - -void HidbusBase::ActivateDevice() { - if (is_activated) { - return; - } - is_activated = true; - OnInit(); -} - -void HidbusBase::DeactivateDevice() { - if (is_activated) { - OnRelease(); - } - is_activated = false; -} - -bool HidbusBase::IsDeviceActivated() const { - return is_activated; -} - -void HidbusBase::Enable(bool enable) { - device_enabled = enable; -} - -bool HidbusBase::IsEnabled() const { - return device_enabled; -} - -bool HidbusBase::IsPollingMode() const { - return polling_mode_enabled; -} - -JoyPollingMode HidbusBase::GetPollingMode() const { - return polling_mode; -} - -void HidbusBase::SetPollingMode(JoyPollingMode mode) { - polling_mode = mode; - polling_mode_enabled = true; -} - -void HidbusBase::DisablePollingMode() { - polling_mode_enabled = false; -} - -void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { - transfer_memory = t_mem; -} - -Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const { - return send_command_async_event->GetReadableEvent(); -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h deleted file mode 100644 index ec41684e1..000000000 --- a/src/core/hle/service/hid/hidbus/hidbus_base.h +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <span> -#include "common/typed_address.h" -#include "core/hle/result.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Service::KernelHelpers { -class ServiceContext; -} - -namespace Service::HID { - -// This is nn::hidbus::JoyPollingMode -enum class JoyPollingMode : u32 { - SixAxisSensorDisable, - SixAxisSensorEnable, - ButtonOnly, -}; - -struct DataAccessorHeader { - Result result{ResultUnknown}; - INSERT_PADDING_WORDS(0x1); - std::array<u8, 0x18> unused{}; - u64 latest_entry{}; - u64 total_entries{}; -}; -static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size"); - -struct JoyDisableSixAxisPollingData { - std::array<u8, 0x26> data; - u8 out_size; - INSERT_PADDING_BYTES(0x1); - u64 sampling_number; -}; -static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30, - "JoyDisableSixAxisPollingData is an invalid size"); - -struct JoyEnableSixAxisPollingData { - std::array<u8, 0x8> data; - u8 out_size; - INSERT_PADDING_BYTES(0x7); - u64 sampling_number; -}; -static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18, - "JoyEnableSixAxisPollingData is an invalid size"); - -struct JoyButtonOnlyPollingData { - std::array<u8, 0x2c> data; - u8 out_size; - INSERT_PADDING_BYTES(0x3); - u64 sampling_number; -}; -static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38, - "JoyButtonOnlyPollingData is an invalid size"); - -struct JoyDisableSixAxisPollingEntry { - u64 sampling_number; - JoyDisableSixAxisPollingData polling_data; -}; -static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38, - "JoyDisableSixAxisPollingEntry is an invalid size"); - -struct JoyEnableSixAxisPollingEntry { - u64 sampling_number; - JoyEnableSixAxisPollingData polling_data; -}; -static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20, - "JoyEnableSixAxisPollingEntry is an invalid size"); - -struct JoyButtonOnlyPollingEntry { - u64 sampling_number; - JoyButtonOnlyPollingData polling_data; -}; -static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40, - "JoyButtonOnlyPollingEntry is an invalid size"); - -struct JoyDisableSixAxisDataAccessor { - DataAccessorHeader header{}; - std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{}; -}; -static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298, - "JoyDisableSixAxisDataAccessor is an invalid size"); - -struct JoyEnableSixAxisDataAccessor { - DataAccessorHeader header{}; - std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{}; -}; -static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190, - "JoyEnableSixAxisDataAccessor is an invalid size"); - -struct ButtonOnlyPollingDataAccessor { - DataAccessorHeader header; - std::array<JoyButtonOnlyPollingEntry, 0xb> entries; -}; -static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0, - "ButtonOnlyPollingDataAccessor is an invalid size"); - -class HidbusBase { -public: - explicit HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_); - virtual ~HidbusBase(); - - void ActivateDevice(); - - void DeactivateDevice(); - - bool IsDeviceActivated() const; - - // Enables/disables the device - void Enable(bool enable); - - // returns true if device is enabled - bool IsEnabled() const; - - // returns true if polling mode is enabled - bool IsPollingMode() const; - - // returns polling mode - JoyPollingMode GetPollingMode() const; - - // Sets and enables JoyPollingMode - void SetPollingMode(JoyPollingMode mode); - - // Disables JoyPollingMode - void DisablePollingMode(); - - // Called on EnableJoyPollingReceiveMode - void SetTransferMemoryAddress(Common::ProcessAddress t_mem); - - Kernel::KReadableEvent& GetSendCommandAsycEvent() const; - - virtual void OnInit() {} - - virtual void OnRelease() {} - - // Updates device transfer memory - virtual void OnUpdate() {} - - // Returns the device ID of the joycon - virtual u8 GetDeviceId() const { - return {}; - } - - // Assigns a command from data - virtual bool SetCommand(std::span<const u8> data) { - return {}; - } - - // Returns a reply from a command - virtual std::vector<u8> GetReply() const { - return {}; - } - -protected: - bool is_activated{}; - bool device_enabled{}; - bool polling_mode_enabled{}; - JoyPollingMode polling_mode = {}; - // TODO(German77): All data accessors need to be replaced with a ring lifo object - JoyDisableSixAxisDataAccessor disable_sixaxis_data{}; - JoyEnableSixAxisDataAccessor enable_sixaxis_data{}; - ButtonOnlyPollingDataAccessor button_only_data{}; - - Common::ProcessAddress transfer_memory{}; - - Core::System& system; - Kernel::KEvent* send_command_async_event; - KernelHelpers::ServiceContext& service_context; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp deleted file mode 100644 index 378108012..000000000 --- a/src/core/hle/service/hid/hidbus/ringcon.cpp +++ /dev/null @@ -1,292 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/hid/hidbus/ringcon.h" -#include "core/memory.h" - -namespace Service::HID { - -RingController::RingController(Core::System& system_, - KernelHelpers::ServiceContext& service_context_) - : HidbusBase(system_, service_context_) { - input = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); -} - -RingController::~RingController() = default; - -void RingController::OnInit() { - input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Ring); - return; -} - -void RingController::OnRelease() { - input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - return; -}; - -void RingController::OnUpdate() { - if (!is_activated) { - return; - } - - if (!device_enabled) { - return; - } - - if (!polling_mode_enabled || transfer_memory == 0) { - return; - } - - // TODO: Increment multitasking counters from motion and sensor data - - switch (polling_mode) { - case JoyPollingMode::SixAxisSensorEnable: { - enable_sixaxis_data.header.total_entries = 10; - enable_sixaxis_data.header.result = ResultSuccess; - const auto& last_entry = - enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; - - enable_sixaxis_data.header.latest_entry = - (enable_sixaxis_data.header.latest_entry + 1) % 10; - auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; - - curr_entry.sampling_number = last_entry.sampling_number + 1; - curr_entry.polling_data.sampling_number = curr_entry.sampling_number; - - const RingConData ringcon_value = GetSensorValue(); - curr_entry.polling_data.out_size = sizeof(ringcon_value); - std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value)); - - system.ApplicationMemory().WriteBlock(transfer_memory, &enable_sixaxis_data, - sizeof(enable_sixaxis_data)); - break; - } - default: - LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); - break; - } -} - -RingController::RingConData RingController::GetSensorValue() const { - RingConData ringcon_sensor_value{ - .status = DataValid::Valid, - .data = 0, - }; - - const f32 force_value = input->GetRingSensorForce().force * range; - ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value; - - return ringcon_sensor_value; -} - -u8 RingController::GetDeviceId() const { - return device_id; -} - -std::vector<u8> RingController::GetReply() const { - const RingConCommands current_command = command; - - switch (current_command) { - case RingConCommands::GetFirmwareVersion: - return GetFirmwareVersionReply(); - case RingConCommands::ReadId: - return GetReadIdReply(); - case RingConCommands::c20105: - return GetC020105Reply(); - case RingConCommands::ReadUnkCal: - return GetReadUnkCalReply(); - case RingConCommands::ReadFactoryCal: - return GetReadFactoryCalReply(); - case RingConCommands::ReadUserCal: - return GetReadUserCalReply(); - case RingConCommands::ReadRepCount: - return GetReadRepCountReply(); - case RingConCommands::ReadTotalPushCount: - return GetReadTotalPushCountReply(); - case RingConCommands::ResetRepCount: - return GetResetRepCountReply(); - case RingConCommands::SaveCalData: - return GetSaveDataReply(); - default: - return GetErrorReply(); - } -} - -bool RingController::SetCommand(std::span<const u8> data) { - if (data.size() < 4) { - LOG_ERROR(Service_HID, "Command size not supported {}", data.size()); - command = RingConCommands::Error; - return false; - } - - std::memcpy(&command, data.data(), sizeof(RingConCommands)); - - switch (command) { - case RingConCommands::GetFirmwareVersion: - case RingConCommands::ReadId: - case RingConCommands::c20105: - case RingConCommands::ReadUnkCal: - case RingConCommands::ReadFactoryCal: - case RingConCommands::ReadUserCal: - case RingConCommands::ReadRepCount: - case RingConCommands::ReadTotalPushCount: - ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); - send_command_async_event->Signal(); - return true; - case RingConCommands::ResetRepCount: - ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); - total_rep_count = 0; - send_command_async_event->Signal(); - return true; - case RingConCommands::SaveCalData: { - ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); - - SaveCalData save_info{}; - std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); - user_calibration = save_info.calibration; - send_command_async_event->Signal(); - return true; - } - default: - LOG_ERROR(Service_HID, "Command not implemented {}", command); - command = RingConCommands::Error; - // Signal a reply to avoid softlocking the game - send_command_async_event->Signal(); - return false; - } -} - -std::vector<u8> RingController::GetFirmwareVersionReply() const { - const FirmwareVersionReply reply{ - .status = DataValid::Valid, - .firmware = version, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetReadIdReply() const { - // The values are hardcoded from a real joycon - const ReadIdReply reply{ - .status = DataValid::Valid, - .id_l_x0 = 8, - .id_l_x0_2 = 41, - .id_l_x4 = 22294, - .id_h_x0 = 19777, - .id_h_x0_2 = 13621, - .id_h_x4 = 8245, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetC020105Reply() const { - const Cmd020105Reply reply{ - .status = DataValid::Valid, - .data = 1, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetReadUnkCalReply() const { - const ReadUnkCalReply reply{ - .status = DataValid::Valid, - .data = 0, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetReadFactoryCalReply() const { - const ReadFactoryCalReply reply{ - .status = DataValid::Valid, - .calibration = factory_calibration, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetReadUserCalReply() const { - const ReadUserCalReply reply{ - .status = DataValid::Valid, - .calibration = user_calibration, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetReadRepCountReply() const { - const GetThreeByteReply reply{ - .status = DataValid::Valid, - .data = {total_rep_count, 0, 0}, - .crc = GetCrcValue({total_rep_count, 0, 0, 0}), - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetReadTotalPushCountReply() const { - const GetThreeByteReply reply{ - .status = DataValid::Valid, - .data = {total_push_count, 0, 0}, - .crc = GetCrcValue({total_push_count, 0, 0, 0}), - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetResetRepCountReply() const { - return GetReadRepCountReply(); -} - -std::vector<u8> RingController::GetSaveDataReply() const { - const StatusReply reply{ - .status = DataValid::Valid, - }; - - return GetDataVector(reply); -} - -std::vector<u8> RingController::GetErrorReply() const { - const ErrorReply reply{ - .status = DataValid::BadCRC, - }; - - return GetDataVector(reply); -} - -u8 RingController::GetCrcValue(const std::vector<u8>& data) const { - u8 crc = 0; - for (std::size_t index = 0; index < data.size(); index++) { - for (u8 i = 0x80; i > 0; i >>= 1) { - bool bit = (crc & 0x80) != 0; - if ((data[index] & i) != 0) { - bit = !bit; - } - crc <<= 1; - if (bit) { - crc ^= 0x8d; - } - } - } - return crc; -} - -template <typename T> -std::vector<u8> RingController::GetDataVector(const T& reply) const { - static_assert(std::is_trivially_copyable_v<T>); - std::vector<u8> data; - data.resize(sizeof(reply)); - std::memcpy(data.data(), &reply, sizeof(reply)); - return data; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h deleted file mode 100644 index f42f3ea41..000000000 --- a/src/core/hle/service/hid/hidbus/ringcon.h +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <span> - -#include "common/common_types.h" -#include "core/hle/service/hid/hidbus/hidbus_base.h" - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::HID { - -class RingController final : public HidbusBase { -public: - explicit RingController(Core::System& system_, KernelHelpers::ServiceContext& service_context_); - ~RingController() override; - - void OnInit() override; - - void OnRelease() override; - - // Updates ringcon transfer memory - void OnUpdate() override; - - // Returns the device ID of the joycon - u8 GetDeviceId() const override; - - // Assigns a command from data - bool SetCommand(std::span<const u8> data) override; - - // Returns a reply from a command - std::vector<u8> GetReply() const override; - -private: - // These values are obtained from a real ring controller - static constexpr s16 idle_value = 2280; - static constexpr s16 idle_deadzone = 120; - static constexpr s16 range = 2500; - - // Most missing command names are leftovers from other firmware versions - enum class RingConCommands : u32 { - GetFirmwareVersion = 0x00020000, - ReadId = 0x00020100, - JoyPolling = 0x00020101, - Unknown1 = 0x00020104, - c20105 = 0x00020105, - Unknown2 = 0x00020204, - Unknown3 = 0x00020304, - Unknown4 = 0x00020404, - ReadUnkCal = 0x00020504, - ReadFactoryCal = 0x00020A04, - Unknown5 = 0x00021104, - Unknown6 = 0x00021204, - Unknown7 = 0x00021304, - ReadUserCal = 0x00021A04, - ReadRepCount = 0x00023104, - ReadTotalPushCount = 0x00023204, - ResetRepCount = 0x04013104, - Unknown8 = 0x04011104, - Unknown9 = 0x04011204, - Unknown10 = 0x04011304, - SaveCalData = 0x10011A04, - Error = 0xFFFFFFFF, - }; - - enum class DataValid : u32 { - Valid, - BadCRC, - Cal, - }; - - struct FirmwareVersion { - u8 sub; - u8 main; - }; - static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size"); - - struct FactoryCalibration { - s32_le os_max; - s32_le hk_max; - s32_le zero_min; - s32_le zero_max; - }; - static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size"); - - struct CalibrationValue { - s16 value; - u16 crc; - }; - static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size"); - - struct UserCalibration { - CalibrationValue os_max; - CalibrationValue hk_max; - CalibrationValue zero; - }; - static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size"); - - struct SaveCalData { - RingConCommands command; - UserCalibration calibration; - INSERT_PADDING_BYTES_NOINIT(4); - }; - static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size"); - static_assert(std::is_trivially_copyable_v<SaveCalData>, - "SaveCalData must be trivially copyable"); - - struct FirmwareVersionReply { - DataValid status; - FirmwareVersion firmware; - INSERT_PADDING_BYTES(0x2); - }; - static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size"); - - struct Cmd020105Reply { - DataValid status; - u8 data; - INSERT_PADDING_BYTES(0x3); - }; - static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size"); - - struct StatusReply { - DataValid status; - }; - static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size"); - - struct GetThreeByteReply { - DataValid status; - std::array<u8, 3> data; - u8 crc; - }; - static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size"); - - struct ReadUnkCalReply { - DataValid status; - u16 data; - INSERT_PADDING_BYTES(0x2); - }; - static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size"); - - struct ReadFactoryCalReply { - DataValid status; - FactoryCalibration calibration; - }; - static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size"); - - struct ReadUserCalReply { - DataValid status; - UserCalibration calibration; - INSERT_PADDING_BYTES(0x4); - }; - static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size"); - - struct ReadIdReply { - DataValid status; - u16 id_l_x0; - u16 id_l_x0_2; - u16 id_l_x4; - u16 id_h_x0; - u16 id_h_x0_2; - u16 id_h_x4; - }; - static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size"); - - struct ErrorReply { - DataValid status; - INSERT_PADDING_BYTES(0x3); - }; - static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size"); - - struct RingConData { - DataValid status; - s16_le data; - INSERT_PADDING_BYTES(0x2); - }; - static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size"); - - // Returns RingConData struct with pressure sensor values - RingConData GetSensorValue() const; - - // Returns 8 byte reply with firmware version - std::vector<u8> GetFirmwareVersionReply() const; - - // Returns 16 byte reply with ID values - std::vector<u8> GetReadIdReply() const; - - // (STUBBED) Returns 8 byte reply - std::vector<u8> GetC020105Reply() const; - - // (STUBBED) Returns 8 byte empty reply - std::vector<u8> GetReadUnkCalReply() const; - - // Returns 20 byte reply with factory calibration values - std::vector<u8> GetReadFactoryCalReply() const; - - // Returns 20 byte reply with user calibration values - std::vector<u8> GetReadUserCalReply() const; - - // Returns 8 byte reply - std::vector<u8> GetReadRepCountReply() const; - - // Returns 8 byte reply - std::vector<u8> GetReadTotalPushCountReply() const; - - // Returns 8 byte reply - std::vector<u8> GetResetRepCountReply() const; - - // Returns 4 byte save data reply - std::vector<u8> GetSaveDataReply() const; - - // Returns 8 byte error reply - std::vector<u8> GetErrorReply() const; - - // Returns 8 bit redundancy check from provided data - u8 GetCrcValue(const std::vector<u8>& data) const; - - // Converts structs to an u8 vector equivalent - template <typename T> - std::vector<u8> GetDataVector(const T& reply) const; - - RingConCommands command{RingConCommands::Error}; - - // These counters are used in multitasking mode while the switch is sleeping - // Total steps taken - u8 total_rep_count = 0; - // Total times the ring was pushed - u8 total_push_count = 0; - - const u8 device_id = 0x20; - const FirmwareVersion version = { - .sub = 0x0, - .main = 0x2c, - }; - const FactoryCalibration factory_calibration = { - .os_max = idle_value + range + idle_deadzone, - .hk_max = idle_value - range - idle_deadzone, - .zero_min = idle_value - idle_deadzone, - .zero_max = idle_value + idle_deadzone, - }; - UserCalibration user_calibration = { - .os_max = {.value = range, .crc = 228}, - .hk_max = {.value = -range, .crc = 239}, - .zero = {.value = idle_value, .crc = 225}, - }; - - Core::HID::EmulatedController* input; -}; -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp deleted file mode 100644 index 36573274e..000000000 --- a/src/core/hle/service/hid/hidbus/starlink.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/hidbus/starlink.h" - -namespace Service::HID { -constexpr u8 DEVICE_ID = 0x28; - -Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_) - : HidbusBase(system_, service_context_) {} -Starlink::~Starlink() = default; - -void Starlink::OnInit() { - return; -} - -void Starlink::OnRelease() { - return; -}; - -void Starlink::OnUpdate() { - if (!is_activated) { - return; - } - if (!device_enabled) { - return; - } - if (!polling_mode_enabled || transfer_memory == 0) { - return; - } - - LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); -} - -u8 Starlink::GetDeviceId() const { - return DEVICE_ID; -} - -std::vector<u8> Starlink::GetReply() const { - return {}; -} - -bool Starlink::SetCommand(std::span<const u8> data) { - LOG_ERROR(Service_HID, "Command not implemented"); - return false; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h deleted file mode 100644 index a276aa88f..000000000 --- a/src/core/hle/service/hid/hidbus/starlink.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hle/service/hid/hidbus/hidbus_base.h" - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::HID { - -class Starlink final : public HidbusBase { -public: - explicit Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_); - ~Starlink() override; - - void OnInit() override; - - void OnRelease() override; - - // Updates ringcon transfer memory - void OnUpdate() override; - - // Returns the device ID of the joycon - u8 GetDeviceId() const override; - - // Assigns a command from data - bool SetCommand(std::span<const u8> data) override; - - // Returns a reply from a command - std::vector<u8> GetReply() const override; -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp deleted file mode 100644 index 8160b7218..000000000 --- a/src/core/hle/service/hid/hidbus/stubbed.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/hidbus/stubbed.h" - -namespace Service::HID { -constexpr u8 DEVICE_ID = 0xFF; - -HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_) - : HidbusBase(system_, service_context_) {} -HidbusStubbed::~HidbusStubbed() = default; - -void HidbusStubbed::OnInit() { - return; -} - -void HidbusStubbed::OnRelease() { - return; -}; - -void HidbusStubbed::OnUpdate() { - if (!is_activated) { - return; - } - if (!device_enabled) { - return; - } - if (!polling_mode_enabled || transfer_memory == 0) { - return; - } - - LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); -} - -u8 HidbusStubbed::GetDeviceId() const { - return DEVICE_ID; -} - -std::vector<u8> HidbusStubbed::GetReply() const { - return {}; -} - -bool HidbusStubbed::SetCommand(std::span<const u8> data) { - LOG_ERROR(Service_HID, "Command not implemented"); - return false; -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h deleted file mode 100644 index 2e58d42fc..000000000 --- a/src/core/hle/service/hid/hidbus/stubbed.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hle/service/hid/hidbus/hidbus_base.h" - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::HID { - -class HidbusStubbed final : public HidbusBase { -public: - explicit HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_); - ~HidbusStubbed() override; - - void OnInit() override; - - void OnRelease() override; - - // Updates ringcon transfer memory - void OnUpdate() override; - - // Returns the device ID of the joycon - u8 GetDeviceId() const override; - - // Assigns a command from data - bool SetCommand(std::span<const u8> data) override; - - // Returns a reply from a command - std::vector<u8> GetReply() const override; -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 008debfd1..18e544f2f 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -6,22 +6,22 @@ #include "core/core.h" #include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/hid/errors.h" -#include "core/hle/service/hid/hid_util.h" #include "core/hle/service/hid/irs.h" -#include "core/hle/service/hid/irsensor/clustering_processor.h" -#include "core/hle/service/hid/irsensor/image_transfer_processor.h" -#include "core/hle/service/hid/irsensor/ir_led_processor.h" -#include "core/hle/service/hid/irsensor/moment_processor.h" -#include "core/hle/service/hid/irsensor/pointing_processor.h" -#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/irsensor/clustering_processor.h" +#include "hid_core/irsensor/image_transfer_processor.h" +#include "hid_core/irsensor/ir_led_processor.h" +#include "hid_core/irsensor/moment_processor.h" +#include "hid_core/irsensor/pointing_processor.h" +#include "hid_core/irsensor/tera_plugin_processor.h" namespace Service::IRS { @@ -315,7 +315,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && npad_id != Core::HID::NpadIdType::Handheld) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Service::HID::InvalidNpadId); + rb.Push(Service::HID::ResultInvalidNpadId); return; } diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index c8e6dab17..06b7279ee 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -4,10 +4,10 @@ #pragma once #include "core/core.h" -#include "core/hid/hid_types.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/service.h" +#include "hid_core/hid_types.h" +#include "hid_core/irsensor/irs_types.h" +#include "hid_core/irsensor/processor_base.h" namespace Core::HID { class EmulatedController; diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h deleted file mode 100644 index 255d1d296..000000000 --- a/src/core/hle/service/hid/irs_ring_lifo.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> - -#include "common/common_types.h" - -namespace Service::IRS { - -template <typename State, std::size_t max_buffer_size> -struct Lifo { - s64 sampling_number{}; - s64 buffer_count{}; - std::array<State, max_buffer_size> entries{}; - - const State& ReadCurrentEntry() const { - return entries[GetBufferTail()]; - } - - const State& ReadPreviousEntry() const { - return entries[GetPreviousEntryIndex()]; - } - - s64 GetBufferTail() const { - return sampling_number % max_buffer_size; - } - - std::size_t GetPreviousEntryIndex() const { - return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size); - } - - std::size_t GetNextEntryIndex() const { - return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size); - } - - void WriteNextEntry(const State& new_state) { - if (buffer_count < static_cast<s64>(max_buffer_size)) { - buffer_count++; - } - sampling_number++; - entries[GetBufferTail()] = new_state; - } -}; - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp deleted file mode 100644 index c559eb0d5..000000000 --- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include <queue> - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/irsensor/clustering_processor.h" - -namespace Service::IRS { -ClusteringProcessor::ClusteringProcessor(Core::System& system_, - Core::IrSensor::DeviceFormat& device_format, - std::size_t npad_index) - : device{device_format}, system{system_} { - npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); - - device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; - device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; - SetDefaultConfig(); - - shared_memory = std::construct_at( - reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data)); - - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, - .is_npad_service = true, - }; - callback_key = npad_device->SetCallback(engine_callback); -} - -ClusteringProcessor::~ClusteringProcessor() { - npad_device->DeleteCallback(callback_key); -}; - -void ClusteringProcessor::StartProcessor() { - device.camera_status = Core::IrSensor::IrCameraStatus::Available; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; -} - -void ClusteringProcessor::SuspendProcessor() {} - -void ClusteringProcessor::StopProcessor() {} - -void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { - if (type != Core::HID::ControllerTriggerType::IrSensor) { - return; - } - - next_state = {}; - const auto& camera_data = npad_device->GetCamera(); - auto filtered_image = camera_data.data; - - RemoveLowIntensityData(filtered_image); - - const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x); - const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y); - const auto window_end_x = - window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width); - const auto window_end_y = - window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height); - - for (std::size_t y = window_start_y; y < window_end_y; y++) { - for (std::size_t x = window_start_x; x < window_end_x; x++) { - u8 pixel = GetPixel(filtered_image, x, y); - if (pixel == 0) { - continue; - } - const auto cluster = GetClusterProperties(filtered_image, x, y); - if (cluster.pixel_count > current_config.pixel_count_max) { - continue; - } - if (cluster.pixel_count < current_config.pixel_count_min) { - continue; - } - // Cluster object limit reached - if (next_state.object_count >= next_state.data.size()) { - continue; - } - next_state.data[next_state.object_count] = cluster; - next_state.object_count++; - } - } - - next_state.sampling_number = camera_data.sample; - next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count(); - next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; - shared_memory->clustering_lifo.WriteNextEntry(next_state); - - if (!IsProcessorActive()) { - StartProcessor(); - } -} - -void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) { - for (u8& pixel : data) { - if (pixel < current_config.pixel_count_min) { - pixel = 0; - } - } -} - -ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data, - std::size_t x, - std::size_t y) { - using DataPoint = Common::Point<std::size_t>; - std::queue<DataPoint> search_points{}; - ClusteringData current_cluster = GetPixelProperties(data, x, y); - SetPixel(data, x, y, 0); - search_points.emplace<DataPoint>({x, y}); - - while (!search_points.empty()) { - const auto point = search_points.front(); - search_points.pop(); - - // Avoid negative numbers - if (point.x == 0 || point.y == 0) { - continue; - } - - std::array<DataPoint, 4> new_points{ - DataPoint{point.x - 1, point.y}, - {point.x, point.y - 1}, - {point.x + 1, point.y}, - {point.x, point.y + 1}, - }; - - for (const auto new_point : new_points) { - if (new_point.x >= width) { - continue; - } - if (new_point.y >= height) { - continue; - } - if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) { - continue; - } - const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y); - current_cluster = MergeCluster(current_cluster, cluster); - SetPixel(data, new_point.x, new_point.y, 0); - search_points.emplace<DataPoint>({new_point.x, new_point.y}); - } - } - - return current_cluster; -} - -ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties( - const std::vector<u8>& data, std::size_t x, std::size_t y) const { - return { - .average_intensity = GetPixel(data, x, y) / 255.0f, - .centroid = - { - .x = static_cast<f32>(x), - .y = static_cast<f32>(y), - - }, - .pixel_count = 1, - .bound = - { - .x = static_cast<s16>(x), - .y = static_cast<s16>(y), - .width = 1, - .height = 1, - }, - }; -} - -ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster( - const ClusteringData a, const ClusteringData b) const { - const f32 a_pixel_count = static_cast<f32>(a.pixel_count); - const f32 b_pixel_count = static_cast<f32>(b.pixel_count); - const f32 pixel_count = a_pixel_count + b_pixel_count; - const f32 average_intensity = - (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count; - const Core::IrSensor::IrsCentroid centroid = { - .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count, - .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count, - }; - s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x; - s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y; - s16 a_bound_end_x = a.bound.x + a.bound.width; - s16 a_bound_end_y = a.bound.y + a.bound.height; - s16 b_bound_end_x = b.bound.x + b.bound.width; - s16 b_bound_end_y = b.bound.y + b.bound.height; - - const Core::IrSensor::IrsRect bound = { - .x = bound_start_x, - .y = bound_start_y, - .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x) - : static_cast<s16>(b_bound_end_x - bound_start_x), - .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y) - : static_cast<s16>(b_bound_end_y - bound_start_y), - }; - - return { - .average_intensity = average_intensity, - .centroid = centroid, - .pixel_count = static_cast<u32>(pixel_count), - .bound = bound, - }; -} - -u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { - if ((y * width) + x >= data.size()) { - return 0; - } - return data[(y * width) + x]; -} - -void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { - if ((y * width) + x >= data.size()) { - return; - } - data[(y * width) + x] = value; -} - -void ClusteringProcessor::SetDefaultConfig() { - using namespace std::literals::chrono_literals; - current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count(); - current_config.camera_config.gain = 2; - current_config.camera_config.is_negative_used = false; - current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds; - current_config.window_of_interest = { - .x = 0, - .y = 0, - .width = width, - .height = height, - }; - current_config.pixel_count_min = 3; - current_config.pixel_count_max = static_cast<u32>(GetDataSize(format)); - current_config.is_external_light_filter_enabled = true; - current_config.object_intensity_min = 150; - - npad_device->SetCameraFormat(format); -} - -void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { - current_config.camera_config.exposure_time = config.camera_config.exposure_time; - current_config.camera_config.gain = config.camera_config.gain; - current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; - current_config.camera_config.light_target = - static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); - current_config.window_of_interest = config.window_of_interest; - current_config.pixel_count_min = config.pixel_count_min; - current_config.pixel_count_max = config.pixel_count_max; - current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; - current_config.object_intensity_min = config.object_intensity_min; - - LOG_INFO(Service_IRS, - "Processor config, exposure_time={}, gain={}, is_negative_used={}, " - "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, " - "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}", - current_config.camera_config.exposure_time, current_config.camera_config.gain, - current_config.camera_config.is_negative_used, - current_config.camera_config.light_target, current_config.window_of_interest.x, - current_config.window_of_interest.y, current_config.window_of_interest.width, - current_config.window_of_interest.height, current_config.pixel_count_min, - current_config.pixel_count_max, current_config.is_external_light_filter_enabled, - current_config.object_intensity_min); - - npad_device->SetCameraFormat(format); -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h deleted file mode 100644 index 83f34734a..000000000 --- a/src/core/hle/service/hid/irsensor/clustering_processor.h +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irs_ring_lifo.h" -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Core { -class System; -} - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::IRS { -class ClusteringProcessor final : public ProcessorBase { -public: - explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, - std::size_t npad_index); - ~ClusteringProcessor() override; - - // Called when the processor is initialized - void StartProcessor() override; - - // Called when the processor is suspended - void SuspendProcessor() override; - - // Called when the processor is stopped - void StopProcessor() override; - - // Sets config parameters of the camera - void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); - -private: - static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240; - static constexpr std::size_t width = 320; - static constexpr std::size_t height = 240; - - // This is nn::irsensor::ClusteringProcessorConfig - struct ClusteringProcessorConfig { - Core::IrSensor::CameraConfig camera_config; - Core::IrSensor::IrsRect window_of_interest; - u32 pixel_count_min; - u32 pixel_count_max; - u32 object_intensity_min; - bool is_external_light_filter_enabled; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(ClusteringProcessorConfig) == 0x30, - "ClusteringProcessorConfig is an invalid size"); - - // This is nn::irsensor::AdaptiveClusteringProcessorConfig - struct AdaptiveClusteringProcessorConfig { - Core::IrSensor::AdaptiveClusteringMode mode; - Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; - }; - static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, - "AdaptiveClusteringProcessorConfig is an invalid size"); - - // This is nn::irsensor::ClusteringData - struct ClusteringData { - f32 average_intensity; - Core::IrSensor::IrsCentroid centroid; - u32 pixel_count; - Core::IrSensor::IrsRect bound; - }; - static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); - - // This is nn::irsensor::ClusteringProcessorState - struct ClusteringProcessorState { - s64 sampling_number; - u64 timestamp; - u8 object_count; - INSERT_PADDING_BYTES(3); - Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; - std::array<ClusteringData, 0x10> data; - }; - static_assert(sizeof(ClusteringProcessorState) == 0x198, - "ClusteringProcessorState is an invalid size"); - - struct ClusteringSharedMemory { - Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo; - static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size"); - INSERT_PADDING_WORDS(0x11F); - }; - static_assert(sizeof(ClusteringSharedMemory) == 0xE20, - "ClusteringSharedMemory is an invalid size"); - - void OnControllerUpdate(Core::HID::ControllerTriggerType type); - void RemoveLowIntensityData(std::vector<u8>& data); - ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y); - ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x, - std::size_t y) const; - ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const; - u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const; - void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value); - - // Sets config parameters of the camera - void SetDefaultConfig(); - - ClusteringSharedMemory* shared_memory = nullptr; - ClusteringProcessorState next_state{}; - - ClusteringProcessorConfig current_config{}; - Core::IrSensor::DeviceFormat& device; - Core::HID::EmulatedController* npad_device; - int callback_key{}; - - Core::System& system; -}; -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp deleted file mode 100644 index 22067a591..000000000 --- a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/core.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/irsensor/image_transfer_processor.h" -#include "core/memory.h" - -namespace Service::IRS { -ImageTransferProcessor::ImageTransferProcessor(Core::System& system_, - Core::IrSensor::DeviceFormat& device_format, - std::size_t npad_index) - : device{device_format}, system{system_} { - npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); - - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, - .is_npad_service = true, - }; - callback_key = npad_device->SetCallback(engine_callback); - - device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; - device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; -} - -ImageTransferProcessor::~ImageTransferProcessor() { - npad_device->DeleteCallback(callback_key); -}; - -void ImageTransferProcessor::StartProcessor() { - is_active = true; - device.camera_status = Core::IrSensor::IrCameraStatus::Available; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; - processor_state.sampling_number = 0; - processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; -} - -void ImageTransferProcessor::SuspendProcessor() {} - -void ImageTransferProcessor::StopProcessor() {} - -void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { - if (type != Core::HID::ControllerTriggerType::IrSensor) { - return; - } - if (transfer_memory == 0) { - return; - } - - const auto& camera_data = npad_device->GetCamera(); - - // This indicates how much ambient light is present - processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; - processor_state.sampling_number = camera_data.sample; - - if (camera_data.format != current_config.origin_format) { - LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, - current_config.origin_format); - system.ApplicationMemory().ZeroBlock(transfer_memory, - GetDataSize(current_config.trimming_format)); - return; - } - - if (current_config.origin_format > current_config.trimming_format) { - LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", - current_config.origin_format, current_config.trimming_format); - system.ApplicationMemory().ZeroBlock(transfer_memory, - GetDataSize(current_config.trimming_format)); - return; - } - - std::vector<u8> window_data{}; - const auto origin_width = GetDataWidth(current_config.origin_format); - const auto origin_height = GetDataHeight(current_config.origin_format); - const auto trimming_width = GetDataWidth(current_config.trimming_format); - const auto trimming_height = GetDataHeight(current_config.trimming_format); - window_data.resize(GetDataSize(current_config.trimming_format)); - - if (trimming_width + current_config.trimming_start_x > origin_width || - trimming_height + current_config.trimming_start_y > origin_height) { - LOG_WARNING(Service_IRS, - "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", - current_config.trimming_start_x, current_config.trimming_start_y, - trimming_width, trimming_height, origin_width, origin_height); - system.ApplicationMemory().ZeroBlock(transfer_memory, - GetDataSize(current_config.trimming_format)); - return; - } - - for (std::size_t y = 0; y < trimming_height; y++) { - for (std::size_t x = 0; x < trimming_width; x++) { - const std::size_t window_index = (y * trimming_width) + x; - const std::size_t origin_index = - ((y + current_config.trimming_start_y) * origin_width) + x + - current_config.trimming_start_x; - window_data[window_index] = camera_data.data[origin_index]; - } - } - - system.ApplicationMemory().WriteBlock(transfer_memory, window_data.data(), - GetDataSize(current_config.trimming_format)); - - if (!IsProcessorActive()) { - StartProcessor(); - } -} - -void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { - current_config.camera_config.exposure_time = config.camera_config.exposure_time; - current_config.camera_config.gain = config.camera_config.gain; - current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; - current_config.camera_config.light_target = - static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); - current_config.origin_format = - static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); - current_config.trimming_format = - static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); - current_config.trimming_start_x = 0; - current_config.trimming_start_y = 0; - - npad_device->SetCameraFormat(current_config.origin_format); -} - -void ImageTransferProcessor::SetConfig( - Core::IrSensor::PackedImageTransferProcessorExConfig config) { - current_config.camera_config.exposure_time = config.camera_config.exposure_time; - current_config.camera_config.gain = config.camera_config.gain; - current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; - current_config.camera_config.light_target = - static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); - current_config.origin_format = - static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); - current_config.trimming_format = - static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); - current_config.trimming_start_x = config.trimming_start_x; - current_config.trimming_start_y = config.trimming_start_y; - - npad_device->SetCameraFormat(current_config.origin_format); -} - -void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { - transfer_memory = t_mem; -} - -Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( - std::vector<u8>& data) const { - const auto size = GetDataSize(current_config.trimming_format); - data.resize(size); - system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); - return processor_state; -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h deleted file mode 100644 index 7f42d8453..000000000 --- a/src/core/hle/service/hid/irsensor/image_transfer_processor.h +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/typed_address.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Core { -class System; -} - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::IRS { -class ImageTransferProcessor final : public ProcessorBase { -public: - explicit ImageTransferProcessor(Core::System& system_, - Core::IrSensor::DeviceFormat& device_format, - std::size_t npad_index); - ~ImageTransferProcessor() override; - - // Called when the processor is initialized - void StartProcessor() override; - - // Called when the processor is suspended - void SuspendProcessor() override; - - // Called when the processor is stopped - void StopProcessor() override; - - // Sets config parameters of the camera - void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); - void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); - - // Transfer memory where the image data will be stored - void SetTransferMemoryAddress(Common::ProcessAddress t_mem); - - Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; - -private: - // This is nn::irsensor::ImageTransferProcessorConfig - struct ImageTransferProcessorConfig { - Core::IrSensor::CameraConfig camera_config; - Core::IrSensor::ImageTransferProcessorFormat format; - }; - static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, - "ImageTransferProcessorConfig is an invalid size"); - - // This is nn::irsensor::ImageTransferProcessorExConfig - struct ImageTransferProcessorExConfig { - Core::IrSensor::CameraConfig camera_config; - Core::IrSensor::ImageTransferProcessorFormat origin_format; - Core::IrSensor::ImageTransferProcessorFormat trimming_format; - u16 trimming_start_x; - u16 trimming_start_y; - bool is_external_light_filter_enabled; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, - "ImageTransferProcessorExConfig is an invalid size"); - - void OnControllerUpdate(Core::HID::ControllerTriggerType type); - - ImageTransferProcessorExConfig current_config{}; - Core::IrSensor::ImageTransferProcessorState processor_state{}; - Core::IrSensor::DeviceFormat& device; - Core::HID::EmulatedController* npad_device; - int callback_key{}; - - Core::System& system; - Common::ProcessAddress transfer_memory{}; -}; -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp deleted file mode 100644 index 8e6dd99e4..000000000 --- a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/hid/irsensor/ir_led_processor.h" - -namespace Service::IRS { -IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) - : device(device_format) { - device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; - device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; -} - -IrLedProcessor::~IrLedProcessor() = default; - -void IrLedProcessor::StartProcessor() {} - -void IrLedProcessor::SuspendProcessor() {} - -void IrLedProcessor::StopProcessor() {} - -void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { - current_config.light_target = - static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h deleted file mode 100644 index c3d8693c9..000000000 --- a/src/core/hle/service/hid/irsensor/ir_led_processor.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_types.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Service::IRS { -class IrLedProcessor final : public ProcessorBase { -public: - explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); - ~IrLedProcessor() override; - - // Called when the processor is initialized - void StartProcessor() override; - - // Called when the processor is suspended - void SuspendProcessor() override; - - // Called when the processor is stopped - void StopProcessor() override; - - // Sets config parameters of the camera - void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); - -private: - // This is nn::irsensor::IrLedProcessorConfig - struct IrLedProcessorConfig { - Core::IrSensor::CameraLightTarget light_target; - }; - static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); - - struct IrLedProcessorState { - s64 sampling_number; - u64 timestamp; - std::array<u8, 0x8> data; - }; - static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); - - IrLedProcessorConfig current_config{}; - Core::IrSensor::DeviceFormat& device; -}; - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp deleted file mode 100644 index cf045bda7..000000000 --- a/src/core/hle/service/hid/irsensor/moment_processor.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/irsensor/moment_processor.h" - -namespace Service::IRS { -static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30; -static constexpr std::size_t ImageWidth = 40; -static constexpr std::size_t ImageHeight = 30; - -MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, - std::size_t npad_index) - : device(device_format), system{system_} { - npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); - - device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; - device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; - - shared_memory = std::construct_at( - reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data)); - - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, - .is_npad_service = true, - }; - callback_key = npad_device->SetCallback(engine_callback); -} - -MomentProcessor::~MomentProcessor() { - npad_device->DeleteCallback(callback_key); -}; - -void MomentProcessor::StartProcessor() { - device.camera_status = Core::IrSensor::IrCameraStatus::Available; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; -} - -void MomentProcessor::SuspendProcessor() {} - -void MomentProcessor::StopProcessor() {} - -void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { - if (type != Core::HID::ControllerTriggerType::IrSensor) { - return; - } - - next_state = {}; - const auto& camera_data = npad_device->GetCamera(); - - const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width); - const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height); - const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x); - const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y); - - const std::size_t block_width = window_width / Columns; - const std::size_t block_height = window_height / Rows; - - for (std::size_t row = 0; row < Rows; row++) { - for (std::size_t column = 0; column < Columns; column++) { - const size_t x_pos = (column * block_width) + window_start_x; - const size_t y_pos = (row * block_height) + window_start_y; - auto& statistic = next_state.statistic[column + (row * Columns)]; - statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height); - } - } - - next_state.sampling_number = camera_data.sample; - next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count(); - next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; - shared_memory->moment_lifo.WriteNextEntry(next_state); - - if (!IsProcessorActive()) { - StartProcessor(); - } -} - -u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { - if ((y * ImageWidth) + x >= data.size()) { - return 0; - } - return data[(y * ImageWidth) + x]; -} - -MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data, - std::size_t start_x, - std::size_t start_y, - std::size_t width, - std::size_t height) const { - // The actual implementation is always 320x240 - static constexpr std::size_t RealWidth = 320; - static constexpr std::size_t RealHeight = 240; - static constexpr std::size_t Threshold = 30; - MomentStatistic statistic{}; - std::size_t active_points{}; - - // Sum all data points on the block that meet with the threshold - for (std::size_t y = 0; y < width; y++) { - for (std::size_t x = 0; x < height; x++) { - const size_t x_pos = x + start_x; - const size_t y_pos = y + start_y; - const auto pixel = - GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight); - - if (pixel < Threshold) { - continue; - } - - statistic.average_intensity += pixel; - - statistic.centroid.x += static_cast<float>(x_pos); - statistic.centroid.y += static_cast<float>(y_pos); - - active_points++; - } - } - - // Return an empty field if no points were available - if (active_points == 0) { - return {}; - } - - // Finally calculate the actual centroid and average intensity - statistic.centroid.x /= static_cast<float>(active_points); - statistic.centroid.y /= static_cast<float>(active_points); - statistic.average_intensity /= static_cast<f32>(width * height); - - return statistic; -} - -void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { - current_config.camera_config.exposure_time = config.camera_config.exposure_time; - current_config.camera_config.gain = config.camera_config.gain; - current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; - current_config.camera_config.light_target = - static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); - current_config.window_of_interest = config.window_of_interest; - current_config.preprocess = - static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); - current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; - - npad_device->SetCameraFormat(format); -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h deleted file mode 100644 index 398cfbdc1..000000000 --- a/src/core/hle/service/hid/irsensor/moment_processor.h +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_types.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irs_ring_lifo.h" -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Core { -class System; -} - -namespace Core::HID { -class EmulatedController; -} // namespace Core::HID - -namespace Service::IRS { -class MomentProcessor final : public ProcessorBase { -public: - explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, - std::size_t npad_index); - ~MomentProcessor() override; - - // Called when the processor is initialized - void StartProcessor() override; - - // Called when the processor is suspended - void SuspendProcessor() override; - - // Called when the processor is stopped - void StopProcessor() override; - - // Sets config parameters of the camera - void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); - -private: - static constexpr std::size_t Columns = 8; - static constexpr std::size_t Rows = 6; - - // This is nn::irsensor::MomentProcessorConfig - struct MomentProcessorConfig { - Core::IrSensor::CameraConfig camera_config; - Core::IrSensor::IrsRect window_of_interest; - Core::IrSensor::MomentProcessorPreprocess preprocess; - u32 preprocess_intensity_threshold; - }; - static_assert(sizeof(MomentProcessorConfig) == 0x28, - "MomentProcessorConfig is an invalid size"); - - // This is nn::irsensor::MomentStatistic - struct MomentStatistic { - f32 average_intensity; - Core::IrSensor::IrsCentroid centroid; - }; - static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); - - // This is nn::irsensor::MomentProcessorState - struct MomentProcessorState { - s64 sampling_number; - u64 timestamp; - Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; - INSERT_PADDING_BYTES(4); - std::array<MomentStatistic, Columns * Rows> statistic; - }; - static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); - - struct MomentSharedMemory { - Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo; - }; - static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size"); - - void OnControllerUpdate(Core::HID::ControllerTriggerType type); - u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const; - MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x, - std::size_t start_y, std::size_t width, std::size_t height) const; - - MomentSharedMemory* shared_memory = nullptr; - MomentProcessorState next_state{}; - - MomentProcessorConfig current_config{}; - Core::IrSensor::DeviceFormat& device; - Core::HID::EmulatedController* npad_device; - int callback_key{}; - - Core::System& system; -}; - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp deleted file mode 100644 index 929f177fc..000000000 --- a/src/core/hle/service/hid/irsensor/pointing_processor.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/hid/irsensor/pointing_processor.h" - -namespace Service::IRS { -PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) - : device(device_format) { - device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; - device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; -} - -PointingProcessor::~PointingProcessor() = default; - -void PointingProcessor::StartProcessor() {} - -void PointingProcessor::SuspendProcessor() {} - -void PointingProcessor::StopProcessor() {} - -void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { - current_config.window_of_interest = config.window_of_interest; -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h deleted file mode 100644 index d63423aff..000000000 --- a/src/core/hle/service/hid/irsensor/pointing_processor.h +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Service::IRS { -class PointingProcessor final : public ProcessorBase { -public: - explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); - ~PointingProcessor() override; - - // Called when the processor is initialized - void StartProcessor() override; - - // Called when the processor is suspended - void SuspendProcessor() override; - - // Called when the processor is stopped - void StopProcessor() override; - - // Sets config parameters of the camera - void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); - -private: - // This is nn::irsensor::PointingProcessorConfig - struct PointingProcessorConfig { - Core::IrSensor::IrsRect window_of_interest; - }; - static_assert(sizeof(PointingProcessorConfig) == 0x8, - "PointingProcessorConfig is an invalid size"); - - struct PointingProcessorMarkerData { - u8 pointing_status; - INSERT_PADDING_BYTES(3); - u32 unknown; - float unknown_float1; - float position_x; - float position_y; - float unknown_float2; - Core::IrSensor::IrsRect window_of_interest; - }; - static_assert(sizeof(PointingProcessorMarkerData) == 0x20, - "PointingProcessorMarkerData is an invalid size"); - - struct PointingProcessorMarkerState { - s64 sampling_number; - u64 timestamp; - std::array<PointingProcessorMarkerData, 0x3> data; - }; - static_assert(sizeof(PointingProcessorMarkerState) == 0x70, - "PointingProcessorMarkerState is an invalid size"); - - PointingProcessorConfig current_config{}; - Core::IrSensor::DeviceFormat& device; -}; - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp deleted file mode 100644 index 4d43ca17a..000000000 --- a/src/core/hle/service/hid/irsensor/processor_base.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Service::IRS { - -ProcessorBase::ProcessorBase() {} -ProcessorBase::~ProcessorBase() = default; - -bool ProcessorBase::IsProcessorActive() const { - return is_active; -} - -std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { - switch (format) { - case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: - return 320 * 240; - case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: - return 160 * 120; - case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: - return 80 * 60; - case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: - return 40 * 30; - case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: - return 20 * 15; - default: - return 0; - } -} - -std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { - switch (format) { - case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: - return 320; - case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: - return 160; - case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: - return 80; - case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: - return 40; - case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: - return 20; - default: - return 0; - } -} - -std::size_t ProcessorBase::GetDataHeight( - Core::IrSensor::ImageTransferProcessorFormat format) const { - switch (format) { - case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: - return 240; - case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: - return 120; - case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: - return 60; - case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: - return 30; - case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: - return 15; - default: - return 0; - } -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h deleted file mode 100644 index bc0d2977b..000000000 --- a/src/core/hle/service/hid/irsensor/processor_base.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hid/irs_types.h" - -namespace Service::IRS { -class ProcessorBase { -public: - explicit ProcessorBase(); - virtual ~ProcessorBase(); - - virtual void StartProcessor() = 0; - virtual void SuspendProcessor() = 0; - virtual void StopProcessor() = 0; - - bool IsProcessorActive() const; - -protected: - /// Returns the number of bytes the image uses - std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; - - /// Returns the width of the image - std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; - - /// Returns the height of the image - std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; - - bool is_active{false}; -}; -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp deleted file mode 100644 index e691c840a..000000000 --- a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" - -namespace Service::IRS { -TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) - : device(device_format) { - device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; - device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; - device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; -} - -TeraPluginProcessor::~TeraPluginProcessor() = default; - -void TeraPluginProcessor::StartProcessor() {} - -void TeraPluginProcessor::SuspendProcessor() {} - -void TeraPluginProcessor::StopProcessor() {} - -void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { - current_config.mode = config.mode; - current_config.unknown_1 = config.unknown_1; - current_config.unknown_2 = config.unknown_2; - current_config.unknown_3 = config.unknown_3; -} - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h deleted file mode 100644 index bbea7ed0b..000000000 --- a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_types.h" -#include "core/hid/irs_types.h" -#include "core/hle/service/hid/irsensor/processor_base.h" - -namespace Service::IRS { -class TeraPluginProcessor final : public ProcessorBase { -public: - explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); - ~TeraPluginProcessor() override; - - // Called when the processor is initialized - void StartProcessor() override; - - // Called when the processor is suspended - void SuspendProcessor() override; - - // Called when the processor is stopped - void StopProcessor() override; - - // Sets config parameters of the camera - void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); - -private: - // This is nn::irsensor::TeraPluginProcessorConfig - struct TeraPluginProcessorConfig { - u8 mode; - u8 unknown_1; - u8 unknown_2; - u8 unknown_3; - }; - static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, - "TeraPluginProcessorConfig is an invalid size"); - - struct TeraPluginProcessorState { - s64 sampling_number; - u64 timestamp; - Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; - std::array<u8, 0x12c> data; - }; - static_assert(sizeof(TeraPluginProcessorState) == 0x140, - "TeraPluginProcessorState is an invalid size"); - - TeraPluginProcessorConfig current_config{}; - Core::IrSensor::DeviceFormat& device; -}; - -} // namespace Service::IRS diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp deleted file mode 100644 index 84b4be3ed..000000000 --- a/src/core/hle/service/hid/resource_manager.cpp +++ /dev/null @@ -1,358 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hid/hid_core.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/service/hid/resource_manager.h" -#include "core/hle/service/ipc_helpers.h" - -#include "core/hle/service/hid/controllers/applet_resource.h" -#include "core/hle/service/hid/controllers/capture_button.h" -#include "core/hle/service/hid/controllers/console_six_axis.h" -#include "core/hle/service/hid/controllers/debug_mouse.h" -#include "core/hle/service/hid/controllers/debug_pad.h" -#include "core/hle/service/hid/controllers/digitizer.h" -#include "core/hle/service/hid/controllers/gesture.h" -#include "core/hle/service/hid/controllers/home_button.h" -#include "core/hle/service/hid/controllers/keyboard.h" -#include "core/hle/service/hid/controllers/mouse.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/palma.h" -#include "core/hle/service/hid/controllers/seven_six_axis.h" -#include "core/hle/service/hid/controllers/six_axis.h" -#include "core/hle/service/hid/controllers/sleep_button.h" -#include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/hle/service/hid/controllers/types/shared_memory_format.h" -#include "core/hle/service/hid/controllers/unique_pad.h" - -namespace Service::HID { - -// Updating period for each HID device. -// Period time is obtained by measuring the number of samples in a second on HW using a homebrew -// Correct npad_update_ns is 4ms this is overclocked to lower input lag -constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) -constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) -constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) -constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) - -ResourceManager::ResourceManager(Core::System& system_) - : system{system_}, service_context{system_, "hid"} { - applet_resource = std::make_shared<AppletResource>(system); -} - -ResourceManager::~ResourceManager() = default; - -void ResourceManager::Initialize() { - if (is_initialized) { - return; - } - - system.HIDCore().ReloadInputDevices(); - - InitializeHidCommonSampler(); - InitializeTouchScreenSampler(); - InitializeConsoleSixAxisSampler(); - InitializeAHidSampler(); - - is_initialized = true; -} - -std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const { - return applet_resource; -} - -std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const { - return capture_button; -} - -std::shared_ptr<ConsoleSixAxis> ResourceManager::GetConsoleSixAxis() const { - return console_six_axis; -} - -std::shared_ptr<DebugMouse> ResourceManager::GetDebugMouse() const { - return debug_mouse; -} - -std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const { - return debug_pad; -} - -std::shared_ptr<Digitizer> ResourceManager::GetDigitizer() const { - return digitizer; -} - -std::shared_ptr<Gesture> ResourceManager::GetGesture() const { - return gesture; -} - -std::shared_ptr<HomeButton> ResourceManager::GetHomeButton() const { - return home_button; -} - -std::shared_ptr<Keyboard> ResourceManager::GetKeyboard() const { - return keyboard; -} - -std::shared_ptr<Mouse> ResourceManager::GetMouse() const { - return mouse; -} - -std::shared_ptr<NPad> ResourceManager::GetNpad() const { - return npad; -} - -std::shared_ptr<Palma> ResourceManager::GetPalma() const { - return palma; -} - -std::shared_ptr<SevenSixAxis> ResourceManager::GetSevenSixAxis() const { - return seven_six_axis; -} - -std::shared_ptr<SixAxis> ResourceManager::GetSixAxis() const { - return six_axis; -} - -std::shared_ptr<SleepButton> ResourceManager::GetSleepButton() const { - return sleep_button; -} - -std::shared_ptr<TouchScreen> ResourceManager::GetTouchScreen() const { - return touch_screen; -} - -std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const { - return unique_pad; -} - -Result ResourceManager::CreateAppletResource(u64 aruid) { - if (aruid == 0) { - const auto result = RegisterCoreAppletResource(); - if (result.IsError()) { - return result; - } - return GetNpad()->Activate(); - } - - const auto result = CreateAppletResourceImpl(aruid); - if (result.IsError()) { - return result; - } - - // Homebrew doesn't try to activate some controllers, so we activate them by default - npad->Activate(); - six_axis->Activate(); - touch_screen->Activate(); - - return GetNpad()->Activate(aruid); -} - -Result ResourceManager::CreateAppletResourceImpl(u64 aruid) { - std::scoped_lock lock{shared_mutex}; - return applet_resource->CreateAppletResource(aruid); -} - -void ResourceManager::InitializeHidCommonSampler() { - debug_pad = std::make_shared<DebugPad>(system.HIDCore()); - mouse = std::make_shared<Mouse>(system.HIDCore()); - debug_mouse = std::make_shared<DebugMouse>(system.HIDCore()); - keyboard = std::make_shared<Keyboard>(system.HIDCore()); - unique_pad = std::make_shared<UniquePad>(system.HIDCore()); - npad = std::make_shared<NPad>(system.HIDCore(), service_context); - gesture = std::make_shared<Gesture>(system.HIDCore()); - home_button = std::make_shared<HomeButton>(system.HIDCore()); - sleep_button = std::make_shared<SleepButton>(system.HIDCore()); - capture_button = std::make_shared<CaptureButton>(system.HIDCore()); - digitizer = std::make_shared<Digitizer>(system.HIDCore()); - - palma = std::make_shared<Palma>(system.HIDCore(), service_context); - six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad); - - debug_pad->SetAppletResource(applet_resource); - digitizer->SetAppletResource(applet_resource); - keyboard->SetAppletResource(applet_resource); - npad->SetAppletResource(applet_resource); - six_axis->SetAppletResource(applet_resource); - mouse->SetAppletResource(applet_resource); - debug_mouse->SetAppletResource(applet_resource); - home_button->SetAppletResource(applet_resource); - sleep_button->SetAppletResource(applet_resource); - capture_button->SetAppletResource(applet_resource); -} - -void ResourceManager::InitializeTouchScreenSampler() { - gesture = std::make_shared<Gesture>(system.HIDCore()); - touch_screen = std::make_shared<TouchScreen>(system.HIDCore()); - - touch_screen->SetAppletResource(applet_resource); - gesture->SetAppletResource(applet_resource); -} - -void ResourceManager::InitializeConsoleSixAxisSampler() { - console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore()); - seven_six_axis = std::make_shared<SevenSixAxis>(system); - - console_six_axis->SetAppletResource(applet_resource); -} - -void ResourceManager::InitializeAHidSampler() { - // TODO -} - -Result ResourceManager::RegisterCoreAppletResource() { - std::scoped_lock lock{shared_mutex}; - return applet_resource->RegisterCoreAppletResource(); -} - -Result ResourceManager::UnregisterCoreAppletResource() { - std::scoped_lock lock{shared_mutex}; - return applet_resource->UnregisterCoreAppletResource(); -} - -Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) { - std::scoped_lock lock{shared_mutex}; - return applet_resource->RegisterAppletResourceUserId(aruid, bool_value); -} - -void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) { - std::scoped_lock lock{shared_mutex}; - applet_resource->UnregisterAppletResourceUserId(aruid); -} - -Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { - std::scoped_lock lock{shared_mutex}; - return applet_resource->GetSharedMemoryHandle(out_handle, aruid); -} - -void ResourceManager::FreeAppletResourceId(u64 aruid) { - std::scoped_lock lock{shared_mutex}; - applet_resource->FreeAppletResourceId(aruid); -} - -void ResourceManager::EnableInput(u64 aruid, bool is_enabled) { - std::scoped_lock lock{shared_mutex}; - applet_resource->EnableInput(aruid, is_enabled); -} - -void ResourceManager::EnableSixAxisSensor(u64 aruid, bool is_enabled) { - std::scoped_lock lock{shared_mutex}; - applet_resource->EnableSixAxisSensor(aruid, is_enabled); -} - -void ResourceManager::EnablePadInput(u64 aruid, bool is_enabled) { - std::scoped_lock lock{shared_mutex}; - applet_resource->EnablePadInput(aruid, is_enabled); -} - -void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) { - std::scoped_lock lock{shared_mutex}; - applet_resource->EnableTouchScreen(aruid, is_enabled); -} - -void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - debug_pad->OnUpdate(core_timing); - digitizer->OnUpdate(core_timing); - unique_pad->OnUpdate(core_timing); - gesture->OnUpdate(core_timing); - touch_screen->OnUpdate(core_timing); - palma->OnUpdate(core_timing); - home_button->OnUpdate(core_timing); - sleep_button->OnUpdate(core_timing); - capture_button->OnUpdate(core_timing); -} - -void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - npad->OnUpdate(core_timing); -} - -void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - mouse->OnUpdate(core_timing); - debug_mouse->OnUpdate(core_timing); - keyboard->OnUpdate(core_timing); -} - -void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - six_axis->OnUpdate(core_timing); - seven_six_axis->OnUpdate(core_timing); - console_six_axis->OnUpdate(core_timing); -} - -IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource, - u64 applet_resource_user_id) - : ServiceFramework{system_, "IAppletResource"}, aruid{applet_resource_user_id}, - resource_manager{resource} { - static const FunctionInfo functions[] = { - {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, - }; - RegisterHandlers(functions); - - // Register update callbacks - npad_update_event = Core::Timing::CreateEvent( - "HID::UpdatePadCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateNpad(ns_late); - return std::nullopt; - }); - default_update_event = Core::Timing::CreateEvent( - "HID::UpdateDefaultCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateControllers(ns_late); - return std::nullopt; - }); - mouse_keyboard_update_event = Core::Timing::CreateEvent( - "HID::UpdateMouseKeyboardCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateMouseKeyboard(ns_late); - return std::nullopt; - }); - motion_update_event = Core::Timing::CreateEvent( - "HID::UpdateMotionCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateMotion(ns_late); - return std::nullopt; - }); - - system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); - system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, - default_update_event); - system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, - mouse_keyboard_update_event); - system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, - motion_update_event); -} - -IAppletResource::~IAppletResource() { - system.CoreTiming().UnscheduleEvent(npad_update_event); - system.CoreTiming().UnscheduleEvent(default_update_event); - system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); - system.CoreTiming().UnscheduleEvent(motion_update_event); - resource_manager->FreeAppletResourceId(aruid); -} - -void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { - Kernel::KSharedMemory* handle; - const auto result = resource_manager->GetSharedMemoryHandle(&handle, aruid); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid, result.raw); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(result); - rb.PushCopyObjects(handle); -} - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h deleted file mode 100644 index 70d9b6550..000000000 --- a/src/core/hle/service/hid/resource_manager.h +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Core::Timing { -struct EventType; -} - -namespace Kernel { -class KSharedMemory; -} - -namespace Service::HID { -class AppletResource; -class CaptureButton; -class Controller_Stubbed; -class ConsoleSixAxis; -class DebugMouse; -class DebugPad; -class Digitizer; -class Gesture; -class HomeButton; -class Keyboard; -class Mouse; -class NPad; -class Palma; -class SevenSixAxis; -class SixAxis; -class SleepButton; -class TouchScreen; -class UniquePad; - -class ResourceManager { - -public: - explicit ResourceManager(Core::System& system_); - ~ResourceManager(); - - void Initialize(); - - std::shared_ptr<AppletResource> GetAppletResource() const; - std::shared_ptr<CaptureButton> GetCaptureButton() const; - std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const; - std::shared_ptr<DebugMouse> GetDebugMouse() const; - std::shared_ptr<DebugPad> GetDebugPad() const; - std::shared_ptr<Digitizer> GetDigitizer() const; - std::shared_ptr<Gesture> GetGesture() const; - std::shared_ptr<HomeButton> GetHomeButton() const; - std::shared_ptr<Keyboard> GetKeyboard() const; - std::shared_ptr<Mouse> GetMouse() const; - std::shared_ptr<NPad> GetNpad() const; - std::shared_ptr<Palma> GetPalma() const; - std::shared_ptr<SevenSixAxis> GetSevenSixAxis() const; - std::shared_ptr<SixAxis> GetSixAxis() const; - std::shared_ptr<SleepButton> GetSleepButton() const; - std::shared_ptr<TouchScreen> GetTouchScreen() const; - std::shared_ptr<UniquePad> GetUniquePad() const; - - Result CreateAppletResource(u64 aruid); - - Result RegisterCoreAppletResource(); - Result UnregisterCoreAppletResource(); - Result RegisterAppletResourceUserId(u64 aruid, bool bool_value); - void UnregisterAppletResourceUserId(u64 aruid); - - Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid); - void FreeAppletResourceId(u64 aruid); - - void EnableInput(u64 aruid, bool is_enabled); - void EnableSixAxisSensor(u64 aruid, bool is_enabled); - void EnablePadInput(u64 aruid, bool is_enabled); - void EnableTouchScreen(u64 aruid, bool is_enabled); - - void UpdateControllers(std::chrono::nanoseconds ns_late); - void UpdateNpad(std::chrono::nanoseconds ns_late); - void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); - void UpdateMotion(std::chrono::nanoseconds ns_late); - -private: - Result CreateAppletResourceImpl(u64 aruid); - void InitializeHidCommonSampler(); - void InitializeTouchScreenSampler(); - void InitializeConsoleSixAxisSampler(); - void InitializeAHidSampler(); - - bool is_initialized{false}; - - mutable std::mutex shared_mutex; - std::shared_ptr<AppletResource> applet_resource = nullptr; - - std::shared_ptr<CaptureButton> capture_button = nullptr; - std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; - std::shared_ptr<DebugMouse> debug_mouse = nullptr; - std::shared_ptr<DebugPad> debug_pad = nullptr; - std::shared_ptr<Digitizer> digitizer = nullptr; - std::shared_ptr<Gesture> gesture = nullptr; - std::shared_ptr<HomeButton> home_button = nullptr; - std::shared_ptr<Keyboard> keyboard = nullptr; - std::shared_ptr<Mouse> mouse = nullptr; - std::shared_ptr<NPad> npad = nullptr; - std::shared_ptr<Palma> palma = nullptr; - std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr; - std::shared_ptr<SixAxis> six_axis = nullptr; - std::shared_ptr<SleepButton> sleep_button = nullptr; - std::shared_ptr<TouchScreen> touch_screen = nullptr; - std::shared_ptr<UniquePad> unique_pad = nullptr; - - // TODO: Create these resources - // std::shared_ptr<AudioControl> audio_control = nullptr; - // std::shared_ptr<ButtonConfig> button_config = nullptr; - // std::shared_ptr<Config> config = nullptr; - // std::shared_ptr<Connection> connection = nullptr; - // std::shared_ptr<CustomConfig> custom_config = nullptr; - // std::shared_ptr<Digitizer> digitizer = nullptr; - // std::shared_ptr<Hdls> hdls = nullptr; - // std::shared_ptr<PlayReport> play_report = nullptr; - // std::shared_ptr<Rail> rail = nullptr; - - Core::System& system; - KernelHelpers::ServiceContext service_context; -}; - -class IAppletResource final : public ServiceFramework<IAppletResource> { -public: - explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource, - u64 applet_resource_user_id); - ~IAppletResource() override; - -private: - void GetSharedMemoryHandle(HLERequestContext& ctx); - - std::shared_ptr<Core::Timing::EventType> npad_update_event; - std::shared_ptr<Core::Timing::EventType> default_update_event; - std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; - std::shared_ptr<Core::Timing::EventType> motion_update_event; - - u64 aruid; - std::shared_ptr<ResourceManager> resource_manager; -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h deleted file mode 100644 index 0816784e0..000000000 --- a/src/core/hle/service/hid/ring_lifo.h +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_types.h" - -namespace Service::HID { - -template <typename State> -struct AtomicStorage { - s64 sampling_number; - State state; -}; - -template <typename State, std::size_t max_buffer_size> -struct Lifo { - s64 timestamp{}; - s64 total_buffer_count = static_cast<s64>(max_buffer_size); - s64 buffer_tail{}; - s64 buffer_count{}; - std::array<AtomicStorage<State>, max_buffer_size> entries{}; - - const AtomicStorage<State>& ReadCurrentEntry() const { - return entries[buffer_tail]; - } - - const AtomicStorage<State>& ReadPreviousEntry() const { - return entries[GetPreviousEntryIndex()]; - } - - std::size_t GetPreviousEntryIndex() const { - return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size); - } - - std::size_t GetNextEntryIndex() const { - return static_cast<size_t>((buffer_tail + 1) % max_buffer_size); - } - - void WriteNextEntry(const State& new_state) { - if (buffer_count < static_cast<s64>(max_buffer_size) - 1) { - buffer_count++; - } - buffer_tail = GetNextEntryIndex(); - const auto& previous_entry = ReadPreviousEntry(); - entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1; - entries[buffer_tail].state = new_state; - } -}; - -} // namespace Service::HID diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 39df77e43..50e1ed756 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/scratch_buffer.h" +#include "core/guest_memory.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" @@ -23,19 +24,6 @@ #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" -namespace { -static thread_local std::array read_buffer_data_a{ - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), -}; -static thread_local std::array read_buffer_data_x{ - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), -}; -} // Anonymous namespace - namespace Service { SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_) @@ -181,22 +169,22 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { } } - buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); - buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); - buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); - buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); + buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors); + buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors); + buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors); + buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors); for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { - buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); + buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); } for (u32 i = 0; i < command_header->num_buf_a_descriptors; ++i) { - buffer_a_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); + buffer_a_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); } for (u32 i = 0; i < command_header->num_buf_b_descriptors; ++i) { - buffer_b_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); + buffer_b_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); } for (u32 i = 0; i < command_header->num_buf_w_descriptors; ++i) { - buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); + buffer_w_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); } const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size; @@ -246,7 +234,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) { if (command_header->buf_c_descriptor_flags == IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) { - buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); + buffer_c_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); } else { u32 num_buf_c_descriptors = static_cast<u32>(command_header->buf_c_descriptor_flags.Value()) - 2; @@ -256,7 +244,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { ASSERT(num_buf_c_descriptors < 14); for (u32 i = 0; i < num_buf_c_descriptors; ++i) { - buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); + buffer_c_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); } } } @@ -343,48 +331,27 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons } std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const { - static thread_local std::array read_buffer_a{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0); ASSERT_OR_EXECUTE_MSG( BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_a[buffer_index]; - return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), - BufferDescriptorA()[buffer_index].Size(), - &read_buffer_data_a[buffer_index]); + return gm.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]); } std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const { - static thread_local std::array read_buffer_x{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0); ASSERT_OR_EXECUTE_MSG( BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_x[buffer_index]; - return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), - BufferDescriptorX()[buffer_index].Size(), - &read_buffer_data_x[buffer_index]); + return gm.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]); } std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - static thread_local std::array read_buffer_a{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; - static thread_local std::array read_buffer_x{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0); const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; @@ -401,18 +368,14 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons ASSERT_OR_EXECUTE_MSG( BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_a[buffer_index]; - return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), - BufferDescriptorA()[buffer_index].Size(), - &read_buffer_data_a[buffer_index]); + return gm.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]); } else { ASSERT_OR_EXECUTE_MSG( BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_x[buffer_index]; - return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), - BufferDescriptorX()[buffer_index].Size(), - &read_buffer_data_x[buffer_index]); + return gm.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]); } } @@ -538,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const { } } +void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) { + ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve( + Kernel::LimitableResource::SessionCountMax, 1)); + + auto* session = Kernel::KSession::Create(kernel); + session->Initialize(nullptr, 0); + Kernel::KSession::Register(kernel, session); + + auto& server = manager.lock()->GetServerManager(); + auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server); + next_manager->SetSessionHandler(std::move(s)); + server.RegisterSession(&session->GetServerSession(), next_manager); + + AddMoveObject(&session->GetClientSession()); +} + std::string HLERequestContext::Description() const { if (!command_header) { return "No command header available"; diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 40d86943e..c2e0e5e8c 100644 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -41,6 +41,8 @@ class KernelCore; class KHandleTable; class KProcess; class KServerSession; +template <typename T> +class KScopedAutoObject; class KThread; } // namespace Kernel @@ -232,19 +234,19 @@ public: } [[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const { - return buffer_x_desciptors; + return buffer_x_descriptors; } [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const { - return buffer_a_desciptors; + return buffer_a_descriptors; } [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const { - return buffer_b_desciptors; + return buffer_b_descriptors; } [[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const { - return buffer_c_desciptors; + return buffer_c_descriptors; } [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const { @@ -337,6 +339,8 @@ public: outgoing_move_objects.emplace_back(object); } + void AddMoveInterface(SessionRequestHandlerPtr s); + void AddCopyObject(Kernel::KAutoObject* object) { outgoing_copy_objects.emplace_back(object); } @@ -406,11 +410,11 @@ private: std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header; std::optional<IPC::DataPayloadHeader> data_payload_header; std::optional<IPC::DomainMessageHeader> domain_message_header; - std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; - std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; - std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; - std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors; - std::vector<IPC::BufferDescriptorC> buffer_c_desciptors; + std::vector<IPC::BufferDescriptorX> buffer_x_descriptors; + std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors; + std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors; + std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors; + std::vector<IPC::BufferDescriptorC> buffer_c_descriptors; u32_le command{}; u64 pid{}; @@ -424,6 +428,9 @@ private: Kernel::KernelCore& kernel; Core::Memory::Memory& memory; + + mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{}; + mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{}; }; } // namespace Service diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 77aa6d7d1..1f2cbcb61 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -6,12 +6,12 @@ #include "core/core.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/jit/jit.h" #include "core/hle/service/jit/jit_code_memory.h" #include "core/hle/service/jit/jit_context.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" #include "core/memory.h" namespace Service::JIT { @@ -21,20 +21,24 @@ struct CodeRange { u64 size; }; +using Struct32 = std::array<u64, 4>; +static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size"); + class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { public: explicit IJitEnvironment(Core::System& system_, - Kernel::KScopedAutoObject<Kernel::KProcess>&& process_, + Kernel::KScopedAutoObject<Kernel::KProcess> process_, CodeMemory&& user_rx_, CodeMemory&& user_ro_) : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)}, user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)}, context{system_.ApplicationMemory()} { + // clang-format off static const FunctionInfo functions[] = { - {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, - {1, &IJitEnvironment::Control, "Control"}, - {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, - {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, + {0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"}, + {1, C<&IJitEnvironment::Control>, "Control"}, + {1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"}, + {1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"}, }; // clang-format on @@ -50,28 +54,10 @@ public: configuration.sys_ro_memory = configuration.user_ro_memory; } - void GenerateCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct InputParameters { - u32 data_size; - u64 command; - std::array<CodeRange, 2> ranges; - Struct32 data; - }; - - struct OutputParameters { - s32 return_value; - std::array<CodeRange, 2> ranges; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw<InputParameters>()}; - - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; - std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); - + Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0, + Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, + u32 data_size, u64 command, CodeRange range0, CodeRange range1, + Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) { // Function call prototype: // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, @@ -83,66 +69,36 @@ public: // other arguments are used to transfer state between the game and the plugin. const VAddr ret_ptr{context.AddHeap(0u)}; - const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; - const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; - const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; - const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; - - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; - const VAddr data_ptr{context.AddHeap(parameters.data)}; + const VAddr c0_in_ptr{context.AddHeap(range0)}; + const VAddr c1_in_ptr{context.AddHeap(range1)}; + const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))}; + const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))}; + + const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())}; + const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())}; + const VAddr data_ptr{context.AddHeap(data)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; // The callback does not directly return a value, it only writes to the output pointer context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, - configuration_ptr, parameters.command, input_ptr, input_buffer.size(), - c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, - output_buffer.size()); - - const s32 return_value{context.GetHeap<s32>(ret_ptr)}; - - if (return_value == 0) { - // The callback has written to the output executable code range, - // requiring an instruction cache invalidation - Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(), - configuration.user_rx_memory.offset, - configuration.user_rx_memory.size); - - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - const OutputParameters out{ - .return_value = return_value, - .ranges = - { - context.GetHeap<CodeRange>(c0_out_ptr), - context.GetHeap<CodeRange>(c1_out_ptr), - }, - }; - - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(out); - } else { - LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } - }; + configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr, + c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size()); - void Control(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); + *out_return_value = context.GetHeap<s32>(ret_ptr); + *out_range0 = context.GetHeap<CodeRange>(c0_out_ptr); + *out_range1 = context.GetHeap<CodeRange>(c1_out_ptr); + context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size()); - IPC::RequestParser rp{ctx}; - const auto command{rp.PopRaw<u64>()}; + if (*out_return_value != 0) { + LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); + R_THROW(ResultUnknown); + } - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; - std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + R_SUCCEED(); + } + Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data, + OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) { // Function call prototype: // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, // u8* output_buf, size_t output_size); @@ -152,53 +108,30 @@ public: const VAddr ret_ptr{context.AddHeap(0u)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())}; + const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())}; const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, - command, input_ptr, input_buffer.size(), - output_ptr, output_buffer.size())}; - - const s32 return_value{context.GetHeap<s32>(ret_ptr)}; - - if (wrapper_value == 0 && return_value == 0) { - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(return_value); - } else { - LOG_WARNING(Service_JIT, "plugin Control callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } - } - - void LoadPlugin(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); + command, input_ptr, in_data.size(), output_ptr, + out_data.size())}; - IPC::RequestParser rp{ctx}; - const auto tmem_size{rp.PopRaw<u64>()}; - const auto tmem_handle{ctx.GetCopyHandle(0)}; - const auto nro_plugin{ctx.ReadBuffer(1)}; + *out_return_value = context.GetHeap<s32>(ret_ptr); + context.GetHeap(output_ptr, out_data.data(), out_data.size()); - if (tmem_size == 0) { - LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (wrapper_value == 0 && *out_return_value == 0) { + R_SUCCEED(); } - auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)}; - if (tmem.IsNull()) { - LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_WARNING(Service_JIT, "plugin Control callback failed"); + R_THROW(ResultUnknown); + } + + Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem, + InBuffer<BufferAttr_HipcMapAlias> nrr, + InBuffer<BufferAttr_HipcMapAlias> nro) { + if (!tmem) { + LOG_ERROR(Service_JIT, "Invalid transfer memory handle!"); + R_THROW(ResultUnknown); } // Set up the configuration with the required TransferMemory address @@ -206,7 +139,7 @@ public: configuration.transfer_memory.size = tmem_size; // Gather up all the callbacks from the loaded plugin - auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; + auto symbols{Core::Symbols::GetSymbols(nro, true)}; const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; callbacks.rtld_fini = GetSymbol("_fini"); @@ -223,16 +156,12 @@ public: if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || callbacks.OnPrepared == 0) { LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } - if (!context.LoadNRO(nro_plugin)) { + if (!context.LoadNRO(nro)) { LOG_ERROR(Service_JIT, "failed to load plugin"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } context.MapProcessMemory(configuration.sys_ro_memory.offset, @@ -252,9 +181,7 @@ public: const auto version{context.CallFunction(callbacks.GetVersion)}; if (version != 1) { LOG_ERROR(Service_JIT, "unknown plugin version {}", version); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } // Function prototype: @@ -280,22 +207,19 @@ public: const auto configuration_ptr{context.AddHeap(configuration)}; context.CallFunction(callbacks.OnPrepared, configuration_ptr); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void GetCodeAddress(HLERequestContext& ctx) { + Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) { LOG_DEBUG(Service_JIT, "called"); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(configuration.user_rx_memory.offset); - rb.Push(configuration.user_ro_memory.offset); + *rx_offset = configuration.user_rx_memory.offset; + *ro_offset = configuration.user_ro_memory.offset; + + R_SUCCEED(); } private: - using Struct32 = std::array<u8, 32>; - struct GuestCallbacks { VAddr rtld_fini; VAddr rtld_init; @@ -335,7 +259,7 @@ public: explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + {0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"}, }; // clang-format on @@ -343,76 +267,33 @@ public: } private: - void CreateJitEnvironment(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct Parameters { - u64 rx_size; - u64 ro_size; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw<Parameters>()}; - const auto process_handle{ctx.GetCopyHandle(0)}; - const auto rx_mem_handle{ctx.GetCopyHandle(1)}; - const auto ro_mem_handle{ctx.GetCopyHandle(2)}; - - if (parameters.rx_size == 0 || parameters.ro_size == 0) { - LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)}; - if (process.IsNull()) { - LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, + u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process, + InCopyHandle<Kernel::KCodeMemory>& rx_mem, + InCopyHandle<Kernel::KCodeMemory>& ro_mem) { + if (!process) { + LOG_ERROR(Service_JIT, "process is null"); + R_THROW(ResultUnknown); } - - auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)}; - if (rx_mem.IsNull()) { - LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (!rx_mem) { + LOG_ERROR(Service_JIT, "rx_mem is null"); + R_THROW(ResultUnknown); } - - auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)}; - if (ro_mem.IsNull()) { - LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (!ro_mem) { + LOG_ERROR(Service_JIT, "ro_mem is null"); + R_THROW(ResultUnknown); } CodeMemory rx, ro; - Result res; - - res = rx.Initialize(*process, *rx_mem, parameters.rx_size, - Kernel::Svc::MemoryPermission::ReadExecute, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } - res = ro.Initialize(*process, *ro_mem, parameters.ro_size, - Kernel::Svc::MemoryPermission::Read, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } + R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute, + generate_random)); + R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read, + generate_random)); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx), - std::move(ro)); + *out_jit_environment = + std::make_shared<IJitEnvironment>(system, process.Get(), std::move(rx), std::move(ro)); + R_SUCCEED(); } private: diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index f51e63564..f080f7ffa 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -65,6 +65,9 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } void ServiceContext::CloseEvent(Kernel::KEvent* event) { + if (!event) { + return; + } event->GetReadableEvent().Close(); event->Close(); } diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index c28eed926..b4d16fed5 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -4,15 +4,18 @@ #include <memory> #include "common/logging/log.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/mii/mii.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_result.h" #include "core/hle/service/mii/types/char_info.h" +#include "core/hle/service/mii/types/raw_data.h" #include "core/hle/service/mii/types/store_data.h" #include "core/hle/service/mii/types/ver3_store_data.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" namespace Service::Mii { @@ -24,549 +27,302 @@ public: is_system_} { // clang-format off static const FunctionInfo functions[] = { - {0, &IDatabaseService::IsUpdated, "IsUpdated"}, - {1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"}, - {2, &IDatabaseService::GetCount, "GetCount"}, - {3, &IDatabaseService::Get, "Get"}, - {4, &IDatabaseService::Get1, "Get1"}, - {5, &IDatabaseService::UpdateLatest, "UpdateLatest"}, - {6, &IDatabaseService::BuildRandom, "BuildRandom"}, - {7, &IDatabaseService::BuildDefault, "BuildDefault"}, - {8, &IDatabaseService::Get2, "Get2"}, - {9, &IDatabaseService::Get3, "Get3"}, - {10, &IDatabaseService::UpdateLatest1, "UpdateLatest1"}, - {11, &IDatabaseService::FindIndex, "FindIndex"}, - {12, &IDatabaseService::Move, "Move"}, - {13, &IDatabaseService::AddOrReplace, "AddOrReplace"}, - {14, &IDatabaseService::Delete, "Delete"}, - {15, &IDatabaseService::DestroyFile, "DestroyFile"}, - {16, &IDatabaseService::DeleteFile, "DeleteFile"}, - {17, &IDatabaseService::Format, "Format"}, + {0, D<&IDatabaseService::IsUpdated>, "IsUpdated"}, + {1, D<&IDatabaseService::IsFullDatabase>, "IsFullDatabase"}, + {2, D<&IDatabaseService::GetCount>, "GetCount"}, + {3, D<&IDatabaseService::Get>, "Get"}, + {4, D<&IDatabaseService::Get1>, "Get1"}, + {5, D<&IDatabaseService::UpdateLatest>, "UpdateLatest"}, + {6, D<&IDatabaseService::BuildRandom>, "BuildRandom"}, + {7, D<&IDatabaseService::BuildDefault>, "BuildDefault"}, + {8, D<&IDatabaseService::Get2>, "Get2"}, + {9, D<&IDatabaseService::Get3>, "Get3"}, + {10, D<&IDatabaseService::UpdateLatest1>, "UpdateLatest1"}, + {11, D<&IDatabaseService::FindIndex>, "FindIndex"}, + {12, D<&IDatabaseService::Move>, "Move"}, + {13, D<&IDatabaseService::AddOrReplace>, "AddOrReplace"}, + {14, D<&IDatabaseService::Delete>, "Delete"}, + {15, D<&IDatabaseService::DestroyFile>, "DestroyFile"}, + {16, D<&IDatabaseService::DeleteFile>, "DeleteFile"}, + {17, D<&IDatabaseService::Format>, "Format"}, {18, nullptr, "Import"}, {19, nullptr, "Export"}, - {20, &IDatabaseService::IsBrokenDatabaseWithClearFlag, "IsBrokenDatabaseWithClearFlag"}, - {21, &IDatabaseService::GetIndex, "GetIndex"}, - {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, - {23, &IDatabaseService::Convert, "Convert"}, - {24, &IDatabaseService::ConvertCoreDataToCharInfo, "ConvertCoreDataToCharInfo"}, - {25, &IDatabaseService::ConvertCharInfoToCoreData, "ConvertCharInfoToCoreData"}, - {26, &IDatabaseService::Append, "Append"}, + {20, D<&IDatabaseService::IsBrokenDatabaseWithClearFlag>, "IsBrokenDatabaseWithClearFlag"}, + {21, D<&IDatabaseService::GetIndex>, "GetIndex"}, + {22, D<&IDatabaseService::SetInterfaceVersion>, "SetInterfaceVersion"}, + {23, D<&IDatabaseService::Convert>, "Convert"}, + {24, D<&IDatabaseService::ConvertCoreDataToCharInfo>, "ConvertCoreDataToCharInfo"}, + {25, D<&IDatabaseService::ConvertCharInfoToCoreData>, "ConvertCharInfoToCoreData"}, + {26, D<&IDatabaseService::Append>, "Append"}, }; // clang-format on RegisterHandlers(functions); + m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>( + "set:sys", true); manager->Initialize(metadata); } private: - void IsUpdated(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - + Result IsUpdated(Out<bool> out_is_updated, SourceFlag source_flag) { LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); - const bool is_updated = manager->IsUpdated(metadata, source_flag); + *out_is_updated = manager->IsUpdated(metadata, source_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(is_updated); + R_SUCCEED(); } - void IsFullDatabase(HLERequestContext& ctx) { + Result IsFullDatabase(Out<bool> out_is_full_database) { LOG_DEBUG(Service_Mii, "called"); - const bool is_full_database = manager->IsFullDatabase(); + *out_is_full_database = manager->IsFullDatabase(); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(is_full_database); + R_SUCCEED(); } - void GetCount(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - - const u32 mii_count = manager->GetCount(metadata, source_flag); + Result GetCount(Out<u32> out_mii_count, SourceFlag source_flag) { + *out_mii_count = manager->GetCount(metadata, source_flag); - LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count); + LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, + *out_mii_count); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(mii_count); + R_SUCCEED(); } - void Get(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()}; - - u32 mii_count{}; - std::vector<CharInfoElement> char_info_elements(output_size); - const auto result = manager->Get(metadata, char_info_elements, mii_count, source_flag); - - if (mii_count != 0) { - ctx.WriteBuffer(char_info_elements); - } + Result Get(Out<u32> out_mii_count, SourceFlag source_flag, + OutArray<CharInfoElement, BufferAttr_HipcMapAlias> char_info_element_buffer) { + const auto result = + manager->Get(metadata, char_info_element_buffer, *out_mii_count, source_flag); - LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag, - output_size, mii_count); + LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, + *out_mii_count); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(mii_count); + R_RETURN(result); } - void Get1(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()}; + Result Get1(Out<u32> out_mii_count, SourceFlag source_flag, + OutArray<CharInfo, BufferAttr_HipcMapAlias> char_info_buffer) { + const auto result = manager->Get(metadata, char_info_buffer, *out_mii_count, source_flag); - u32 mii_count{}; - std::vector<CharInfo> char_info(output_size); - const auto result = manager->Get(metadata, char_info, mii_count, source_flag); + LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, + *out_mii_count); - if (mii_count != 0) { - ctx.WriteBuffer(char_info); - } - - LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag, - output_size, mii_count); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(mii_count); + R_RETURN(result); } - void UpdateLatest(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto char_info{rp.PopRaw<CharInfo>()}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - + Result UpdateLatest(Out<CharInfo> out_char_info, CharInfo& char_info, SourceFlag source_flag) { LOG_INFO(Service_Mii, "called with source_flag={}", source_flag); - CharInfo new_char_info{}; - const auto result = manager->UpdateLatest(metadata, new_char_info, char_info, source_flag); - if (result.IsFailure()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw(new_char_info); + R_RETURN(manager->UpdateLatest(metadata, *out_char_info, char_info, source_flag)); } - void BuildRandom(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto age{rp.PopRaw<Age>()}; - const auto gender{rp.PopRaw<Gender>()}; - const auto race{rp.PopRaw<Race>()}; - + Result BuildRandom(Out<CharInfo> out_char_info, Age age, Gender gender, Race race) { LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race); - if (age > Age::All) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultInvalidArgument); - return; - } - - if (gender > Gender::All) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultInvalidArgument); - return; - } - - if (race > Race::All) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultInvalidArgument); - return; - } - - CharInfo char_info{}; - manager->BuildRandom(char_info, age, gender, race); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw(char_info); - } + R_UNLESS(age <= Age::All, ResultInvalidArgument); + R_UNLESS(gender <= Gender::All, ResultInvalidArgument); + R_UNLESS(race <= Race::All, ResultInvalidArgument); - void BuildDefault(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto index{rp.Pop<u32>()}; + manager->BuildRandom(*out_char_info, age, gender, race); - LOG_DEBUG(Service_Mii, "called with index={}", index); + R_SUCCEED(); + } - if (index > 5) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultInvalidArgument); - return; - } + Result BuildDefault(Out<CharInfo> out_char_info, s32 index) { + LOG_DEBUG(Service_Mii, "called with index={}", index); + R_UNLESS(index < static_cast<s32>(RawData::DefaultMii.size()), ResultInvalidArgument); - CharInfo char_info{}; - manager->BuildDefault(char_info, index); + manager->BuildDefault(*out_char_info, index); - IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw(char_info); + R_SUCCEED(); } - void Get2(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - const auto output_size{ctx.GetWriteBufferNumElements<StoreDataElement>()}; + Result Get2(Out<u32> out_mii_count, SourceFlag source_flag, + OutArray<StoreDataElement, BufferAttr_HipcMapAlias> store_data_element_buffer) { + const auto result = + manager->Get(metadata, store_data_element_buffer, *out_mii_count, source_flag); - u32 mii_count{}; - std::vector<StoreDataElement> store_data_elements(output_size); - const auto result = manager->Get(metadata, store_data_elements, mii_count, source_flag); + LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, + *out_mii_count); - if (mii_count != 0) { - ctx.WriteBuffer(store_data_elements); - } - - LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag, - output_size, mii_count); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(mii_count); + R_RETURN(result); } - void Get3(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - const auto output_size{ctx.GetWriteBufferNumElements<StoreData>()}; - - u32 mii_count{}; - std::vector<StoreData> store_data(output_size); - const auto result = manager->Get(metadata, store_data, mii_count, source_flag); + Result Get3(Out<u32> out_mii_count, SourceFlag source_flag, + OutArray<StoreData, BufferAttr_HipcMapAlias> store_data_buffer) { + const auto result = manager->Get(metadata, store_data_buffer, *out_mii_count, source_flag); - if (mii_count != 0) { - ctx.WriteBuffer(store_data); - } + LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, + *out_mii_count); - LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag, - output_size, mii_count); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(mii_count); + R_RETURN(result); } - void UpdateLatest1(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto store_data{rp.PopRaw<StoreData>()}; - const auto source_flag{rp.PopRaw<SourceFlag>()}; - + Result UpdateLatest1(Out<StoreData> out_store_data, StoreData& store_data, + SourceFlag source_flag) { LOG_INFO(Service_Mii, "called with source_flag={}", source_flag); + R_UNLESS(is_system, ResultPermissionDenied); - Result result = ResultSuccess; - if (!is_system) { - result = ResultPermissionDenied; - } - - StoreData new_store_data{}; - if (result.IsSuccess()) { - result = manager->UpdateLatest(metadata, new_store_data, store_data, source_flag); - } - - if (result.IsFailure()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(StoreData) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw<StoreData>(new_store_data); + R_RETURN(manager->UpdateLatest(metadata, *out_store_data, store_data, source_flag)); } - void FindIndex(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto create_id{rp.PopRaw<Common::UUID>()}; - const auto is_special{rp.PopRaw<bool>()}; - + Result FindIndex(Out<s32> out_index, Common::UUID create_id, bool is_special) { LOG_INFO(Service_Mii, "called with create_id={}, is_special={}", create_id.FormattedString(), is_special); - const s32 index = manager->FindIndex(create_id, is_special); + *out_index = manager->FindIndex(create_id, is_special); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(index); + R_SUCCEED(); } - void Move(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto create_id{rp.PopRaw<Common::UUID>()}; - const auto new_index{rp.PopRaw<s32>()}; - + Result Move(Common::UUID create_id, s32 new_index) { LOG_INFO(Service_Mii, "called with create_id={}, new_index={}", create_id.FormattedString(), new_index); + R_UNLESS(is_system, ResultPermissionDenied); - Result result = ResultSuccess; - if (!is_system) { - result = ResultPermissionDenied; - } + const u32 count = manager->GetCount(metadata, SourceFlag::Database); - if (result.IsSuccess()) { - const u32 count = manager->GetCount(metadata, SourceFlag::Database); - if (new_index < 0 || new_index >= static_cast<s32>(count)) { - result = ResultInvalidArgument; - } - } + R_UNLESS(new_index >= 0 && new_index < static_cast<s32>(count), ResultInvalidArgument); - if (result.IsSuccess()) { - result = manager->Move(metadata, new_index, create_id); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(manager->Move(metadata, new_index, create_id)); } - void AddOrReplace(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto store_data{rp.PopRaw<StoreData>()}; - + Result AddOrReplace(StoreData& store_data) { LOG_INFO(Service_Mii, "called"); + R_UNLESS(is_system, ResultPermissionDenied); - Result result = ResultSuccess; - - if (!is_system) { - result = ResultPermissionDenied; - } + const auto result = manager->AddOrReplace(metadata, store_data); - if (result.IsSuccess()) { - result = manager->AddOrReplace(metadata, store_data); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(result); } - void Delete(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto create_id{rp.PopRaw<Common::UUID>()}; - + Result Delete(Common::UUID create_id) { LOG_INFO(Service_Mii, "called, create_id={}", create_id.FormattedString()); + R_UNLESS(is_system, ResultPermissionDenied); - Result result = ResultSuccess; - - if (!is_system) { - result = ResultPermissionDenied; - } - - if (result.IsSuccess()) { - result = manager->Delete(metadata, create_id); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(manager->Delete(metadata, create_id)); } - void DestroyFile(HLERequestContext& ctx) { - // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled"); - const bool is_db_test_mode_enabled = false; + Result DestroyFile() { + bool is_db_test_mode_enabled{}; + m_set_sys->GetSettingsItemValue(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); - Result result = ResultSuccess; - - if (!is_db_test_mode_enabled) { - result = ResultTestModeOnly; - } - - if (result.IsSuccess()) { - result = manager->DestroyFile(metadata); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(manager->DestroyFile(metadata)); } - void DeleteFile(HLERequestContext& ctx) { - // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled"); - const bool is_db_test_mode_enabled = false; + Result DeleteFile() { + bool is_db_test_mode_enabled{}; + m_set_sys->GetSettingsItemValue(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); - Result result = ResultSuccess; - - if (!is_db_test_mode_enabled) { - result = ResultTestModeOnly; - } - - if (result.IsSuccess()) { - result = manager->DeleteFile(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(manager->DeleteFile()); } - void Format(HLERequestContext& ctx) { - // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled"); - const bool is_db_test_mode_enabled = false; + Result Format() { + bool is_db_test_mode_enabled{}; + m_set_sys->GetSettingsItemValue(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); - Result result = ResultSuccess; - - if (!is_db_test_mode_enabled) { - result = ResultTestModeOnly; - } - - if (result.IsSuccess()) { - result = manager->Format(metadata); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(manager->Format(metadata)); } - void IsBrokenDatabaseWithClearFlag(HLERequestContext& ctx) { + Result IsBrokenDatabaseWithClearFlag(Out<bool> out_is_broken_with_clear_flag) { LOG_DEBUG(Service_Mii, "called"); + R_UNLESS(is_system, ResultPermissionDenied); - bool is_broken_with_clear_flag = false; - Result result = ResultSuccess; + *out_is_broken_with_clear_flag = manager->IsBrokenWithClearFlag(metadata); - if (!is_system) { - result = ResultPermissionDenied; - } - - if (result.IsSuccess()) { - is_broken_with_clear_flag = manager->IsBrokenWithClearFlag(metadata); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push<u8>(is_broken_with_clear_flag); + R_SUCCEED(); } - void GetIndex(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw<CharInfo>()}; - + Result GetIndex(Out<s32> out_index, CharInfo& char_info) { LOG_DEBUG(Service_Mii, "called"); - s32 index{}; - const auto result = manager->GetIndex(metadata, info, index); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(index); + R_RETURN(manager->GetIndex(metadata, char_info, *out_index)); } - void SetInterfaceVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto interface_version{rp.PopRaw<u32>()}; - + Result SetInterfaceVersion(u32 interface_version) { LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version); manager->SetInterfaceVersion(metadata, interface_version); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void Convert(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; - + Result Convert(Out<CharInfo> out_char_info, Ver3StoreData& mii_v3) { LOG_INFO(Service_Mii, "called"); - CharInfo char_info{}; - const auto result = manager->ConvertV3ToCharInfo(char_info, mii_v3); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; - rb.Push(result); - rb.PushRaw<CharInfo>(char_info); + R_RETURN(manager->ConvertV3ToCharInfo(*out_char_info, mii_v3)); } - void ConvertCoreDataToCharInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto core_data{rp.PopRaw<CoreData>()}; - + Result ConvertCoreDataToCharInfo(Out<CharInfo> out_char_info, CoreData& core_data) { LOG_INFO(Service_Mii, "called"); - CharInfo char_info{}; - const auto result = manager->ConvertCoreDataToCharInfo(char_info, core_data); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; - rb.Push(result); - rb.PushRaw<CharInfo>(char_info); + R_RETURN(manager->ConvertCoreDataToCharInfo(*out_char_info, core_data)); } - void ConvertCharInfoToCoreData(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto char_info{rp.PopRaw<CharInfo>()}; - + Result ConvertCharInfoToCoreData(Out<CoreData> out_core_data, CharInfo& char_info) { LOG_INFO(Service_Mii, "called"); - CoreData core_data{}; - const auto result = manager->ConvertCharInfoToCoreData(core_data, char_info); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)}; - rb.Push(result); - rb.PushRaw<CoreData>(core_data); + R_RETURN(manager->ConvertCharInfoToCoreData(*out_core_data, char_info)); } - void Append(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto char_info{rp.PopRaw<CharInfo>()}; - + Result Append(CharInfo& char_info) { LOG_INFO(Service_Mii, "called"); - const auto result = manager->Append(metadata, char_info); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(manager->Append(metadata, char_info)); } std::shared_ptr<MiiManager> manager = nullptr; DatabaseSessionMetadata metadata{}; bool is_system{}; + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; }; -MiiDBModule::MiiDBModule(Core::System& system_, const char* name_, - std::shared_ptr<MiiManager> mii_manager, bool is_system_) +IStaticService::IStaticService(Core::System& system_, const char* name_, + std::shared_ptr<MiiManager> mii_manager, bool is_system_) : ServiceFramework{system_, name_}, manager{mii_manager}, is_system{is_system_} { // clang-format off static const FunctionInfo functions[] = { - {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, + {0, D<&IStaticService::GetDatabaseService>, "GetDatabaseService"}, }; // clang-format on RegisterHandlers(functions); - - if (manager == nullptr) { - manager = std::make_shared<MiiManager>(); - } } -MiiDBModule::~MiiDBModule() = default; - -void MiiDBModule::GetDatabaseService(HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDatabaseService>(system, manager, is_system); +IStaticService::~IStaticService() = default; +Result IStaticService::GetDatabaseService( + Out<SharedPointer<IDatabaseService>> out_database_service) { LOG_DEBUG(Service_Mii, "called"); + + *out_database_service = std::make_shared<IDatabaseService>(system, manager, is_system); + + R_SUCCEED(); } -std::shared_ptr<MiiManager> MiiDBModule::GetMiiManager() { +std::shared_ptr<MiiManager> IStaticService::GetMiiManager() { return manager; } -class MiiImg final : public ServiceFramework<MiiImg> { +class IImageDatabaseService final : public ServiceFramework<IImageDatabaseService> { public: - explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} { + explicit IImageDatabaseService(Core::System& system_) : ServiceFramework{system_, "miiimg"} { // clang-format off static const FunctionInfo functions[] = { - {0, &MiiImg::Initialize, "Initialize"}, + {0, D<&IImageDatabaseService::Initialize>, "Initialize"}, {10, nullptr, "Reload"}, - {11, &MiiImg::GetCount, "GetCount"}, + {11, D<&IImageDatabaseService::GetCount>, "GetCount"}, {12, nullptr, "IsEmpty"}, {13, nullptr, "IsFull"}, {14, nullptr, "GetAttribute"}, @@ -585,31 +341,30 @@ public: } private: - void Initialize(HLERequestContext& ctx) { + Result Initialize() { LOG_INFO(Service_Mii, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void GetCount(HLERequestContext& ctx) { + Result GetCount(Out<u32> out_count) { LOG_DEBUG(Service_Mii, "called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); + *out_count = 0; + + R_SUCCEED(); } }; void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - std::shared_ptr<MiiManager> manager = nullptr; + std::shared_ptr<MiiManager> manager = std::make_shared<MiiManager>(); server_manager->RegisterNamedService( - "mii:e", std::make_shared<MiiDBModule>(system, "mii:e", manager, true)); + "mii:e", std::make_shared<IStaticService>(system, "mii:e", manager, true)); server_manager->RegisterNamedService( - "mii:u", std::make_shared<MiiDBModule>(system, "mii:u", manager, false)); - server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); + "mii:u", std::make_shared<IStaticService>(system, "mii:u", manager, false)); + server_manager->RegisterNamedService("miiimg", std::make_shared<IImageDatabaseService>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h index 9aa4426f6..8683ac1a5 100644 --- a/src/core/hle/service/mii/mii.h +++ b/src/core/hle/service/mii/mii.h @@ -3,7 +3,7 @@ #pragma once -#include "core/hle/service/service.h" +#include "core/hle/service/cmif_types.h" namespace Core { class System; @@ -11,19 +11,20 @@ class System; namespace Service::Mii { class MiiManager; +class IDatabaseService; -class MiiDBModule final : public ServiceFramework<MiiDBModule> { +class IStaticService final : public ServiceFramework<IStaticService> { public: - explicit MiiDBModule(Core::System& system_, const char* name_, - std::shared_ptr<MiiManager> mii_manager, bool is_system_); - ~MiiDBModule() override; + explicit IStaticService(Core::System& system_, const char* name_, + std::shared_ptr<MiiManager> mii_manager, bool is_system_); + ~IStaticService() override; std::shared_ptr<MiiManager> GetMiiManager(); private: - void GetDatabaseService(HLERequestContext& ctx); + Result GetDatabaseService(Out<SharedPointer<IDatabaseService>> out_database_service); - std::shared_ptr<MiiManager> manager = nullptr; + std::shared_ptr<MiiManager> manager{nullptr}; bool is_system{}; }; diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp index 9556e9193..4274a92c9 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp +++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp @@ -19,7 +19,7 @@ namespace Service::NFP::AmiiboCrypto { bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { const auto& amiibo_data = ntag_file.user_memory; LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); - LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); + LOG_DEBUG(Service_NFP, "compatibility_container=0x{0:x}", ntag_file.compatibility_container); LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); @@ -49,7 +49,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { if (ntag_file.static_lock != 0xE00F) { return false; } - if (ntag_file.compability_container != 0xEEFF10F1U) { + if (ntag_file.compatibility_container != 0xEEFF10F1U) { return false; } if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) { @@ -78,7 +78,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2; encoded_data.internal_number = nfc_data.internal_number; encoded_data.static_lock = nfc_data.static_lock; - encoded_data.compability_container = nfc_data.compability_container; + encoded_data.compatibility_container = nfc_data.compatibility_container; encoded_data.hmac_data = nfc_data.user_memory.hmac_data; encoded_data.constant_value = nfc_data.user_memory.constant_value; encoded_data.write_counter = nfc_data.user_memory.write_counter; @@ -112,7 +112,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2; nfc_data.internal_number = encoded_data.internal_number; nfc_data.static_lock = encoded_data.static_lock; - nfc_data.compability_container = encoded_data.compability_container; + nfc_data.compatibility_container = encoded_data.compatibility_container; nfc_data.user_memory.hmac_data = encoded_data.hmac_data; nfc_data.user_memory.constant_value = encoded_data.constant_value; nfc_data.user_memory.write_counter = encoded_data.write_counter; @@ -257,7 +257,7 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou out_data.uid_crc_check2 = in_data.uid_crc_check2; out_data.internal_number = in_data.internal_number; out_data.static_lock = in_data.static_lock; - out_data.compability_container = in_data.compability_container; + out_data.compatibility_container = in_data.compatibility_container; out_data.constant_value = in_data.constant_value; out_data.write_counter = in_data.write_counter; diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index f97e5b44c..1e2d2d212 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used @@ -22,9 +24,6 @@ #include "common/string_util.h" #include "common/tiny_mt.h" #include "core/core.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/mii/mii_manager.h" @@ -32,7 +31,11 @@ #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/mifare_result.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hid_types.h" namespace Service::NFC { NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, @@ -75,7 +78,7 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { return; } - if (!is_initalized) { + if (!is_initialized) { return; } @@ -207,7 +210,7 @@ void NfcDevice::Initialize() { return; } - is_initalized = npad_device->AddNfcHandle(); + is_initialized = npad_device->AddNfcHandle(); } void NfcDevice::Finalize() { @@ -226,7 +229,7 @@ void NfcDevice::Finalize() { } device_state = DeviceState::Unavailable; - is_initalized = false; + is_initialized = false; } Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) { @@ -393,8 +396,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet return result; } -Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span<const u8> command_data, +Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data) { // Not implemented return ResultSuccess; @@ -441,7 +443,10 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target device_state = DeviceState::TagMounted; mount_target = mount_target_; - if (!is_corrupted && mount_target != NFP::MountTarget::Rom) { + const bool create_backup = + mount_target == NFP::MountTarget::All || mount_target == NFP::MountTarget::Ram || + (mount_target == NFP::MountTarget::Rom && HasBackup(encrypted_tag_data.uuid).IsError()); + if (!is_corrupted && create_backup) { std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); WriteBackupData(encrypted_tag_data.uuid, data); @@ -1396,27 +1401,41 @@ void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, } NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const { - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); - Time::TimeZone::CalendarInfo calendar_info{}; + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{}; + static_service->GetTimeZoneService(timezone_service); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + NFP::AmiiboDate amiibo_date{}; amiibo_date.SetYear(2000); amiibo_date.SetMonth(1); amiibo_date.SetDay(1); - if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { - amiibo_date.SetYear(calendar_info.time.year); - amiibo_date.SetMonth(calendar_info.time.month); - amiibo_date.SetDay(calendar_info.time.day); + if (timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time) == + ResultSuccess) { + amiibo_date.SetYear(calendar_time.year); + amiibo_date.SetMonth(calendar_time.month); + amiibo_date.SetDay(calendar_time.day); } return amiibo_date; } -u64 NfcDevice::GetCurrentPosixTime() const { - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - return standard_steady_clock.GetCurrentTimePoint(system).time_point; +s64 NfcDevice::GetCurrentPosixTime() const { + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + return time_point.time_point; } u64 NfcDevice::RemoveVersionByte(u64 application_id) const { diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index d8efe25ec..d59202d18 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -11,7 +11,6 @@ #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_types.h" #include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" namespace Kernel { class KEvent; @@ -49,8 +48,8 @@ public: Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); - Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span<const u8> command_data, std::span<u8> out_data); + Result SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data, + std::span<u8> out_data); Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target); Result Unmount(); @@ -108,7 +107,7 @@ private: NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; - u64 GetCurrentPosixTime() const; + s64 GetCurrentPosixTime() const; u64 RemoveVersionByte(u64 application_id) const; void UpdateSettingsCrc(); void UpdateRegisterInfoCrc(); @@ -126,7 +125,7 @@ private: Kernel::KEvent* deactivate_event = nullptr; Kernel::KEvent* availability_change_event = nullptr; - bool is_initalized{}; + bool is_initialized{}; NfcProtocol allowed_protocols{}; DeviceState device_state{DeviceState::Unavailable}; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index ad534177d..b60699c45 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -5,15 +5,17 @@ #include "common/logging/log.h" #include "core/core.h" -#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/service/hid/hid_util.h" +#include "core/hle/service/glue/time/static.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/common/device_manager.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/hid_types.h" +#include "hid_core/hid_util.h" namespace Service::NFC { @@ -82,11 +84,19 @@ Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max continue; } if (skip_fatal_errors) { - constexpr u64 MinimumRecoveryTime = 60; - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point - - time_since_last_error; + constexpr s64 MinimumRecoveryTime = 60; + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", + true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + + const s64 elapsed_time = time_point.time_point - time_since_last_error; if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) { continue; } @@ -250,8 +260,7 @@ Result DeviceManager::WriteMifare(u64 device_handle, return result; } -Result DeviceManager::SendCommandByPassThrough(u64 device_handle, - const Time::Clock::TimeSpanType& timeout, +Result DeviceManager::SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data) { std::scoped_lock lock{mutex}; @@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device, if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 || operation_result == ResultUnknown115) { - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point; + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + + time_since_last_error = time_point.time_point; } return operation_result; diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index c9f038e32..c56a2fbda 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -8,13 +8,12 @@ #include <optional> #include <span> -#include "core/hid/hid_types.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nfc/mifare_types.h" #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_types.h" #include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" +#include "hid_core/hid_types.h" namespace Service::NFC { class NfcDevice; @@ -42,7 +41,7 @@ public: std::span<MifareReadBlockData> read_data); Result WriteMifare(u64 device_handle, std::span<const MifareWriteBlockParameter> write_parameters); - Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, + Result SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data); // Nfp device manager @@ -92,7 +91,7 @@ private: const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const; bool is_initialized = false; - u64 time_since_last_error = 0; + s64 time_since_last_error = 0; mutable std::mutex mutex; std::array<std::shared_ptr<NfcDevice>, 10> devices{}; diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 179c7ba2c..3e2c7deab 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -3,7 +3,6 @@ #include "common/logging/log.h" #include "core/core.h" -#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/common/device.h" @@ -14,7 +13,7 @@ #include "core/hle/service/nfc/nfc_result.h" #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_result.h" -#include "core/hle/service/time/clock_types.h" +#include "hid_core/hid_types.h" namespace Service::NFC { @@ -261,10 +260,10 @@ void NfcInterface::WriteMifare(HLERequestContext& ctx) { void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; - const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()}; + const auto timeout{rp.PopRaw<s64>()}; const auto command_data{ctx.ReadBuffer()}; LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", - device_handle, timeout.ToSeconds(), command_data.size()); + device_handle, timeout, command_data.size()); std::vector<u8> out_data(1); auto result = @@ -302,7 +301,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const { return result; } - if (result.module != ErrorModule::NFC) { + if (result.GetModule() != ErrorModule::NFC) { return result; } diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp index 34ef9d82d..5ba6d1742 100644 --- a/src/core/hle/service/nfp/nfp_interface.cpp +++ b/src/core/hle/service/nfp/nfp_interface.cpp @@ -3,7 +3,6 @@ #include "common/logging/log.h" #include "core/core.h" -#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/common/device.h" @@ -12,6 +11,7 @@ #include "core/hle/service/nfp/nfp_interface.h" #include "core/hle/service/nfp/nfp_result.h" #include "core/hle/service/nfp/nfp_types.h" +#include "hid_core/hid_types.h" namespace Service::NFP { diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index f96d21220..2505eb551 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -243,12 +243,12 @@ static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid si struct NTAG215File { u8 uid_crc_check2; u8 internal_number; - u16 static_lock; // Set defined pages as read only - u32 compability_container; // Defines available memory - HashData hmac_data; // Hash - u8 constant_value; // Must be A5 - u16_be write_counter; // Number of times the amiibo has been written? - u8 amiibo_version; // Amiibo file version + u16 static_lock; // Set defined pages as read only + u32 compatibility_container; // Defines available memory + HashData hmac_data; // Hash + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + u8 amiibo_version; // Amiibo file version AmiiboSettings settings; Service::Mii::Ver3StoreData owner_mii; // Mii data u64_be application_id; // Game id @@ -278,7 +278,7 @@ struct EncryptedNTAG215File { u8 uuid_crc_check2; u8 internal_number; u16 static_lock; // Set defined pages as read only - u32 compability_container; // Defines available memory + u32 compatibility_container; // Defines available memory EncryptedAmiiboFile user_memory; // Writable data u32 dynamic_lock; // Dynamic lock u32 CFG0; // Defines memory protected by password diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp index 036a1e9b7..d187be935 100644 --- a/src/core/hle/service/ns/language.cpp +++ b/src/core/hle/service/ns/language.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/ns/language.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings_server.h" namespace Service::NS { @@ -415,4 +415,4 @@ std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage return std::nullopt; } } -} // namespace Service::NS
\ No newline at end of file +} // namespace Service::NS diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h index ab6b71029..dad9934f2 100644 --- a/src/core/hle/service/ns/language.h +++ b/src/core/hle/service/ns/language.h @@ -5,10 +5,7 @@ #include <optional> #include "common/common_types.h" - -namespace Service::Set { -enum class LanguageCode : u64; -} +#include "core/hle/service/set/system_settings_server.h" namespace Service::NS { /// This is nn::ns::detail::ApplicationLanguage diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index f9e0e272d..2258ee609 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -6,7 +6,7 @@ #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" -#include "core/file_sys/vfs.h" +#include "core/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" @@ -16,7 +16,7 @@ #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pdm_qry.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings_server.h" namespace Service::NS { diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index 37ca24f5d..dc1b4d5be 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -2,27 +2,136 @@ // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later +#include <atomic> +#include <deque> +#include <mutex> + +#include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "core/memory.h" #include "video_core/host1x/host1x.h" namespace Service::Nvidia::NvCore { +Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_) + : id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {} + +Session::~Session() = default; + struct ContainerImpl { - explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) - : file{host1x_}, manager{host1x_}, device_file_data{} {} + explicit ContainerImpl(Container& core, Tegra::Host1x::Host1x& host1x_) + : host1x{host1x_}, file{core, host1x_}, manager{host1x_}, device_file_data{} {} + Tegra::Host1x::Host1x& host1x; NvMap file; SyncpointManager manager; Container::Host1xDeviceFileData device_file_data; + std::deque<Session> sessions; + size_t new_ids{}; + std::deque<size_t> id_pool; + std::mutex session_guard; }; Container::Container(Tegra::Host1x::Host1x& host1x_) { - impl = std::make_unique<ContainerImpl>(host1x_); + impl = std::make_unique<ContainerImpl>(*this, host1x_); } Container::~Container() = default; +SessionId Container::OpenSession(Kernel::KProcess* process) { + using namespace Common::Literals; + + std::scoped_lock lk(impl->session_guard); + for (auto& session : impl->sessions) { + if (!session.is_active) { + continue; + } + if (session.process == process) { + return session.id; + } + } + size_t new_id{}; + auto* memory_interface = &process->GetMemory(); + auto& smmu = impl->host1x.MemoryManager(); + auto asid = smmu.RegisterProcess(memory_interface); + if (!impl->id_pool.empty()) { + new_id = impl->id_pool.front(); + impl->id_pool.pop_front(); + impl->sessions[new_id] = Session{SessionId{new_id}, process, asid}; + } else { + new_id = impl->new_ids++; + impl->sessions.emplace_back(SessionId{new_id}, process, asid); + } + auto& session = impl->sessions[new_id]; + session.is_active = true; + // Optimization + if (process->IsApplication()) { + auto& page_table = process->GetPageTable().GetBasePageTable(); + auto heap_start = page_table.GetHeapRegionStart(); + + Kernel::KProcessAddress cur_addr = heap_start; + size_t region_size = 0; + VAddr region_start = 0; + while (true) { + Kernel::KMemoryInfo mem_info{}; + Kernel::Svc::PageInfo page_info{}; + R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), + cur_addr)); + auto svc_mem_info = mem_info.GetSvcMemoryInfo(); + + // Check if this memory block is heap. + if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) { + if (svc_mem_info.size > region_size) { + region_size = svc_mem_info.size; + region_start = svc_mem_info.base_address; + } + } + + // Check if we're done. + const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; + if (next_address <= GetInteger(cur_addr)) { + break; + } + + cur_addr = next_address; + } + session.has_preallocated_area = false; + auto start_region = region_size >= 32_MiB ? smmu.Allocate(region_size) : 0; + if (start_region != 0) { + session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size, + asid, impl->host1x); + smmu.TrackContinuity(start_region, region_start, region_size, asid); + session.has_preallocated_area = true; + LOG_DEBUG(Debug, "Preallocation created!"); + } + } + return SessionId{new_id}; +} + +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]; + auto& smmu = impl->host1x.MemoryManager(); + if (session.has_preallocated_area) { + const DAddr region_start = session.mapper->GetRegionStart(); + const size_t region_size = session.mapper->GetRegionSize(); + session.mapper.reset(); + smmu.Free(region_start, region_size); + session.has_preallocated_area = false; + } + session.is_active = false; + smmu.UnregisterProcess(impl->sessions[session_id.id].asid); + impl->id_pool.emplace_front(session_id.id); +} + +Session* Container::GetSession(SessionId session_id) { + std::atomic_thread_fence(std::memory_order_acquire); + return &impl->sessions[session_id.id]; +} + NvMap& Container::GetNvMapFile() { return impl->file; } diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index b4b63ac90..b4d3938a8 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -8,24 +8,56 @@ #include <memory> #include <unordered_map> +#include "core/device_memory_manager.h" #include "core/hle/service/nvdrv/nvdata.h" +namespace Kernel { +class KProcess; +} + namespace Tegra::Host1x { class Host1x; } // namespace Tegra::Host1x namespace Service::Nvidia::NvCore { +class HeapMapper; class NvMap; class SyncpointManager; struct ContainerImpl; +struct SessionId { + size_t id; +}; + +struct Session { + Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_); + ~Session(); + + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + Session(Session&&) = default; + Session& operator=(Session&&) = default; + + SessionId id; + Kernel::KProcess* process; + Core::Asid asid; + bool has_preallocated_area{}; + std::unique_ptr<HeapMapper> mapper{}; + bool is_active{}; +}; + class Container { public: explicit Container(Tegra::Host1x::Host1x& host1x); ~Container(); + SessionId OpenSession(Kernel::KProcess* process); + void CloseSession(SessionId id); + + Session* GetSession(SessionId id); + NvMap& GetNvMapFile(); const NvMap& GetNvMapFile() const; diff --git a/src/core/hle/service/nvdrv/core/heap_mapper.cpp b/src/core/hle/service/nvdrv/core/heap_mapper.cpp new file mode 100644 index 000000000..096dc5deb --- /dev/null +++ b/src/core/hle/service/nvdrv/core/heap_mapper.cpp @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <mutex> + +#include <boost/container/small_vector.hpp> +#define BOOST_NO_MT +#include <boost/pool/detail/mutex.hpp> +#undef BOOST_NO_MT +#include <boost/icl/interval.hpp> +#include <boost/icl/interval_base_set.hpp> +#include <boost/icl/interval_set.hpp> +#include <boost/icl/split_interval_map.hpp> +#include <boost/pool/pool.hpp> +#include <boost/pool/pool_alloc.hpp> +#include <boost/pool/poolfwd.hpp> + +#include "core/hle/service/nvdrv/core/heap_mapper.h" +#include "video_core/host1x/host1x.h" + +namespace boost { +template <typename T> +class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>; +} + +namespace Service::Nvidia::NvCore { + +using IntervalCompare = std::less<DAddr>; +using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>; +using IntervalAllocator = boost::fast_pool_allocator<DAddr>; +using IntervalSet = boost::icl::interval_set<DAddr>; +using IntervalType = typename IntervalSet::interval_type; + +template <typename Type> +struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> { + // types + typedef counter_add_functor<Type> type; + typedef boost::icl::identity_based_inplace_combine<Type> base_type; + + // public member functions + void operator()(Type& current, const Type& added) const { + current += added; + if (current < base_type::identity_element()) { + current = base_type::identity_element(); + } + } + + // public static functions + static void version(Type&){}; +}; + +using OverlapCombine = counter_add_functor<int>; +using OverlapSection = boost::icl::inter_section<int>; +using OverlapCounter = boost::icl::split_interval_map<DAddr, int>; + +struct HeapMapper::HeapMapperInternal { + HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} + ~HeapMapperInternal() = default; + + template <typename Func> + void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, + Func&& func) { + const DAddr start_address = cpu_addr; + const DAddr end_address = start_address + size; + const IntervalType search_interval{start_address, end_address}; + auto it = current_range.lower_bound(search_interval); + if (it == current_range.end()) { + return; + } + auto end_it = current_range.upper_bound(search_interval); + for (; it != end_it; it++) { + auto& inter = it->first; + DAddr inter_addr_end = inter.upper(); + DAddr inter_addr = inter.lower(); + if (inter_addr_end > end_address) { + inter_addr_end = end_address; + } + if (inter_addr < start_address) { + inter_addr = start_address; + } + func(inter_addr, inter_addr_end, it->second); + } + } + + void RemoveEachInOverlapCounter(OverlapCounter& current_range, + const IntervalType search_interval, int subtract_value) { + bool any_removals = false; + current_range.add(std::make_pair(search_interval, subtract_value)); + do { + any_removals = false; + auto it = current_range.lower_bound(search_interval); + if (it == current_range.end()) { + return; + } + auto end_it = current_range.upper_bound(search_interval); + for (; it != end_it; it++) { + if (it->second <= 0) { + any_removals = true; + current_range.erase(it); + break; + } + } + } while (any_removals); + } + + IntervalSet base_set; + OverlapCounter mapping_overlaps; + Tegra::MaxwellDeviceMemoryManager& device_memory; + std::mutex guard; +}; + +HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, + Tegra::Host1x::Host1x& host1x) + : m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_asid{asid} { + m_internal = std::make_unique<HeapMapperInternal>(host1x); +} + +HeapMapper::~HeapMapper() { + m_internal->device_memory.Unmap(m_daddress, m_size); +} + +DAddr HeapMapper::Map(VAddr start, size_t size) { + std::scoped_lock lk(m_internal->guard); + m_internal->base_set.clear(); + const IntervalType interval{start, start + size}; + m_internal->base_set.insert(interval); + m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, + [this](VAddr start_addr, VAddr end_addr, int) { + const IntervalType other{start_addr, end_addr}; + m_internal->base_set.subtract(other); + }); + if (!m_internal->base_set.empty()) { + auto it = m_internal->base_set.begin(); + auto end_it = m_internal->base_set.end(); + for (; it != end_it; it++) { + const VAddr inter_addr_end = it->upper(); + const VAddr inter_addr = it->lower(); + const size_t offset = inter_addr - m_vaddress; + const size_t sub_size = inter_addr_end - inter_addr; + m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, + m_asid); + } + } + m_internal->mapping_overlaps += std::make_pair(interval, 1); + m_internal->base_set.clear(); + return m_daddress + (start - m_vaddress); +} + +void HeapMapper::Unmap(VAddr start, size_t size) { + std::scoped_lock lk(m_internal->guard); + m_internal->base_set.clear(); + m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, + [this](VAddr start_addr, VAddr end_addr, int value) { + if (value <= 1) { + const IntervalType other{start_addr, end_addr}; + m_internal->base_set.insert(other); + } + }); + if (!m_internal->base_set.empty()) { + auto it = m_internal->base_set.begin(); + auto end_it = m_internal->base_set.end(); + for (; it != end_it; it++) { + const VAddr inter_addr_end = it->upper(); + const VAddr inter_addr = it->lower(); + const size_t offset = inter_addr - m_vaddress; + const size_t sub_size = inter_addr_end - inter_addr; + m_internal->device_memory.Unmap(m_daddress + offset, sub_size); + } + } + const IntervalType to_remove{start, start + size}; + m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1); + m_internal->base_set.clear(); +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/heap_mapper.h b/src/core/hle/service/nvdrv/core/heap_mapper.h new file mode 100644 index 000000000..491a12e4f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/heap_mapper.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "core/device_memory_manager.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +class HeapMapper { +public: + HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, + Tegra::Host1x::Host1x& host1x); + ~HeapMapper(); + + bool IsInBounds(VAddr start, size_t size) const { + VAddr end = start + size; + return start >= m_vaddress && end <= (m_vaddress + m_size); + } + + DAddr Map(VAddr start, size_t size); + + void Unmap(VAddr start, size_t size); + + DAddr GetRegionStart() const { + return m_daddress; + } + + size_t GetRegionSize() const { + return m_size; + } + +private: + struct HeapMapperInternal; + VAddr m_vaddress; + DAddr m_daddress; + size_t m_size; + Core::Asid m_asid; + std::unique_ptr<HeapMapperInternal> m_internal; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 0ca05257e..bc1c033c6 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -2,14 +2,19 @@ // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later +#include <functional> + #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/memory.h" #include "video_core/host1x/host1x.h" using Core::Memory::YUZU_PAGESIZE; +constexpr size_t BIG_PAGE_SIZE = YUZU_PAGESIZE * 16; namespace Service::Nvidia::NvCore { NvMap::Handle::Handle(u64 size_, Id id_) @@ -17,9 +22,9 @@ NvMap::Handle::Handle(u64 size_, Id id_) flags.raw = 0; } -NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { +NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, + NvCore::SessionId pSessionId) { std::scoped_lock lock(mutex); - // Handles cannot be allocated twice if (allocated) { return NvResult::AccessDenied; @@ -28,6 +33,7 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) flags = pFlags; kind = pKind; align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; + session_id = pSessionId; // This flag is only applicable for handles with an address passed if (pAddress) { @@ -63,7 +69,7 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) { return NvResult::Success; } -NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} +NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {} void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { std::scoped_lock lock(handles_lock); @@ -78,12 +84,30 @@ void NvMap::UnmapHandle(Handle& handle_description) { handle_description.unmap_queue_entry.reset(); } + // Free and unmap the handle from Host1x GMMU + if (handle_description.pin_virt_address) { + host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), + handle_description.aligned_size); + host1x.Allocator().Free(handle_description.pin_virt_address, + static_cast<u32>(handle_description.aligned_size)); + handle_description.pin_virt_address = 0; + } + // Free and unmap the handle from the SMMU - host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), - handle_description.aligned_size); - host1x.Allocator().Free(handle_description.pin_virt_address, - static_cast<u32>(handle_description.aligned_size)); - handle_description.pin_virt_address = 0; + const size_t map_size = handle_description.aligned_size; + if (!handle_description.in_heap) { + auto& smmu = host1x.MemoryManager(); + size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE); + smmu.Unmap(handle_description.d_address, map_size); + smmu.Free(handle_description.d_address, static_cast<size_t>(aligned_up)); + handle_description.d_address = 0; + return; + } + const VAddr vaddress = handle_description.address; + auto* session = core.GetSession(handle_description.session_id); + session->mapper->Unmap(vaddress, map_size); + handle_description.d_address = 0; + handle_description.in_heap = false; } bool NvMap::TryRemoveHandle(const Handle& handle_description) { @@ -124,22 +148,33 @@ std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) { } } -VAddr NvMap::GetHandleAddress(Handle::Id handle) { +DAddr NvMap::GetHandleAddress(Handle::Id handle) { std::scoped_lock lock(handles_lock); try { - return handles.at(handle)->address; + return handles.at(handle)->d_address; } catch (std::out_of_range&) { return 0; } } -u32 NvMap::PinHandle(NvMap::Handle::Id handle) { +DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { auto handle_description{GetHandle(handle)}; if (!handle_description) [[unlikely]] { return 0; } std::scoped_lock lock(handle_description->mutex); + const auto map_low_area = [&] { + if (handle_description->pin_virt_address == 0) { + auto& gmmu_allocator = host1x.Allocator(); + auto& gmmu = host1x.GMMU(); + u32 address = + gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)); + gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address, + handle_description->aligned_size); + handle_description->pin_virt_address = address; + } + }; if (!handle_description->pins) { // If we're in the unmap queue we can just remove ourselves and return since we're already // mapped @@ -151,37 +186,58 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { unmap_queue.erase(*handle_description->unmap_queue_entry); handle_description->unmap_queue_entry.reset(); + if (low_area_pin) { + map_low_area(); + handle_description->pins++; + return static_cast<DAddr>(handle_description->pin_virt_address); + } + handle_description->pins++; - return handle_description->pin_virt_address; + return handle_description->d_address; } } + using namespace std::placeholders; // If not then allocate some space and map it - u32 address{}; - auto& smmu_allocator = host1x.Allocator(); - auto& smmu_memory_manager = host1x.MemoryManager(); - while ((address = smmu_allocator.Allocate( - static_cast<u32>(handle_description->aligned_size))) == 0) { - // Free handles until the allocation succeeds - std::scoped_lock queueLock(unmap_queue_lock); - if (auto freeHandleDesc{unmap_queue.front()}) { - // Handles in the unmap queue are guaranteed not to be pinned so don't bother - // checking if they are before unmapping - std::scoped_lock freeLock(freeHandleDesc->mutex); - if (handle_description->pin_virt_address) - UnmapHandle(*freeHandleDesc); - } else { - LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + DAddr address{}; + auto& smmu = host1x.MemoryManager(); + auto* session = core.GetSession(handle_description->session_id); + const VAddr vaddress = handle_description->address; + const size_t map_size = handle_description->aligned_size; + if (session->has_preallocated_area && session->mapper->IsInBounds(vaddress, map_size)) { + handle_description->d_address = session->mapper->Map(vaddress, map_size); + handle_description->in_heap = true; + } else { + size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE); + while ((address = smmu.Allocate(aligned_up)) == 0) { + // Free handles until the allocation succeeds + std::scoped_lock queueLock(unmap_queue_lock); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handle_description->d_address) + UnmapHandle(*freeHandleDesc); + } else { + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + } } + + handle_description->d_address = address; + smmu.Map(address, vaddress, map_size, session->asid, true); + handle_description->in_heap = false; } + } - smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address, - handle_description->aligned_size); - handle_description->pin_virt_address = address; + if (low_area_pin) { + map_low_area(); } handle_description->pins++; - return handle_description->pin_virt_address; + if (low_area_pin) { + return static_cast<DAddr>(handle_description->pin_virt_address); + } + return handle_description->d_address; } void NvMap::UnpinHandle(Handle::Id handle) { @@ -232,7 +288,7 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); } else if (handle_description->dupes == 0) { // Force unmap the handle - if (handle_description->pin_virt_address) { + if (handle_description->d_address) { std::scoped_lock queueLock(unmap_queue_lock); UnmapHandle(*handle_description); } @@ -270,4 +326,17 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna return freeInfo; } +void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { + auto handles_copy = [&] { + std::scoped_lock lk{handles_lock}; + return handles; + }(); + + for (auto& [id, handle] : handles_copy) { + if (handle->session_id.id == session_id.id) { + FreeHandle(id, false); + } + } +} + } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index a8e573890..b8be599ae 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -14,6 +14,7 @@ #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" namespace Tegra { @@ -25,6 +26,8 @@ class Host1x; } // namespace Tegra namespace Service::Nvidia::NvCore { + +class Container; /** * @brief The nvmap core class holds the global state for nvmap and provides methods to manage * handles @@ -48,7 +51,7 @@ public: using Id = u32; Id id; //!< A globally unique identifier for this handle - s32 pins{}; + s64 pins{}; u32 pin_virt_address{}; std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{}; @@ -61,15 +64,18 @@ public: } flags{}; static_assert(sizeof(Flags) == sizeof(u32)); - u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, - //!< this can also be in the nvdrv tmem + VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to, + //!< this can also be in the nvdrv tmem bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC //!< call u8 kind{}; //!< Used for memory compression bool allocated{}; //!< If the handle has been allocated with `Alloc` + bool in_heap{}; + NvCore::SessionId session_id{}; - u64 dma_map_addr{}; //! remove me after implementing pinning. + DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds + //!< to, this can also be in the nvdrv tmem Handle(u64 size, Id id); @@ -77,7 +83,8 @@ public: * @brief Sets up the handle with the given memory config, can allocate memory from the tmem * if a 0 address is passed */ - [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); + [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, + NvCore::SessionId pSessionId); /** * @brief Increases the dupe counter of the handle for the given session @@ -108,7 +115,7 @@ public: bool can_unlock; //!< If the address region is ready to be unlocked }; - explicit NvMap(Tegra::Host1x::Host1x& host1x); + explicit NvMap(Container& core, Tegra::Host1x::Host1x& host1x); /** * @brief Creates an unallocated handle of the given size @@ -117,7 +124,7 @@ public: std::shared_ptr<Handle> GetHandle(Handle::Id handle); - VAddr GetHandleAddress(Handle::Id handle); + DAddr GetHandleAddress(Handle::Id handle); /** * @brief Maps a handle into the SMMU address space @@ -125,7 +132,7 @@ public: * number of calls to `UnpinHandle` * @return The SMMU virtual address that the handle has been mapped to */ - u32 PinHandle(Handle::Id handle); + DAddr PinHandle(Handle::Id handle, bool low_area_pin); /** * @brief When this has been called an equal number of times to `PinHandle` for the supplied @@ -145,6 +152,8 @@ public: */ std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session); + void UnmapAllHandles(NvCore::SessionId session_id); + private: std::list<std::shared_ptr<Handle>> unmap_queue{}; std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` @@ -172,5 +181,7 @@ private: * @return If the handle was removed from the map */ bool TryRemoveHandle(const Handle& handle_description); + + Container& core; }; } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index a04538d5d..8adaddc60 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -7,6 +7,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" namespace Core { @@ -62,7 +63,7 @@ public: * Called once a device is opened * @param fd The device fd */ - virtual void OnOpen(DeviceFD fd) = 0; + virtual void OnOpen(NvCore::SessionId session_id, DeviceFD fd) = 0; /** * Called once a device is closed diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 05a43d8dc..c1ebbd62d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -35,14 +35,14 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in return NvResult::NotImplemented; } -void nvdisp_disp0::OnOpen(DeviceFD fd) {} +void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, const Common::Rectangle<int>& crop_rect, std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { - const VAddr addr = nvmap.GetHandleAddress(buffer_handle); + const DAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index daee05fe8..5f13a50a2 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -32,7 +32,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 6b3639008..e6646ba04 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -86,7 +86,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i return NvResult::NotImplemented; } -void nvhost_as_gpu::OnOpen(DeviceFD fd) {} +void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {} NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { @@ -206,6 +206,8 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) { static_cast<u32>(aligned_size >> page_size_bits)); } + nvmap.UnpinHandle(mapping->handle); + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state // Only FreeSpace can unmap them fully if (mapping->sparse_alloc) { @@ -293,12 +295,12 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { return NvResult::BadValue; } - VAddr cpu_address{static_cast<VAddr>( - handle->address + - (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; + DAddr base = nvmap.PinHandle(entry.handle, false); + DAddr device_address{static_cast<DAddr>( + base + (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; - gmmu->Map(virtual_address, cpu_address, size, static_cast<Tegra::PTEKind>(entry.kind), - use_big_pages); + gmmu->Map(virtual_address, device_address, size, + static_cast<Tegra::PTEKind>(entry.kind), use_big_pages); } } @@ -331,9 +333,9 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { } u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; - VAddr cpu_address{mapping->ptr + params.buffer_offset}; + VAddr device_address{mapping->ptr + params.buffer_offset}; - gmmu->Map(gpu_address, cpu_address, params.mapping_size, + gmmu->Map(gpu_address, device_address, params.mapping_size, static_cast<Tegra::PTEKind>(params.kind), mapping->big_page); return NvResult::Success; @@ -349,7 +351,8 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { return NvResult::BadValue; } - VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)}; + DAddr device_address{ + static_cast<DAddr>(nvmap.PinHandle(params.handle, false) + params.buffer_offset)}; u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; bool big_page{[&]() { @@ -373,15 +376,14 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { } const bool use_big_pages = alloc->second.big_pages && big_page; - gmmu->Map(params.offset, cpu_address, size, static_cast<Tegra::PTEKind>(params.kind), + gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind), use_big_pages); - auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true, - use_big_pages, alloc->second.sparse)}; + auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size, + true, use_big_pages, alloc->second.sparse)}; alloc->second.mappings.push_back(mapping); mapping_map[params.offset] = mapping; } else { - auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; @@ -394,11 +396,11 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { return NvResult::InsufficientMemory; } - gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), + gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size), static_cast<Tegra::PTEKind>(params.kind), big_page); - auto mapping{ - std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)}; + auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size, + false, big_page, false)}; mapping_map[params.offset] = mapping; } @@ -433,6 +435,8 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { gmmu->Unmap(params.offset, mapping->size); } + nvmap.UnpinHandle(mapping->handle); + mapping_map.erase(params.offset); } catch (const std::out_of_range&) { LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 932997e75..7d0a99988 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -55,7 +55,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; @@ -90,7 +90,7 @@ private: u64_le align; }; }; - static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); + static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitializeEx is incorrect size"); struct IoctlFreeSpace { u64_le offset{}; @@ -159,16 +159,18 @@ private: NvCore::NvMap& nvmap; struct Mapping { - VAddr ptr; + NvCore::NvMap::Handle::Id handle; + DAddr ptr; u64 offset; u64 size; bool fixed; bool big_page; // Only valid if fixed == false bool sparse_alloc; - Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) - : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), - sparse_alloc(sparse_alloc_) {} + Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_, + bool big_page_, bool sparse_alloc_) + : handle(handle_), ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), + big_page(big_page_), sparse_alloc(sparse_alloc_) {} }; struct Allocation { @@ -212,9 +214,6 @@ private: bool initialised{}; } vm; std::shared_ptr<Tegra::MemoryManager> gmmu; - - // s32 channel{}; - // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index b8dd34e24..250d01de3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -76,7 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inp return NvResult::NotImplemented; } -void nvhost_ctrl::OnOpen(DeviceFD fd) {} +void nvhost_ctrl::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {} diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 992124b60..403f1a746 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -32,7 +32,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 61a2df121..ddd85678b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -15,7 +15,7 @@ namespace Service::Nvidia::Devices { nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) : nvdevice{system_}, events_interface{events_interface_} { error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); - unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); + unknown_event = events_interface.CreateEvent("CtrlGpuUnknownEvent"); } nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { events_interface.FreeEvent(error_notifier_event); @@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> return NvResult::NotImplemented; } -void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} +void nvhost_ctrl_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index d170299bd..d2ab05b21 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -28,7 +28,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index b0395c2f0..bf12d69a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -120,7 +120,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu return NvResult::NotImplemented; } -void nvhost_gpu::OnOpen(DeviceFD fd) {} +void nvhost_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_gpu::OnClose(DeviceFD fd) {} NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 88fd228ff..e34a978db 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -47,7 +47,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f43914e1b..2c0ac2a46 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -35,7 +35,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in case 0x7: return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output); case 0x9: - return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output); + return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output, fd); case 0xa: return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output); default: @@ -68,9 +68,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in return NvResult::NotImplemented; } -void nvhost_nvdec::OnOpen(DeviceFD fd) { +void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream started"); system.SetNVDECActive(true); + sessions[fd] = session_id; } void nvhost_nvdec::OnClose(DeviceFD fd) { @@ -81,6 +82,10 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { system.GPU().ClearCdmaInstance(iter->second); } system.SetNVDECActive(false); + auto it = sessions.find(fd); + if (it != sessions.end()) { + sessions.erase(it); + } } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index ad2233c49..627686757 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -20,7 +20,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 74c701b95..a0a7bfa40 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" @@ -95,6 +96,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); auto& gpu = system.GPU(); + auto* session = core.GetSession(sessions[fd]); + if (gpu.UseNvdec()) { for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i]; @@ -106,8 +109,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De const auto object = nvmap.GetHandle(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - system.ApplicationMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), - cmdlist.size() * sizeof(u32)); + session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), + cmdlist.size() * sizeof(u32)); gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); } // Some games expect command_buffers to be written back @@ -133,10 +136,12 @@ NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) { return NvResult::Success; } -NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) { +NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, + DeviceFD fd) { const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size())); for (size_t i = 0; i < num_entries; i++) { - entries[i].map_address = nvmap.PinHandle(entries[i].map_handle); + DAddr pin_address = nvmap.PinHandle(entries[i].map_handle, true); + entries[i].map_address = static_cast<u32>(pin_address); } return NvResult::Success; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 7ce748e18..900db81d2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -4,7 +4,9 @@ #pragma once #include <deque> +#include <unordered_map> #include <vector> + #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" @@ -111,7 +113,7 @@ protected: NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd); NvResult GetSyncpoint(IoctlGetSyncpoint& params); NvResult GetWaitbase(IoctlGetWaitbase& params); - NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); + NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, DeviceFD fd); NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); NvResult SetSubmitTimeout(u32 timeout); @@ -125,6 +127,7 @@ protected: NvCore::NvMap& nvmap; NvCore::ChannelType channel_type; std::array<u32, MaxSyncPoints> device_syncpoints{}; + std::unordered_map<DeviceFD, NvCore::SessionId> sessions; }; }; // namespace Devices } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 9e6b86458..f87d53f12 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -44,7 +44,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in return NvResult::NotImplemented; } -void nvhost_nvjpg::OnOpen(DeviceFD fd) {} +void nvhost_nvjpg::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_nvjpg::OnClose(DeviceFD fd) {} NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 790c97f6a..def9c254d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -22,7 +22,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; private: diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 87f8d7c22..bf090f5eb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -33,7 +33,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu case 0x3: return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output); case 0x9: - return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output); + return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output, fd); case 0xa: return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output); default: @@ -68,7 +68,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu return NvResult::NotImplemented; } -void nvhost_vic::OnOpen(DeviceFD fd) {} +void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { + sessions[fd] = session_id; +} void nvhost_vic::OnClose(DeviceFD fd) { auto& host1x_file = core.Host1xDeviceFile(); @@ -76,6 +78,7 @@ void nvhost_vic::OnClose(DeviceFD fd) { if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } + sessions.erase(fd); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index cadbcb0a5..0cc04354a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -19,7 +19,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 71b2e62ec..da61a3bfe 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -36,9 +36,9 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, case 0x3: return WrapFixed(this, &nvmap::IocFromId, input, output); case 0x4: - return WrapFixed(this, &nvmap::IocAlloc, input, output); + return WrapFixed(this, &nvmap::IocAlloc, input, output, fd); case 0x5: - return WrapFixed(this, &nvmap::IocFree, input, output); + return WrapFixed(this, &nvmap::IocFree, input, output, fd); case 0x9: return WrapFixed(this, &nvmap::IocParam, input, output); case 0xe: @@ -67,8 +67,15 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st return NvResult::NotImplemented; } -void nvmap::OnOpen(DeviceFD fd) {} -void nvmap::OnClose(DeviceFD fd) {} +void nvmap::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { + sessions[fd] = session_id; +} +void nvmap::OnClose(DeviceFD fd) { + auto it = sessions.find(fd); + if (it != sessions.end()) { + sessions.erase(it); + } +} NvResult nvmap::IocCreate(IocCreateParams& params) { LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); @@ -87,7 +94,7 @@ NvResult nvmap::IocCreate(IocCreateParams& params) { return NvResult::Success; } -NvResult nvmap::IocAlloc(IocAllocParams& params) { +NvResult nvmap::IocAlloc(IocAllocParams& params, DeviceFD fd) { LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { @@ -116,15 +123,15 @@ NvResult nvmap::IocAlloc(IocAllocParams& params) { return NvResult::InsufficientMemory; } - const auto result = - handle_description->Alloc(params.flags, params.align, params.kind, params.address); + const auto result = handle_description->Alloc(params.flags, params.align, params.kind, + params.address, sessions[fd]); if (result != NvResult::Success) { LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); return result; } bool is_out_io{}; - ASSERT(system.ApplicationProcess() - ->GetPageTable() + auto process = container.GetSession(sessions[fd])->process; + ASSERT(process->GetPageTable() .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, handle_description->size, Kernel::KMemoryPermission::None, true, false) @@ -224,7 +231,7 @@ NvResult nvmap::IocParam(IocParamParams& params) { return NvResult::Success; } -NvResult nvmap::IocFree(IocFreeParams& params) { +NvResult nvmap::IocFree(IocFreeParams& params, DeviceFD fd) { LOG_DEBUG(Service_NVDRV, "called"); if (!params.handle) { @@ -233,9 +240,9 @@ NvResult nvmap::IocFree(IocFreeParams& params) { } if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + auto process = container.GetSession(sessions[fd])->process; if (freeInfo->can_unlock) { - ASSERT(system.ApplicationProcess() - ->GetPageTable() + ASSERT(process->GetPageTable() .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) .IsSuccess()); } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 049c11028..d07d85f88 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -33,7 +33,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; enum class HandleParameterType : u32_le { @@ -100,11 +100,11 @@ public: static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); NvResult IocCreate(IocCreateParams& params); - NvResult IocAlloc(IocAllocParams& params); + NvResult IocAlloc(IocAllocParams& params, DeviceFD fd); NvResult IocGetId(IocGetIdParams& params); NvResult IocFromId(IocFromIdParams& params); NvResult IocParam(IocParamParams& params); - NvResult IocFree(IocFreeParams& params); + NvResult IocFree(IocFreeParams& params, DeviceFD fd); private: /// Id to use for the next handle that is created. @@ -115,6 +115,7 @@ private: NvCore::Container& container; NvCore::NvMap& file; + std::unordered_map<DeviceFD, NvCore::SessionId> sessions; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 0e2f47075..38f35e79f 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -51,7 +51,7 @@ enum class NvResult : u32 { DispNoDisplaysAttached = 0x20003, DispModeNotSupported = 0x20004, DispNotFound = 0x20005, - DispAttachDissallowed = 0x20006, + DispAttachDisallowed = 0x20006, DispTypeNotSupported = 0x20007, DispAuthenticationFailed = 0x20008, DispNotAttached = 0x20009, diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 9e46ee8dd..cb256e5b4 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -45,13 +45,22 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) { void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); auto module = std::make_shared<Module>(system); - server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv")); - server_manager->RegisterNamedService("nvdrv:a", - std::make_shared<NVDRV>(system, module, "nvdrv:a")); - server_manager->RegisterNamedService("nvdrv:s", - std::make_shared<NVDRV>(system, module, "nvdrv:s")); - server_manager->RegisterNamedService("nvdrv:t", - std::make_shared<NVDRV>(system, module, "nvdrv:t")); + const auto NvdrvInterfaceFactoryForApplication = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv"); + }; + const auto NvdrvInterfaceFactoryForApplets = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv:a"); + }; + const auto NvdrvInterfaceFactoryForSysmodules = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv:s"); + }; + const auto NvdrvInterfaceFactoryForTesting = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv:t"); + }; + server_manager->RegisterNamedService("nvdrv", NvdrvInterfaceFactoryForApplication); + server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets); + 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)); @@ -113,7 +122,7 @@ NvResult Module::VerifyFD(DeviceFD fd) const { return NvResult::Success; } -DeviceFD Module::Open(const std::string& device_name) { +DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_id) { auto it = builders.find(device_name); if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); @@ -124,7 +133,7 @@ DeviceFD Module::Open(const std::string& device_name) { auto& builder = it->second; auto device = builder(fd)->second; - device->OnOpen(fd); + device->OnOpen(session_id, fd); return fd; } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index d8622b3ca..c594f0e5e 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -77,7 +77,7 @@ public: NvResult VerifyFD(DeviceFD fd) const; /// Opens a device node and returns a file descriptor to it. - DeviceFD Open(const std::string& device_name); + DeviceFD Open(const std::string& device_name, NvCore::SessionId session_id); /// Sends an ioctl command to the specified file descriptor. NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output); @@ -93,6 +93,10 @@ public: NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); + NvCore::Container& GetContainer() { + return container; + } + private: friend class EventInterface; friend class Service::Nvnflinger::Nvnflinger; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index c8a880e84..ffe72f281 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -3,8 +3,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "common/logging/log.h" +#include "common/scope_exit.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nvdrv/nvdata.h" @@ -27,7 +30,7 @@ void NVDRV::Open(HLERequestContext& ctx) { } const auto& buffer = ctx.ReadBuffer(); - const std::string device_name(buffer.begin(), buffer.end()); + const std::string device_name(Common::StringFromBuffer(buffer)); if (device_name == "/dev/nvhost-prof-gpu") { rb.Push<DeviceFD>(0); @@ -37,7 +40,7 @@ void NVDRV::Open(HLERequestContext& ctx) { return; } - DeviceFD fd = nvdrv->Open(device_name); + DeviceFD fd = nvdrv->Open(device_name, session_id); rb.Push<DeviceFD>(fd); rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed); @@ -150,12 +153,29 @@ void NVDRV::Close(HLERequestContext& ctx) { void NVDRV::Initialize(HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + SCOPE_EXIT({ + rb.Push(ResultSuccess); + rb.PushEnum(NvResult::Success); + }); - is_initialized = true; + if (is_initialized) { + // No need to initialize again + return; + } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NvResult::Success); + IPC::RequestParser rp{ctx}; + const auto process_handle{ctx.GetCopyHandle(0)}; + // The transfer memory is lent to nvdrv as a work buffer since nvdrv is + // unable to allocate as much memory on its own. For HLE it's unnecessary to handle it + [[maybe_unused]] const auto transfer_memory_handle{ctx.GetCopyHandle(1)}; + [[maybe_unused]] const auto transfer_memory_size = rp.Pop<u32>(); + + auto& container = nvdrv->GetContainer(); + auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle); + session_id = container.OpenSession(process.GetPointerUnsafe()); + + is_initialized = true; } void NVDRV::QueryEvent(HLERequestContext& ctx) { @@ -242,6 +262,9 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* RegisterHandlers(functions); } -NVDRV::~NVDRV() = default; +NVDRV::~NVDRV() { + 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 6e98115dc..f2195ae1e 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -35,6 +35,7 @@ private: u64 pid{}; bool is_initialized{}; + NvCore::SessionId session_id{}; Common::ScratchBuffer<u8> output_buffer; Common::ScratchBuffer<u8> inline_output_buffer; }; diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index 2fef6cc1a..86e272b41 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp @@ -87,19 +87,20 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, R_SUCCEED(); } -Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { +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) == Nvidia::NvResult::Success, VI::ResultOperationFailed); + 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) { + u32 size, Nvidia::DeviceFD nvmap_fd) { // Assign the allocated memory to the handle. Nvidia::Devices::nvmap::IocAllocParams alloc_params{ .handle = handle, @@ -109,16 +110,16 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce .kind = 0, .address = GetInteger(buffer), }; - R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); + 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, +Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd, Common::ProcessAddress buffer, u32 size) { // Get the nvmap device. - auto nvmap_fd = nvdrv.Open("/dev/nvmap"); auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); ASSERT(nvmap != nullptr); @@ -127,11 +128,11 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, // Ensure we maintain a clean state on failure. ON_RESULT_FAILURE { - ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle))); + ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd))); }; // Assign the allocated memory to the handle. - R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size)); + R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); } constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; @@ -197,9 +198,13 @@ Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u 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, map_address, - SharedBufferSize)); + 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; diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h index c809c01b4..033bf4bbe 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h @@ -4,6 +4,8 @@ #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" @@ -53,7 +55,8 @@ private: 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; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index aa8aaa2d9..71d6fdb0c 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -112,9 +112,7 @@ void Nvnflinger::ShutdownLayers() { { const auto lock_guard = Lock(); for (auto& display : displays) { - for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { - display.GetLayer(layer).GetConsumer().Abandon(); - } + display.Abandon(); } is_abandoned = true; @@ -126,7 +124,7 @@ void Nvnflinger::ShutdownLayers() { void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { nvdrv = std::move(instance); - disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {}); } std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) { @@ -176,24 +174,28 @@ void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { display.CreateLayer(layer_id, buffer_id, nvdrv->container); } -void Nvnflinger::OpenLayer(u64 layer_id) { +bool Nvnflinger::OpenLayer(u64 layer_id) { const auto lock_guard = Lock(); for (auto& display : displays) { if (auto* layer = display.FindLayer(layer_id); layer) { - layer->Open(); + return layer->Open(); } } + + return false; } -void Nvnflinger::CloseLayer(u64 layer_id) { +bool Nvnflinger::CloseLayer(u64 layer_id) { const auto lock_guard = Lock(); for (auto& display : displays) { if (auto* layer = display.FindLayer(layer_id); layer) { - layer->Close(); + return layer->Close(); } } + + return false; } void Nvnflinger::DestroyLayer(u64 layer_id) { @@ -223,7 +225,8 @@ Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 return VI::ResultNotFound; } - return display->GetVSyncEvent(out_vsync_event); + *out_vsync_event = display->GetVSyncEvent(); + return ResultSuccess; } VI::Display* Nvnflinger::FindDisplay(u64 display_id) { diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 871285764..a60e0ae6b 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -74,10 +74,10 @@ public: [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); /// Opens a layer on all displays for the given layer ID. - void OpenLayer(u64 layer_id); + bool OpenLayer(u64 layer_id); /// Closes a layer on all displays for the given layer ID. - void CloseLayer(u64 layer_id); + bool CloseLayer(u64 layer_id); /// Destroys the given layer ID. void DestroyLayer(u64 layer_id); diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp index ce70946ec..ede2a1193 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp @@ -22,11 +22,13 @@ GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap, : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) { if (this->BufferId() > 0) { m_nvmap->DuplicateHandle(this->BufferId(), true); + m_nvmap->PinHandle(this->BufferId(), false); } } GraphicBuffer::~GraphicBuffer() { if (m_nvmap != nullptr && this->BufferId() > 0) { + m_nvmap->UnpinHandle(this->BufferId()); m_nvmap->FreeHandle(this->BufferId(), true); } } diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp index c13ffa6f6..3d0f2aeb7 100644 --- a/src/core/hle/service/pcv/pcv.cpp +++ b/src/core/hle/service/pcv/pcv.cpp @@ -54,8 +54,8 @@ public: class IClkrstSession final : public ServiceFramework<IClkrstSession> { public: - explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) - : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { + explicit IClkrstSession(Core::System& system_, DeviceCode device_code_) + : ServiceFramework{system_, "IClkrstSession"}, device_code(device_code_) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "SetClockEnabled"}, @@ -93,7 +93,7 @@ private: rb.Push<u32>(clock_rate); } - DeviceCode deivce_code; + DeviceCode device_code; u32 clock_rate{}; }; @@ -118,9 +118,9 @@ private: void OpenSession(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>()); - const auto unkonwn_input = rp.Pop<u32>(); + const auto unknown_input = rp.Pop<u32>(); - LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); + LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unknown_input); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index d92499f05..b52468e41 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -22,27 +22,26 @@ constexpr Result ResultProcessNotFound{ErrorModule::PM, 1}; constexpr u64 NO_PROCESS_FOUND_PID{0}; -std::optional<Kernel::KProcess*> SearchProcessList( - const std::vector<Kernel::KProcess*>& process_list, - std::function<bool(Kernel::KProcess*)> predicate) { +using ProcessList = std::list<Kernel::KScopedAutoObject<Kernel::KProcess>>; + +template <typename F> +Kernel::KScopedAutoObject<Kernel::KProcess> SearchProcessList(ProcessList& process_list, + F&& predicate) { const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); if (iter == process_list.end()) { - return std::nullopt; + return nullptr; } - return *iter; + return iter->GetPointerUnsafe(); } -void GetApplicationPidGeneric(HLERequestContext& ctx, - const std::vector<Kernel::KProcess*>& process_list) { - const auto process = SearchProcessList(process_list, [](const auto& proc) { - return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin; - }); +void GetApplicationPidGeneric(HLERequestContext& ctx, ProcessList& process_list) { + auto process = SearchProcessList(process_list, [](auto& p) { return p->IsApplication(); }); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push(process.has_value() ? (*process)->GetProcessId() : NO_PROCESS_FOUND_PID); + rb.Push(process.IsNull() ? NO_PROCESS_FOUND_PID : process->GetProcessId()); } } // Anonymous namespace @@ -80,8 +79,7 @@ private: class DebugMonitor final : public ServiceFramework<DebugMonitor> { public: - explicit DebugMonitor(Core::System& system_) - : ServiceFramework{system_, "pm:dmnt"}, kernel{system_.Kernel()} { + explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "pm:dmnt"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetJitDebugProcessIdList"}, @@ -106,12 +104,11 @@ private: LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); - const auto process = - SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) { - return proc->GetProgramId() == program_id; - }); + auto list = kernel.GetProcessList(); + auto process = SearchProcessList( + list, [program_id](auto& p) { return p->GetProgramId() == program_id; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -119,12 +116,13 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push((*process)->GetProcessId()); + rb.Push(process->GetProcessId()); } void GetApplicationProcessId(HLERequestContext& ctx) { LOG_DEBUG(Service_PM, "called"); - GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + auto list = kernel.GetProcessList(); + GetApplicationPidGeneric(ctx, list); } void AtmosphereGetProcessInfo(HLERequestContext& ctx) { @@ -135,11 +133,10 @@ private: LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); - const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) { - return proc->GetProcessId() == pid; - }); + auto list = kernel.GetProcessList(); + auto process = SearchProcessList(list, [pid](auto& p) { return p->GetProcessId() == pid; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -159,7 +156,7 @@ private: OverrideStatus override_status{}; ProgramLocation program_location{ - .program_id = (*process)->GetProgramId(), + .program_id = process->GetProgramId(), .storage_id = 0, }; @@ -169,14 +166,11 @@ private: rb.PushRaw(program_location); rb.PushRaw(override_status); } - - const Kernel::KernelCore& kernel; }; class Info final : public ServiceFramework<Info> { public: - explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) - : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { + explicit Info(Core::System& system_) : ServiceFramework{system_, "pm:info"} { static const FunctionInfo functions[] = { {0, &Info::GetProgramId, "GetProgramId"}, {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"}, @@ -193,11 +187,11 @@ private: LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); - const auto process = SearchProcessList(process_list, [process_id](const auto& proc) { - return proc->GetProcessId() == process_id; - }); + auto list = kernel.GetProcessList(); + auto process = SearchProcessList( + list, [process_id](auto& p) { return p->GetProcessId() == process_id; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -205,7 +199,7 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push((*process)->GetProgramId()); + rb.Push(process->GetProgramId()); } void AtmosphereGetProcessId(HLERequestContext& ctx) { @@ -214,11 +208,11 @@ private: LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); - const auto process = SearchProcessList(process_list, [program_id](const auto& proc) { - return proc->GetProgramId() == program_id; - }); + auto list = system.Kernel().GetProcessList(); + auto process = SearchProcessList( + list, [program_id](auto& p) { return p->GetProgramId() == program_id; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -226,16 +220,13 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push((*process)->GetProcessId()); + rb.Push(process->GetProcessId()); } - - const std::vector<Kernel::KProcess*>& process_list; }; class Shell final : public ServiceFramework<Shell> { public: - explicit Shell(Core::System& system_) - : ServiceFramework{system_, "pm:shell"}, kernel{system_.Kernel()} { + explicit Shell(Core::System& system_) : ServiceFramework{system_, "pm:shell"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LaunchProgram"}, @@ -257,10 +248,9 @@ public: private: void GetApplicationProcessIdForShell(HLERequestContext& ctx) { LOG_DEBUG(Service_PM, "called"); - GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + auto list = kernel.GetProcessList(); + GetApplicationPidGeneric(ctx, list); } - - const Kernel::KernelCore& kernel; }; void LoopProcess(Core::System& system) { @@ -268,8 +258,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system)); server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system)); - server_manager->RegisterNamedService( - "pm:info", std::make_shared<Info>(system, system.Kernel().GetProcessList())); + server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system)); server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index cd0cc9287..44310756b 100644 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -4,9 +4,13 @@ #include <memory> #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/psc/psc.h" -#include "core/hle/service/server_manager.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/psc/time/power_state_service.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" #include "core/hle/service/service.h" namespace Service::PSC { @@ -76,6 +80,17 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system)); server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system)); + + auto time = std::make_shared<Time::TimeManager>(system); + + server_manager->RegisterNamedService( + "time:m", std::make_shared<Time::ServiceManager>(system, time, server_manager.get())); + server_manager->RegisterNamedService( + "time:su", std::make_shared<Time::StaticService>( + system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su")); + server_manager->RegisterNamedService("time:al", + std::make_shared<Time::IAlarmService>(system, time)); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp new file mode 100644 index 000000000..5e52c19f8 --- /dev/null +++ b/src/core/hle/service/psc/time/alarms.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/alarms.h" +#include "core/hle/service/psc/time/manager.h" + +namespace Service::PSC::Time { +Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type) + : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} { + m_event->Clear(); + + switch (type) { + case WakeupAlarm: + m_priority = 1; + break; + case BackgroundTaskAlarm: + m_priority = 0; + break; + default: + UNREACHABLE(); + return; + } +} + +Alarm::~Alarm() { + m_ctx.CloseEvent(m_event); +} + +Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager) + : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock}, + m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent( + "Psc:Alarms:Event")} {} + +Alarms::~Alarms() { + m_ctx.CloseEvent(m_event); +} + +Result Alarms::Enable(Alarm& alarm, s64 time) { + R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized); + + std::scoped_lock l{m_mutex}; + R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered); + + auto time_ns{time + m_steady_clock.GetRawTime()}; + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + time_ns = Common::AlignUp(time_ns, one_second_ns); + alarm.SetAlertTime(time_ns); + + Insert(alarm); + R_RETURN(UpdateClosestAndSignal()); +} + +void Alarms::Disable(Alarm& alarm) { + std::scoped_lock l{m_mutex}; + if (!alarm.IsLinked()) { + return; + } + + Erase(alarm); + UpdateClosestAndSignal(); +} + +void Alarms::CheckAndSignal() { + std::scoped_lock l{m_mutex}; + if (m_alarms.empty()) { + return; + } + + bool alarm_signalled{false}; + for (auto& alarm : m_alarms) { + if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) { + alarm.Signal(); + alarm.Lock(); + Erase(alarm); + + m_power_state_request_manager.UpdatePendingPowerStateRequestPriority( + alarm.GetPriority()); + alarm_signalled = true; + } + } + + if (!alarm_signalled) { + return; + } + + m_power_state_request_manager.SignalPowerStateRequestAvailability(); + UpdateClosestAndSignal(); +} + +bool Alarms::GetClosestAlarm(Alarm** out_alarm) { + std::scoped_lock l{m_mutex}; + auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + *out_alarm = alarm; + return alarm != nullptr; +} + +void Alarms::Insert(Alarm& alarm) { + // Alarms are sorted by alert time, then priority + auto it{m_alarms.begin()}; + while (it != m_alarms.end()) { + if (alarm.GetAlertTime() < it->GetAlertTime() || + (alarm.GetAlertTime() == it->GetAlertTime() && + alarm.GetPriority() < it->GetPriority())) { + m_alarms.insert(it, alarm); + return; + } + it++; + } + + m_alarms.push_back(alarm); +} + +void Alarms::Erase(Alarm& alarm) { + m_alarms.erase(m_alarms.iterator_to(alarm)); +} + +Result Alarms::UpdateClosestAndSignal() { + m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + R_SUCCEED_IF(m_closest_alarm == nullptr); + + m_event->Signal(); + + R_SUCCEED(); +} + +IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager) + : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"}, + {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm); +} + +void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm); +} + +ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type) + : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"}, + m_alarms{alarms}, m_alarm{system, m_ctx, type} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"}, + {1, &ISteadyClockAlarm::Enable, "Enable"}, + {2, &ISteadyClockAlarm::Disable, "Disable"}, + {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"}, + {10, nullptr, "CreateWakeLock"}, + {11, nullptr, "DestroyWakeLock"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_alarm.GetEventHandle()); +} + +void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto res = m_alarms.Enable(m_alarm, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISteadyClockAlarm::Disable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + m_alarms.Disable(m_alarm); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<bool>(m_alarm.IsLinked()); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h new file mode 100644 index 000000000..597770028 --- /dev/null +++ b/src/core/hle/service/psc/time/alarms.h @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager; + +enum AlarmType : u32 { + WakeupAlarm = 0, + BackgroundTaskAlarm = 1, +}; + +struct Alarm : public Common::IntrusiveListBaseNode<Alarm> { + using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType; + + Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type); + ~Alarm(); + + Kernel::KReadableEvent& GetEventHandle() { + return m_event->GetReadableEvent(); + } + + s64 GetAlertTime() const { + return m_alert_time; + } + + void SetAlertTime(s64 time) { + m_alert_time = time; + } + + u32 GetPriority() const { + return m_priority; + } + + void Signal() { + m_event->Signal(); + } + + Result Lock() { + // TODO + // if (m_lock_service) { + // return m_lock_service->Lock(); + // } + R_SUCCEED(); + } + + KernelHelpers::ServiceContext& m_ctx; + + u32 m_priority; + Kernel::KEvent* m_event{}; + s64 m_alert_time{}; + // TODO + // nn::psc::sf::IPmStateLock* m_lock_service{}; +}; + +class Alarms { +public: + explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager); + ~Alarms(); + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + s64 GetRawTime() { + return m_steady_clock.GetRawTime(); + } + + Result Enable(Alarm& alarm, s64 time); + void Disable(Alarm& alarm); + void CheckAndSignal(); + bool GetClosestAlarm(Alarm** out_alarm); + +private: + void Insert(Alarm& alarm); + void Erase(Alarm& alarm); + Result UpdateClosestAndSignal(); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + StandardSteadyClockCore& m_steady_clock; + PowerStateRequestManager& m_power_state_request_manager; + Alarm::AlarmList m_alarms; + Kernel::KEvent* m_event{}; + Alarm* m_closest_alarm{}; + std::mutex m_mutex; +}; + +class IAlarmService final : public ServiceFramework<IAlarmService> { +public: + explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager); + + ~IAlarmService() override = default; + +private: + void CreateWakeupAlarm(HLERequestContext& ctx); + void CreateBackgroundTaskAlarm(HLERequestContext& ctx); + + Core::System& m_system; + Alarms& m_alarms; +}; + +class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> { +public: + explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type); + + ~ISteadyClockAlarm() override = default; + +private: + void GetAlarmEvent(HLERequestContext& ctx); + void Enable(HLERequestContext& ctx); + void Disable(HLERequestContext& ctx); + void IsEnabled(HLERequestContext& ctx); + + KernelHelpers::ServiceContext m_ctx; + + Alarms& m_alarms; + Alarm m_alarm; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp new file mode 100644 index 000000000..ac8700f76 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" + +namespace Service::PSC::Time { + +void ContextWriter::SignalAllNodes() { + std::scoped_lock l{m_mutex}; + for (auto& operation : m_operation_events) { + operation.m_event->Signal(); + } +} + +void ContextWriter::Link(OperationEvent& operation_event) { + std::scoped_lock l{m_mutex}; + m_operation_events.push_back(operation_event); +} + +LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system, + SharedMemory& shared_memory) + : m_system{system}, m_shared_memory{shared_memory} {} + +Result LocalSystemClockContextWriter::Write(SystemClockContext& context) { + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + m_shared_memory.SetLocalSystemContext(context); + + SignalAllNodes(); + + R_SUCCEED(); +} + +NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system, + SharedMemory& shared_memory, + SystemClockCore& system_clock) + : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {} + +Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) { + s64 time{}; + [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time); + + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + m_shared_memory.SetNetworkSystemContext(context); + + SignalAllNodes(); + + R_SUCCEED(); +} + +EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter( + Core::System& system) + : m_system{system} {} + +Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) { + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + SignalAllNodes(); + + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h new file mode 100644 index 000000000..afd3725d4 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/context_writers.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class ContextWriter { +private: + using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType; + +public: + virtual ~ContextWriter() = default; + + virtual Result Write(SystemClockContext& context) = 0; + void SignalAllNodes(); + void Link(OperationEvent& operation_event); + +private: + OperationEventList m_operation_events; + std::mutex m_mutex; +}; + +class LocalSystemClockContextWriter : public ContextWriter { +public: + explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + SharedMemory& m_shared_memory; + bool m_in_use{}; + SystemClockContext m_context{}; +}; + +class NetworkSystemClockContextWriter : public ContextWriter { +public: + explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory, + SystemClockCore& system_clock); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + SharedMemory& m_shared_memory; + bool m_in_use{}; + SystemClockContext m_context{}; + SystemClockCore& m_system_clock; +}; + +class EphemeralNetworkSystemClockContextWriter : public ContextWriter { +public: + EphemeralNetworkSystemClockContextWriter(Core::System& system); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + bool m_in_use{}; + SystemClockContext m_context{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h new file mode 100644 index 000000000..0a68867d9 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class EphemeralNetworkSystemClockCore : public SystemClockCore { +public: + explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~EphemeralNetworkSystemClockCore() override = default; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp new file mode 100644 index 000000000..36dca6689 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" + +namespace Service::PSC::Time { + +void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePoint(time_point) == ResultSuccess && + context.steady_time_point.IdMatches(time_point)) { + SetContextAndWrite(context); + } else if (SetCurrentTime(time) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to SetCurrentTime"); + } + + SetInitialized(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h new file mode 100644 index 000000000..176ba3e94 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardLocalSystemClockCore : public SystemClockCore { +public: + explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardLocalSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 time); +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp new file mode 100644 index 000000000..8d6cb7db1 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" + +namespace Service::PSC::Time { + +void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) { + if (SetContextAndWrite(context) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to SetContext"); + } + m_sufficient_accuracy = accuracy; + SetInitialized(); +} + +bool StandardNetworkSystemClockCore::IsAccuracySufficient() { + if (!IsInitialized()) { + return false; + } + + SystemClockContext context{}; + SteadyClockTimePoint current_time_point{}; + if (GetCurrentTimePoint(current_time_point) != ResultSuccess || + GetContext(context) != ResultSuccess) { + return false; + } + + s64 seconds{}; + if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) != + ResultSuccess) { + return false; + } + + if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds)) + .count() < m_sufficient_accuracy) { + return true; + } + + return false; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h new file mode 100644 index 000000000..933d2c8e3 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <chrono> + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardNetworkSystemClockCore : public SystemClockCore { +public: + explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardNetworkSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 accuracy); + bool IsAccuracySufficient(); + +private: + s64 m_sufficient_accuracy{ + std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp new file mode 100644 index 000000000..7a72d7aa2 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" + +namespace Service::PSC::Time { + +void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_clock_source_id = clock_source_id; + m_rtc_offset = rtc_offset; + m_internal_offset = internal_offset; + m_test_offset = test_offset; + if (is_rtc_reset_detected) { + SetResetDetected(); + } + SetInitialized(); +} + +void StandardSteadyClockCore::SetRtcOffset(s64 offset) { + m_rtc_offset = offset; +} + +void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + + m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count(); + m_continuous_adjustment_time_point.diff_scale = 0; + m_continuous_adjustment_time_point.shift_amount = 0; + m_continuous_adjustment_time_point.lower = time; + m_continuous_adjustment_time_point.upper = time; + m_continuous_adjustment_time_point.clock_source_id = clock_source_id; +} + +void StandardSteadyClockCore::GetContinuousAdjustment( + ContinuousAdjustmentTimePoint& out_time_point) const { + out_time_point = m_continuous_adjustment_time_point; +} + +void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto uptime_ns{ConvertToTimeSpan(ticks).count()}; + auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) * + m_continuous_adjustment_time_point.diff_scale) >> + m_continuous_adjustment_time_point.shift_amount}; + auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower}; + + auto last_time_point{m_continuous_adjustment_time_point.upper}; + m_continuous_adjustment_time_point.upper = in_time; + auto t1{std::min<s64>(expected_time, last_time_point)}; + expected_time = std::max<s64>(expected_time, last_time_point); + expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time; + + auto new_diff{in_time < expected_time ? -55 : 55}; + + m_continuous_adjustment_time_point.rtc_offset = uptime_ns; + m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14; + m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff; + m_continuous_adjustment_time_point.lower = expected_time; +} + +Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto current_time_ns = GetCurrentRawTimePointImpl(); + auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns)); + out_time_point.time_point = current_time_s.count(); + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() { + std::scoped_lock l{m_mutex}; + auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())}; + auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count(); + auto time_point = std::max<s64>(current_time_ns, m_cached_time_point); + m_cached_time_point = time_point; + return time_point; +} + +s64 StandardSteadyClockCore::GetTestOffsetImpl() const { + return m_test_offset; +} + +void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) { + m_test_offset = offset; +} + +s64 StandardSteadyClockCore::GetInternalOffsetImpl() const { + return m_internal_offset; +} + +void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) { + m_internal_offset = offset; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h new file mode 100644 index 000000000..bbf98fcd5 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class StandardSteadyClockCore : public SteadyClockCore { +public: + explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {} + ~StandardSteadyClockCore() override = default; + + void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset, + s64 test_offset, bool is_rtc_reset_detected); + + void SetRtcOffset(s64 offset); + void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time); + void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const; + void UpdateContinuousAdjustmentTime(s64 time); + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + std::mutex m_mutex; + s64 m_test_offset{}; + s64 m_internal_offset{}; + ClockSourceId m_clock_source_id{}; + s64 m_rtc_offset{}; + s64 m_cached_time_point{}; + ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp new file mode 100644 index 000000000..9e9be05d6 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" + +namespace Service::PSC::Time { + +StandardUserSystemClockCore::StandardUserSystemClockCore( + Core::System& system, StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock) + : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system}, + m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock}, + m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent( + "Psc:StandardUserSystemClockCore:Event")} {} + +StandardUserSystemClockCore::~StandardUserSystemClockCore() { + m_ctx.CloseEvent(m_event); +} + +Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) { + R_SUCCEED_IF(m_automatic_correction == automatic_correction); + R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches()); + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + m_automatic_correction = automatic_correction; + R_SUCCEED(); +} + +Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const { + if (!m_automatic_correction) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + if (!m_network_system_clock.CheckClockSourceMatches()) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + R_RETURN(m_local_system_clock.GetContext(out_context)); +} + +Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) { + R_RETURN(ResultNotImplemented); +} + +Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) { + out_time_point = m_time_point; + R_SUCCEED(); +} + +void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) { + m_time_point = time_point; + m_event->Signal(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h new file mode 100644 index 000000000..a7fe7648d --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class StandardUserSystemClockCore : public SystemClockCore { +public: + explicit StandardUserSystemClockCore(Core::System& system, + StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock); + ~StandardUserSystemClockCore() override; + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + bool GetAutomaticCorrection() const { + return m_automatic_correction; + } + Result SetAutomaticCorrection(bool automatic_correction); + + Result GetContext(SystemClockContext& out_context) const override; + Result SetContext(SystemClockContext& context) override; + + Result GetTimePoint(SteadyClockTimePoint& out_time_point); + void SetTimePointAndSignal(SteadyClockTimePoint& time_point); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + bool m_automatic_correction{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + SteadyClockTimePoint m_time_point{}; + Kernel::KEvent* m_event{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h new file mode 100644 index 000000000..f933cc15f --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <chrono> + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class SteadyClockCore { +public: + SteadyClockCore() = default; + virtual ~SteadyClockCore() = default; + + void SetInitialized() { + m_initialized = true; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetResetDetected() { + m_reset_detected = true; + } + + bool IsResetDetected() const { + return m_reset_detected; + } + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_TRY(GetCurrentTimePointImpl(out_time_point)); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + out_time_point.time_point += GetTestOffsetImpl() / one_second_ns; + out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns; + R_SUCCEED(); + } + + s64 GetTestOffset() const { + return GetTestOffsetImpl(); + } + + void SetTestOffset(s64 offset) { + SetTestOffsetImpl(offset); + } + + s64 GetInternalOffset() const { + return GetInternalOffsetImpl(); + } + + s64 GetRawTime() { + return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl(); + } + + Result GetRtcValue(s64& out_value) { + R_RETURN(GetRtcValueImpl(out_value)); + } + + Result GetSetupResultValue() { + R_RETURN(GetSetupResultValueImpl()); + } + +private: + virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0; + virtual s64 GetCurrentRawTimePointImpl() = 0; + virtual s64 GetTestOffsetImpl() const = 0; + virtual void SetTestOffsetImpl(s64 offset) = 0; + virtual s64 GetInternalOffsetImpl() const = 0; + virtual void SetInternalOffsetImpl(s64 offset) = 0; + virtual Result GetRtcValueImpl(s64& out_value) = 0; + virtual Result GetSetupResultValueImpl() = 0; + + bool m_initialized{}; + bool m_reset_detected{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp new file mode 100644 index 000000000..c507ef517 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" + +namespace Service::PSC::Time { + +bool SystemClockCore::CheckClockSourceMatches() { + SystemClockContext context{}; + if (GetContext(context) != ResultSuccess) { + return false; + } + + SteadyClockTimePoint time_point{}; + if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) { + return false; + } + + return context.steady_time_point.IdMatches(time_point); +} + +Result SystemClockCore::GetCurrentTime(s64* out_time) const { + R_UNLESS(out_time != nullptr, ResultInvalidArgument); + + SystemClockContext context{}; + SteadyClockTimePoint time_point{}; + + R_TRY(m_steady_clock.GetCurrentTimePoint(time_point)); + R_TRY(GetContext(context)); + + R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch); + + *out_time = context.offset + time_point.time_point; + R_SUCCEED(); +} + +Result SystemClockCore::SetCurrentTime(s64 time) { + SteadyClockTimePoint time_point{}; + R_TRY(m_steady_clock.GetCurrentTimePoint(time_point)); + + SystemClockContext context{ + .offset = time - time_point.time_point, + .steady_time_point = time_point, + }; + R_RETURN(SetContextAndWrite(context)); +} + +Result SystemClockCore::GetContext(SystemClockContext& out_context) const { + out_context = m_context; + R_SUCCEED(); +} + +Result SystemClockCore::SetContext(SystemClockContext& context) { + m_context = context; + R_SUCCEED(); +} + +Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) { + R_TRY(SetContext(context)); + + if (m_context_writer) { + R_RETURN(m_context_writer->Write(context)); + } + + R_SUCCEED(); +} + +void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) { + if (m_context_writer) { + m_context_writer->Link(operation_event); + } +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h new file mode 100644 index 000000000..73811712e --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { +class ContextWriter; + +class SystemClockCore { +public: + explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {} + virtual ~SystemClockCore() = default; + + SteadyClockCore& GetSteadyClock() { + return m_steady_clock; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetInitialized() { + m_initialized = true; + } + + void SetContextWriter(ContextWriter& context_writer) { + m_context_writer = &context_writer; + } + + bool CheckClockSourceMatches(); + + Result GetCurrentTime(s64* out_time) const; + Result SetCurrentTime(s64 time); + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point)); + } + + virtual Result GetContext(SystemClockContext& out_context) const; + virtual Result SetContext(SystemClockContext& context); + Result SetContextAndWrite(SystemClockContext& context); + + void LinkOperationEvent(OperationEvent& operation_event); + +private: + bool m_initialized{}; + ContextWriter* m_context_writer{}; + SteadyClockCore& m_steady_clock; + SystemClockContext m_context{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp new file mode 100644 index 000000000..22da1fbcc --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" + +namespace Service::PSC::Time { + +Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count(); + out_time_point.time_point = current_time_s; + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePointImpl(time_point) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!"); + } + return std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::seconds(time_point.time_point)) + .count(); +} + +s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {} + +s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h new file mode 100644 index 000000000..a7bea86a2 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "common/uuid.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TickBasedSteadyClockCore : public SteadyClockCore { +public: + explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {} + ~TickBasedSteadyClockCore() override = default; + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp new file mode 100644 index 000000000..a6d9f02ca --- /dev/null +++ b/src/core/hle/service/psc/time/common.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { +OperationEvent::OperationEvent(Core::System& system) + : m_ctx{system, "Time:OperationEvent"}, m_event{ + m_ctx.CreateEvent("Time:OperationEvent:Event")} {} + +OperationEvent::~OperationEvent() { + m_ctx.CloseEvent(m_event); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h new file mode 100644 index 000000000..d17b31143 --- /dev/null +++ b/src/core/hle/service/psc/time/common.h @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <chrono> + +#include "common/common_types.h" +#include "common/intrusive_list.h" +#include "common/uuid.h" +#include "common/wall_clock.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/errors.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +using ClockSourceId = Common::UUID; + +struct SteadyClockTimePoint { + constexpr bool IdMatches(SteadyClockTimePoint& other) { + return clock_source_id == other.clock_source_id; + } + bool operator==(const SteadyClockTimePoint& other) const = default; + + s64 time_point; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!"); +static_assert(std::is_trivial_v<ClockSourceId>); + +struct SystemClockContext { + bool operator==(const SystemClockContext& other) const = default; + + s64 offset; + SteadyClockTimePoint steady_time_point; +}; +static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!"); +static_assert(std::is_trivial_v<SystemClockContext>); + +enum class TimeType : u8 { + UserSystemClock, + NetworkSystemClock, + LocalSystemClock, +}; + +struct CalendarTime { + s16 year; + s8 month; + s8 day; + s8 hour; + s8 minute; + s8 second; +}; +static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!"); + +struct CalendarAdditionalInfo { + s32 day_of_week; + s32 day_of_year; + std::array<char, 8> name; + s32 is_dst; + s32 ut_offset; +}; +static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!"); + +struct LocationName { + std::array<char, 36> name; +}; +static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!"); + +struct RuleVersion { + std::array<char, 16> version; +}; +static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!"); + +struct ClockSnapshot { + SystemClockContext user_context; + SystemClockContext network_context; + s64 user_time; + s64 network_time; + CalendarTime user_calendar_time; + CalendarTime network_calendar_time; + CalendarAdditionalInfo user_calendar_additional_time; + CalendarAdditionalInfo network_calendar_additional_time; + SteadyClockTimePoint steady_clock_time_point; + LocationName location_name; + bool is_automatic_correction_enabled; + TimeType type; + u16 unk_CE; +}; +static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!"); +static_assert(std::is_trivial_v<ClockSnapshot>); + +struct ContinuousAdjustmentTimePoint { + s64 rtc_offset; + s64 diff_scale; + s64 shift_amount; + s64 lower; + s64 upper; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38, + "ContinuousAdjustmentTimePoint has the wrong size!"); +static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>); + +struct AlarmInfo { + s64 alert_time; + u32 priority; +}; +static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!"); + +struct StaticServiceSetupInfo { + bool can_write_local_clock; + bool can_write_user_clock; + bool can_write_network_clock; + bool can_write_timezone_device_location; + bool can_write_steady_clock; + bool can_write_uninitialized_clock; +}; +static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!"); + +struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> { + using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType; + + OperationEvent(Core::System& system); + ~OperationEvent(); + + KernelHelpers::ServiceContext m_ctx; + Kernel::KEvent* m_event{}; +}; + +constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) { + constexpr auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + + constexpr s64 max{Common::WallClock::CNTFRQ * + (std::numeric_limits<s64>::max() / one_second_ns)}; + + if (ticks > max) { + return std::chrono::nanoseconds(std::numeric_limits<s64>::max()); + } else if (ticks < -max) { + return std::chrono::nanoseconds(std::numeric_limits<s64>::min()); + } + + auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns}; + auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ}; + + return std::chrono::nanoseconds(a + b); +} + +constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, SteadyClockTimePoint& a, + SteadyClockTimePoint& b) { + R_UNLESS(out_seconds, ResultInvalidArgument); + R_UNLESS(a.IdMatches(b), ResultInvalidArgument); + R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits<s64>::max(), + ResultOverflow); + R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(), + ResultOverflow); + + *out_seconds = b.time_point - a.time_point; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/errors.h b/src/core/hle/service/psc/time/errors.h new file mode 100644 index 000000000..6d833a006 --- /dev/null +++ b/src/core/hle/service/psc/time/errors.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::PSC::Time { + +constexpr Result ResultPermissionDenied{ErrorModule::Time, 1}; +constexpr Result ResultClockMismatch{ErrorModule::Time, 102}; +constexpr Result ResultClockUninitialized{ErrorModule::Time, 103}; +constexpr Result ResultTimeNotFound{ErrorModule::Time, 200}; +constexpr Result ResultOverflow{ErrorModule::Time, 201}; +constexpr Result ResultFailed{ErrorModule::Time, 801}; +constexpr Result ResultInvalidArgument{ErrorModule::Time, 901}; +constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902}; +constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903}; +constexpr Result ResultRtcTimeout{ErrorModule::Time, 988}; +constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989}; +constexpr Result ResultNotImplemented{ErrorModule::Time, 990}; +constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h new file mode 100644 index 000000000..62ded1247 --- /dev/null +++ b/src/core/hle/service/psc/time/manager.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/psc/time/alarms.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/psc/time/shared_memory.h" +#include "core/hle/service/psc/time/time_zone.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager { +public: + explicit TimeManager(Core::System& system) + : m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system}, + m_standard_local_system_clock{m_standard_steady_clock}, + m_standard_network_system_clock{m_standard_steady_clock}, + m_standard_user_system_clock{m_system, m_standard_local_system_clock, + m_standard_network_system_clock}, + m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system}, + m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock, + m_power_state_request_manager}, + m_local_system_clock_context_writer{m_system, m_shared_memory}, + m_network_system_clock_context_writer{m_system, m_shared_memory, + m_standard_user_system_clock}, + m_ephemeral_network_clock_context_writer{m_system} {} + + Core::System& m_system; + + StandardSteadyClockCore m_standard_steady_clock; + TickBasedSteadyClockCore m_tick_based_steady_clock; + StandardLocalSystemClockCore m_standard_local_system_clock; + StandardNetworkSystemClockCore m_standard_network_system_clock; + StandardUserSystemClockCore m_standard_user_system_clock; + EphemeralNetworkSystemClockCore m_ephemeral_network_clock; + TimeZone m_time_zone; + SharedMemory m_shared_memory; + PowerStateRequestManager m_power_state_request_manager; + Alarms m_alarms; + LocalSystemClockContextWriter m_local_system_clock_context_writer; + NetworkSystemClockContextWriter m_network_system_clock_context_writer; + EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_request_manager.cpp b/src/core/hle/service/psc/time/power_state_request_manager.cpp new file mode 100644 index 000000000..17de0bf4d --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_request_manager.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" + +namespace Service::PSC::Time { + +PowerStateRequestManager::PowerStateRequestManager(Core::System& system) + : m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"}, + m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {} + +PowerStateRequestManager::~PowerStateRequestManager() { + m_ctx.CloseEvent(m_event); +} + +void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) { + std::scoped_lock l{m_mutex}; + if (m_has_pending_request) { + m_pending_request_priority = std::max(m_pending_request_priority, priority); + } else { + m_pending_request_priority = priority; + m_has_pending_request = true; + } +} + +void PowerStateRequestManager::SignalPowerStateRequestAvailability() { + std::scoped_lock l{m_mutex}; + if (m_has_pending_request) { + if (!m_has_available_request) { + m_has_available_request = true; + } + m_has_pending_request = false; + m_available_request_priority = m_pending_request_priority; + m_event->Signal(); + } +} + +bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) { + std::scoped_lock l{m_mutex}; + auto had_request{m_has_available_request}; + if (m_has_available_request) { + out_priority = m_available_request_priority; + m_has_available_request = false; + m_event->Clear(); + } + return had_request; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_request_manager.h b/src/core/hle/service/psc/time/power_state_request_manager.h new file mode 100644 index 000000000..30a0c947d --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_request_manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class PowerStateRequestManager { +public: + explicit PowerStateRequestManager(Core::System& system); + ~PowerStateRequestManager(); + + Kernel::KReadableEvent& GetReadableEvent() { + return m_event->GetReadableEvent(); + } + + void UpdatePendingPowerStateRequestPriority(u32 priority); + void SignalPowerStateRequestAvailability(); + bool GetAndClearPowerStateRequest(u32& out_priority); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + Kernel::KEvent* m_event{}; + bool m_has_pending_request{}; + u32 m_pending_request_priority{}; + bool m_has_available_request{}; + u32 m_available_request_priority{}; + std::mutex m_mutex; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_service.cpp b/src/core/hle/service/psc/time/power_state_service.cpp new file mode 100644 index 000000000..b0ae71bf9 --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_service.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/power_state_service.h" + +namespace Service::PSC::Time { + +IPowerStateRequestHandler::IPowerStateRequestHandler( + Core::System& system_, PowerStateRequestManager& power_state_request_manager) + : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{ + power_state_request_manager} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle, "GetPowerStateRequestEventReadableHandle"}, + {1, &IPowerStateRequestHandler::GetAndClearPowerStateRequest, "GetAndClearPowerStateRequest"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +void IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_power_state_request_manager.GetReadableEvent()); +} + +void IPowerStateRequestHandler::GetAndClearPowerStateRequest(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 priority{}; + auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority); + + if (cleared) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(priority); + rb.Push(cleared); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(cleared); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_service.h b/src/core/hle/service/psc/time/power_state_service.h new file mode 100644 index 000000000..3ebfddb79 --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_service.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> { +public: + explicit IPowerStateRequestHandler(Core::System& system, + PowerStateRequestManager& power_state_request_manager); + + ~IPowerStateRequestHandler() override = default; + +private: + void GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx); + void GetAndClearPowerStateRequest(HLERequestContext& ctx); + + Core::System& m_system; + PowerStateRequestManager& m_power_state_request_manager; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp new file mode 100644 index 000000000..60820aa9b --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.cpp @@ -0,0 +1,494 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/power_state_service.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" + +namespace Service::PSC::Time { + +ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time, + ServerManager* server_manager) + : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)}, + m_server_manager{*server_manager}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, + m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms}, + m_local_system_context_writer{m_time->m_local_system_clock_context_writer}, + m_network_system_context_writer{m_time->m_network_system_clock_context_writer}, + m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer}, + m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ServiceManager::Handle_GetStaticServiceAsUser, "GetStaticServiceAsUser"}, + {5, &ServiceManager::Handle_GetStaticServiceAsAdmin, "GetStaticServiceAsAdmin"}, + {6, &ServiceManager::Handle_GetStaticServiceAsRepair, "GetStaticServiceAsRepair"}, + {9, &ServiceManager::Handle_GetStaticServiceAsServiceManager, "GetStaticServiceAsServiceManager"}, + {10, &ServiceManager::Handle_SetupStandardSteadyClockCore, "SetupStandardSteadyClockCore"}, + {11, &ServiceManager::Handle_SetupStandardLocalSystemClockCore, "SetupStandardLocalSystemClockCore"}, + {12, &ServiceManager::Handle_SetupStandardNetworkSystemClockCore, "SetupStandardNetworkSystemClockCore"}, + {13, &ServiceManager::Handle_SetupStandardUserSystemClockCore, "SetupStandardUserSystemClockCore"}, + {14, &ServiceManager::Handle_SetupTimeZoneServiceCore, "SetupTimeZoneServiceCore"}, + {15, &ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore, "SetupEphemeralNetworkSystemClockCore"}, + {50, &ServiceManager::Handle_GetStandardLocalClockOperationEvent, "GetStandardLocalClockOperationEvent"}, + {51, &ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager, "GetStandardNetworkClockOperationEventForServiceManager"}, + {52, &ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager, "GetEphemeralNetworkClockOperationEventForServiceManager"}, + {60, &ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"}, + {100, &ServiceManager::Handle_SetStandardSteadyClockBaseTime, "SetStandardSteadyClockBaseTime"}, + {200, &ServiceManager::Handle_GetClosestAlarmUpdatedEvent, "GetClosestAlarmUpdatedEvent"}, + {201, &ServiceManager::Handle_CheckAndSignalAlarms, "CheckAndSignalAlarms"}, + {202, &ServiceManager::Handle_GetClosestAlarmInfo, "GetClosestAlarmInfo "}, + }; + // clang-format on + RegisterHandlers(functions); + + m_local_system_context_writer.Link(m_local_operation); + m_network_system_context_writer.Link(m_network_operation); + m_ephemeral_system_context_writer.Link(m_ephemeral_operation); +} + +void ServiceManager::SetupSAndP() { + if (!m_is_s_and_p_setup) { + m_is_s_and_p_setup = true; + m_server_manager.RegisterNamedService( + "time:s", std::make_shared<StaticService>( + m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s")); + m_server_manager.RegisterNamedService("time:p", + std::make_shared<IPowerStateRequestHandler>( + m_system, m_time->m_power_state_request_manager)); + } +} + +void ServiceManager::CheckAndSetupServicesSAndP() { + if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() && + m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() && + m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) { + SetupSAndP(); + } +} + +void ServiceManager::Handle_GetStaticServiceAsUser(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsUser(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsAdmin(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsRepair(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsServiceManager(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + bool reset_detected; + Common::UUID clock_source_id; + s64 rtc_offset; + s64 internal_offset; + s64 test_offset; + }; + static_assert(sizeof(Parameters) == 0x30); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset, + params.internal_offset, params.test_offset, + params.reset_detected); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + auto time{rp.Pop<s64>()}; + + auto res = SetupStandardLocalSystemClockCore(context, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + auto accuracy{rp.Pop<s64>()}; + + auto res = SetupStandardNetworkSystemClockCore(context, accuracy); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + bool automatic_correction; + SteadyClockTimePoint time_point; + }; + static_assert(sizeof(Parameters) == 0x20); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + u32 location_count; + LocationName name; + SteadyClockTimePoint time_point; + RuleVersion rule_version; + }; + static_assert(sizeof(Parameters) == 0x50); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + + auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version, + params.location_count, rule_buffer); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = SetupEphemeralNetworkSystemClockCore(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardLocalClockOperationEvent(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardNetworkClockOperationEventForServiceManager(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetEphemeralNetworkClockOperationEventForServiceManager(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto base_time{rp.Pop<s64>()}; + + auto res = SetStandardSteadyClockBaseTime(base_time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetClosestAlarmUpdatedEvent(&event); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void ServiceManager::Handle_CheckAndSignalAlarms(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = CheckAndSignalAlarms(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + AlarmInfo alarm_info{}; + bool is_valid{}; + s64 time{}; + auto res = GetClosestAlarmInfo(is_valid, alarm_info, time); + + struct OutParameters { + bool is_valid; + AlarmInfo alarm_info; + s64 time; + }; + static_assert(sizeof(OutParameters) == 0x20); + + OutParameters out_params{ + .is_valid = is_valid, + .alarm_info = alarm_info, + .time = time, + }; + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<OutParameters>(out_params); +} + +// =============================== Implementations =========================== + +Result ServiceManager::GetStaticService(std::shared_ptr<StaticService>& out_service, + StaticServiceSetupInfo setup_info, const char* name) { + out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name); + R_SUCCEED(); +} + +Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u")); +} + +Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a")); +} + +Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r")); +} + +Result ServiceManager::GetStaticServiceAsServiceManager( + std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm")); +} + +Result ServiceManager::SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset, + is_rtc_reset_detected); + auto time = m_steady_clock.GetRawTime(); + auto ticks = m_system.CoreTiming().GetClockTicks(); + auto boot_time = time - ConvertToTimeSpan(ticks).count(); + m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time); + m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time); + + ContinuousAdjustmentTimePoint time_point{}; + m_steady_clock.GetContinuousAdjustment(time_point); + m_shared_memory.SetContinuousAdjustment(time_point); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) { + m_local_system_clock.SetContextWriter(m_local_system_context_writer); + m_local_system_clock.Initialize(context, time); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context, + s64 accuracy) { + // TODO this is a hack! The network clock should be updated independently, from the ntc service + // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot + // to avoid it being stuck at 0. + if (context == Service::PSC::Time::SystemClockContext{}) { + m_local_system_clock.GetContext(context); + } + + m_network_system_clock.SetContextWriter(m_network_system_context_writer); + m_network_system_clock.Initialize(context, accuracy); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point, + bool automatic_correction) { + // TODO this is a hack! The user clock should be updated independently, from the ntc service + // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot + // to avoid it being stuck at 0. + if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) { + m_local_system_clock.GetCurrentTimePoint(time_point); + } + + m_user_system_clock.SetAutomaticCorrection(automatic_correction); + m_user_system_clock.SetTimePointAndSignal(time_point); + m_user_system_clock.SetInitialized(); + m_shared_memory.SetAutomaticCorrection(automatic_correction); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name, + SteadyClockTimePoint& time_point, + RuleVersion& rule_version, u32 location_count, + std::span<const u8> rule_buffer) { + if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to parse time zone binary!"); + } + + m_time_zone.SetTimePoint(time_point); + m_time_zone.SetTotalLocationNameCount(location_count); + m_time_zone.SetRuleVersion(rule_version); + m_time_zone.SetInitialized(); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupEphemeralNetworkSystemClockCore() { + m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer); + m_ephemeral_network_clock.SetInitialized(); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event) { + *out_event = m_local_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager( + Kernel::KEvent** out_event) { + *out_event = m_network_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager( + Kernel::KEvent** out_event) { + *out_event = m_ephemeral_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + Kernel::KEvent** out_event) { + *out_event = &m_user_system_clock.GetEvent(); + R_SUCCEED(); +} + +Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) { + m_steady_clock.SetRtcOffset(base_time); + auto time = m_steady_clock.GetRawTime(); + auto ticks = m_system.CoreTiming().GetClockTicks(); + auto diff = time - ConvertToTimeSpan(ticks).count(); + m_shared_memory.UpdateBaseTime(diff); + m_steady_clock.UpdateContinuousAdjustmentTime(diff); + + ContinuousAdjustmentTimePoint time_point{}; + m_steady_clock.GetContinuousAdjustment(time_point); + m_shared_memory.SetContinuousAdjustment(time_point); + R_SUCCEED(); +} + +Result ServiceManager::GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event) { + *out_event = &m_alarms.GetEvent(); + R_SUCCEED(); +} + +Result ServiceManager::CheckAndSignalAlarms() { + m_alarms.CheckAndSignal(); + R_SUCCEED(); +} + +Result ServiceManager::GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time) { + Alarm* alarm{nullptr}; + out_is_valid = m_alarms.GetClosestAlarm(&alarm); + if (out_is_valid) { + out_info = { + .alert_time = alarm->GetAlertTime(), + .priority = alarm->GetPriority(), + }; + out_time = m_alarms.GetRawTime(); + } + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/service_manager.h b/src/core/hle/service/psc/time/service_manager.h new file mode 100644 index 000000000..1d9952317 --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> +#include <memory> + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::PSC::Time { +class StaticService; + +class ServiceManager final : public ServiceFramework<ServiceManager> { +public: + explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time, + ServerManager* server_manager); + ~ServiceManager() override = default; + + Result GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsServiceManager(std::shared_ptr<StaticService>& out_service); + Result SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected); + Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time); + Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy); + Result SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point, + bool automatic_correction); + Result SetupTimeZoneServiceCore(LocationName& name, SteadyClockTimePoint& time_point, + RuleVersion& rule_version, u32 location_count, + std::span<const u8> rule_buffer); + Result SetupEphemeralNetworkSystemClockCore(); + Result GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event); + Result GetStandardNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event); + Result GetEphemeralNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(Kernel::KEvent** out_event); + Result SetStandardSteadyClockBaseTime(s64 base_time); + Result GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event); + Result CheckAndSignalAlarms(); + Result GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time); + +private: + void CheckAndSetupServicesSAndP(); + void SetupSAndP(); + Result GetStaticService(std::shared_ptr<StaticService>& out_service, + StaticServiceSetupInfo setup_info, const char* name); + + void Handle_GetStaticServiceAsUser(HLERequestContext& ctx); + void Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx); + void Handle_GetStaticServiceAsRepair(HLERequestContext& ctx); + void Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx); + void Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx); + void Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx); + void Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx); + void Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx); + void Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx); + void Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx); + void Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx); + void Handle_GetStandardNetworkClockOperationEventForServiceManager(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkClockOperationEventForServiceManager(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx); + void Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx); + void Handle_CheckAndSignalAlarms(HLERequestContext& ctx); + void Handle_GetClosestAlarmInfo(HLERequestContext& ctx); + + Core::System& m_system; + std::shared_ptr<TimeManager> m_time; + ServerManager& m_server_manager; + bool m_is_s_and_p_setup{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + StandardSteadyClockCore& m_steady_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; + Alarms& m_alarms; + LocalSystemClockContextWriter& m_local_system_context_writer; + NetworkSystemClockContextWriter& m_network_system_context_writer; + EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer; + OperationEvent m_local_operation; + OperationEvent m_network_operation; + OperationEvent m_ephemeral_operation; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp new file mode 100644 index 000000000..defaceebe --- /dev/null +++ b/src/core/hle/service/psc/time/shared_memory.cpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Service::PSC::Time { +namespace { +template <typename T> +constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { + while (true) { + // Get the counter. + auto counter = p->m_counter; + + // Get the value. + auto value = p->m_value[counter % 2]; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_acquire); + + // Check that the counter matches. + if (counter == p->m_counter) { + return value; + } + } +} + +template <typename T> +constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { + // Get the current counter. + auto counter = p->m_counter; + + // Increment the counter. + ++counter; + + // Store the updated value. + p->m_value[counter % 2] = value; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_release); + + // Set the updated counter. + p->m_counter = counter; +} +} // namespace + +SharedMemory::SharedMemory(Core::System& system) + : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()}, + m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} { + std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); +} + +void SharedMemory::SetLocalSystemContext(SystemClockContext& context) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context); +} + +void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context); +} + +void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, + {time_point, clock_source_id}); +} + +void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point); +} + +void SharedMemory::SetAutomaticCorrection(bool automatic_correction) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction); +} + +void SharedMemory::UpdateBaseTime(s64 time) { + SteadyClockTimePoint time_point{ + ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)}; + + time_point.time_point = time; + + WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/shared_memory.h b/src/core/hle/service/psc/time/shared_memory.h new file mode 100644 index 000000000..f9bf97d5c --- /dev/null +++ b/src/core/hle/service/psc/time/shared_memory.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { + +template <typename T> +struct LockFreeAtomicType { + u32 m_counter; + std::array<T, 2> m_value; +}; + +struct SharedMemoryStruct { + LockFreeAtomicType<SteadyClockTimePoint> steady_time_points; + LockFreeAtomicType<SystemClockContext> local_system_clock_contexts; + LockFreeAtomicType<SystemClockContext> network_system_clock_contexts; + LockFreeAtomicType<bool> automatic_corrections; + LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points; + std::array<char, 0xEB8> pad0148; +}; +static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0, + "steady_time_points are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38, + "local_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80, + "network_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8, + "automatic_corrections are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0, + "continuous_adjustment_time_points are in the wrong place!"); +static_assert(sizeof(SharedMemoryStruct) == 0x1000, + "Time's SharedMemoryStruct has the wrong size!"); +static_assert(std::is_trivial_v<SharedMemoryStruct>); + +class SharedMemory { +public: + explicit SharedMemory(Core::System& system); + + Kernel::KSharedMemory& GetKSharedMemory() { + return m_k_shared_memory; + } + + void SetLocalSystemContext(SystemClockContext& context); + void SetNetworkSystemContext(SystemClockContext& context); + void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff); + void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point); + void SetAutomaticCorrection(bool automatic_correction); + void UpdateBaseTime(s64 time); + +private: + Core::System& m_system; + Kernel::KSharedMemory& m_k_shared_memory; + SharedMemoryStruct* m_shared_memory_ptr; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp new file mode 100644 index 000000000..6f8cf3f88 --- /dev/null +++ b/src/core/hle/service/psc/time/static.cpp @@ -0,0 +1,500 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/psc/time/shared_memory.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::PSC::Time { +namespace { +constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point, + SystemClockContext& context) { + R_UNLESS(out_time != nullptr, ResultInvalidArgument); + R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch); + + *out_time = context.offset + time_point.time_point; + R_SUCCEED(); +} +} // namespace + +StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name) + : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{ + m_time->m_shared_memory} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"}, + {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"}, + {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"}, + {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"}, + {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"}, + {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"}, + {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +Result StaticService::GetClockSnapshotImpl(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, TimeType type) { + out_snapshot.user_context = user_context; + out_snapshot.network_context = network_context; + + R_TRY( + m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot.steady_clock_time_point)); + + out_snapshot.is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection(); + + R_TRY(m_time_zone.GetLocationName(out_snapshot.location_name)); + + R_TRY(GetTimeFromTimePointAndContext( + &out_snapshot.user_time, out_snapshot.steady_clock_time_point, out_snapshot.user_context)); + + R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.user_calendar_time, + out_snapshot.user_calendar_additional_time, + out_snapshot.user_time)); + + if (GetTimeFromTimePointAndContext(&out_snapshot.network_time, + out_snapshot.steady_clock_time_point, + out_snapshot.network_context) != ResultSuccess) { + out_snapshot.network_time = 0; + } + + R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.network_calendar_time, + out_snapshot.network_calendar_additional_time, + out_snapshot.network_time)); + out_snapshot.type = type; + out_snapshot.unk_CE = 0; + R_SUCCEED(); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SteadyClock> service{}; + auto res = GetStandardSteadyClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<TimeZoneService> service{}; + auto res = GetTimeZoneService(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KSharedMemory* shared_memory{}; + auto res = GetSharedMemoryNativeHandle(&shared_memory); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(shared_memory); +} + +void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(m_setup_info.can_write_steady_clock ? ResultNotImplemented : ResultPermissionDenied); +} + +void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_enabled{}; + auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop<bool>()}; + + auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_sufficient{}; + auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_sufficient); +} + +void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SteadyClockTimePoint time_point{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum<TimeType>()}; + + ClockSnapshot snapshot{}; + auto res = GetClockSnapshot(snapshot, type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto clock_type{rp.PopEnum<TimeType>()}; + [[maybe_unused]] auto alignment{rp.Pop<u32>()}; + auto user_context{rp.PopRaw<SystemClockContext>()}; + auto network_context{rp.PopRaw<SystemClockContext>()}; + + ClockSnapshot snapshot{}; + auto res = + GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + ClockSnapshot a{}; + ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot)); + + s64 difference{}; + auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(difference); +} + +void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + ClockSnapshot a{}; + ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot)); + + s64 time{}; + auto res = CalculateSpanBetween(time, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result StaticService::GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock, + m_setup_info.can_write_user_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service) { + out_service = + std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) { + out_service = + std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone, + m_setup_info.can_write_timezone_device_location); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock, + m_setup_info.can_write_local_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) { + *out_shared_memory = &m_shared_memory.GetKSharedMemory(); + R_SUCCEED(); +} + +Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled) { + R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); + + out_is_enabled = m_user_system_clock.GetAutomaticCorrection(); + R_SUCCEED(); +} + +Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction) { + R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(), + ResultClockUninitialized); + R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied); + + R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction)); + + m_shared_memory.SetAutomaticCorrection(automatic_correction); + + SteadyClockTimePoint time_point{}; + R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); + + m_user_system_clock.SetTimePointAndSignal(time_point); + m_user_system_clock.GetEvent().Signal(); + R_SUCCEED(); +} + +Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) { + out_is_sufficient = m_network_system_clock.IsAccuracySufficient(); + R_SUCCEED(); +} + +Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + SteadyClockTimePoint& out_time_point) { + R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); + + m_user_system_clock.GetTimePoint(out_time_point); + + R_SUCCEED(); +} + +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, + SystemClockContext& context) { + R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); + + SteadyClockTimePoint time_point{}; + R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); + + R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time{ConvertToTimeSpan(ticks).count()}; + out_time = ((context.offset + time_point.time_point) - (current_time / one_second_ns)); + R_SUCCEED(); +} + +Result StaticService::GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type) { + SystemClockContext user_context{}; + R_TRY(m_user_system_clock.GetContext(user_context)); + + SystemClockContext network_context{}; + R_TRY(m_network_system_clock.GetContext(network_context)); + + R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); +} + +Result StaticService::GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, + TimeType type) { + R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); +} + +Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, + ClockSnapshot& a, + ClockSnapshot& b) { + auto diff_s = + std::chrono::seconds(b.user_context.offset) - std::chrono::seconds(a.user_context.offset); + + if (a.user_context == b.user_context || + !a.user_context.steady_time_point.IdMatches(b.user_context.steady_time_point)) { + out_time = 0; + R_SUCCEED(); + } + + if (!a.is_automatic_correction_enabled || !b.is_automatic_correction_enabled) { + out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count(); + R_SUCCEED(); + } + + if (a.network_context.steady_time_point.IdMatches(a.steady_clock_time_point) || + b.network_context.steady_time_point.IdMatches(b.steady_clock_time_point)) { + out_time = 0; + R_SUCCEED(); + } + + out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count(); + R_SUCCEED(); +} + +Result StaticService::CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b) { + s64 time_s{}; + auto res = + GetSpanBetweenTimePoints(&time_s, a.steady_clock_time_point, b.steady_clock_time_point); + + if (res != ResultSuccess) { + R_UNLESS(a.network_time != 0 && b.network_time != 0, ResultTimeNotFound); + time_s = b.network_time - a.network_time; + } + + out_time = + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(time_s)).count(); + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/static.h b/src/core/hle/service/psc/time/static.h new file mode 100644 index 000000000..498cd5ab5 --- /dev/null +++ b/src/core/hle/service/psc/time/static.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { +class TimeManager; +class StandardLocalSystemClockCore; +class StandardUserSystemClockCore; +class StandardNetworkSystemClockCore; +class TimeZone; +class SystemClock; +class SteadyClock; +class TimeZoneService; +class EphemeralNetworkSystemClockCore; +class SharedMemory; + +class StaticService final : public ServiceFramework<StaticService> { +public: + explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service); + Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service); + Result GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory); + Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled); + Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction); + Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + SteadyClockTimePoint& out_time_point); + Result CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, SystemClockContext& context); + Result GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type); + Result GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, + TimeType type); + Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, ClockSnapshot& a, + ClockSnapshot& b); + Result CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b); + +private: + Result GetClockSnapshotImpl(ClockSnapshot& out_snapshot, SystemClockContext& user_context, + SystemClockContext& network_context, TimeType type); + + void Handle_GetStandardUserSystemClock(HLERequestContext& ctx); + void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetStandardSteadyClock(HLERequestContext& ctx); + void Handle_GetTimeZoneService(HLERequestContext& ctx); + void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx); + void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx); + void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx); + void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); + void Handle_GetClockSnapshot(HLERequestContext& ctx); + void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); + void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); + void Handle_CalculateSpanBetween(HLERequestContext& ctx); + + Core::System& m_system; + StaticServiceSetupInfo m_setup_info; + std::shared_ptr<TimeManager> m_time; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp new file mode 100644 index 000000000..1ed5c7679 --- /dev/null +++ b/src/core/hle/service/psc/time/steady_clock.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/steady_clock.h" + +namespace Service::PSC::Time { + +SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> manager, + bool can_write_steady_clock, bool can_write_uninitialized_clock) + : ServiceFramework{system_, "ISteadyClock"}, m_system{system}, + m_clock_core{manager->m_standard_steady_clock}, + m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"}, + {2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"}, + {3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"}, + {100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"}, + {101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"}, + {102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"}, + {200, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void SteadyClock::Handle_GetCurrentTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SteadyClockTimePoint time_point{}; + auto res = GetCurrentTimePoint(time_point); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void SteadyClock::Handle_GetTestOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 test_offset{}; + auto res = GetTestOffset(test_offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(test_offset); +} + +void SteadyClock::Handle_SetTestOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto test_offset{rp.Pop<s64>()}; + + auto res = SetTestOffset(test_offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SteadyClock::Handle_GetRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 rtc_value{}; + auto res = GetRtcValue(rtc_value); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(rtc_value); +} + +void SteadyClock::Handle_IsRtcResetDetected(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool reset_detected{false}; + auto res = IsRtcResetDetected(reset_detected); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(reset_detected); +} + +void SteadyClock::Handle_GetSetupResultValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Result result_value{ResultSuccess}; + auto res = GetSetupResultValue(result_value); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(result_value); +} + +void SteadyClock::Handle_GetInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 internal_offset{}; + auto res = GetInternalOffset(internal_offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(internal_offset); +} + +// =============================== Implementations =========================== + +Result SteadyClock::GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetCurrentTimePoint(out_time_point)); +} + +Result SteadyClock::GetTestOffset(s64& out_test_offset) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_test_offset = m_clock_core.GetTestOffset(); + R_SUCCEED(); +} + +Result SteadyClock::SetTestOffset(s64 test_offset) { + R_UNLESS(m_can_write_steady_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + m_clock_core.SetTestOffset(test_offset); + R_SUCCEED(); +} + +Result SteadyClock::GetRtcValue(s64& out_rtc_value) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetRtcValue(out_rtc_value)); +} + +Result SteadyClock::IsRtcResetDetected(bool& out_is_detected) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_is_detected = m_clock_core.IsResetDetected(); + R_SUCCEED(); +} + +Result SteadyClock::GetSetupResultValue(Result& out_result) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_result = m_clock_core.GetSetupResultValue(); + R_SUCCEED(); +} + +Result SteadyClock::GetInternalOffset(s64& out_internal_offset) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_internal_offset = m_clock_core.GetInternalOffset(); + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/steady_clock.h b/src/core/hle/service/psc/time/steady_clock.h new file mode 100644 index 000000000..115e9b138 --- /dev/null +++ b/src/core/hle/service/psc/time/steady_clock.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class SteadyClock final : public ServiceFramework<SteadyClock> { +public: + explicit SteadyClock(Core::System& system, std::shared_ptr<TimeManager> manager, + bool can_write_steady_clock, bool can_write_uninitialized_clock); + + ~SteadyClock() override = default; + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point); + Result GetTestOffset(s64& out_test_offset); + Result SetTestOffset(s64 test_offset); + Result GetRtcValue(s64& out_rtc_value); + Result IsRtcResetDetected(bool& out_is_detected); + Result GetSetupResultValue(Result& out_result); + Result GetInternalOffset(s64& out_internal_offset); + +private: + void Handle_GetCurrentTimePoint(HLERequestContext& ctx); + void Handle_GetTestOffset(HLERequestContext& ctx); + void Handle_SetTestOffset(HLERequestContext& ctx); + void Handle_GetRtcValue(HLERequestContext& ctx); + void Handle_IsRtcResetDetected(HLERequestContext& ctx); + void Handle_GetSetupResultValue(HLERequestContext& ctx); + void Handle_GetInternalOffset(HLERequestContext& ctx); + + Core::System& m_system; + + StandardSteadyClockCore& m_clock_core; + bool m_can_write_steady_clock; + bool m_can_write_uninitialized_clock; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp new file mode 100644 index 000000000..13d2f1d11 --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/system_clock.h" + +namespace Service::PSC::Time { + +SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock, + bool can_write_uninitialized_clock) + : ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core}, + m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &SystemClock::Handle_GetCurrentTime, "GetCurrentTime"}, + {1, &SystemClock::Handle_SetCurrentTime, "SetCurrentTime"}, + {2, &SystemClock::Handle_GetSystemClockContext, "GetSystemClockContext"}, + {3, &SystemClock::Handle_SetSystemClockContext, "SetSystemClockContext"}, + {4, &SystemClock::Handle_GetOperationEventReadableHandle, "GetOperationEventReadableHandle"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void SystemClock::Handle_GetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 time{}; + auto res = GetCurrentTime(time); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto res = SetCurrentTime(time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SystemClock::Handle_GetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SystemClockContext context{}; + auto res = GetSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<SystemClockContext>(context); +} + +void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + + auto res = SetSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SystemClock::Handle_GetOperationEventReadableHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetOperationEventReadableHandle(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +// =============================== Implementations =========================== + +Result SystemClock::GetCurrentTime(s64& out_time) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetCurrentTime(&out_time)); +} + +Result SystemClock::SetCurrentTime(s64 time) { + R_UNLESS(m_can_write_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.SetCurrentTime(time)); +} + +Result SystemClock::GetSystemClockContext(SystemClockContext& out_context) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetContext(out_context)); +} + +Result SystemClock::SetSystemClockContext(SystemClockContext& context) { + R_UNLESS(m_can_write_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.SetContextAndWrite(context)); +} + +Result SystemClock::GetOperationEventReadableHandle(Kernel::KEvent** out_event) { + if (!m_operation_event) { + m_operation_event = std::make_unique<OperationEvent>(m_system); + R_UNLESS(m_operation_event != nullptr, ResultFailed); + + m_clock_core.LinkOperationEvent(*m_operation_event); + } + + *out_event = m_operation_event->m_event; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/system_clock.h b/src/core/hle/service/psc/time/system_clock.h new file mode 100644 index 000000000..f30027e7b --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class SystemClock final : public ServiceFramework<SystemClock> { +public: + explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, + bool can_write_clock, bool can_write_uninitialized_clock); + + ~SystemClock() override = default; + + Result GetCurrentTime(s64& out_time); + Result SetCurrentTime(s64 time); + Result GetSystemClockContext(SystemClockContext& out_context); + Result SetSystemClockContext(SystemClockContext& context); + Result GetOperationEventReadableHandle(Kernel::KEvent** out_event); + +private: + void Handle_GetCurrentTime(HLERequestContext& ctx); + void Handle_SetCurrentTime(HLERequestContext& ctx); + void Handle_GetSystemClockContext(HLERequestContext& ctx); + void Handle_SetSystemClockContext(HLERequestContext& ctx); + void Handle_GetOperationEventReadableHandle(HLERequestContext& ctx); + + Core::System& m_system; + + SystemClockCore& m_clock_core; + bool m_can_write_clock; + bool m_can_write_uninitialized_clock; + std::unique_ptr<OperationEvent> m_operation_event{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp new file mode 100644 index 000000000..cfee8f866 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone.cpp @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/time_zone.h" + +namespace Service::PSC::Time { +namespace { +constexpr Result ValidateRule(Tz::Rule& rule) { + if (rule.typecnt > static_cast<s32>(Tz::TZ_MAX_TYPES) || + rule.timecnt > static_cast<s32>(Tz::TZ_MAX_TIMES) || + rule.charcnt > static_cast<s32>(Tz::TZ_MAX_CHARS)) { + R_RETURN(ResultTimeZoneOutOfRange); + } + + for (s32 i = 0; i < rule.timecnt; i++) { + if (rule.types[i] >= rule.typecnt) { + R_RETURN(ResultTimeZoneOutOfRange); + } + } + + for (s32 i = 0; i < rule.typecnt; i++) { + if (rule.ttis[i].tt_desigidx >= static_cast<s32>(rule.chars.size())) { + R_RETURN(ResultTimeZoneOutOfRange); + } + } + R_SUCCEED(); +} + +constexpr bool GetTimeZoneTime(s64& out_time, Tz::Rule& rule, s64 time, s32 index, + s32 index_offset) { + s32 found_idx{}; + s32 expected_index{index + index_offset}; + s64 time_to_find{time + rule.ttis[rule.types[index]].tt_utoff - + rule.ttis[rule.types[expected_index]].tt_utoff}; + + if (rule.timecnt > 1 && rule.ats[0] <= time_to_find) { + s32 low{1}; + s32 high{rule.timecnt}; + + while (low < high) { + auto mid{(low + high) / 2}; + if (rule.ats[mid] <= time_to_find) { + low = mid + 1; + } else if (rule.ats[mid] > time_to_find) { + high = mid; + } + } + found_idx = low - 1; + } + + if (found_idx == expected_index) { + out_time = time_to_find; + } + return found_idx == expected_index; +} +} // namespace + +void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) { + std::scoped_lock l{m_mutex}; + m_steady_clock_time_point = time_point; +} + +void TimeZone::SetTotalLocationNameCount(u32 count) { + std::scoped_lock l{m_mutex}; + m_total_location_name_count = count; +} + +void TimeZone::SetRuleVersion(RuleVersion& rule_version) { + std::scoped_lock l{m_mutex}; + m_rule_version = rule_version; +} + +Result TimeZone::GetLocationName(LocationName& out_name) { + std::scoped_lock l{m_mutex}; + R_UNLESS(m_initialized, ResultClockUninitialized); + out_name = m_location; + R_SUCCEED(); +} + +Result TimeZone::GetTotalLocationCount(u32& out_count) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + + out_count = m_total_location_name_count; + R_SUCCEED(); +} + +Result TimeZone::GetRuleVersion(RuleVersion& out_rule_version) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + out_rule_version = m_rule_version; + R_SUCCEED(); +} + +Result TimeZone::GetTimePoint(SteadyClockTimePoint& out_time_point) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + out_time_point = m_steady_clock_time_point; + R_SUCCEED(); +} + +Result TimeZone::ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + std::scoped_lock l{m_mutex}; + R_RETURN(ToCalendarTimeImpl(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time, + CalendarAdditionalInfo& calendar_additional, s64 time) { + // This is checked outside the mutex. Bug? + if (!m_initialized) { + return ResultClockUninitialized; + } + + std::scoped_lock l{m_mutex}; + R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule)); +} + +Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) { + std::scoped_lock l{m_mutex}; + + Tz::Rule tmp_rule{}; + R_TRY(ParseBinaryImpl(tmp_rule, binary)); + + m_my_rule = tmp_rule; + m_location = name; + + R_SUCCEED(); +} + +Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary) { + std::scoped_lock l{m_mutex}; + R_RETURN(ParseBinaryImpl(out_rule, binary)); +} + +Result TimeZone::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule) { + std::scoped_lock l{m_mutex}; + + auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1); + + if (res != ResultSuccess) { + if (res == ResultTimeZoneNotFound) { + res = ResultSuccess; + out_count = 0; + } + } else if (out_count == 2 && out_times[0] > out_times[1]) { + std::swap(out_times[0], out_times[1]); + } + R_RETURN(res); +} + +Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, CalendarTime& calendar) { + std::scoped_lock l{m_mutex}; + + auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1); + + if (res != ResultSuccess) { + if (res == ResultTimeZoneNotFound) { + res = ResultSuccess; + out_count = 0; + } + } else if (out_count == 2 && out_times[0] > out_times[1]) { + std::swap(out_times[0], out_times[1]); + } + R_RETURN(res); +} + +Result TimeZone::ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary) { + if (Tz::ParseTimeZoneBinary(out_rule, binary)) { + R_RETURN(ResultTimeZoneParseFailed); + } + R_SUCCEED(); +} + +Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + R_TRY(ValidateRule(rule)); + + Tz::CalendarTimeInternal calendar_internal{}; + time_t time_tmp{static_cast<time_t>(time)}; + if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) { + R_RETURN(ResultOverflow); + } + + out_calendar_time.year = static_cast<s16>(calendar_internal.tm_year + 1900); + out_calendar_time.month = static_cast<s8>(calendar_internal.tm_mon + 1); + out_calendar_time.day = static_cast<s8>(calendar_internal.tm_mday); + out_calendar_time.hour = static_cast<s8>(calendar_internal.tm_hour); + out_calendar_time.minute = static_cast<s8>(calendar_internal.tm_min); + out_calendar_time.second = static_cast<s8>(calendar_internal.tm_sec); + + out_additional_info.day_of_week = calendar_internal.tm_wday; + out_additional_info.day_of_year = calendar_internal.tm_yday; + + std::memcpy(out_additional_info.name.data(), calendar_internal.tm_zone.data(), + out_additional_info.name.size()); + out_additional_info.name[out_additional_info.name.size() - 1] = '\0'; + + out_additional_info.is_dst = calendar_internal.tm_isdst; + out_additional_info.ut_offset = calendar_internal.tm_utoff; + + R_SUCCEED(); +} + +Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule, s32 is_dst) { + R_TRY(ValidateRule(rule)); + + calendar.month -= 1; + calendar.year -= 1900; + + Tz::CalendarTimeInternal internal{ + .tm_sec = calendar.second, + .tm_min = calendar.minute, + .tm_hour = calendar.hour, + .tm_mday = calendar.day, + .tm_mon = calendar.month, + .tm_year = calendar.year, + .tm_wday = 0, + .tm_yday = 0, + .tm_isdst = is_dst, + .tm_zone = {}, + .tm_utoff = 0, + .time_index = 0, + }; + time_t time_tmp{}; + auto res = Tz::mktime_tzname(&time_tmp, &rule, &internal); + s64 time = static_cast<s64>(time_tmp); + + if (res == 1) { + R_RETURN(ResultOverflow); + } else if (res == 2) { + R_RETURN(ResultTimeZoneNotFound); + } + + if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute || + internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day || + internal.tm_mon != calendar.month || internal.tm_year != calendar.year) { + R_RETURN(ResultTimeZoneNotFound); + } + + if (res != 0) { + ASSERT(false); + } + + out_times[0] = time; + if (out_times_count < 2) { + out_count = 1; + R_SUCCEED(); + } + + s64 time2{}; + if (internal.time_index > 0 && GetTimeZoneTime(time2, rule, time, internal.time_index, -1)) { + out_times[1] = time2; + out_count = 2; + R_SUCCEED(); + } + + if (((internal.time_index + 1) < rule.timecnt) && + GetTimeZoneTime(time2, rule, time, internal.time_index, 1)) { + out_times[1] = time2; + out_count = 2; + R_SUCCEED(); + } + + out_count = 1; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h new file mode 100644 index 000000000..ce2acca17 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include <span> + +#include <tz/tz.h> +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class TimeZone { +public: + TimeZone() = default; + + bool IsInitialized() const { + return m_initialized; + } + + void SetInitialized() { + m_initialized = true; + } + + void SetTimePoint(SteadyClockTimePoint& time_point); + void SetTotalLocationNameCount(u32 count); + void SetRuleVersion(RuleVersion& rule_version); + Result GetLocationName(LocationName& out_name); + Result GetTotalLocationCount(u32& out_count); + Result GetRuleVersion(RuleVersion& out_rule_version); + Result GetTimePoint(SteadyClockTimePoint& out_time_point); + + Result ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time, + CalendarAdditionalInfo& calendar_additional, s64 time); + Result ParseBinary(LocationName& name, std::span<const u8> binary); + Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar); + +private: + Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule, s32 is_dst); + + bool m_initialized{}; + std::recursive_mutex m_mutex; + LocationName m_location{}; + Tz::Rule m_my_rule{}; + SteadyClockTimePoint m_steady_clock_time_point{}; + u32 m_total_location_name_count{}; + RuleVersion m_rule_version{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp new file mode 100644 index 000000000..e304c8387 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -0,0 +1,289 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <tz/tz.h> +#include "core/core.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::PSC::Time { + +TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core, + TimeZone& time_zone, bool can_write_timezone_device_location) + : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core}, + m_time_zone{time_zone}, m_can_write_timezone_device_location{ + can_write_timezone_device_location} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"}, + {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"}, + {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"}, + {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, + {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"}, + {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"}, + {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"}, + {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"}, + {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"}, + {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"}, + {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + LocationName name{}; + auto res = GetDeviceLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<LocationName>(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] auto name{rp.PopRaw<LocationName>()}; + + if (!m_can_write_timezone_device_location) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultPermissionDenied); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 count{}; + auto res = GetTotalLocationNameCount(count); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + RuleVersion rule_version{}; + auto res = GetTimeZoneRuleVersion(rule_version); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(RuleVersion) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<RuleVersion>(rule_version); +} + +void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + LocationName name{}; + SteadyClockTimePoint time_point{}; + auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(LocationName) / sizeof(u32)) + + (sizeof(SteadyClockTimePoint) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<LocationName>(name); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<LocationName>()}; + + auto binary{ctx.ReadBuffer()}; + auto res = SetDeviceLocationNameWithTimeZoneRule(name, binary); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + auto res = ParseTimeZoneBinary(rule, binary); + + ctx.WriteBuffer(rule); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + Tz::Rule rule{}; + std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule)); + + CalendarTime calendar_time{}; + CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTime(calendar_time, additional_info, time, rule); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) + + (sizeof(CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<CalendarTime>(calendar_time); + rb.PushRaw<CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + CalendarTime calendar_time{}; + CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) + + (sizeof(CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<CalendarTime>(calendar_time); + rb.PushRaw<CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<CalendarTime>()}; + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTime(count, times, times_count, calendar, rule); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<CalendarTime>()}; + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +// =============================== Implementations =========================== + +Result TimeZoneService::GetDeviceLocationName(LocationName& out_location_name) { + R_RETURN(m_time_zone.GetLocationName(out_location_name)); +} + +Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) { + R_RETURN(m_time_zone.GetTotalLocationCount(out_count)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(RuleVersion& out_rule_version) { + R_RETURN(m_time_zone.GetRuleVersion(out_rule_version)); +} + +Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point, + LocationName& location_name) { + R_TRY(m_time_zone.GetLocationName(location_name)); + R_RETURN(m_time_zone.GetTimePoint(out_time_point)); +} + +Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + std::span<const u8> binary) { + R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); + R_TRY(m_time_zone.ParseBinary(location_name, binary)); + + SteadyClockTimePoint time_point{}; + R_TRY(m_clock_core.GetCurrentTimePoint(time_point)); + + m_time_zone.SetTimePoint(time_point); + R_SUCCEED(); +} + +Result TimeZoneService::ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary) { + R_RETURN(m_time_zone.ParseBinaryInto(out_rule, binary)); +} + +Result TimeZoneService::ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + R_RETURN(m_time_zone.ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZoneService::ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, + s64 time) { + R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); +} + +Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, CalendarTime& calendar_time, + Tz::Rule& rule) { + R_RETURN(m_time_zone.ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule)); +} + +Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, CalendarTime& calendar_time) { + R_RETURN( + m_time_zone.ToPosixTimeWithMyRule(out_count, out_times, out_times_count, calendar_time)); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h new file mode 100644 index 000000000..074c1d4ae --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone_service.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Tz { +struct Rule; +} + +namespace Service::PSC::Time { + +class TimeZoneService final : public ServiceFramework<TimeZoneService> { +public: + explicit TimeZoneService(Core::System& system, StandardSteadyClockCore& clock_core, + TimeZone& time_zone, bool can_write_timezone_device_location); + + ~TimeZoneService() override = default; + + Result GetDeviceLocationName(LocationName& out_location_name); + Result GetTotalLocationNameCount(u32& out_count); + Result GetTimeZoneRuleVersion(RuleVersion& out_rule_version); + Result GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point, + LocationName& location_name); + Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + std::span<const u8> binary); + Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar_time); + +private: + void Handle_GetDeviceLocationName(HLERequestContext& ctx); + void Handle_SetDeviceLocationName(HLERequestContext& ctx); + void Handle_GetTotalLocationNameCount(HLERequestContext& ctx); + void Handle_LoadLocationNameList(HLERequestContext& ctx); + void Handle_LoadTimeZoneRule(HLERequestContext& ctx); + void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx); + void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx); + void Handle_ParseTimeZoneBinary(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx); + void Handle_ToCalendarTime(HLERequestContext& ctx); + void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx); + void Handle_ToPosixTime(HLERequestContext& ctx); + void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx); + + Core::System& m_system; + + StandardSteadyClockCore& m_clock_core; + TimeZone& m_time_zone; + bool m_can_write_timezone_device_location; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index f0658bb5d..51196170a 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -6,13 +6,13 @@ #include "common/scope_exit.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ro/ro.h" #include "core/hle/service/ro/ro_nro_utils.h" #include "core/hle/service/ro/ro_results.h" #include "core/hle/service/ro/ro_types.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" namespace Service::RO { @@ -500,46 +500,64 @@ private: } }; -class RoInterface { +class RoInterface : public ServiceFramework<RoInterface> { public: - explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind) - : m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {} + explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro, + NrrKind nrr_kind) + : ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId), + m_nrr_kind(nrr_kind) { + + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"}, + {1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"}, + {2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"}, + {3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"}, + {4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"}, + {10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + ~RoInterface() { m_ro->UnregisterProcess(m_context_id); } - Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address, - u64 nro_size, u64 bss_address, u64 bss_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); - R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address, + Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid, + u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); + R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, nro_size, bss_address, bss_size)); } - Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address)); } - Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN( m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true)); } - Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address)); } - Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) { + Result RegisterProcessHandle(ClientProcessId client_pid, + InCopyHandle<Kernel::KProcess>& process) { // Register the process. - R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid)); + R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid)); } - Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size, - Kernel::KProcess* process) { + Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, + InCopyHandle<Kernel::KProcess>& process) { // Validate the process. - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); // Register the module. R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, @@ -552,137 +570,6 @@ private: NrrKind m_nrr_kind{}; }; -class IRoInterface : public ServiceFramework<IRoInterface> { -public: - explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro, - NrrKind nrr_kind) - : ServiceFramework{system_, name_}, interface { - ro, nrr_kind - } { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"}, - {1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"}, - {2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"}, - {3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"}, - {4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"}, - {10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void MapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - u64 nro_size; - u64 bss_address; - u64 bss_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - - u64 load_address = 0; - auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(), - params.nro_address, params.nro_size, - params.bss_address, params.bss_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(load_address); - } - - void UnmapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = - interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void UnregisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessModuleInfo( - client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - RoInterface interface; -}; - } // namespace void LoopProcess(Core::System& system) { @@ -691,11 +578,11 @@ void LoopProcess(Core::System& system) { auto ro = std::make_shared<RoContext>(); const auto RoInterfaceFactoryForUser = [&, ro] { - return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User); + return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User); }; const auto RoInterfaceFactoryForJitPlugin = [&, ro] { - return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); + return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); }; server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp index 15edb23e0..8ef49387d 100644 --- a/src/core/hle/service/server_manager.cpp +++ b/src/core/hle/service/server_manager.cpp @@ -256,8 +256,13 @@ Result ServerManager::WaitAndProcessImpl() { // Wait for a signal. s32 out_index{-1}; - R_TRY(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), - num_objs, -1)); + R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, + wait_objs.data(), num_objs, -1)) { + R_CATCH(Kernel::ResultSessionClosed) { + // On session closed, index is updated and we don't want to return an error. + } + } + R_END_TRY_CATCH; ASSERT(out_index >= 0 && out_index < num_objs); // Set the output index. diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 39124c5fd..06cbad268 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -66,7 +66,6 @@ #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/time/time.h" #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" #include "core/reporter.h" @@ -246,6 +245,9 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& 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); }); @@ -269,13 +271,10 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("time", [&] { Time::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); // clang-format on } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index d539ed0f4..22d1343d5 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -206,6 +206,22 @@ protected: RegisterHandlersBaseTipc(functions, n); } +protected: + template <bool Domain, auto F> + void CmifReplyWrap(HLERequestContext& ctx); + + /** + * Wraps the template pointer-to-member function for use in a domain session. + */ + template <auto F> + static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>; + + /** + * Wraps the template pointer-to-member function for use in a non-domain session. + */ + template <auto F> + static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>; + private: /** * This function is used to allow invocation of pointers to handlers stored in the base class diff --git a/src/core/hle/service/set/appln_settings.cpp b/src/core/hle/service/set/appln_settings.cpp deleted file mode 100644 index a5d802757..000000000 --- a/src/core/hle/service/set/appln_settings.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/set/appln_settings.h" - -namespace Service::Set { - -ApplnSettings DefaultApplnSettings() { - return {}; -} - -} // namespace Service::Set diff --git a/src/core/hle/service/set/appln_settings.h b/src/core/hle/service/set/appln_settings.h deleted file mode 100644 index 126375860..000000000 --- a/src/core/hle/service/set/appln_settings.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <cstddef> - -#include "common/common_types.h" - -namespace Service::Set { -struct ApplnSettings { - std::array<u8, 0x10> reserved_000; - - // nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0 - std::array<u8, 0x10> mii_author_id; - - std::array<u8, 0x30> reserved_020; - - // nn::settings::system::ServiceDiscoveryControlSettings - std::array<u8, 0x4> service_discovery_control_settings; - - std::array<u8, 0x20> reserved_054; - - bool in_repair_process_enable_flag; - - std::array<u8, 0x3> pad_075; -}; -static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10); -static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50); -static_assert(offsetof(ApplnSettings, in_repair_process_enable_flag) == 0x74); -static_assert(sizeof(ApplnSettings) == 0x78, "ApplnSettings has the wrong size!"); - -ApplnSettings DefaultApplnSettings(); - -} // namespace Service::Set diff --git a/src/core/hle/service/set/device_settings.cpp b/src/core/hle/service/set/device_settings.cpp deleted file mode 100644 index e423ce38a..000000000 --- a/src/core/hle/service/set/device_settings.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/set/device_settings.h" - -namespace Service::Set { - -DeviceSettings DefaultDeviceSettings() { - return {}; -} - -} // namespace Service::Set diff --git a/src/core/hle/service/set/device_settings.h b/src/core/hle/service/set/device_settings.h deleted file mode 100644 index f291d0ebe..000000000 --- a/src/core/hle/service/set/device_settings.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <cstddef> - -#include "common/common_types.h" - -namespace Service::Set { -struct DeviceSettings { - std::array<u8, 0x10> reserved_000; - - // nn::settings::BatteryLot - std::array<u8, 0x18> ptm_battery_lot; - // nn::settings::system::PtmFuelGaugeParameter - std::array<u8, 0x18> ptm_fuel_gauge_parameter; - u8 ptm_battery_version; - // nn::settings::system::PtmCycleCountReliability - u32 ptm_cycle_count_reliability; - - std::array<u8, 0x48> reserved_048; - - // nn::settings::system::AnalogStickUserCalibration L - std::array<u8, 0x10> analog_user_stick_calibration_l; - // nn::settings::system::AnalogStickUserCalibration R - std::array<u8, 0x10> analog_user_stick_calibration_r; - - std::array<u8, 0x20> reserved_0B0; - - // nn::settings::system::ConsoleSixAxisSensorAccelerationBias - std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias; - // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias - std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias; - // nn::settings::system::ConsoleSixAxisSensorAccelerationGain - std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; - // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain - std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; - // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias - std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias; - // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration - std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration; -}; -static_assert(offsetof(DeviceSettings, ptm_battery_lot) == 0x10); -static_assert(offsetof(DeviceSettings, ptm_cycle_count_reliability) == 0x44); -static_assert(offsetof(DeviceSettings, analog_user_stick_calibration_l) == 0x90); -static_assert(offsetof(DeviceSettings, console_six_axis_sensor_acceleration_bias) == 0xD0); -static_assert(offsetof(DeviceSettings, console_six_axis_sensor_angular_acceleration) == 0x13C); -static_assert(sizeof(DeviceSettings) == 0x160, "DeviceSettings has the wrong size!"); - -DeviceSettings DefaultDeviceSettings(); - -} // namespace Service::Set diff --git a/src/core/hle/service/set/factory_settings_server.cpp b/src/core/hle/service/set/factory_settings_server.cpp new file mode 100644 index 000000000..a8e307ae2 --- /dev/null +++ b/src/core/hle/service/set/factory_settings_server.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/factory_settings_server.h" + +namespace Service::Set { + +IFactorySettingsServer::IFactorySettingsServer(Core::System& system_) + : ServiceFramework{system_, "set:cal"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetBluetoothBdAddress"}, + {1, nullptr, "GetConfigurationId1"}, + {2, nullptr, "GetAccelerometerOffset"}, + {3, nullptr, "GetAccelerometerScale"}, + {4, nullptr, "GetGyroscopeOffset"}, + {5, nullptr, "GetGyroscopeScale"}, + {6, nullptr, "GetWirelessLanMacAddress"}, + {7, nullptr, "GetWirelessLanCountryCodeCount"}, + {8, nullptr, "GetWirelessLanCountryCodes"}, + {9, nullptr, "GetSerialNumber"}, + {10, nullptr, "SetInitialSystemAppletProgramId"}, + {11, nullptr, "SetOverlayDispProgramId"}, + {12, nullptr, "GetBatteryLot"}, + {14, nullptr, "GetEciDeviceCertificate"}, + {15, nullptr, "GetEticketDeviceCertificate"}, + {16, nullptr, "GetSslKey"}, + {17, nullptr, "GetSslCertificate"}, + {18, nullptr, "GetGameCardKey"}, + {19, nullptr, "GetGameCardCertificate"}, + {20, nullptr, "GetEciDeviceKey"}, + {21, nullptr, "GetEticketDeviceKey"}, + {22, nullptr, "GetSpeakerParameter"}, + {23, nullptr, "GetLcdVendorId"}, + {24, nullptr, "GetEciDeviceCertificate2"}, + {25, nullptr, "GetEciDeviceKey2"}, + {26, nullptr, "GetAmiiboKey"}, + {27, nullptr, "GetAmiiboEcqvCertificate"}, + {28, nullptr, "GetAmiiboEcdsaCertificate"}, + {29, nullptr, "GetAmiiboEcqvBlsKey"}, + {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, + {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, + {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, + {33, nullptr, "GetAnalogStickModuleTypeL"}, + {34, nullptr, "GetAnalogStickModelParameterL"}, + {35, nullptr, "GetAnalogStickFactoryCalibrationL"}, + {36, nullptr, "GetAnalogStickModuleTypeR"}, + {37, nullptr, "GetAnalogStickModelParameterR"}, + {38, nullptr, "GetAnalogStickFactoryCalibrationR"}, + {39, nullptr, "GetConsoleSixAxisSensorModuleType"}, + {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"}, + {41, nullptr, "GetBatteryVersion"}, + {42, nullptr, "GetDeviceId"}, + {43, nullptr, "GetConsoleSixAxisSensorMountType"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFactorySettingsServer::~IFactorySettingsServer() = default; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/factory_settings_server.h b/src/core/hle/service/set/factory_settings_server.h new file mode 100644 index 000000000..e64cd1380 --- /dev/null +++ b/src/core/hle/service/set/factory_settings_server.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::Set { + +class IFactorySettingsServer final : public ServiceFramework<IFactorySettingsServer> { +public: + explicit IFactorySettingsServer(Core::System& system_); + ~IFactorySettingsServer() override; +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/firmware_debug_settings_server.cpp b/src/core/hle/service/set/firmware_debug_settings_server.cpp new file mode 100644 index 000000000..b3a5e623b --- /dev/null +++ b/src/core/hle/service/set/firmware_debug_settings_server.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/firmware_debug_settings_server.h" + +namespace Service::Set { + +IFirmwareDebugSettingsServer::IFirmwareDebugSettingsServer(Core::System& system_) + : ServiceFramework{system_, "set:fd"} { + // clang-format off + static const FunctionInfo functions[] = { + {2, nullptr, "SetSettingsItemValue"}, + {3, nullptr, "ResetSettingsItemValue"}, + {4, nullptr, "CreateSettingsItemKeyIterator"}, + {10, nullptr, "ReadSettings"}, + {11, nullptr, "ResetSettings"}, + {20, nullptr, "SetWebInspectorFlag"}, + {21, nullptr, "SetAllowedSslHosts"}, + {22, nullptr, "SetHostFsMountPoint"}, + {23, nullptr, "SetMemoryUsageRateFlag"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFirmwareDebugSettingsServer::~IFirmwareDebugSettingsServer() = default; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/firmware_debug_settings_server.h b/src/core/hle/service/set/firmware_debug_settings_server.h new file mode 100644 index 000000000..5dae2263e --- /dev/null +++ b/src/core/hle/service/set/firmware_debug_settings_server.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::Set { + +class IFirmwareDebugSettingsServer final : public ServiceFramework<IFirmwareDebugSettingsServer> { +public: + explicit IFirmwareDebugSettingsServer(Core::System& system_); + ~IFirmwareDebugSettingsServer() override; +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/private_settings.cpp b/src/core/hle/service/set/private_settings.cpp deleted file mode 100644 index 70bf65727..000000000 --- a/src/core/hle/service/set/private_settings.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/set/private_settings.h" - -namespace Service::Set { - -PrivateSettings DefaultPrivateSettings() { - return {}; -} - -} // namespace Service::Set diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h index b63eaf45c..b02291ce7 100644 --- a/src/core/hle/service/set/private_settings.h +++ b/src/core/hle/service/set/private_settings.h @@ -9,7 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/uuid.h" -#include "core/hle/service/time/clock_types.h" +#include "core/hle/service/psc/time/common.h" namespace Service::Set { @@ -29,14 +29,14 @@ static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid s struct InitialLaunchSettings { InitialLaunchFlag flags; INSERT_PADDING_BYTES(0x4); - Service::Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); #pragma pack(push, 4) struct InitialLaunchSettingsPacked { InitialLaunchFlag flags; - Service::Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; #pragma pack(pop) static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp deleted file mode 100644 index 2082b8ef7..000000000 --- a/src/core/hle/service/set/set.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <array> -#include <chrono> -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/set/set.h" - -namespace Service::Set { -namespace { -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; - } - - ctx.WriteBuffer(layout); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} -} // Anonymous namespace - -LanguageCode GetLanguageCodeFromIndex(std::size_t index) { - return available_language_codes.at(index); -} - -void SET::GetAvailableLanguageCodes(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES); -} - -void SET::MakeLanguageCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto index = rp.Pop<u32>(); - - 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; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(available_language_codes[index]); -} - -void SET::GetAvailableLanguageCodes2(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES); -} - -void SET::GetAvailableLanguageCodeCount(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES); -} - -void SET::GetAvailableLanguageCodeCount2(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES); -} - -void SET::GetQuestFlag(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<s32>(Settings::values.quest_flag.GetValue())); -} - -void SET::GetLanguageCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue()); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum( - available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]); -} - -void SET::GetRegionCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(Settings::values.region_index.GetValue())); -} - -void SET::GetKeyCodeMap(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); - GetKeyCodeMapImpl(ctx); -} - -void SET::GetKeyCodeMap2(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); - GetKeyCodeMapImpl(ctx); -} - -void SET::GetDeviceNickName(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - ctx.WriteBuffer(Settings::values.device_name.GetValue()); -} - -SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &SET::GetLanguageCode, "GetLanguageCode"}, - {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, - {2, &SET::MakeLanguageCode, "MakeLanguageCode"}, - {3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, - {4, &SET::GetRegionCode, "GetRegionCode"}, - {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, - {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, - {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"}, - {8, &SET::GetQuestFlag, "GetQuestFlag"}, - {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, - {10, nullptr, "GetFirmwareVersionForDebug"}, - {11, &SET::GetDeviceNickName, "GetDeviceNickName"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -SET::~SET() = default; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h deleted file mode 100644 index 6ef3da410..000000000 --- a/src/core/hle/service/set/set.h +++ /dev/null @@ -1,95 +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" -#include "core/hle/service/set/system_settings.h" - -namespace Core { -class System; -} - -namespace Service::Set { -enum class KeyboardLayout : u64 { - Japanese = 0, - EnglishUs = 1, - EnglishUsInternational = 2, - EnglishUk = 3, - French = 4, - FrenchCa = 5, - Spanish = 6, - SpanishLatin = 7, - German = 8, - Italian = 9, - Portuguese = 10, - Russian = 11, - Korean = 12, - ChineseSimplified = 13, - ChineseTraditional = 14, -}; - -constexpr std::array<LanguageCode, 18> available_language_codes = {{ - LanguageCode::JA, - LanguageCode::EN_US, - LanguageCode::FR, - LanguageCode::DE, - LanguageCode::IT, - LanguageCode::ES, - LanguageCode::ZH_CN, - LanguageCode::KO, - LanguageCode::NL, - LanguageCode::PT, - LanguageCode::RU, - LanguageCode::ZH_TW, - LanguageCode::EN_GB, - LanguageCode::FR_CA, - LanguageCode::ES_419, - LanguageCode::ZH_HANS, - LanguageCode::ZH_HANT, - LanguageCode::PT_BR, -}}; - -static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{ - {LanguageCode::JA, KeyboardLayout::Japanese}, - {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, - {LanguageCode::FR, KeyboardLayout::French}, - {LanguageCode::DE, KeyboardLayout::German}, - {LanguageCode::IT, KeyboardLayout::Italian}, - {LanguageCode::ES, KeyboardLayout::Spanish}, - {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, - {LanguageCode::KO, KeyboardLayout::Korean}, - {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, - {LanguageCode::PT, KeyboardLayout::Portuguese}, - {LanguageCode::RU, KeyboardLayout::Russian}, - {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, - {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, - {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, - {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, - {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, - {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, - {LanguageCode::PT_BR, KeyboardLayout::Portuguese}, -}}; - -LanguageCode GetLanguageCodeFromIndex(std::size_t idx); - -class SET final : public ServiceFramework<SET> { -public: - explicit SET(Core::System& system_); - ~SET() 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); -}; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp deleted file mode 100644 index d2c0d536f..000000000 --- a/src/core/hle/service/set/set_cal.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/set/set_cal.h" - -namespace Service::Set { - -SET_CAL::SET_CAL(Core::System& system_) : ServiceFramework{system_, "set:cal"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetBluetoothBdAddress"}, - {1, nullptr, "GetConfigurationId1"}, - {2, nullptr, "GetAccelerometerOffset"}, - {3, nullptr, "GetAccelerometerScale"}, - {4, nullptr, "GetGyroscopeOffset"}, - {5, nullptr, "GetGyroscopeScale"}, - {6, nullptr, "GetWirelessLanMacAddress"}, - {7, nullptr, "GetWirelessLanCountryCodeCount"}, - {8, nullptr, "GetWirelessLanCountryCodes"}, - {9, nullptr, "GetSerialNumber"}, - {10, nullptr, "SetInitialSystemAppletProgramId"}, - {11, nullptr, "SetOverlayDispProgramId"}, - {12, nullptr, "GetBatteryLot"}, - {14, nullptr, "GetEciDeviceCertificate"}, - {15, nullptr, "GetEticketDeviceCertificate"}, - {16, nullptr, "GetSslKey"}, - {17, nullptr, "GetSslCertificate"}, - {18, nullptr, "GetGameCardKey"}, - {19, nullptr, "GetGameCardCertificate"}, - {20, nullptr, "GetEciDeviceKey"}, - {21, nullptr, "GetEticketDeviceKey"}, - {22, nullptr, "GetSpeakerParameter"}, - {23, nullptr, "GetLcdVendorId"}, - {24, nullptr, "GetEciDeviceCertificate2"}, - {25, nullptr, "GetEciDeviceKey2"}, - {26, nullptr, "GetAmiiboKey"}, - {27, nullptr, "GetAmiiboEcqvCertificate"}, - {28, nullptr, "GetAmiiboEcdsaCertificate"}, - {29, nullptr, "GetAmiiboEcqvBlsKey"}, - {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, - {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, - {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, - {33, nullptr, "GetAnalogStickModuleTypeL"}, - {34, nullptr, "GetAnalogStickModelParameterL"}, - {35, nullptr, "GetAnalogStickFactoryCalibrationL"}, - {36, nullptr, "GetAnalogStickModuleTypeR"}, - {37, nullptr, "GetAnalogStickModelParameterR"}, - {38, nullptr, "GetAnalogStickFactoryCalibrationR"}, - {39, nullptr, "GetConsoleSixAxisSensorModuleType"}, - {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"}, - {41, nullptr, "GetBatteryVersion"}, - {42, nullptr, "GetDeviceId"}, - {43, nullptr, "GetConsoleSixAxisSensorMountType"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -SET_CAL::~SET_CAL() = default; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h deleted file mode 100644 index 8f50278ed..000000000 --- a/src/core/hle/service/set/set_cal.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::Set { - -class SET_CAL final : public ServiceFramework<SET_CAL> { -public: - explicit SET_CAL(Core::System& system_); - ~SET_CAL() override; -}; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp deleted file mode 100644 index 278ef32e1..000000000 --- a/src/core/hle/service/set/set_fd.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/set/set_fd.h" - -namespace Service::Set { - -SET_FD::SET_FD(Core::System& system_) : ServiceFramework{system_, "set:fd"} { - // clang-format off - static const FunctionInfo functions[] = { - {2, nullptr, "SetSettingsItemValue"}, - {3, nullptr, "ResetSettingsItemValue"}, - {4, nullptr, "CreateSettingsItemKeyIterator"}, - {10, nullptr, "ReadSettings"}, - {11, nullptr, "ResetSettings"}, - {20, nullptr, "SetWebInspectorFlag"}, - {21, nullptr, "SetAllowedSslHosts"}, - {22, nullptr, "SetHostFsMountPoint"}, - {23, nullptr, "SetMemoryUsageRateFlag"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -SET_FD::~SET_FD() = default; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h deleted file mode 100644 index 150a7cbce..000000000 --- a/src/core/hle/service/set/set_fd.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::Set { - -class SET_FD final : public ServiceFramework<SET_FD> { -public: - explicit SET_FD(Core::System& system_); - ~SET_FD() override; -}; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp deleted file mode 100644 index 8e637f963..000000000 --- a/src/core/hle/service/set/set_sys.cpp +++ /dev/null @@ -1,1272 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <fstream> - -#include "common/assert.h" -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/errors.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/service/filesystem/filesystem.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/set/set.h" -#include "core/hle/service/set/set_sys.h" - -namespace Service::Set { - -namespace { -constexpr u32 SETTINGS_VERSION{1u}; -constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); -struct SettingsHeader { - u64 magic; - u32 version; - u32 reserved; -}; -} // Anonymous namespace - -Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, - GetFirmwareVersionType type) { - constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; - auto& fsc = system.GetFileSystemController(); - - // Attempt to load version data from disk - const FileSys::RegisteredCache* bis_system{}; - std::unique_ptr<FileSys::NCA> nca{}; - FileSys::VirtualDir romfs{}; - - bis_system = fsc.GetSystemNANDContents(); - if (bis_system) { - nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data); - } - if (nca) { - if (auto nca_romfs = nca->GetRomFS(); nca_romfs) { - romfs = FileSys::ExtractRomFS(nca_romfs); - } - } - if (!romfs) { - romfs = FileSys::ExtractRomFS( - FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); - } - - const auto early_exit_failure = [](std::string_view desc, Result code) { - LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", - desc); - return code; - }; - - const auto ver_file = romfs->GetFile("file"); - if (ver_file == nullptr) { - return early_exit_failure("The system version archive didn't contain the file 'file'.", - FileSys::ERROR_INVALID_ARGUMENT); - } - - auto data = ver_file->ReadAllBytes(); - if (data.size() != sizeof(FirmwareVersionFormat)) { - return early_exit_failure("The system version file 'file' was not the correct size.", - FileSys::ERROR_OUT_OF_BOUNDS); - } - - std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); - - // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will - // zero out the REVISION_MINOR field. - if (type == GetFirmwareVersionType::Version1) { - out_firmware.revision_minor = 0; - } - - return ResultSuccess; -} - -bool SET_SYS::LoadSettingsFile(std::filesystem::path& path, auto&& default_func) { - using settings_type = decltype(default_func()); - - if (!Common::FS::CreateDirs(path)) { - return false; - } - - auto settings_file = path / "settings.dat"; - auto exists = std::filesystem::exists(settings_file); - auto file_size_ok = exists && std::filesystem::file_size(settings_file) == - sizeof(SettingsHeader) + sizeof(settings_type); - - auto ResetToDefault = [&]() { - auto default_settings{default_func()}; - - SettingsHeader hdr{ - .magic = SETTINGS_MAGIC, - .version = SETTINGS_VERSION, - .reserved = 0u, - }; - - std::ofstream out_settings_file(settings_file, std::ios::out | std::ios::binary); - out_settings_file.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr)); - out_settings_file.write(reinterpret_cast<const char*>(&default_settings), - sizeof(settings_type)); - out_settings_file.flush(); - out_settings_file.close(); - }; - - constexpr auto IsHeaderValid = [](std::ifstream& file) -> bool { - if (!file.is_open()) { - return false; - } - SettingsHeader hdr{}; - file.read(reinterpret_cast<char*>(&hdr), sizeof(hdr)); - return hdr.magic == SETTINGS_MAGIC && hdr.version == SETTINGS_VERSION; - }; - - if (!exists || !file_size_ok) { - ResetToDefault(); - } - - std::ifstream file(settings_file, std::ios::binary | std::ios::in); - if (!IsHeaderValid(file)) { - file.close(); - ResetToDefault(); - file = std::ifstream(settings_file, std::ios::binary | std::ios::in); - if (!IsHeaderValid(file)) { - return false; - } - } - - if constexpr (std::is_same_v<settings_type, PrivateSettings>) { - file.read(reinterpret_cast<char*>(&m_private_settings), sizeof(settings_type)); - } else if constexpr (std::is_same_v<settings_type, DeviceSettings>) { - file.read(reinterpret_cast<char*>(&m_device_settings), sizeof(settings_type)); - } else if constexpr (std::is_same_v<settings_type, ApplnSettings>) { - file.read(reinterpret_cast<char*>(&m_appln_settings), sizeof(settings_type)); - } else if constexpr (std::is_same_v<settings_type, SystemSettings>) { - file.read(reinterpret_cast<char*>(&m_system_settings), sizeof(settings_type)); - } else { - UNREACHABLE(); - } - file.close(); - - return true; -} - -bool SET_SYS::StoreSettingsFile(std::filesystem::path& path, auto& settings) { - using settings_type = std::decay_t<decltype(settings)>; - - if (!Common::FS::IsDir(path)) { - return false; - } - - auto settings_base = path / "settings"; - auto settings_tmp_file = settings_base; - settings_tmp_file = settings_tmp_file.replace_extension("tmp"); - std::ofstream file(settings_tmp_file, std::ios::binary | std::ios::out); - if (!file.is_open()) { - return false; - } - - SettingsHeader hdr{ - .magic = SETTINGS_MAGIC, - .version = SETTINGS_VERSION, - .reserved = 0u, - }; - file.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr)); - - if constexpr (std::is_same_v<settings_type, PrivateSettings>) { - file.write(reinterpret_cast<const char*>(&m_private_settings), sizeof(settings_type)); - } else if constexpr (std::is_same_v<settings_type, DeviceSettings>) { - file.write(reinterpret_cast<const char*>(&m_device_settings), sizeof(settings_type)); - } else if constexpr (std::is_same_v<settings_type, ApplnSettings>) { - file.write(reinterpret_cast<const char*>(&m_appln_settings), sizeof(settings_type)); - } else if constexpr (std::is_same_v<settings_type, SystemSettings>) { - file.write(reinterpret_cast<const char*>(&m_system_settings), sizeof(settings_type)); - } else { - UNREACHABLE(); - } - file.close(); - - std::filesystem::rename(settings_tmp_file, settings_base.replace_extension("dat")); - - return true; -} - -void SET_SYS::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); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { - 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); -} - -void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { - 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); -} - -void SET_SYS::GetExternalSteadyClockSourceId(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - Common::UUID id{}; - auto res = GetExternalSteadyClockSourceId(id); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw(id); -} - -void SET_SYS::SetExternalSteadyClockSourceId(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::RequestParser rp{ctx}; - auto id{rp.PopRaw<Common::UUID>()}; - - auto res = SetExternalSteadyClockSourceId(id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::GetUserSystemClockContext(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - Service::Time::Clock::SystemClockContext context{}; - auto res = GetUserSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, - 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw(context); -} - -void SET_SYS::SetUserSystemClockContext(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; - - auto res = SetUserSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.account_settings); -} - -void SET_SYS::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); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetEulaVersions(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - ctx.WriteBuffer(m_system_settings.eula_versions); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.eula_version_count); -} - -void SET_SYS::SetEulaVersions(HLERequestContext& ctx) { - const auto elements = ctx.GetReadBufferNumElements<EulaVersion>(); - const auto buffer_data = ctx.ReadBuffer(); - - LOG_INFO(Service_SET, "called, elements={}", elements); - ASSERT(elements <= 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); - SetSaveNeeded(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetColorSetId(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(m_system_settings.color_set_id); -} - -void SET_SYS::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); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetNotificationSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.notification_settings); -} - -void SET_SYS::SetNotificationSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.notification_settings = rp.PopRaw<NotificationSettings>(); - SetSaveNeeded(); - - 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); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetAccountNotificationSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - ctx.WriteBuffer(m_system_settings.account_notification_settings); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.account_notification_settings_count); -} - -void SET_SYS::SetAccountNotificationSettings(HLERequestContext& ctx) { - const auto elements = ctx.GetReadBufferNumElements<AccountNotificationSettings>(); - const auto buffer_data = ctx.ReadBuffer(); - - LOG_INFO(Service_SET, "called, elements={}", elements); - - ASSERT(elements <= 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)); - SetSaveNeeded(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -// FIXME: implement support for the real system_settings.ini - -template <typename T> -static std::vector<u8> ToBytes(const T& value) { - static_assert(std::is_trivially_copyable_v<T>); - - const auto* begin = reinterpret_cast<const u8*>(&value); - const auto* end = begin + sizeof(T); - - return std::vector<u8>(begin, end); -} - -using Settings = - std::map<std::string, std::map<std::string, std::vector<u8>, std::less<>>, std::less<>>; - -static Settings GetSettings() { - Settings ret; - - ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); - ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); - - // Time - ret["time"]["notify_time_to_fs_interval_seconds"] = ToBytes(s32{600}); - ret["time"]["standard_network_clock_sufficient_accuracy_minutes"] = - ToBytes(s32{43200}); // 30 days - ret["time"]["standard_steady_clock_rtc_update_interval_minutes"] = ToBytes(s32{5}); - ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0}); - ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); - - return ret; -} - -void SET_SYS::GetSettingsItemValueSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - // 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{setting_category_buf.begin(), setting_category_buf.end()}; - - // 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{setting_name_buf.begin(), setting_name_buf.end()}; - - 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(); - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess); - rb.Push(response_size); -} - -void SET_SYS::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{setting_category_buf.begin(), setting_category_buf.end()}; - - // 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{setting_name_buf.begin(), setting_name_buf.end()}; - - std::vector<u8> value; - auto response = GetSettingsItemValue(value, 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); -} - -void SET_SYS::GetTvSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.tv_settings); -} - -void SET_SYS::SetTvSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.tv_settings = rp.PopRaw<TvSettings>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, - "called, flags={}, cmu_mode={}, constrast_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.constrast_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); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetDebugModeFlag(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(0); -} - -void SET_SYS::GetQuestFlag(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(QuestFlag::Retail); -} - -void SET_SYS::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "called"); - - Service::Time::TimeZone::LocationName name{}; - auto res = GetDeviceTimeZoneLocationName(name); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw<Service::Time::TimeZone::LocationName>(name); -} - -void SET_SYS::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "called"); - - IPC::RequestParser rp{ctx}; - auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()}; - - auto res = SetDeviceTimeZoneLocationName(name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::SetRegionCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.region_code = rp.PopEnum<RegionCode>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetNetworkSystemClockContext(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - Service::Time::Clock::SystemClockContext context{}; - auto res = GetNetworkSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, - 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw(context); -} - -void SET_SYS::SetNetworkSystemClockContext(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; - - auto res = SetNetworkSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - bool enabled{}; - auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - rb.PushRaw(enabled); -} - -void SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::RequestParser rp{ctx}; - auto enabled{rp.Pop<bool>()}; - - auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::GetPrimaryAlbumStorage(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(PrimaryAlbumStorage::SdCard); -} - -void SET_SYS::GetSleepSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - IPC::ResponseBuilder rb{ctx, 5}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.sleep_settings); -} - -void SET_SYS::SetSleepSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.sleep_settings = rp.PopRaw<SleepSettings>(); - SetSaveNeeded(); - - 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, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetInitialLaunchSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.initial_launch_settings_packed); -} - -void SET_SYS::SetInitialLaunchSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - auto inital_launch_settings = rp.PopRaw<InitialLaunchSettings>(); - - m_system_settings.initial_launch_settings_packed.flags = inital_launch_settings.flags; - m_system_settings.initial_launch_settings_packed.timestamp = inital_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); -} - -void SET_SYS::GetDeviceNickName(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); - - ctx.WriteBuffer(::Settings::values.device_name.GetValue()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::SetDeviceNickName(HLERequestContext& ctx) { - const std::string device_name = Common::StringFromBuffer(ctx.ReadBuffer()); - - LOG_INFO(Service_SET, "called, device_name={}", device_name); - - ::Settings::values.device_name = device_name; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetProductModel(HLERequestContext& ctx) { - 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); -} - -void SET_SYS::GetMiiAuthorId(HLERequestContext& ctx) { - const auto author_id = Common::UUID::MakeDefault(); - - LOG_WARNING(Service_SET, "(STUBBED) called, author_id={}", author_id.FormattedString()); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(author_id); -} - -void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { - u8 auto_update_flag{}; - - LOG_WARNING(Service_SET, "(STUBBED) called, auto_update_flag={}", auto_update_flag); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(auto_update_flag); -} - -void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { - u8 battery_percentage_flag{1}; - - LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", - battery_percentage_flag); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(battery_percentage_flag); -} - -void SET_SYS::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called."); - - IPC::RequestParser rp{ctx}; - auto offset{rp.Pop<s64>()}; - - auto res = SetExternalSteadyClockInternalOffset(offset); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called."); - - s64 offset{}; - auto res = GetExternalSteadyClockInternalOffset(offset); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.Push(offset); -} - -void SET_SYS::GetErrorReportSharePermission(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(ErrorReportSharePermission::Denied); -} - -void SET_SYS::GetAppletLaunchFlags(HLERequestContext& ctx) { - 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); -} - -void SET_SYS::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); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void SET_SYS::GetKeyboardLayout(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 selected_keyboard_layout = KeyboardLayout::EnglishUs; - if (key_code != language_to_layout.end()) { - selected_keyboard_layout = key_code->second; - } - - LOG_INFO(Service_SET, "called, selected_keyboard_layout={}", selected_keyboard_layout); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(selected_keyboard_layout)); -} - -void SET_SYS::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "called."); - - Service::Time::Clock::SteadyClockTimePoint time_point{}; - auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); -} - -void SET_SYS::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "called."); - - IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; - - auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "called."); - - Service::Time::Clock::SteadyClockTimePoint time_point{}; - auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); -} - -void SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "called."); - - IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; - - auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); -} - -void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "(STUBBED) called"); - - const HomeMenuScheme default_color = { - .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); -} - -void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { - LOG_WARNING(Service_SET, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(false); -} - -SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"}, m_system{system} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &SET_SYS::SetLanguageCode, "SetLanguageCode"}, - {1, nullptr, "SetNetworkSettings"}, - {2, nullptr, "GetNetworkSettings"}, - {3, &SET_SYS::GetFirmwareVersion, "GetFirmwareVersion"}, - {4, &SET_SYS::GetFirmwareVersion2, "GetFirmwareVersion2"}, - {5, nullptr, "GetFirmwareVersionDigest"}, - {7, nullptr, "GetLockScreenFlag"}, - {8, nullptr, "SetLockScreenFlag"}, - {9, nullptr, "GetBacklightSettings"}, - {10, nullptr, "SetBacklightSettings"}, - {11, nullptr, "SetBluetoothDevicesSettings"}, - {12, nullptr, "GetBluetoothDevicesSettings"}, - {13, &SET_SYS::GetExternalSteadyClockSourceId, "GetExternalSteadyClockSourceId"}, - {14, &SET_SYS::SetExternalSteadyClockSourceId, "SetExternalSteadyClockSourceId"}, - {15, &SET_SYS::GetUserSystemClockContext, "GetUserSystemClockContext"}, - {16, &SET_SYS::SetUserSystemClockContext, "SetUserSystemClockContext"}, - {17, &SET_SYS::GetAccountSettings, "GetAccountSettings"}, - {18, &SET_SYS::SetAccountSettings, "SetAccountSettings"}, - {19, nullptr, "GetAudioVolume"}, - {20, nullptr, "SetAudioVolume"}, - {21, &SET_SYS::GetEulaVersions, "GetEulaVersions"}, - {22, &SET_SYS::SetEulaVersions, "SetEulaVersions"}, - {23, &SET_SYS::GetColorSetId, "GetColorSetId"}, - {24, &SET_SYS::SetColorSetId, "SetColorSetId"}, - {25, nullptr, "GetConsoleInformationUploadFlag"}, - {26, nullptr, "SetConsoleInformationUploadFlag"}, - {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, - {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, - {29, &SET_SYS::GetNotificationSettings, "GetNotificationSettings"}, - {30, &SET_SYS::SetNotificationSettings, "SetNotificationSettings"}, - {31, &SET_SYS::GetAccountNotificationSettings, "GetAccountNotificationSettings"}, - {32, &SET_SYS::SetAccountNotificationSettings, "SetAccountNotificationSettings"}, - {35, nullptr, "GetVibrationMasterVolume"}, - {36, nullptr, "SetVibrationMasterVolume"}, - {37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, - {38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"}, - {39, &SET_SYS::GetTvSettings, "GetTvSettings"}, - {40, &SET_SYS::SetTvSettings, "SetTvSettings"}, - {41, nullptr, "GetEdid"}, - {42, nullptr, "SetEdid"}, - {43, nullptr, "GetAudioOutputMode"}, - {44, nullptr, "SetAudioOutputMode"}, - {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, - {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, - {47, &SET_SYS::GetQuestFlag, "GetQuestFlag"}, - {48, nullptr, "SetQuestFlag"}, - {49, nullptr, "GetDataDeletionSettings"}, - {50, nullptr, "SetDataDeletionSettings"}, - {51, nullptr, "GetInitialSystemAppletProgramId"}, - {52, nullptr, "GetOverlayDispProgramId"}, - {53, &SET_SYS::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"}, - {54, &SET_SYS::SetDeviceTimeZoneLocationName, "SetDeviceTimeZoneLocationName"}, - {55, nullptr, "GetWirelessCertificationFileSize"}, - {56, nullptr, "GetWirelessCertificationFile"}, - {57, &SET_SYS::SetRegionCode, "SetRegionCode"}, - {58, &SET_SYS::GetNetworkSystemClockContext, "GetNetworkSystemClockContext"}, - {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"}, - {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"}, - {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, - {62, &SET_SYS::GetDebugModeFlag, "GetDebugModeFlag"}, - {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, - {64, nullptr, "SetPrimaryAlbumStorage"}, - {65, nullptr, "GetUsb30EnableFlag"}, - {66, nullptr, "SetUsb30EnableFlag"}, - {67, nullptr, "GetBatteryLot"}, - {68, nullptr, "GetSerialNumber"}, - {69, nullptr, "GetNfcEnableFlag"}, - {70, nullptr, "SetNfcEnableFlag"}, - {71, &SET_SYS::GetSleepSettings, "GetSleepSettings"}, - {72, &SET_SYS::SetSleepSettings, "SetSleepSettings"}, - {73, nullptr, "GetWirelessLanEnableFlag"}, - {74, nullptr, "SetWirelessLanEnableFlag"}, - {75, &SET_SYS::GetInitialLaunchSettings, "GetInitialLaunchSettings"}, - {76, &SET_SYS::SetInitialLaunchSettings, "SetInitialLaunchSettings"}, - {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"}, - {78, &SET_SYS::SetDeviceNickName, "SetDeviceNickName"}, - {79, &SET_SYS::GetProductModel, "GetProductModel"}, - {80, nullptr, "GetLdnChannel"}, - {81, nullptr, "SetLdnChannel"}, - {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, - {83, nullptr, "GetTelemetryDirtyFlags"}, - {84, nullptr, "GetPtmBatteryLot"}, - {85, nullptr, "SetPtmBatteryLot"}, - {86, nullptr, "GetPtmFuelGaugeParameter"}, - {87, nullptr, "SetPtmFuelGaugeParameter"}, - {88, nullptr, "GetBluetoothEnableFlag"}, - {89, nullptr, "SetBluetoothEnableFlag"}, - {90, &SET_SYS::GetMiiAuthorId, "GetMiiAuthorId"}, - {91, nullptr, "SetShutdownRtcValue"}, - {92, nullptr, "GetShutdownRtcValue"}, - {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, - {94, nullptr, "GetFatalDirtyFlags"}, - {95, &SET_SYS::GetAutoUpdateEnableFlag, "GetAutoUpdateEnableFlag"}, - {96, nullptr, "SetAutoUpdateEnableFlag"}, - {97, nullptr, "GetNxControllerSettings"}, - {98, nullptr, "SetNxControllerSettings"}, - {99, &SET_SYS::GetBatteryPercentageFlag, "GetBatteryPercentageFlag"}, - {100, nullptr, "SetBatteryPercentageFlag"}, - {101, nullptr, "GetExternalRtcResetFlag"}, - {102, nullptr, "SetExternalRtcResetFlag"}, - {103, nullptr, "GetUsbFullKeyEnableFlag"}, - {104, nullptr, "SetUsbFullKeyEnableFlag"}, - {105, &SET_SYS::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"}, - {106, &SET_SYS::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"}, - {107, nullptr, "GetBacklightSettingsEx"}, - {108, nullptr, "SetBacklightSettingsEx"}, - {109, nullptr, "GetHeadphoneVolumeWarningCount"}, - {110, nullptr, "SetHeadphoneVolumeWarningCount"}, - {111, nullptr, "GetBluetoothAfhEnableFlag"}, - {112, nullptr, "SetBluetoothAfhEnableFlag"}, - {113, nullptr, "GetBluetoothBoostEnableFlag"}, - {114, nullptr, "SetBluetoothBoostEnableFlag"}, - {115, nullptr, "GetInRepairProcessEnableFlag"}, - {116, nullptr, "SetInRepairProcessEnableFlag"}, - {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, - {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, - {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, - {120, nullptr, "GetPushNotificationActivityModeOnSleep"}, - {121, nullptr, "SetPushNotificationActivityModeOnSleep"}, - {122, nullptr, "GetServiceDiscoveryControlSettings"}, - {123, nullptr, "SetServiceDiscoveryControlSettings"}, - {124, &SET_SYS::GetErrorReportSharePermission, "GetErrorReportSharePermission"}, - {125, nullptr, "SetErrorReportSharePermission"}, - {126, &SET_SYS::GetAppletLaunchFlags, "GetAppletLaunchFlags"}, - {127, &SET_SYS::SetAppletLaunchFlags, "SetAppletLaunchFlags"}, - {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, - {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, - {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, - {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"}, - {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"}, - {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, - {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, - {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, - {136, &SET_SYS::GetKeyboardLayout, "GetKeyboardLayout"}, - {137, nullptr, "SetKeyboardLayout"}, - {138, nullptr, "GetWebInspectorFlag"}, - {139, nullptr, "GetAllowedSslHosts"}, - {140, nullptr, "GetHostFsMountPoint"}, - {141, nullptr, "GetRequiresRunRepairTimeReviser"}, - {142, nullptr, "SetRequiresRunRepairTimeReviser"}, - {143, nullptr, "SetBlePairingSettings"}, - {144, nullptr, "GetBlePairingSettings"}, - {145, nullptr, "GetConsoleSixAxisSensorAngularVelocityTimeBias"}, - {146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"}, - {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, - {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, - {149, nullptr, "GetRebootlessSystemUpdateVersion"}, - {150, &SET_SYS::GetDeviceTimeZoneLocationUpdatedTime, "GetDeviceTimeZoneLocationUpdatedTime"}, - {151, &SET_SYS::SetDeviceTimeZoneLocationUpdatedTime, "SetDeviceTimeZoneLocationUpdatedTime"}, - {152, &SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, - {153, &SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, - {154, nullptr, "GetAccountOnlineStorageSettings"}, - {155, nullptr, "SetAccountOnlineStorageSettings"}, - {156, nullptr, "GetPctlReadyFlag"}, - {157, nullptr, "SetPctlReadyFlag"}, - {158, nullptr, "GetAnalogStickUserCalibrationL"}, - {159, nullptr, "SetAnalogStickUserCalibrationL"}, - {160, nullptr, "GetAnalogStickUserCalibrationR"}, - {161, nullptr, "SetAnalogStickUserCalibrationR"}, - {162, nullptr, "GetPtmBatteryVersion"}, - {163, nullptr, "SetPtmBatteryVersion"}, - {164, nullptr, "GetUsb30HostEnableFlag"}, - {165, nullptr, "SetUsb30HostEnableFlag"}, - {166, nullptr, "GetUsb30DeviceEnableFlag"}, - {167, nullptr, "SetUsb30DeviceEnableFlag"}, - {168, nullptr, "GetThemeId"}, - {169, nullptr, "SetThemeId"}, - {170, &SET_SYS::GetChineseTraditionalInputMethod, "GetChineseTraditionalInputMethod"}, - {171, nullptr, "SetChineseTraditionalInputMethod"}, - {172, nullptr, "GetPtmCycleCountReliability"}, - {173, nullptr, "SetPtmCycleCountReliability"}, - {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"}, - {175, nullptr, "GetThemeSettings"}, - {176, nullptr, "SetThemeSettings"}, - {177, nullptr, "GetThemeKey"}, - {178, nullptr, "SetThemeKey"}, - {179, nullptr, "GetZoomFlag"}, - {180, nullptr, "SetZoomFlag"}, - {181, nullptr, "GetT"}, - {182, nullptr, "SetT"}, - {183, nullptr, "GetPlatformRegion"}, - {184, nullptr, "SetPlatformRegion"}, - {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, - {186, nullptr, "GetMemoryUsageRateFlag"}, - {187, nullptr, "GetTouchScreenMode"}, - {188, nullptr, "SetTouchScreenMode"}, - {189, nullptr, "GetButtonConfigSettingsFull"}, - {190, nullptr, "SetButtonConfigSettingsFull"}, - {191, nullptr, "GetButtonConfigSettingsEmbedded"}, - {192, nullptr, "SetButtonConfigSettingsEmbedded"}, - {193, nullptr, "GetButtonConfigSettingsLeft"}, - {194, nullptr, "SetButtonConfigSettingsLeft"}, - {195, nullptr, "GetButtonConfigSettingsRight"}, - {196, nullptr, "SetButtonConfigSettingsRight"}, - {197, nullptr, "GetButtonConfigRegisteredSettingsEmbedded"}, - {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, - {199, nullptr, "GetButtonConfigRegisteredSettings"}, - {200, nullptr, "SetButtonConfigRegisteredSettings"}, - {201, &SET_SYS::GetFieldTestingFlag, "GetFieldTestingFlag"}, - {202, nullptr, "SetFieldTestingFlag"}, - {203, nullptr, "GetPanelCrcMode"}, - {204, nullptr, "SetPanelCrcMode"}, - {205, nullptr, "GetNxControllerSettingsEx"}, - {206, nullptr, "SetNxControllerSettingsEx"}, - {207, nullptr, "GetHearingProtectionSafeguardFlag"}, - {208, nullptr, "SetHearingProtectionSafeguardFlag"}, - {209, nullptr, "GetHearingProtectionSafeguardRemainingTime"}, - {210, nullptr, "SetHearingProtectionSafeguardRemainingTime"}, - }; - // clang-format on - - RegisterHandlers(functions); - - SetupSettings(); - m_save_thread = - std::jthread([this](std::stop_token stop_token) { StoreSettingsThreadFunc(stop_token); }); -} - -SET_SYS::~SET_SYS() { - SetSaveNeeded(); - m_save_thread.request_stop(); -} - -void SET_SYS::SetupSettings() { - auto system_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; - if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) { - ASSERT(false); - } - - auto private_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; - if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) { - ASSERT(false); - } - - auto device_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; - if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) { - ASSERT(false); - } - - auto appln_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; - if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) { - ASSERT(false); - } -} - -void SET_SYS::StoreSettings() { - auto system_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; - if (!StoreSettingsFile(system_dir, m_system_settings)) { - LOG_ERROR(HW_GPU, "Failed to store System settings"); - } - - auto private_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; - if (!StoreSettingsFile(private_dir, m_private_settings)) { - LOG_ERROR(HW_GPU, "Failed to store Private settings"); - } - - auto device_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; - if (!StoreSettingsFile(device_dir, m_device_settings)) { - LOG_ERROR(HW_GPU, "Failed to store Device settings"); - } - - auto appln_dir = - Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; - if (!StoreSettingsFile(appln_dir, m_appln_settings)) { - LOG_ERROR(HW_GPU, "Failed to store ApplLn settings"); - } -} - -void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) { - Common::SetCurrentThreadName("SettingsStore"); - - while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) { - std::scoped_lock l{m_save_needed_mutex}; - if (!std::exchange(m_save_needed, false)) { - continue; - } - StoreSettings(); - } -} - -void SET_SYS::SetSaveNeeded() { - std::scoped_lock l{m_save_needed_mutex}; - m_save_needed = true; -} - -Result SET_SYS::GetSettingsItemValue(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); - - out_value = settings[category][name]; - R_SUCCEED(); -} - -Result SET_SYS::GetExternalSteadyClockSourceId(Common::UUID& out_id) { - out_id = m_private_settings.external_clock_source_id; - R_SUCCEED(); -} - -Result SET_SYS::SetExternalSteadyClockSourceId(Common::UUID id) { - m_private_settings.external_clock_source_id = id; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context) { - out_context = m_system_settings.user_system_clock_context; - R_SUCCEED(); -} - -Result SET_SYS::SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context) { - m_system_settings.user_system_clock_context = context; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name) { - out_name = m_system_settings.device_time_zone_location_name; - R_SUCCEED(); -} - -Result SET_SYS::SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name) { - m_system_settings.device_time_zone_location_name = name; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::GetNetworkSystemClockContext( - Service::Time::Clock::SystemClockContext& out_context) { - out_context = m_system_settings.network_system_clock_context; - R_SUCCEED(); -} - -Result SET_SYS::SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context) { - m_system_settings.network_system_clock_context = context; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) { - out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; - R_SUCCEED(); -} - -Result SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled(bool enabled) { - m_system_settings.user_system_clock_automatic_correction_enabled = enabled; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::SetExternalSteadyClockInternalOffset(s64 offset) { - m_private_settings.external_steady_clock_internal_offset = offset; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::GetExternalSteadyClockInternalOffset(s64& out_offset) { - out_offset = m_private_settings.external_steady_clock_internal_offset; - R_SUCCEED(); -} - -Result SET_SYS::GetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point) { - out_time_point = m_system_settings.device_time_zone_location_updated_time; - R_SUCCEED(); -} - -Result SET_SYS::SetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& time_point) { - m_system_settings.device_time_zone_location_updated_time = time_point; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result SET_SYS::GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point) { - out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; - R_SUCCEED(); -} - -Result SET_SYS::SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint out_time_point) { - m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; - SetSaveNeeded(); - R_SUCCEED(); -} - -} // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h deleted file mode 100644 index 853f76fce..000000000 --- a/src/core/hle/service/set/set_sys.h +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <filesystem> -#include <mutex> -#include <string> -#include <thread> - -#include "common/polyfill_thread.h" -#include "common/uuid.h" -#include "core/hle/result.h" -#include "core/hle/service/service.h" -#include "core/hle/service/set/appln_settings.h" -#include "core/hle/service/set/device_settings.h" -#include "core/hle/service/set/private_settings.h" -#include "core/hle/service/set/system_settings.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Core { -class System; -} - -namespace Service::Set { -enum class GetFirmwareVersionType { - Version1, - Version2, -}; - -struct FirmwareVersionFormat { - u8 major; - u8 minor; - u8 micro; - INSERT_PADDING_BYTES(1); - u8 revision_major; - u8 revision_minor; - INSERT_PADDING_BYTES(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"); - -Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, - GetFirmwareVersionType type); - -class SET_SYS final : public ServiceFramework<SET_SYS> { -public: - explicit SET_SYS(Core::System& system_); - ~SET_SYS() override; - - Result GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category, - const std::string& name); - - Result GetExternalSteadyClockSourceId(Common::UUID& out_id); - Result SetExternalSteadyClockSourceId(Common::UUID id); - Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); - Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context); - Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name); - Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name); - Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); - Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context); - Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); - Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); - Result SetExternalSteadyClockInternalOffset(s64 offset); - Result GetExternalSteadyClockInternalOffset(s64& out_offset); - Result GetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point); - Result SetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& time_point); - Result GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point); - Result SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint time_point); - -private: - void SetLanguageCode(HLERequestContext& ctx); - void GetFirmwareVersion(HLERequestContext& ctx); - void GetFirmwareVersion2(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 GetSettingsItemValueSize(HLERequestContext& ctx); - void GetSettingsItemValue(HLERequestContext& ctx); - void GetTvSettings(HLERequestContext& ctx); - void SetTvSettings(HLERequestContext& ctx); - void GetDebugModeFlag(HLERequestContext& ctx); - void GetQuestFlag(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 GetSleepSettings(HLERequestContext& ctx); - void SetSleepSettings(HLERequestContext& ctx); - void GetInitialLaunchSettings(HLERequestContext& ctx); - void SetInitialLaunchSettings(HLERequestContext& ctx); - void GetDeviceNickName(HLERequestContext& ctx); - void SetDeviceNickName(HLERequestContext& ctx); - void GetProductModel(HLERequestContext& ctx); - void GetMiiAuthorId(HLERequestContext& ctx); - void GetAutoUpdateEnableFlag(HLERequestContext& ctx); - void GetBatteryPercentageFlag(HLERequestContext& ctx); - void SetExternalSteadyClockInternalOffset(HLERequestContext& ctx); - void GetExternalSteadyClockInternalOffset(HLERequestContext& ctx); - void GetErrorReportSharePermission(HLERequestContext& ctx); - void GetAppletLaunchFlags(HLERequestContext& ctx); - void SetAppletLaunchFlags(HLERequestContext& ctx); - void GetKeyboardLayout(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 GetFieldTestingFlag(HLERequestContext& ctx); - - bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func); - bool StoreSettingsFile(std::filesystem::path& path, auto& settings); - void SetupSettings(); - void StoreSettings(); - void StoreSettingsThreadFunc(std::stop_token stop_token); - void SetSaveNeeded(); - - Core::System& m_system; - SystemSettings m_system_settings{}; - PrivateSettings m_private_settings{}; - DeviceSettings m_device_settings{}; - ApplnSettings m_appln_settings{}; - std::jthread m_save_thread; - std::mutex m_save_needed_mutex; - bool m_save_needed{false}; -}; - -} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/appln_settings.cpp b/src/core/hle/service/set/setting_formats/appln_settings.cpp new file mode 100644 index 000000000..f7c7d5b91 --- /dev/null +++ b/src/core/hle/service/set/setting_formats/appln_settings.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/setting_formats/appln_settings.h" + +namespace Service::Set { + +ApplnSettings DefaultApplnSettings() { + ApplnSettings settings{}; + + settings.mii_author_id = Common::UUID::MakeDefault(); + + return settings; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/appln_settings.h b/src/core/hle/service/set/setting_formats/appln_settings.h new file mode 100644 index 000000000..ba9af998a --- /dev/null +++ b/src/core/hle/service/set/setting_formats/appln_settings.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstddef> + +#include "common/common_types.h" +#include "common/uuid.h" +#include "core/hle/service/set/settings_types.h" + +namespace Service::Set { +struct ApplnSettings { + INSERT_PADDING_BYTES(0x10); // Reserved + + // nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0 + Common::UUID mii_author_id; + INSERT_PADDING_BYTES(0x30); // Reserved + + // nn::settings::system::ServiceDiscoveryControlSettings + u32 service_discovery_control_settings; + INSERT_PADDING_BYTES(0x20); // Reserved + + bool in_repair_process_enable_flag; + INSERT_PADDING_BYTES(0x3); +}; +static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10); +static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50); +static_assert(offsetof(ApplnSettings, in_repair_process_enable_flag) == 0x74); +static_assert(sizeof(ApplnSettings) == 0x78, "ApplnSettings has the wrong size!"); + +ApplnSettings DefaultApplnSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/device_settings.cpp b/src/core/hle/service/set/setting_formats/device_settings.cpp new file mode 100644 index 000000000..5f295404d --- /dev/null +++ b/src/core/hle/service/set/setting_formats/device_settings.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/setting_formats/device_settings.h" + +namespace Service::Set { + +DeviceSettings DefaultDeviceSettings() { + return {}; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/device_settings.h b/src/core/hle/service/set/setting_formats/device_settings.h new file mode 100644 index 000000000..2827756f6 --- /dev/null +++ b/src/core/hle/service/set/setting_formats/device_settings.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstddef> + +#include "common/common_types.h" +#include "common/vector_math.h" +#include "core/hle/service/set/settings_types.h" + +namespace Service::Set { +struct DeviceSettings { + INSERT_PADDING_BYTES(0x10); // Reserved + + // nn::settings::BatteryLot + std::array<u8, 0x18> ptm_battery_lot; + // nn::settings::system::PtmFuelGaugeParameter + std::array<u8, 0x18> ptm_fuel_gauge_parameter; + u8 ptm_battery_version; + // nn::settings::system::PtmCycleCountReliability + u32 ptm_cycle_count_reliability; + INSERT_PADDING_BYTES(0x48); // Reserved + + // nn::settings::system::AnalogStickUserCalibration L + std::array<u8, 0x10> analog_user_stick_calibration_l; + // nn::settings::system::AnalogStickUserCalibration R + std::array<u8, 0x10> analog_user_stick_calibration_r; + INSERT_PADDING_BYTES(0x20); // Reserved + + // nn::settings::system::ConsoleSixAxisSensorAccelerationBias + Common::Vec3<f32> console_six_axis_sensor_acceleration_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias + Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias; + // nn::settings::system::ConsoleSixAxisSensorAccelerationGain + std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain + std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias + Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration + std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration; +}; +static_assert(offsetof(DeviceSettings, ptm_battery_lot) == 0x10); +static_assert(offsetof(DeviceSettings, ptm_cycle_count_reliability) == 0x44); +static_assert(offsetof(DeviceSettings, analog_user_stick_calibration_l) == 0x90); +static_assert(offsetof(DeviceSettings, console_six_axis_sensor_acceleration_bias) == 0xD0); +static_assert(offsetof(DeviceSettings, console_six_axis_sensor_angular_acceleration) == 0x13C); +static_assert(sizeof(DeviceSettings) == 0x160, "DeviceSettings has the wrong size!"); + +DeviceSettings DefaultDeviceSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/private_settings.cpp b/src/core/hle/service/set/setting_formats/private_settings.cpp new file mode 100644 index 000000000..81c362482 --- /dev/null +++ b/src/core/hle/service/set/setting_formats/private_settings.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/setting_formats/private_settings.h" + +namespace Service::Set { + +PrivateSettings DefaultPrivateSettings() { + return {}; +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h new file mode 100644 index 000000000..6579e95e0 --- /dev/null +++ b/src/core/hle/service/set/setting_formats/private_settings.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "common/uuid.h" +#include "core/hle/service/set/settings_types.h" + +namespace Service::Set { + +struct PrivateSettings { + INSERT_PADDING_BYTES(0x10); // Reserved + + InitialLaunchSettings initial_launch_settings; + INSERT_PADDING_BYTES(0x20); // Reserved + + Common::UUID external_clock_source_id; + s64 shutdown_rtc_value; + s64 external_steady_clock_internal_offset; + INSERT_PADDING_BYTES(0x60); // Reserved + + // nn::settings::system::PlatformRegion + s32 platform_region; + INSERT_PADDING_BYTES(0x4); // Reserved +}; +static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10); +static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50); +static_assert(offsetof(PrivateSettings, shutdown_rtc_value) == 0x60); +static_assert(offsetof(PrivateSettings, external_steady_clock_internal_offset) == 0x68); +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.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp new file mode 100644 index 000000000..16ded43bf --- /dev/null +++ b/src/core/hle/service/set/setting_formats/system_settings.cpp @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/hle/service/set/setting_formats/system_settings.h" + +namespace Service::Set { + +SystemSettings DefaultSystemSettings() { + SystemSettings settings{}; + + settings.version = 0x140000; + settings.flags = 7; + + settings.mii_author_id = Common::UUID::MakeDefault(); + + settings.color_set_id = ColorSet::BasicWhite; + + settings.notification_settings = { + .flags{0x300}, + .volume = NotificationVolume::High, + .start_time = {.hour = 9, .minute = 0}, + .stop_time = {.hour = 21, .minute = 0}, + }; + + settings.tv_settings = { + .flags = {0xC}, + .tv_resolution = TvResolution::Auto, + .hdmi_content_type = HdmiContentType::Game, + .rgb_range = RgbRange::Auto, + .cmu_mode = CmuMode::None, + .tv_underscan = {}, + .tv_gama = 1.0f, + .contrast_ratio = 0.5f, + }; + + settings.initial_launch_settings_packed = { + .flags = {0x10001}, + .timestamp = {}, + }; + + settings.sleep_settings = { + .flags = {0x3}, + .handheld_sleep_plan = HandheldSleepPlan::Sleep10Min, + .console_sleep_plan = ConsoleSleepPlan::Sleep1Hour, + }; + + settings.device_time_zone_location_name = {"UTC"}; + settings.user_system_clock_automatic_correction_enabled = true; + + settings.primary_album_storage = PrimaryAlbumStorage::SdCard; + settings.battery_percentage_flag = true; + settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0; + settings.vibration_master_volume = 1.0f; + + 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; }); + + settings.keyboard_layout = KeyboardLayout::EnglishUs; + if (key_code != language_to_layout.end()) { + settings.keyboard_layout = key_code->second; + } + + return settings; +} + +} // 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 new file mode 100644 index 000000000..ebc373da5 --- /dev/null +++ b/src/core/hle/service/set/setting_formats/system_settings.h @@ -0,0 +1,391 @@ +// 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 "common/vector_math.h" +#include "core/hle/service/set/setting_formats/private_settings.h" +#include "core/hle/service/set/settings_types.h" + +namespace Service::Set { + +struct SystemSettings { + // 0/unwritten (1.0.0), 0x20000 (2.0.0), 0x30000 (3.0.0-3.0.1), 0x40001 (4.0.0-4.1.0), 0x50000 + // (5.0.0-5.1.0), 0x60000 (6.0.0-6.2.0), 0x70000 (7.0.0), 0x80000 (8.0.0-8.1.1), 0x90000 + // (9.0.0-10.0.4), 0x100100 (10.1.0+), 0x120000 (12.0.0-12.1.0), 0x130000 (13.0.0-13.2.1), + // 0x140000 (14.0.0+) + u32 version; + // 0/unwritten (1.0.0), 1 (6.0.0-8.1.0), 2 (8.1.1), 7 (9.0.0+). + // if (flags & 2), defaults are written for AnalogStickUserCalibration + u32 flags; + INSERT_PADDING_BYTES(0x8); // Reserved + + LanguageCode language_code; + INSERT_PADDING_BYTES(0x38); // Reserved + + // nn::settings::system::NetworkSettings + u32 network_setting_count; + bool wireless_lan_enable_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x8); // Reserved + + // nn::settings::system::NetworkSettings + std::array<std::array<u8, 0x400>, 32> network_settings_1B0; + + // nn::settings::system::BluetoothDevicesSettings + std::array<u8, 0x4> bluetooth_device_settings_count; + bool bluetooth_enable_flag; + INSERT_PADDING_BYTES(0x3); + bool bluetooth_afh_enable_flag; + INSERT_PADDING_BYTES(0x3); + bool bluetooth_boost_enable_flag; + INSERT_PADDING_BYTES(0x3); + std::array<std::array<u8, 0x200>, 10> bluetooth_device_settings_first_10; + + s32 ldn_channel; + INSERT_PADDING_BYTES(0x3C); // Reserved + + // nn::util::Uuid MiiAuthorId + Common::UUID mii_author_id; + + INSERT_PADDING_BYTES(0x30); // Reserved + + // nn::settings::system::NxControllerSettings + u32 nx_controller_settings_count; + + INSERT_PADDING_BYTES(0xC); // Reserved + + // nn::settings::system::NxControllerSettings, + // nn::settings::system::NxControllerLegacySettings on 13.0.0+ + std::array<std::array<u8, 0x40>, 10> nx_controller_legacy_settings; + INSERT_PADDING_BYTES(0x170); // Reserved + + bool external_rtc_reset_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x3C); // Reserved + + s32 push_notification_activity_mode_on_sleep; + INSERT_PADDING_BYTES(0x3C); // Reserved + + ErrorReportSharePermission error_report_share_permission; + INSERT_PADDING_BYTES(0x3C); // Reserved + + KeyboardLayout keyboard_layout; + INSERT_PADDING_BYTES(0x3C); // Reserved + + bool web_inspector_flag; + INSERT_PADDING_BYTES(0x3); + + // nn::settings::system::AllowedSslHost + u32 allowed_ssl_host_count; + + bool memory_usage_rate_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x34); // Reserved + + // nn::settings::system::HostFsMountPoint + std::array<u8, 0x100> host_fs_mount_point; + + // nn::settings::system::AllowedSslHost + std::array<std::array<u8, 0x100>, 8> allowed_ssl_hosts; + INSERT_PADDING_BYTES(0x6C0); // Reserved + + // nn::settings::system::BlePairingSettings + u32 ble_pairing_settings_count; + INSERT_PADDING_BYTES(0xC); // Reserved + std::array<std::array<u8, 0x80>, 10> ble_pairing_settings; + + // nn::settings::system::AccountOnlineStorageSettings + u32 account_online_storage_settings_count; + INSERT_PADDING_BYTES(0xC); // Reserved + std::array<std::array<u8, 0x40>, 8> account_online_storage_settings; + + bool pctl_ready_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x3C); // Reserved + + // nn::settings::system::ThemeId + std::array<u8, 0x80> theme_id_type0; + std::array<u8, 0x80> theme_id_type1; + INSERT_PADDING_BYTES(0x100); // Reserved + + ChineseTraditionalInputMethod chinese_traditional_input_method; + INSERT_PADDING_BYTES(0x3C); // Reserved + + bool zoom_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x3C); // Reserved + + // nn::settings::system::ButtonConfigRegisteredSettings + u32 button_config_registered_settings_count; + INSERT_PADDING_BYTES(0xC); // Reserved + + // nn::settings::system::ButtonConfigSettings + u32 button_config_settings_count; + INSERT_PADDING_BYTES(0x4); // Reserved + std::array<std::array<u8, 0x5A8>, 5> button_config_settings; + INSERT_PADDING_BYTES(0x13B0); // Reserved + u32 button_config_settings_embedded_count; + INSERT_PADDING_BYTES(0x4); // Reserved + std::array<std::array<u8, 0x5A8>, 5> button_config_settings_embedded; + INSERT_PADDING_BYTES(0x13B0); // Reserved + u32 button_config_settings_left_count; + INSERT_PADDING_BYTES(0x4); // Reserved + std::array<std::array<u8, 0x5A8>, 5> button_config_settings_left; + INSERT_PADDING_BYTES(0x13B0); // Reserved + u32 button_config_settings_right_count; + INSERT_PADDING_BYTES(0x4); // Reserved + std::array<std::array<u8, 0x5A8>, 5> button_config_settings_right; + INSERT_PADDING_BYTES(0x73B0); // Reserved + // nn::settings::system::ButtonConfigRegisteredSettings + std::array<u8, 0x5C8> button_config_registered_settings_embedded; + std::array<std::array<u8, 0x5C8>, 10> button_config_registered_settings; + INSERT_PADDING_BYTES(0x7FF8); // Reserved + + // nn::settings::system::ConsoleSixAxisSensorAccelerationBias + Common::Vec3<f32> console_six_axis_sensor_acceleration_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias + Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias; + // nn::settings::system::ConsoleSixAxisSensorAccelerationGain + std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain + std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; + // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias + Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias; + // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration + std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration; + INSERT_PADDING_BYTES(0x70); // Reserved + + bool lock_screen_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x4); // Reserved + + ColorSet color_set_id; + + QuestFlag quest_flag; + + SystemRegionCode region_code; + + // Different to nn::settings::system::InitialLaunchSettings? + InitialLaunchSettingsPacked initial_launch_settings_packed; + + bool battery_percentage_flag; + INSERT_PADDING_BYTES(0x3); + + // BitFlagSet<32, nn::settings::system::AppletLaunchFlag> + u32 applet_launch_flag; + + // nn::settings::system::ThemeSettings + std::array<u8, 0x8> theme_settings; + // nn::fssystem::ArchiveMacKey + std::array<u8, 0x10> theme_key; + + bool field_testing_flag; + INSERT_PADDING_BYTES(0x3); + + s32 panel_crc_mode; + INSERT_PADDING_BYTES(0x28); // Reserved + + // nn::settings::system::BacklightSettings + std::array<u8, 0x2C> backlight_settings_mixed_up; + INSERT_PADDING_BYTES(0x64); // Reserved + + // nn::time::SystemClockContext + Service::PSC::Time::SystemClockContext user_system_clock_context; + Service::PSC::Time::SystemClockContext network_system_clock_context; + bool user_system_clock_automatic_correction_enabled; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x4); // Reserved + // nn::time::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint + user_system_clock_automatic_correction_updated_time_point; + INSERT_PADDING_BYTES(0x10); // Reserved + + AccountSettings account_settings; + INSERT_PADDING_BYTES(0xFC); // Reserved + + // nn::settings::system::AudioVolume + std::array<u8, 0x8> audio_volume_type0; + std::array<u8, 0x8> audio_volume_type1; + AudioOutputMode audio_output_mode_hdmi; + AudioOutputMode audio_output_mode_speaker; + AudioOutputMode audio_output_mode_headphone; + bool force_mute_on_headphone_removed; + INSERT_PADDING_BYTES(0x3); + s32 headphone_volume_warning_count; + bool heaphone_volume_update_flag; + INSERT_PADDING_BYTES(0x3); + // nn::settings::system::AudioVolume + std::array<u8, 0x8> audio_volume_type2; + AudioOutputMode audio_output_mode_type3; + AudioOutputMode audio_output_mode_type4; + bool hearing_protection_safeguard_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x4); // Reserved + s64 hearing_protection_safeguard_remaining_time; + INSERT_PADDING_BYTES(0x38); // Reserved + + bool console_information_upload_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x3C); // Reserved + + bool automatic_application_download_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x4); // Reserved + + NotificationSettings notification_settings; + INSERT_PADDING_BYTES(0x60); // Reserved + + // nn::settings::system::AccountNotificationSettings + u32 account_notification_settings_count; + INSERT_PADDING_BYTES(0xC); // Reserved + std::array<AccountNotificationSettings, 8> account_notification_settings; + INSERT_PADDING_BYTES(0x140); // Reserved + + f32 vibration_master_volume; + + bool usb_full_key_enable_flag; + INSERT_PADDING_BYTES(0x3); + + // nn::settings::system::AnalogStickUserCalibration + std::array<u8, 0x10> analog_stick_user_calibration_left; + std::array<u8, 0x10> analog_stick_user_calibration_right; + + // nn::settings::system::TouchScreenMode + s32 touch_screen_mode; + INSERT_PADDING_BYTES(0x14); // Reserved + + TvSettings tv_settings; + + // nn::settings::system::Edid + std::array<u8, 0x100> edid; + INSERT_PADDING_BYTES(0x2E0); // Reserved + + // nn::settings::system::DataDeletionSettings + std::array<u8, 0x8> data_deletion_settings; + INSERT_PADDING_BYTES(0x38); // Reserved + + // nn::ncm::ProgramId + std::array<u8, 0x8> initial_system_applet_program_id; + std::array<u8, 0x8> overlay_disp_program_id; + INSERT_PADDING_BYTES(0x4); // Reserved + + bool requires_run_repair_time_reviser; + INSERT_PADDING_BYTES(0x6B); // Reserved + + // nn::time::LocationName + Service::PSC::Time::LocationName device_time_zone_location_name; + INSERT_PADDING_BYTES(0x4); // Reserved + // nn::time::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint device_time_zone_location_updated_time; + + INSERT_PADDING_BYTES(0xC0); // Reserved + + // nn::settings::system::PrimaryAlbumStorage + PrimaryAlbumStorage primary_album_storage; + INSERT_PADDING_BYTES(0x3C); // Reserved + + bool usb_30_enable_flag; + INSERT_PADDING_BYTES(0x3); + bool usb_30_host_enable_flag; + INSERT_PADDING_BYTES(0x3); + bool usb_30_device_enable_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x34); // Reserved + + bool nfc_enable_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x3C); // Reserved + + // nn::settings::system::SleepSettings + SleepSettings sleep_settings; + INSERT_PADDING_BYTES(0x34); // Reserved + + // nn::settings::system::EulaVersion + u32 eula_version_count; + INSERT_PADDING_BYTES(0xC); // Reserved + std::array<EulaVersion, 32> eula_versions; + INSERT_PADDING_BYTES(0x200); // Reserved + + // nn::settings::system::DeviceNickName + std::array<u8, 0x80> device_nick_name; + INSERT_PADDING_BYTES(0x80); // Reserved + + bool auto_update_enable_flag; + INSERT_PADDING_BYTES(0x3); + INSERT_PADDING_BYTES(0x4C); // Reserved + + // nn::settings::system::BluetoothDevicesSettings + std::array<std::array<u8, 0x200>, 14> bluetooth_device_settings_last_14; + INSERT_PADDING_BYTES(0x2000); // Reserved + + // nn::settings::system::NxControllerSettings + std::array<std::array<u8, 0x800>, 10> nx_controller_settings_data_from_offset_30; +}; + +static_assert(offsetof(SystemSettings, language_code) == 0x10); +static_assert(offsetof(SystemSettings, network_setting_count) == 0x50); +static_assert(offsetof(SystemSettings, network_settings_1B0) == 0x60); +static_assert(offsetof(SystemSettings, bluetooth_device_settings_count) == 0x8060); +static_assert(offsetof(SystemSettings, bluetooth_enable_flag) == 0x8064); +static_assert(offsetof(SystemSettings, bluetooth_device_settings_first_10) == 0x8070); +static_assert(offsetof(SystemSettings, ldn_channel) == 0x9470); +static_assert(offsetof(SystemSettings, mii_author_id) == 0x94B0); +static_assert(offsetof(SystemSettings, nx_controller_settings_count) == 0x94F0); +static_assert(offsetof(SystemSettings, nx_controller_legacy_settings) == 0x9500); +static_assert(offsetof(SystemSettings, external_rtc_reset_flag) == 0x98F0); +static_assert(offsetof(SystemSettings, push_notification_activity_mode_on_sleep) == 0x9930); +static_assert(offsetof(SystemSettings, allowed_ssl_host_count) == 0x99F4); +static_assert(offsetof(SystemSettings, host_fs_mount_point) == 0x9A30); +static_assert(offsetof(SystemSettings, allowed_ssl_hosts) == 0x9B30); +static_assert(offsetof(SystemSettings, ble_pairing_settings_count) == 0xA9F0); +static_assert(offsetof(SystemSettings, ble_pairing_settings) == 0xAA00); +static_assert(offsetof(SystemSettings, account_online_storage_settings_count) == 0xAF00); +static_assert(offsetof(SystemSettings, account_online_storage_settings) == 0xAF10); +static_assert(offsetof(SystemSettings, pctl_ready_flag) == 0xB110); +static_assert(offsetof(SystemSettings, theme_id_type0) == 0xB150); +static_assert(offsetof(SystemSettings, chinese_traditional_input_method) == 0xB350); +static_assert(offsetof(SystemSettings, button_config_registered_settings_count) == 0xB3D0); +static_assert(offsetof(SystemSettings, button_config_settings_count) == 0xB3E0); +static_assert(offsetof(SystemSettings, button_config_settings) == 0xB3E8); +static_assert(offsetof(SystemSettings, button_config_registered_settings_embedded) == 0x1D3E0); +static_assert(offsetof(SystemSettings, console_six_axis_sensor_acceleration_bias) == 0x29370); +static_assert(offsetof(SystemSettings, lock_screen_flag) == 0x29470); +static_assert(offsetof(SystemSettings, battery_percentage_flag) == 0x294A0); +static_assert(offsetof(SystemSettings, field_testing_flag) == 0x294C0); +static_assert(offsetof(SystemSettings, backlight_settings_mixed_up) == 0x294F0); +static_assert(offsetof(SystemSettings, user_system_clock_context) == 0x29580); +static_assert(offsetof(SystemSettings, network_system_clock_context) == 0x295A0); +static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_enabled) == 0x295C0); +static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_updated_time_point) == + 0x295C8); +static_assert(offsetof(SystemSettings, account_settings) == 0x295F0); +static_assert(offsetof(SystemSettings, audio_volume_type0) == 0x296F0); +static_assert(offsetof(SystemSettings, hearing_protection_safeguard_remaining_time) == 0x29730); +static_assert(offsetof(SystemSettings, automatic_application_download_flag) == 0x297B0); +static_assert(offsetof(SystemSettings, notification_settings) == 0x297B8); +static_assert(offsetof(SystemSettings, account_notification_settings) == 0x29840); +static_assert(offsetof(SystemSettings, vibration_master_volume) == 0x29A40); +static_assert(offsetof(SystemSettings, analog_stick_user_calibration_left) == 0x29A48); +static_assert(offsetof(SystemSettings, touch_screen_mode) == 0x29A68); +static_assert(offsetof(SystemSettings, edid) == 0x29AA0); +static_assert(offsetof(SystemSettings, data_deletion_settings) == 0x29E80); +static_assert(offsetof(SystemSettings, requires_run_repair_time_reviser) == 0x29ED4); +static_assert(offsetof(SystemSettings, device_time_zone_location_name) == 0x29F40); +static_assert(offsetof(SystemSettings, nfc_enable_flag) == 0x2A0C0); +static_assert(offsetof(SystemSettings, eula_version_count) == 0x2A140); +static_assert(offsetof(SystemSettings, device_nick_name) == 0x2A950); +static_assert(offsetof(SystemSettings, bluetooth_device_settings_last_14) == 0x2AAA0); +static_assert(offsetof(SystemSettings, nx_controller_settings_data_from_offset_30) == 0x2E6A0); + +static_assert(sizeof(SystemSettings) == 0x336A0, "SystemSettings has the wrong size!"); + +SystemSettings DefaultSystemSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp index c48844f77..73d021ff4 100644 --- a/src/core/hle/service/set/settings.cpp +++ b/src/core/hle/service/set/settings.cpp @@ -2,21 +2,24 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/server_manager.h" -#include "core/hle/service/set/set.h" -#include "core/hle/service/set/set_cal.h" -#include "core/hle/service/set/set_fd.h" -#include "core/hle/service/set/set_sys.h" +#include "core/hle/service/set/factory_settings_server.h" +#include "core/hle/service/set/firmware_debug_settings_server.h" #include "core/hle/service/set/settings.h" +#include "core/hle/service/set/settings_server.h" +#include "core/hle/service/set/system_settings_server.h" namespace Service::Set { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - server_manager->RegisterNamedService("set", std::make_shared<SET>(system)); - server_manager->RegisterNamedService("set:cal", std::make_shared<SET_CAL>(system)); - server_manager->RegisterNamedService("set:fd", std::make_shared<SET_FD>(system)); - server_manager->RegisterNamedService("set:sys", std::make_shared<SET_SYS>(system)); + server_manager->RegisterNamedService("set", std::make_shared<ISettingsServer>(system)); + server_manager->RegisterNamedService("set:cal", + std::make_shared<IFactorySettingsServer>(system)); + server_manager->RegisterNamedService("set:fd", + std::make_shared<IFirmwareDebugSettingsServer>(system)); + server_manager->RegisterNamedService("set:sys", + std::make_shared<ISystemSettingsServer>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/set/settings_server.cpp b/src/core/hle/service/set/settings_server.cpp new file mode 100644 index 000000000..b2caa00ff --- /dev/null +++ b/src/core/hle/service/set/settings_server.cpp @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <array> +#include <chrono> +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_server.h" + +namespace Service::Set { +namespace { +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; + } + + ctx.WriteBuffer(layout); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} +} // Anonymous namespace + +LanguageCode GetLanguageCodeFromIndex(std::size_t index) { + return available_language_codes.at(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"}, + {10, nullptr, "GetFirmwareVersionForDebug"}, + {11, &ISettingsServer::GetDeviceNickName, "GetDeviceNickName"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISettingsServer::~ISettingsServer() = default; + +void ISettingsServer::GetAvailableLanguageCodes(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::MakeLanguageCode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index = rp.Pop<u32>(); + + 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; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(available_language_codes[index]); +} + +void ISettingsServer::GetAvailableLanguageCodes2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::GetAvailableLanguageCodeCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::GetAvailableLanguageCodeCount2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::GetQuestFlag(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(Settings::values.quest_flag.GetValue())); +} + +void ISettingsServer::GetLanguageCode(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue()); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum( + available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]); +} + +void ISettingsServer::GetRegionCode(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(Settings::values.region_index.GetValue())); +} + +void ISettingsServer::GetKeyCodeMap(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); + GetKeyCodeMapImpl(ctx); +} + +void ISettingsServer::GetKeyCodeMap2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); + GetKeyCodeMapImpl(ctx); +} + +void ISettingsServer::GetDeviceNickName(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(Settings::values.device_name.GetValue()); +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/settings_server.h b/src/core/hle/service/set/settings_server.h new file mode 100644 index 000000000..8304e8424 --- /dev/null +++ b/src/core/hle/service/set/settings_server.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" +#include "core/hle/service/set/settings_types.h" + +namespace Core { +class System; +} + +namespace Service::Set { + +LanguageCode GetLanguageCodeFromIndex(std::size_t idx); + +class ISettingsServer final : public ServiceFramework<ISettingsServer> { +public: + explicit ISettingsServer(Core::System& system_); + ~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); +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h new file mode 100644 index 000000000..ceb85b82a --- /dev/null +++ b/src/core/hle/service/set/settings_types.h @@ -0,0 +1,475 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.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::AudioOutputMode +enum class AudioOutputMode : u32 { + ch_1, + ch_2, + ch_5_1, + ch_7_1, +}; + +/// This is nn::settings::system::AudioOutputModeTarget +enum class AudioOutputModeTarget : u32 { + None, + Hdmi, + Speaker, + Headphone, + Type3, + Type4, +}; + +/// This is nn::settings::system::AudioVolumeTarget +enum class AudioVolumeTarget : u32 { + Speaker, + Headphone, +}; + +/// This is nn::settings::system::ClockSourceId +enum class ClockSourceId : u32 { + NetworkSystemClock, + SteadyClock, +}; + +/// This is nn::settings::system::CmuMode +enum class CmuMode : u32 { + None, + ColorInvert, + HighContrast, + GrayScale, +}; + +/// This is nn::settings::system::ChineseTraditionalInputMethod +enum class ChineseTraditionalInputMethod : u32 { + Unknown0 = 0, + Unknown1 = 1, + Unknown2 = 2, +}; + +/// Indicates the current theme set by the system settings +enum class ColorSet : u32 { + BasicWhite = 0, + BasicBlack = 1, +}; + +/// This is nn::settings::system::ConsoleSleepPlan +enum class ConsoleSleepPlan : u32 { + Sleep1Hour, + Sleep2Hour, + Sleep3Hour, + Sleep6Hour, + Sleep12Hour, + Never, +}; + +/// This is nn::settings::system::ErrorReportSharePermission +enum class ErrorReportSharePermission : u32 { + NotConfirmed, + Granted, + Denied, +}; + +/// This is nn::settings::system::EulaVersionClockType +enum class EulaVersionClockType : u32 { + NetworkSystemClock, + SteadyClock, +}; + +/// This is nn::settings::factory::RegionCode +enum class FactoryRegionCode : u32 { + Japan, + Usa, + Europe, + Australia, + China, + Korea, + Taiwan, +}; + +/// This is nn::settings::system::FriendPresenceOverlayPermission +enum class FriendPresenceOverlayPermission : u8 { + NotConfirmed, + NoDisplay, + FavoriteFriends, + Friends, +}; + +enum class GetFirmwareVersionType { + Version1, + Version2, +}; + +/// This is nn::settings::system::HandheldSleepPlan +enum class HandheldSleepPlan : u32 { + Sleep1Min, + Sleep3Min, + Sleep5Min, + Sleep10Min, + Sleep30Min, + Never, +}; + +/// This is nn::settings::system::HdmiContentType +enum class HdmiContentType : u32 { + None, + Graphics, + Cinema, + Photo, + Game, +}; + +enum class KeyboardLayout : u32 { + Japanese = 0, + EnglishUs = 1, + EnglishUsInternational = 2, + EnglishUk = 3, + French = 4, + FrenchCa = 5, + Spanish = 6, + SpanishLatin = 7, + German = 8, + Italian = 9, + Portuguese = 10, + Russian = 11, + Korean = 12, + ChineseSimplified = 13, + ChineseTraditional = 14, +}; + +/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64. +enum class LanguageCode : u64 { + JA = 0x000000000000616A, + EN_US = 0x00000053552D6E65, + FR = 0x0000000000007266, + DE = 0x0000000000006564, + IT = 0x0000000000007469, + ES = 0x0000000000007365, + ZH_CN = 0x0000004E432D687A, + KO = 0x0000000000006F6B, + NL = 0x0000000000006C6E, + PT = 0x0000000000007470, + RU = 0x0000000000007572, + ZH_TW = 0x00000057542D687A, + EN_GB = 0x00000042472D6E65, + FR_CA = 0x00000041432D7266, + ES_419 = 0x00003931342D7365, + ZH_HANS = 0x00736E61482D687A, + ZH_HANT = 0x00746E61482D687A, + PT_BR = 0x00000052422D7470, +}; + +/// This is nn::settings::system::NotificationVolume +enum class NotificationVolume : u32 { + Mute, + Low, + High, +}; + +/// This is nn::settings::system::PrimaryAlbumStorage +enum class PrimaryAlbumStorage : u32 { + Nand, + SdCard, +}; + +/// Indicates the current console is a retail or kiosk unit +enum class QuestFlag : u8 { + Retail = 0, + Kiosk = 1, +}; + +/// This is nn::settings::system::RgbRange +enum class RgbRange : u32 { + Auto, + Full, + Limited, +}; + +/// This is nn::settings::system::RegionCode +enum class SystemRegionCode : u32 { + Japan, + Usa, + Europe, + Australia, + HongKongTaiwanKorea, + China, +}; + +/// This is nn::settings::system::TouchScreenMode +enum class TouchScreenMode : u32 { + Stylus, + Standard, +}; + +/// This is nn::settings::system::TvResolution +enum class TvResolution : u32 { + Auto, + Resolution1080p, + Resolution720p, + Resolution480p, +}; + +constexpr std::array<LanguageCode, 18> available_language_codes = {{ + LanguageCode::JA, + LanguageCode::EN_US, + LanguageCode::FR, + LanguageCode::DE, + LanguageCode::IT, + LanguageCode::ES, + LanguageCode::ZH_CN, + LanguageCode::KO, + LanguageCode::NL, + LanguageCode::PT, + LanguageCode::RU, + LanguageCode::ZH_TW, + LanguageCode::EN_GB, + LanguageCode::FR_CA, + LanguageCode::ES_419, + LanguageCode::ZH_HANS, + LanguageCode::ZH_HANT, + LanguageCode::PT_BR, +}}; + +static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{ + {LanguageCode::JA, KeyboardLayout::Japanese}, + {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, + {LanguageCode::FR, KeyboardLayout::French}, + {LanguageCode::DE, KeyboardLayout::German}, + {LanguageCode::IT, KeyboardLayout::Italian}, + {LanguageCode::ES, KeyboardLayout::Spanish}, + {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, + {LanguageCode::KO, KeyboardLayout::Korean}, + {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, + {LanguageCode::PT, KeyboardLayout::Portuguese}, + {LanguageCode::RU, KeyboardLayout::Russian}, + {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, + {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, + {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, + {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, + {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, + {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, + {LanguageCode::PT_BR, KeyboardLayout::Portuguese}, +}}; + +/// This is nn::settings::system::AccountNotificationFlag +struct AccountNotificationFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> FriendOnlineFlag; + BitField<1, 1, u32> FriendRequestFlag; + BitField<8, 1, u32> CoralInvitationFlag; + }; +}; +static_assert(sizeof(AccountNotificationFlag) == 4, "AccountNotificationFlag is an invalid size"); + +/// This is nn::settings::system::AccountSettings +struct AccountSettings { + u32 flags; +}; +static_assert(sizeof(AccountSettings) == 4, "AccountSettings is an invalid size"); + +/// This is nn::settings::system::DataDeletionFlag +struct DataDeletionFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> AutomaticDeletionFlag; + }; +}; +static_assert(sizeof(DataDeletionFlag) == 4, "DataDeletionFlag is an invalid size"); + +/// 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::SleepFlag +struct SleepFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> SleepsWhilePlayingMedia; + BitField<1, 1, u32> WakesAtPowerStateChange; + }; +}; +static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size"); + +/// This is nn::settings::system::NotificationFlag +struct NotificationFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> RingtoneFlag; + BitField<1, 1, u32> DownloadCompletionFlag; + BitField<8, 1, u32> EnablesNews; + BitField<9, 1, u32> IncomingLampFlag; + }; +}; +static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); + +struct PlatformConfig { + union { + u32 raw{}; + BitField<0, 1, u32> has_rail_interface; + BitField<1, 1, u32> has_sio_mcu; + }; +}; +static_assert(sizeof(PlatformConfig) == 0x4, "PlatformConfig is an invalid size"); + +/// This is nn::settings::system::TvFlag +struct TvFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> Allows4k; + BitField<1, 1, u32> Allows3d; + BitField<2, 1, u32> AllowsCec; + BitField<3, 1, u32> PreventsScreenBurnIn; + }; +}; +static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size"); + +/// This is nn::settings::system::UserSelectorFlag +struct UserSelectorFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> SkipIfSingleUser; + BitField<31, 1, u32> Unknown; + }; +}; +static_assert(sizeof(UserSelectorFlag) == 4, "UserSelectorFlag is an invalid size"); + +/// This is nn::settings::system::AccountNotificationSettings +struct AccountNotificationSettings { + Common::UUID uid; + AccountNotificationFlag flags; + FriendPresenceOverlayPermission friend_presence_permission; + FriendPresenceOverlayPermission friend_invitation_permission; + INSERT_PADDING_BYTES(0x2); +}; +static_assert(sizeof(AccountNotificationSettings) == 0x18, + "AccountNotificationSettings is an invalid size"); + +/// This is nn::settings::factory::BatteryLot +struct BatteryLot { + std::array<char, 0x18> lot_number; +}; +static_assert(sizeof(BatteryLot) == 0x18, "BatteryLot is an invalid size"); + +/// This is nn::settings::system::EulaVersion +struct EulaVersion { + u32 version; + SystemRegionCode region_code; + EulaVersionClockType clock_type; + INSERT_PADDING_BYTES(0x4); + s64 posix_time; + Service::PSC::Time::SteadyClockTimePoint timestamp; +}; +static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); + +struct FirmwareVersionFormat { + u8 major; + u8 minor; + u8 micro; + INSERT_PADDING_BYTES(1); + u8 revision_major; + u8 revision_minor; + INSERT_PADDING_BYTES(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"); + +/// This is nn::settings::system::HomeMenuScheme +struct HomeMenuScheme { + u32 main; + u32 back; + u32 sub; + u32 bezel; + u32 extra; +}; +static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect 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"); + +/// This is nn::settings::system::NotificationTime +struct NotificationTime { + u32 hour; + u32 minute; +}; +static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size"); + +/// This is nn::settings::system::NotificationSettings +struct NotificationSettings { + NotificationFlag flags; + NotificationVolume volume; + NotificationTime start_time; + NotificationTime stop_time; +}; +static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size"); + +/// This is nn::settings::factory::SerialNumber +struct SerialNumber { + std::array<char, 0x18> serial_number; +}; +static_assert(sizeof(SerialNumber) == 0x18, "SerialNumber is an invalid size"); + +/// This is nn::settings::system::SleepSettings +struct SleepSettings { + SleepFlag flags; + HandheldSleepPlan handheld_sleep_plan; + ConsoleSleepPlan console_sleep_plan; +}; +static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size"); + +/// This is nn::settings::system::TvSettings +struct TvSettings { + TvFlag flags; + TvResolution tv_resolution; + HdmiContentType hdmi_content_type; + RgbRange rgb_range; + CmuMode cmu_mode; + u32 tv_underscan; + f32 tv_gama; + f32 contrast_ratio; +}; +static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size"); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/system_settings.cpp deleted file mode 100644 index 2723417ad..000000000 --- a/src/core/hle/service/set/system_settings.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/set/system_settings.h" - -namespace Service::Set { - -SystemSettings DefaultSystemSettings() { - SystemSettings settings{}; - - settings.version = 0x140000; - settings.flags = 7; - - settings.color_set_id = ColorSet::BasicWhite; - - settings.notification_settings = { - .flags{0x300}, - .volume = NotificationVolume::High, - .start_time = {.hour = 9, .minute = 0}, - .stop_time = {.hour = 21, .minute = 0}, - }; - - settings.tv_settings = { - .flags = {0xC}, - .tv_resolution = TvResolution::Auto, - .hdmi_content_type = HdmiContentType::Game, - .rgb_range = RgbRange::Auto, - .cmu_mode = CmuMode::None, - .tv_underscan = {}, - .tv_gama = 1.0f, - .constrast_ratio = 0.5f, - }; - - settings.initial_launch_settings_packed = { - .flags = {0x10001}, - .timestamp = {}, - }; - - settings.sleep_settings = { - .flags = {0x3}, - .handheld_sleep_plan = HandheldSleepPlan::Sleep10Min, - .console_sleep_plan = ConsoleSleepPlan::Sleep1Hour, - }; - - settings.device_time_zone_location_name = {"UTC"}; - settings.user_system_clock_automatic_correction_enabled = false; - - return settings; -} - -} // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/system_settings.h deleted file mode 100644 index ded2906ad..000000000 --- a/src/core/hle/service/set/system_settings.h +++ /dev/null @@ -1,699 +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 "core/hle/service/set/private_settings.h" -#include "core/hle/service/time/clock_types.h" - -namespace Service::Set { - -/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64. -enum class LanguageCode : u64 { - JA = 0x000000000000616A, - EN_US = 0x00000053552D6E65, - FR = 0x0000000000007266, - DE = 0x0000000000006564, - IT = 0x0000000000007469, - ES = 0x0000000000007365, - ZH_CN = 0x0000004E432D687A, - KO = 0x0000000000006F6B, - NL = 0x0000000000006C6E, - PT = 0x0000000000007470, - RU = 0x0000000000007572, - ZH_TW = 0x00000057542D687A, - EN_GB = 0x00000042472D6E65, - FR_CA = 0x00000041432D7266, - ES_419 = 0x00003931342D7365, - ZH_HANS = 0x00736E61482D687A, - ZH_HANT = 0x00746E61482D687A, - PT_BR = 0x00000052422D7470, -}; - -/// This is nn::settings::system::ErrorReportSharePermission -enum class ErrorReportSharePermission : u32 { - NotConfirmed, - Granted, - Denied, -}; - -/// This is nn::settings::system::ChineseTraditionalInputMethod -enum class ChineseTraditionalInputMethod : u32 { - Unknown0 = 0, - Unknown1 = 1, - Unknown2 = 2, -}; - -/// This is nn::settings::system::HomeMenuScheme -struct HomeMenuScheme { - u32 main; - u32 back; - u32 sub; - u32 bezel; - u32 extra; -}; -static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"); - -/// Indicates the current theme set by the system settings -enum class ColorSet : u32 { - BasicWhite = 0, - BasicBlack = 1, -}; - -/// Indicates the current console is a retail or kiosk unit -enum class QuestFlag : u8 { - Retail = 0, - Kiosk = 1, -}; - -/// This is nn::settings::system::RegionCode -enum class RegionCode : u32 { - Japan, - Usa, - Europe, - Australia, - HongKongTaiwanKorea, - China, -}; - -/// This is nn::settings::system::AccountSettings -struct AccountSettings { - u32 flags; -}; -static_assert(sizeof(AccountSettings) == 4, "AccountSettings is an invalid size"); - -/// This is nn::settings::system::NotificationVolume -enum class NotificationVolume : u32 { - Mute, - Low, - High, -}; - -/// This is nn::settings::system::NotificationFlag -struct NotificationFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> RingtoneFlag; - BitField<1, 1, u32> DownloadCompletionFlag; - BitField<8, 1, u32> EnablesNews; - BitField<9, 1, u32> IncomingLampFlag; - }; -}; -static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); - -/// This is nn::settings::system::NotificationTime -struct NotificationTime { - u32 hour; - u32 minute; -}; -static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size"); - -/// This is nn::settings::system::NotificationSettings -struct NotificationSettings { - NotificationFlag flags; - NotificationVolume volume; - NotificationTime start_time; - NotificationTime stop_time; -}; -static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size"); - -/// This is nn::settings::system::AccountNotificationFlag -struct AccountNotificationFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> FriendOnlineFlag; - BitField<1, 1, u32> FriendRequestFlag; - BitField<8, 1, u32> CoralInvitationFlag; - }; -}; -static_assert(sizeof(AccountNotificationFlag) == 4, "AccountNotificationFlag is an invalid size"); - -/// This is nn::settings::system::FriendPresenceOverlayPermission -enum class FriendPresenceOverlayPermission : u8 { - NotConfirmed, - NoDisplay, - FavoriteFriends, - Friends, -}; - -/// This is nn::settings::system::AccountNotificationSettings -struct AccountNotificationSettings { - Common::UUID uid; - AccountNotificationFlag flags; - FriendPresenceOverlayPermission friend_presence_permission; - FriendPresenceOverlayPermission friend_invitation_permission; - INSERT_PADDING_BYTES(0x2); -}; -static_assert(sizeof(AccountNotificationSettings) == 0x18, - "AccountNotificationSettings is an invalid size"); - -/// This is nn::settings::system::TvFlag -struct TvFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> Allows4k; - BitField<1, 1, u32> Allows3d; - BitField<2, 1, u32> AllowsCec; - BitField<3, 1, u32> PreventsScreenBurnIn; - }; -}; -static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size"); - -/// This is nn::settings::system::TvResolution -enum class TvResolution : u32 { - Auto, - Resolution1080p, - Resolution720p, - Resolution480p, -}; - -/// This is nn::settings::system::HdmiContentType -enum class HdmiContentType : u32 { - None, - Graphics, - Cinema, - Photo, - Game, -}; - -/// This is nn::settings::system::RgbRange -enum class RgbRange : u32 { - Auto, - Full, - Limited, -}; - -/// This is nn::settings::system::CmuMode -enum class CmuMode : u32 { - None, - ColorInvert, - HighContrast, - GrayScale, -}; - -/// This is nn::settings::system::TvSettings -struct TvSettings { - TvFlag flags; - TvResolution tv_resolution; - HdmiContentType hdmi_content_type; - RgbRange rgb_range; - CmuMode cmu_mode; - u32 tv_underscan; - f32 tv_gama; - f32 constrast_ratio; -}; -static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size"); - -/// This is nn::settings::system::PrimaryAlbumStorage -enum class PrimaryAlbumStorage : u32 { - Nand, - SdCard, -}; - -/// This is nn::settings::system::HandheldSleepPlan -enum class HandheldSleepPlan : u32 { - Sleep1Min, - Sleep3Min, - Sleep5Min, - Sleep10Min, - Sleep30Min, - Never, -}; - -/// This is nn::settings::system::ConsoleSleepPlan -enum class ConsoleSleepPlan : u32 { - Sleep1Hour, - Sleep2Hour, - Sleep3Hour, - Sleep6Hour, - Sleep12Hour, - Never, -}; - -/// This is nn::settings::system::SleepFlag -struct SleepFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> SleepsWhilePlayingMedia; - BitField<1, 1, u32> WakesAtPowerStateChange; - }; -}; -static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size"); - -/// This is nn::settings::system::SleepSettings -struct SleepSettings { - SleepFlag flags; - HandheldSleepPlan handheld_sleep_plan; - ConsoleSleepPlan console_sleep_plan; -}; -static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size"); - -/// This is nn::settings::system::EulaVersionClockType -enum class EulaVersionClockType : u32 { - NetworkSystemClock, - SteadyClock, -}; - -/// This is nn::settings::system::EulaVersion -struct EulaVersion { - u32 version; - RegionCode region_code; - EulaVersionClockType clock_type; - INSERT_PADDING_BYTES(0x4); - s64 posix_time; - Time::Clock::SteadyClockTimePoint timestamp; -}; -static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); - -struct SystemSettings { - // 0/unwritten (1.0.0), 0x20000 (2.0.0), 0x30000 (3.0.0-3.0.1), 0x40001 (4.0.0-4.1.0), 0x50000 - // (5.0.0-5.1.0), 0x60000 (6.0.0-6.2.0), 0x70000 (7.0.0), 0x80000 (8.0.0-8.1.1), 0x90000 - // (9.0.0-10.0.4), 0x100100 (10.1.0+), 0x120000 (12.0.0-12.1.0), 0x130000 (13.0.0-13.2.1), - // 0x140000 (14.0.0+) - u32 version; - // 0/unwritten (1.0.0), 1 (6.0.0-8.1.0), 2 (8.1.1), 7 (9.0.0+). - // if (flags & 2), defaults are written for AnalogStickUserCalibration - u32 flags; - - std::array<u8, 0x8> reserved_00008; - - // nn::settings::LanguageCode - LanguageCode language_code; - - std::array<u8, 0x38> reserved_00018; - - // nn::settings::system::NetworkSettings - u32 network_setting_count; - bool wireless_lan_enable_flag; - std::array<u8, 0x3> pad_00055; - - std::array<u8, 0x8> reserved_00058; - - // nn::settings::system::NetworkSettings - std::array<std::array<u8, 0x400>, 32> network_settings_1B0; - - // nn::settings::system::BluetoothDevicesSettings - std::array<u8, 0x4> bluetooth_device_settings_count; - bool bluetooth_enable_flag; - std::array<u8, 0x3> pad_08065; - bool bluetooth_afh_enable_flag; - std::array<u8, 0x3> pad_08069; - bool bluetooth_boost_enable_flag; - std::array<u8, 0x3> pad_0806D; - std::array<std::array<u8, 0x200>, 10> bluetooth_device_settings_first_10; - - s32 ldn_channel; - - std::array<u8, 0x3C> reserved_09474; - - // nn::util::Uuid MiiAuthorId - std::array<u8, 0x10> mii_author_id; - - std::array<u8, 0x30> reserved_094C0; - - // nn::settings::system::NxControllerSettings - u32 nx_controller_settings_count; - - std::array<u8, 0xC> reserved_094F4; - - // nn::settings::system::NxControllerSettings, - // nn::settings::system::NxControllerLegacySettings on 13.0.0+ - std::array<std::array<u8, 0x40>, 10> nx_controller_legacy_settings; - - std::array<u8, 0x170> reserved_09780; - - bool external_rtc_reset_flag; - std::array<u8, 0x3> pad_098F1; - - std::array<u8, 0x3C> reserved_098F4; - - s32 push_notification_activity_mode_on_sleep; - - std::array<u8, 0x3C> reserved_09934; - - // nn::settings::system::ErrorReportSharePermission - ErrorReportSharePermission error_report_share_permssion; - - std::array<u8, 0x3C> reserved_09974; - - // nn::settings::KeyboardLayout - std::array<u8, 0x4> keyboard_layout; - - std::array<u8, 0x3C> reserved_099B4; - - bool web_inspector_flag; - std::array<u8, 0x3> pad_099F1; - - // nn::settings::system::AllowedSslHost - u32 allowed_ssl_host_count; - - bool memory_usage_rate_flag; - std::array<u8, 0x3> pad_099F9; - - std::array<u8, 0x34> reserved_099FC; - - // nn::settings::system::HostFsMountPoint - std::array<u8, 0x100> host_fs_mount_point; - - // nn::settings::system::AllowedSslHost - std::array<std::array<u8, 0x100>, 8> allowed_ssl_hosts; - - std::array<u8, 0x6C0> reserved_0A330; - - // nn::settings::system::BlePairingSettings - u32 ble_pairing_settings_count; - std::array<u8, 0xC> reserved_0A9F4; - std::array<std::array<u8, 0x80>, 10> ble_pairing_settings; - - // nn::settings::system::AccountOnlineStorageSettings - u32 account_online_storage_settings_count; - std::array<u8, 0xC> reserved_0AF04; - std::array<std::array<u8, 0x40>, 8> account_online_storage_settings; - - bool pctl_ready_flag; - std::array<u8, 0x3> pad_0B111; - - std::array<u8, 0x3C> reserved_0B114; - - // nn::settings::system::ThemeId - std::array<u8, 0x80> theme_id_type0; - std::array<u8, 0x80> theme_id_type1; - - std::array<u8, 0x100> reserved_0B250; - - // nn::settings::ChineseTraditionalInputMethod - ChineseTraditionalInputMethod chinese_traditional_input_method; - - std::array<u8, 0x3C> reserved_0B354; - - bool zoom_flag; - std::array<u8, 0x3> pad_0B391; - - std::array<u8, 0x3C> reserved_0B394; - - // nn::settings::system::ButtonConfigRegisteredSettings - u32 button_config_registered_settings_count; - std::array<u8, 0xC> reserved_0B3D4; - - // nn::settings::system::ButtonConfigSettings - u32 button_config_settings_count; - std::array<u8, 0x4> reserved_0B3E4; - std::array<std::array<u8, 0x5A8>, 5> button_config_settings; - std::array<u8, 0x13B0> reserved_0D030; - u32 button_config_settings_embedded_count; - std::array<u8, 0x4> reserved_0E3E4; - std::array<std::array<u8, 0x5A8>, 5> button_config_settings_embedded; - std::array<u8, 0x13B0> reserved_10030; - u32 button_config_settings_left_count; - std::array<u8, 0x4> reserved_113E4; - std::array<std::array<u8, 0x5A8>, 5> button_config_settings_left; - std::array<u8, 0x13B0> reserved_13030; - u32 button_config_settings_right_count; - std::array<u8, 0x4> reserved_143E4; - std::array<std::array<u8, 0x5A8>, 5> button_config_settings_right; - std::array<u8, 0x73B0> reserved_16030; - // nn::settings::system::ButtonConfigRegisteredSettings - std::array<u8, 0x5C8> button_config_registered_settings_embedded; - std::array<std::array<u8, 0x5C8>, 10> button_config_registered_settings; - - std::array<u8, 0x7FF8> reserved_21378; - - // nn::settings::system::ConsoleSixAxisSensorAccelerationBias - std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias; - // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias - std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias; - // nn::settings::system::ConsoleSixAxisSensorAccelerationGain - std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; - // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain - std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; - // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias - std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias; - // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration - std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration; - - std::array<u8, 0x70> reserved_29400; - - bool lock_screen_flag; - std::array<u8, 0x3> pad_29471; - - std::array<u8, 0x4> reserved_249274; - - ColorSet color_set_id; - - QuestFlag quest_flag; - - // nn::settings::system::RegionCode - RegionCode region_code; - - // Different to nn::settings::system::InitialLaunchSettings? - InitialLaunchSettingsPacked initial_launch_settings_packed; - - bool battery_percentage_flag; - std::array<u8, 0x3> pad_294A1; - - // BitFlagSet<32, nn::settings::system::AppletLaunchFlag> - u32 applet_launch_flag; - - // nn::settings::system::ThemeSettings - std::array<u8, 0x8> theme_settings; - // nn::fssystem::ArchiveMacKey - std::array<u8, 0x10> theme_key; - - bool field_testing_flag; - std::array<u8, 0x3> pad_294C1; - - s32 panel_crc_mode; - - std::array<u8, 0x28> reserved_294C8; - - // nn::settings::system::BacklightSettings - std::array<u8, 0x2C> backlight_settings_mixed_up; - - std::array<u8, 0x64> reserved_2951C; - - // nn::time::SystemClockContext - Service::Time::Clock::SystemClockContext user_system_clock_context; - Service::Time::Clock::SystemClockContext network_system_clock_context; - bool user_system_clock_automatic_correction_enabled; - std::array<u8, 0x3> pad_295C1; - std::array<u8, 0x4> reserved_295C4; - // nn::time::SteadyClockTimePoint - Service::Time::Clock::SteadyClockTimePoint - user_system_clock_automatic_correction_updated_time_point; - - std::array<u8, 0x10> reserved_295E0; - - // nn::settings::system::AccountSettings - AccountSettings account_settings; - - std::array<u8, 0xFC> reserved_295F4; - - // nn::settings::system::AudioVolume - std::array<u8, 0x8> audio_volume_type0; - std::array<u8, 0x8> audio_volume_type1; - // nn::settings::system::AudioOutputMode - s32 audio_output_mode_type0; - s32 audio_output_mode_type1; - s32 audio_output_mode_type2; - bool force_mute_on_headphone_removed; - std::array<u8, 0x3> pad_2970D; - s32 headphone_volume_warning_count; - bool heaphone_volume_update_flag; - std::array<u8, 0x3> pad_29715; - // nn::settings::system::AudioVolume - std::array<u8, 0x8> audio_volume_type2; - // nn::settings::system::AudioOutputMode - s32 audio_output_mode_type3; - s32 audio_output_mode_type4; - bool hearing_protection_safeguard_flag; - std::array<u8, 0x3> pad_29729; - std::array<u8, 0x4> reserved_2972C; - s64 hearing_protection_safeguard_remaining_time; - std::array<u8, 0x38> reserved_29738; - - bool console_information_upload_flag; - std::array<u8, 0x3> pad_29771; - - std::array<u8, 0x3C> reserved_29774; - - bool automatic_application_download_flag; - std::array<u8, 0x3> pad_297B1; - - std::array<u8, 0x4> reserved_297B4; - - // nn::settings::system::NotificationSettings - NotificationSettings notification_settings; - - std::array<u8, 0x60> reserved_297D0; - - // nn::settings::system::AccountNotificationSettings - u32 account_notification_settings_count; - std::array<u8, 0xC> reserved_29834; - std::array<AccountNotificationSettings, 8> account_notification_settings; - - std::array<u8, 0x140> reserved_29900; - - f32 vibration_master_volume; - - bool usb_full_key_enable_flag; - std::array<u8, 0x3> pad_29A45; - - // nn::settings::system::AnalogStickUserCalibration - std::array<u8, 0x10> analog_stick_user_calibration_left; - std::array<u8, 0x10> analog_stick_user_calibration_right; - - // nn::settings::system::TouchScreenMode - s32 touch_screen_mode; - - std::array<u8, 0x14> reserved_29A6C; - - // nn::settings::system::TvSettings - TvSettings tv_settings; - - // nn::settings::system::Edid - std::array<u8, 0x100> edid; - - std::array<u8, 0x2E0> reserved_29BA0; - - // nn::settings::system::DataDeletionSettings - std::array<u8, 0x8> data_deletion_settings; - - std::array<u8, 0x38> reserved_29E88; - - // nn::ncm::ProgramId - std::array<u8, 0x8> initial_system_applet_program_id; - std::array<u8, 0x8> overlay_disp_program_id; - - std::array<u8, 0x4> reserved_29ED0; - - bool requires_run_repair_time_reviser; - - std::array<u8, 0x6B> reserved_29ED5; - - // nn::time::LocationName - Service::Time::TimeZone::LocationName device_time_zone_location_name; - std::array<u8, 0x4> reserved_29F64; - // nn::time::SteadyClockTimePoint - Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; - - std::array<u8, 0xC0> reserved_29F80; - - // nn::settings::system::PrimaryAlbumStorage - PrimaryAlbumStorage primary_album_storage; - - std::array<u8, 0x3C> reserved_2A044; - - bool usb_30_enable_flag; - std::array<u8, 0x3> pad_2A081; - bool usb_30_host_enable_flag; - std::array<u8, 0x3> pad_2A085; - bool usb_30_device_enable_flag; - std::array<u8, 0x3> pad_2A089; - - std::array<u8, 0x34> reserved_2A08C; - - bool nfc_enable_flag; - std::array<u8, 0x3> pad_2A0C1; - - std::array<u8, 0x3C> reserved_2A0C4; - - // nn::settings::system::SleepSettings - SleepSettings sleep_settings; - - std::array<u8, 0x34> reserved_2A10C; - - // nn::settings::system::EulaVersion - u32 eula_version_count; - std::array<u8, 0xC> reserved_2A144; - std::array<EulaVersion, 32> eula_versions; - - std::array<u8, 0x200> reserved_2A750; - - // nn::settings::system::DeviceNickName - std::array<u8, 0x80> device_nick_name; - - std::array<u8, 0x80> reserved_2A9D0; - - bool auto_update_enable_flag; - std::array<u8, 0x3> pad_2AA51; - - std::array<u8, 0x4C> reserved_2AA54; - - // nn::settings::system::BluetoothDevicesSettings - std::array<std::array<u8, 0x200>, 14> bluetooth_device_settings_last_14; - - std::array<u8, 0x2000> reserved_2C6A0; - - // nn::settings::system::NxControllerSettings - std::array<std::array<u8, 0x800>, 10> nx_controller_settings_data_from_offset_30; -}; - -static_assert(offsetof(SystemSettings, language_code) == 0x10); -static_assert(offsetof(SystemSettings, network_setting_count) == 0x50); -static_assert(offsetof(SystemSettings, network_settings_1B0) == 0x60); -static_assert(offsetof(SystemSettings, bluetooth_device_settings_count) == 0x8060); -static_assert(offsetof(SystemSettings, bluetooth_enable_flag) == 0x8064); -static_assert(offsetof(SystemSettings, bluetooth_device_settings_first_10) == 0x8070); -static_assert(offsetof(SystemSettings, ldn_channel) == 0x9470); -static_assert(offsetof(SystemSettings, mii_author_id) == 0x94B0); -static_assert(offsetof(SystemSettings, nx_controller_settings_count) == 0x94F0); -static_assert(offsetof(SystemSettings, nx_controller_legacy_settings) == 0x9500); -static_assert(offsetof(SystemSettings, external_rtc_reset_flag) == 0x98F0); -static_assert(offsetof(SystemSettings, push_notification_activity_mode_on_sleep) == 0x9930); -static_assert(offsetof(SystemSettings, allowed_ssl_host_count) == 0x99F4); -static_assert(offsetof(SystemSettings, host_fs_mount_point) == 0x9A30); -static_assert(offsetof(SystemSettings, allowed_ssl_hosts) == 0x9B30); -static_assert(offsetof(SystemSettings, ble_pairing_settings_count) == 0xA9F0); -static_assert(offsetof(SystemSettings, ble_pairing_settings) == 0xAA00); -static_assert(offsetof(SystemSettings, account_online_storage_settings_count) == 0xAF00); -static_assert(offsetof(SystemSettings, account_online_storage_settings) == 0xAF10); -static_assert(offsetof(SystemSettings, pctl_ready_flag) == 0xB110); -static_assert(offsetof(SystemSettings, theme_id_type0) == 0xB150); -static_assert(offsetof(SystemSettings, chinese_traditional_input_method) == 0xB350); -static_assert(offsetof(SystemSettings, button_config_registered_settings_count) == 0xB3D0); -static_assert(offsetof(SystemSettings, button_config_settings_count) == 0xB3E0); -static_assert(offsetof(SystemSettings, button_config_settings) == 0xB3E8); -static_assert(offsetof(SystemSettings, button_config_registered_settings_embedded) == 0x1D3E0); -static_assert(offsetof(SystemSettings, console_six_axis_sensor_acceleration_bias) == 0x29370); -static_assert(offsetof(SystemSettings, lock_screen_flag) == 0x29470); -static_assert(offsetof(SystemSettings, battery_percentage_flag) == 0x294A0); -static_assert(offsetof(SystemSettings, field_testing_flag) == 0x294C0); -static_assert(offsetof(SystemSettings, backlight_settings_mixed_up) == 0x294F0); -static_assert(offsetof(SystemSettings, user_system_clock_context) == 0x29580); -static_assert(offsetof(SystemSettings, network_system_clock_context) == 0x295A0); -static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_enabled) == 0x295C0); -static_assert(offsetof(SystemSettings, user_system_clock_automatic_correction_updated_time_point) == - 0x295C8); -static_assert(offsetof(SystemSettings, account_settings) == 0x295F0); -static_assert(offsetof(SystemSettings, audio_volume_type0) == 0x296F0); -static_assert(offsetof(SystemSettings, hearing_protection_safeguard_remaining_time) == 0x29730); -static_assert(offsetof(SystemSettings, automatic_application_download_flag) == 0x297B0); -static_assert(offsetof(SystemSettings, notification_settings) == 0x297B8); -static_assert(offsetof(SystemSettings, account_notification_settings) == 0x29840); -static_assert(offsetof(SystemSettings, vibration_master_volume) == 0x29A40); -static_assert(offsetof(SystemSettings, analog_stick_user_calibration_left) == 0x29A48); -static_assert(offsetof(SystemSettings, touch_screen_mode) == 0x29A68); -static_assert(offsetof(SystemSettings, edid) == 0x29AA0); -static_assert(offsetof(SystemSettings, data_deletion_settings) == 0x29E80); -static_assert(offsetof(SystemSettings, requires_run_repair_time_reviser) == 0x29ED4); -static_assert(offsetof(SystemSettings, device_time_zone_location_name) == 0x29F40); -static_assert(offsetof(SystemSettings, nfc_enable_flag) == 0x2A0C0); -static_assert(offsetof(SystemSettings, eula_version_count) == 0x2A140); -static_assert(offsetof(SystemSettings, device_nick_name) == 0x2A950); -static_assert(offsetof(SystemSettings, bluetooth_device_settings_last_14) == 0x2AAA0); -static_assert(offsetof(SystemSettings, nx_controller_settings_data_from_offset_30) == 0x2E6A0); - -static_assert(sizeof(SystemSettings) == 0x336A0, "SystemSettings has the wrong size!"); - -SystemSettings DefaultSystemSettings(); - -} // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp new file mode 100644 index 000000000..100cb2db4 --- /dev/null +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -0,0 +1,1673 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <fstream> + +#include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/errors.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/service/filesystem/filesystem.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_server.h" +#include "core/hle/service/set/system_settings_server.h" + +namespace Service::Set { + +namespace { +constexpr u32 SETTINGS_VERSION{2u}; +constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); +struct SettingsHeader { + u64 magic; + u32 version; + u32 reserved; +}; +} // Anonymous namespace + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type) { + constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; + auto& fsc = system.GetFileSystemController(); + + // Attempt to load version data from disk + const FileSys::RegisteredCache* bis_system{}; + std::unique_ptr<FileSys::NCA> nca{}; + FileSys::VirtualDir romfs{}; + + bis_system = fsc.GetSystemNANDContents(); + if (bis_system) { + nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data); + } + if (nca) { + if (auto nca_romfs = nca->GetRomFS(); nca_romfs) { + romfs = FileSys::ExtractRomFS(nca_romfs); + } + } + if (!romfs) { + romfs = FileSys::ExtractRomFS( + FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); + } + + const auto early_exit_failure = [](std::string_view desc, Result code) { + LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", + desc); + return code; + }; + + const auto ver_file = romfs->GetFile("file"); + if (ver_file == nullptr) { + return early_exit_failure("The system version archive didn't contain the file 'file'.", + FileSys::ResultInvalidArgument); + } + + auto data = ver_file->ReadAllBytes(); + if (data.size() != sizeof(FirmwareVersionFormat)) { + return early_exit_failure("The system version file 'file' was not the correct size.", + FileSys::ResultOutOfRange); + } + + std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); + + // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will + // zero out the REVISION_MINOR field. + if (type == GetFirmwareVersionType::Version1) { + out_firmware.revision_minor = 0; + } + + return ResultSuccess; +} + +ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) + : ServiceFramework{system_, "set:sys"}, m_system{system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISystemSettingsServer::SetLanguageCode, "SetLanguageCode"}, + {1, nullptr, "SetNetworkSettings"}, + {2, nullptr, "GetNetworkSettings"}, + {3, &ISystemSettingsServer::GetFirmwareVersion, "GetFirmwareVersion"}, + {4, &ISystemSettingsServer::GetFirmwareVersion2, "GetFirmwareVersion2"}, + {5, nullptr, "GetFirmwareVersionDigest"}, + {7, &ISystemSettingsServer::GetLockScreenFlag, "GetLockScreenFlag"}, + {8, &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"}, + {19, nullptr, "GetAudioVolume"}, + {20, nullptr, "SetAudioVolume"}, + {21, &ISystemSettingsServer::GetEulaVersions, "GetEulaVersions"}, + {22, &ISystemSettingsServer::SetEulaVersions, "SetEulaVersions"}, + {23, &ISystemSettingsServer::GetColorSetId, "GetColorSetId"}, + {24, &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"}, + {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"}, + {49, nullptr, "GetDataDeletionSettings"}, + {50, nullptr, "SetDataDeletionSettings"}, + {51, nullptr, "GetInitialSystemAppletProgramId"}, + {52, nullptr, "GetOverlayDispProgramId"}, + {53, &ISystemSettingsServer::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"}, + {54, &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"}, + {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"}, + {80, nullptr, "GetLdnChannel"}, + {81, nullptr, "SetLdnChannel"}, + {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, + {83, nullptr, "GetTelemetryDirtyFlags"}, + {84, nullptr, "GetPtmBatteryLot"}, + {85, nullptr, "SetPtmBatteryLot"}, + {86, nullptr, "GetPtmFuelGaugeParameter"}, + {87, nullptr, "SetPtmFuelGaugeParameter"}, + {88, &ISystemSettingsServer::GetBluetoothEnableFlag, "GetBluetoothEnableFlag"}, + {89, &ISystemSettingsServer::SetBluetoothEnableFlag, "SetBluetoothEnableFlag"}, + {90, &ISystemSettingsServer::GetMiiAuthorId, "GetMiiAuthorId"}, + {91, nullptr, "SetShutdownRtcValue"}, + {92, nullptr, "GetShutdownRtcValue"}, + {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, + {94, nullptr, "GetFatalDirtyFlags"}, + {95, &ISystemSettingsServer::GetAutoUpdateEnableFlag, "GetAutoUpdateEnableFlag"}, + {96, &ISystemSettingsServer::SetAutoUpdateEnableFlag, "SetAutoUpdateEnableFlag"}, + {97, nullptr, "GetNxControllerSettings"}, + {98, nullptr, "SetNxControllerSettings"}, + {99, &ISystemSettingsServer::GetBatteryPercentageFlag, "GetBatteryPercentageFlag"}, + {100, &ISystemSettingsServer::SetBatteryPercentageFlag, "SetBatteryPercentageFlag"}, + {101, nullptr, "GetExternalRtcResetFlag"}, + {102, nullptr, "SetExternalRtcResetFlag"}, + {103, nullptr, "GetUsbFullKeyEnableFlag"}, + {104, nullptr, "SetUsbFullKeyEnableFlag"}, + {105, &ISystemSettingsServer::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"}, + {106, &ISystemSettingsServer::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"}, + {107, nullptr, "GetBacklightSettingsEx"}, + {108, nullptr, "SetBacklightSettingsEx"}, + {109, nullptr, "GetHeadphoneVolumeWarningCount"}, + {110, nullptr, "SetHeadphoneVolumeWarningCount"}, + {111, nullptr, "GetBluetoothAfhEnableFlag"}, + {112, nullptr, "SetBluetoothAfhEnableFlag"}, + {113, nullptr, "GetBluetoothBoostEnableFlag"}, + {114, nullptr, "SetBluetoothBoostEnableFlag"}, + {115, nullptr, "GetInRepairProcessEnableFlag"}, + {116, nullptr, "SetInRepairProcessEnableFlag"}, + {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, + {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, + {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, + {120, &ISystemSettingsServer::GetPushNotificationActivityModeOnSleep, "GetPushNotificationActivityModeOnSleep"}, + {121, &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"}, + {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, + {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, + {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, + {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"}, + {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"}, + {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, + {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, + {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, + {136, &ISystemSettingsServer::GetKeyboardLayout, "GetKeyboardLayout"}, + {137, &ISystemSettingsServer::SetKeyboardLayout, "SetKeyboardLayout"}, + {138, nullptr, "GetWebInspectorFlag"}, + {139, nullptr, "GetAllowedSslHosts"}, + {140, nullptr, "GetHostFsMountPoint"}, + {141, nullptr, "GetRequiresRunRepairTimeReviser"}, + {142, nullptr, "SetRequiresRunRepairTimeReviser"}, + {143, nullptr, "SetBlePairingSettings"}, + {144, nullptr, "GetBlePairingSettings"}, + {145, nullptr, "GetConsoleSixAxisSensorAngularVelocityTimeBias"}, + {146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"}, + {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"}, + {154, nullptr, "GetAccountOnlineStorageSettings"}, + {155, nullptr, "SetAccountOnlineStorageSettings"}, + {156, nullptr, "GetPctlReadyFlag"}, + {157, nullptr, "SetPctlReadyFlag"}, + {158, nullptr, "GetAnalogStickUserCalibrationL"}, + {159, nullptr, "SetAnalogStickUserCalibrationL"}, + {160, nullptr, "GetAnalogStickUserCalibrationR"}, + {161, nullptr, "SetAnalogStickUserCalibrationR"}, + {162, nullptr, "GetPtmBatteryVersion"}, + {163, nullptr, "SetPtmBatteryVersion"}, + {164, nullptr, "GetUsb30HostEnableFlag"}, + {165, nullptr, "SetUsb30HostEnableFlag"}, + {166, nullptr, "GetUsb30DeviceEnableFlag"}, + {167, nullptr, "SetUsb30DeviceEnableFlag"}, + {168, nullptr, "GetThemeId"}, + {169, nullptr, "SetThemeId"}, + {170, &ISystemSettingsServer::GetChineseTraditionalInputMethod, "GetChineseTraditionalInputMethod"}, + {171, nullptr, "SetChineseTraditionalInputMethod"}, + {172, nullptr, "GetPtmCycleCountReliability"}, + {173, nullptr, "SetPtmCycleCountReliability"}, + {174, &ISystemSettingsServer::GetHomeMenuScheme, "GetHomeMenuScheme"}, + {175, nullptr, "GetThemeSettings"}, + {176, nullptr, "SetThemeSettings"}, + {177, nullptr, "GetThemeKey"}, + {178, nullptr, "SetThemeKey"}, + {179, nullptr, "GetZoomFlag"}, + {180, nullptr, "SetZoomFlag"}, + {181, nullptr, "GetT"}, + {182, nullptr, "SetT"}, + {183, nullptr, "GetPlatformRegion"}, + {184, nullptr, "SetPlatformRegion"}, + {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, + {186, nullptr, "GetMemoryUsageRateFlag"}, + {187, nullptr, "GetTouchScreenMode"}, + {188, nullptr, "SetTouchScreenMode"}, + {189, nullptr, "GetButtonConfigSettingsFull"}, + {190, nullptr, "SetButtonConfigSettingsFull"}, + {191, nullptr, "GetButtonConfigSettingsEmbedded"}, + {192, nullptr, "SetButtonConfigSettingsEmbedded"}, + {193, nullptr, "GetButtonConfigSettingsLeft"}, + {194, nullptr, "SetButtonConfigSettingsLeft"}, + {195, nullptr, "GetButtonConfigSettingsRight"}, + {196, nullptr, "SetButtonConfigSettingsRight"}, + {197, nullptr, "GetButtonConfigRegisteredSettingsEmbedded"}, + {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, + {199, nullptr, "GetButtonConfigRegisteredSettings"}, + {200, nullptr, "SetButtonConfigRegisteredSettings"}, + {201, &ISystemSettingsServer::GetFieldTestingFlag, "GetFieldTestingFlag"}, + {202, nullptr, "SetFieldTestingFlag"}, + {203, &ISystemSettingsServer::GetPanelCrcMode, "GetPanelCrcMode"}, + {204, &ISystemSettingsServer::SetPanelCrcMode, "SetPanelCrcMode"}, + {205, nullptr, "GetNxControllerSettingsEx"}, + {206, nullptr, "SetNxControllerSettingsEx"}, + {207, nullptr, "GetHearingProtectionSafeguardFlag"}, + {208, nullptr, "SetHearingProtectionSafeguardFlag"}, + {209, nullptr, "GetHearingProtectionSafeguardRemainingTime"}, + {210, nullptr, "SetHearingProtectionSafeguardRemainingTime"}, + }; + // clang-format on + + RegisterHandlers(functions); + + SetupSettings(); + m_save_thread = + std::jthread([this](std::stop_token stop_token) { StoreSettingsThreadFunc(stop_token); }); +} + +ISystemSettingsServer::~ISystemSettingsServer() { + SetSaveNeeded(); + m_save_thread.request_stop(); +} + +bool ISystemSettingsServer::LoadSettingsFile(std::filesystem::path& path, auto&& default_func) { + using settings_type = decltype(default_func()); + + if (!Common::FS::CreateDirs(path)) { + return false; + } + + auto settings_file = path / "settings.dat"; + auto exists = std::filesystem::exists(settings_file); + auto file_size_ok = exists && std::filesystem::file_size(settings_file) == + sizeof(SettingsHeader) + sizeof(settings_type); + + auto ResetToDefault = [&]() { + auto default_settings{default_func()}; + + SettingsHeader hdr{ + .magic = SETTINGS_MAGIC, + .version = SETTINGS_VERSION, + .reserved = 0u, + }; + + std::ofstream out_settings_file(settings_file, std::ios::out | std::ios::binary); + out_settings_file.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr)); + out_settings_file.write(reinterpret_cast<const char*>(&default_settings), + sizeof(settings_type)); + out_settings_file.flush(); + out_settings_file.close(); + }; + + constexpr auto IsHeaderValid = [](std::ifstream& file) -> bool { + if (!file.is_open()) { + return false; + } + SettingsHeader hdr{}; + file.read(reinterpret_cast<char*>(&hdr), sizeof(hdr)); + return hdr.magic == SETTINGS_MAGIC && hdr.version >= SETTINGS_VERSION; + }; + + if (!exists || !file_size_ok) { + ResetToDefault(); + } + + std::ifstream file(settings_file, std::ios::binary | std::ios::in); + if (!IsHeaderValid(file)) { + file.close(); + ResetToDefault(); + file = std::ifstream(settings_file, std::ios::binary | std::ios::in); + if (!IsHeaderValid(file)) { + return false; + } + } + + if constexpr (std::is_same_v<settings_type, PrivateSettings>) { + file.read(reinterpret_cast<char*>(&m_private_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v<settings_type, DeviceSettings>) { + file.read(reinterpret_cast<char*>(&m_device_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v<settings_type, ApplnSettings>) { + file.read(reinterpret_cast<char*>(&m_appln_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v<settings_type, SystemSettings>) { + file.read(reinterpret_cast<char*>(&m_system_settings), sizeof(settings_type)); + } else { + UNREACHABLE(); + } + file.close(); + + return true; +} + +bool ISystemSettingsServer::StoreSettingsFile(std::filesystem::path& path, auto& settings) { + using settings_type = std::decay_t<decltype(settings)>; + + if (!Common::FS::IsDir(path)) { + return false; + } + + auto settings_base = path / "settings"; + std::filesystem::path settings_tmp_file = settings_base; + settings_tmp_file = settings_tmp_file.replace_extension("tmp"); + std::ofstream file(settings_tmp_file, std::ios::binary | std::ios::out); + if (!file.is_open()) { + return false; + } + + SettingsHeader hdr{ + .magic = SETTINGS_MAGIC, + .version = SETTINGS_VERSION, + .reserved = 0u, + }; + file.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr)); + + if constexpr (std::is_same_v<settings_type, PrivateSettings>) { + file.write(reinterpret_cast<const char*>(&m_private_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v<settings_type, DeviceSettings>) { + file.write(reinterpret_cast<const char*>(&m_device_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v<settings_type, ApplnSettings>) { + file.write(reinterpret_cast<const char*>(&m_appln_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v<settings_type, SystemSettings>) { + file.write(reinterpret_cast<const char*>(&m_system_settings), sizeof(settings_type)); + } else { + UNREACHABLE(); + } + file.close(); + + std::filesystem::rename(settings_tmp_file, settings_base.replace_extension("dat")); + + 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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetFirmwareVersion(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetFirmwareVersion2(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Common::UUID id{}; + const auto res = GetExternalSteadyClockSourceId(id); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(id); +} + +void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + const auto id{rp.PopRaw<Common::UUID>()}; + + const auto res = SetExternalSteadyClockSourceId(id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Service::PSC::Time::SystemClockContext context{}; + const auto res = GetUserSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(context); +} + +void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) { + 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); +} + +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); +} + +void ISystemSettingsServer::SetLockScreenFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.lock_screen_flag = rp.Pop<bool>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetAccountSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.account_settings); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetEulaVersions(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetEulaVersions(HLERequestContext& ctx) { + const auto elements = ctx.GetReadBufferNumElements<EulaVersion>(); + const auto buffer_data = ctx.ReadBuffer(); + + LOG_INFO(Service_SET, "called, elements={}", elements); + ASSERT(elements <= 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); + SetSaveNeeded(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetColorSetId(HLERequestContext& ctx) { + 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); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) { + 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); + + IPC::ResponseBuilder rb{ctx, 8}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.notification_settings); +} + +void ISystemSettingsServer::SetNotificationSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.notification_settings = rp.PopRaw<NotificationSettings>(); + SetSaveNeeded(); + + 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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetAccountNotificationSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called, elements={}", + m_system_settings.account_notification_settings_count); + + ctx.WriteBuffer(m_system_settings.account_notification_settings); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(m_system_settings.account_notification_settings_count); +} + +void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ctx) { + const auto elements = ctx.GetReadBufferNumElements<AccountNotificationSettings>(); + const auto buffer_data = ctx.ReadBuffer(); + + LOG_INFO(Service_SET, "called, elements={}", elements); + + ASSERT(elements <= 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)); + SetSaveNeeded(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) { + f32 vibration_master_volume = {}; + const auto result = GetVibrationMasterVolume(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); +} + +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); + + const auto result = SetVibrationMasterVolume(vibration_master_volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +// FIXME: implement support for the real system_settings.ini + +template <typename T> +static std::vector<u8> ToBytes(const T& value) { + static_assert(std::is_trivially_copyable_v<T>); + + const auto* begin = reinterpret_cast<const u8*>(&value); + const auto* end = begin + sizeof(T); + + return std::vector<u8>(begin, end); +} + +using Settings = + std::map<std::string, std::map<std::string, std::vector<u8>, std::less<>>, std::less<>>; + +static Settings GetSettings() { + Settings ret; + + // AM + ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); + ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); + + // Time + ret["time"]["notify_time_to_fs_interval_seconds"] = ToBytes(s32{600}); + ret["time"]["standard_network_clock_sufficient_accuracy_minutes"] = + ToBytes(s32{43200}); // 30 days + ret["time"]["standard_steady_clock_rtc_update_interval_minutes"] = ToBytes(s32{5}); + ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0}); + ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); + + // HID + ret["hid"]["has_rail_interface"] = ToBytes(bool{true}); + ret["hid"]["has_sio_mcu"] = ToBytes(bool{true}); + ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true}); + ret["hid_debug"]["manages_devices"] = ToBytes(bool{true}); + ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true}); + ret["hid_debug"]["emulate_future_device"] = ToBytes(bool{false}); + ret["hid_debug"]["emulate_mcu_hardware_error"] = ToBytes(bool{false}); + ret["hid_debug"]["enables_rail"] = ToBytes(bool{true}); + ret["hid_debug"]["emulate_firmware_update_failure"] = ToBytes(bool{false}); + ret["hid_debug"]["failure_firmware_update"] = ToBytes(s32{0}); + ret["hid_debug"]["ble_disabled"] = ToBytes(bool{false}); + ret["hid_debug"]["dscale_disabled"] = ToBytes(bool{false}); + ret["hid_debug"]["force_handheld"] = ToBytes(bool{true}); + ret["hid_debug"]["disabled_features_per_id"] = std::vector<u8>(0xa8); + ret["hid_debug"]["touch_firmware_auto_update_disabled"] = ToBytes(bool{false}); + + // Mii + ret["mii"]["is_db_test_mode_enabled"] = ToBytes(bool{false}); + + // Settings + ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false}); + + // Error + ret["err"]["applet_auto_close"] = ToBytes(bool{false}); + + return ret; +} + +void ISystemSettingsServer::GetSettingsItemValueSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + // 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)}; + + 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(); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess); + rb.Push(response_size); +} + +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)}; + + std::vector<u8> value; + auto response = GetSettingsItemValue(value, 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); +} + +void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) { + 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); + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.tv_settings); +} + +void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.tv_settings = rp.PopRaw<TvSettings>(); + SetSaveNeeded(); + + 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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +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); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.PushEnum(output_mode); +} + +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); + + LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void ISystemSettingsServer::GetSpeakerAutoMuteFlag(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetSpeakerAutoMuteFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.force_mute_on_headphone_removed = rp.PopRaw<bool>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", + m_system_settings.force_mute_on_headphone_removed); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.quest_flag = rp.PopEnum<QuestFlag>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { + 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>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + 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); +} + +void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + bool enabled{}; + const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushRaw(enabled); +} + +void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto enabled{rp.Pop<bool>()}; + + const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { + bool is_debug_mode_enabled = false; + GetSettingsItemValue<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); +} + +void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.primary_album_storage = rp.PopEnum<PrimaryAlbumStorage>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, primary_album_storage={}", + m_system_settings.primary_album_storage); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetBatteryLot(HLERequestContext& ctx) { + BatteryLot battery_lot = {"YUZUEMULATOR123456789"}; + + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 8}; + rb.Push(ResultSuccess); + rb.PushRaw(battery_lot); +} + +void ISystemSettingsServer::GetSerialNumber(HLERequestContext& ctx) { + SerialNumber console_serial = {"YUZ10012345678"}; + + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 8}; + rb.Push(ResultSuccess); + rb.PushRaw(console_serial); +} + +void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) { + 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); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetSleepSettings(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetSleepSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.sleep_settings = rp.PopRaw<SleepSettings>(); + SetSaveNeeded(); + + 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, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetWirelessLanEnableFlag(HLERequestContext& ctx) { + 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); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto initial_launch_settings = rp.PopRaw<InitialLaunchSettings>(); + + 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); +} + +void ISystemSettingsServer::GetDeviceNickName(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + ctx.WriteBuffer(::Settings::values.device_name.GetValue()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::SetDeviceNickName(HLERequestContext& ctx) { + const std::string device_name = Common::StringFromBuffer(ctx.ReadBuffer()); + + LOG_INFO(Service_SET, "called, device_name={}", device_name); + + ::Settings::values.device_name = device_name; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetProductModel(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetBluetoothEnableFlag(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetBluetoothEnableFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.bluetooth_enable_flag = rp.Pop<bool>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}", + m_system_settings.bluetooth_enable_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) { + if (m_system_settings.mii_author_id.IsInvalid()) { + m_system_settings.mii_author_id = Common::UUID::MakeDefault(); + SetSaveNeeded(); + } + + 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); +} + +void ISystemSettingsServer::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetAutoUpdateEnableFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.auto_update_enable_flag = rp.Pop<bool>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, auto_update_flag={}", m_system_settings.auto_update_enable_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetBatteryPercentageFlag(HLERequestContext& ctx) { + 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); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto offset{rp.Pop<s64>()}; + + const auto res = SetExternalSteadyClockInternalOffset(offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called."); + + s64 offset{}; + const auto res = GetExternalSteadyClockInternalOffset(offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(offset); +} + +void ISystemSettingsServer::GetPushNotificationActivityModeOnSleep(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetPushNotificationActivityModeOnSleep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.push_notification_activity_mode_on_sleep = rp.Pop<s32>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, push_notification_activity_mode_on_sleep={}", + m_system_settings.push_notification_activity_mode_on_sleep); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetErrorReportSharePermission(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetErrorReportSharePermission(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.error_report_share_permission = rp.PopEnum<ErrorReportSharePermission>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, error_report_share_permission={}", + m_system_settings.error_report_share_permission); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetAppletLaunchFlags(HLERequestContext& ctx) { + 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); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) { + 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)); +} + +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); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetHomeMenuScheme(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "(STUBBED) called"); + + const HomeMenuScheme default_color = { + .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); +} + +void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::GetPanelCrcMode(HLERequestContext& ctx) { + 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); +} + +void ISystemSettingsServer::SetPanelCrcMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.panel_crc_mode = rp.PopRaw<s32>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, panel_crc_mode={}", m_system_settings.panel_crc_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::SetupSettings() { + auto system_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; + if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) { + ASSERT(false); + } + + auto private_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; + if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) { + ASSERT(false); + } + + auto device_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; + if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) { + ASSERT(false); + } + + auto appln_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; + if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) { + ASSERT(false); + } +} + +void ISystemSettingsServer::StoreSettings() { + auto system_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; + if (!StoreSettingsFile(system_dir, m_system_settings)) { + LOG_ERROR(Service_SET, "Failed to store System settings"); + } + + auto private_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; + if (!StoreSettingsFile(private_dir, m_private_settings)) { + LOG_ERROR(Service_SET, "Failed to store Private settings"); + } + + auto device_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; + if (!StoreSettingsFile(device_dir, m_device_settings)) { + LOG_ERROR(Service_SET, "Failed to store Device settings"); + } + + auto appln_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; + if (!StoreSettingsFile(appln_dir, m_appln_settings)) { + LOG_ERROR(Service_SET, "Failed to store ApplLn settings"); + } +} + +void ISystemSettingsServer::StoreSettingsThreadFunc(std::stop_token stop_token) { + Common::SetCurrentThreadName("SettingsStore"); + + while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) { + std::scoped_lock l{m_save_needed_mutex}; + if (!std::exchange(m_save_needed, false)) { + continue; + } + StoreSettings(); + } +} + +void ISystemSettingsServer::SetSaveNeeded() { + std::scoped_lock l{m_save_needed_mutex}; + m_save_needed = true; +} + +Result ISystemSettingsServer::GetSettingsItemValue(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); + + out_value = settings[category][name]; + 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(); +} + +} // 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 new file mode 100644 index 000000000..1982b9723 --- /dev/null +++ b/src/core/hle/service/set/system_settings_server.h @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <filesystem> +#include <mutex> +#include <string> +#include <thread> + +#include "common/polyfill_thread.h" +#include "common/uuid.h" +#include "core/hle/result.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" +#include "core/hle/service/set/setting_formats/device_settings.h" +#include "core/hle/service/set/setting_formats/private_settings.h" +#include "core/hle/service/set/setting_formats/system_settings.h" +#include "core/hle/service/set/settings_types.h" + +namespace Core { +class System; +} + +namespace Service::Set { + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type); + +class ISystemSettingsServer final : public ServiceFramework<ISystemSettingsServer> { +public: + explicit ISystemSettingsServer(Core::System& system_); + ~ISystemSettingsServer() override; + + Result GetSettingsItemValue(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) { + std::vector<u8> data; + const auto result = GetSettingsItemValue(data, category, name); + if (result.IsError()) { + return result; + } + ASSERT(data.size() >= sizeof(T)); + std::memcpy(&value, data.data(), sizeof(T)); + return result; + } + + Result GetVibrationMasterVolume(f32& out_volume) const; + Result SetVibrationMasterVolume(f32 volume); + Result GetAudioOutputMode(AudioOutputMode& out_output_mode, AudioOutputModeTarget target) const; + 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 SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name); + Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; + Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context); + Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const; + Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); + Result SetExternalSteadyClockInternalOffset(s64 offset); + Result GetExternalSteadyClockInternalOffset(s64& out_offset) const; + Result GetDeviceTimeZoneLocationUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; + Result SetDeviceTimeZoneLocationUpdatedTime( + const Service::PSC::Time::SteadyClockTimePoint& time_point); + Result GetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; + Result SetUserSystemClockAutomaticCorrectionUpdatedTime( + const Service::PSC::Time::SteadyClockTimePoint& time_point); + +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 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(); + void StoreSettings(); + void StoreSettingsThreadFunc(std::stop_token stop_token); + void SetSaveNeeded(); + + Core::System& m_system; + SystemSettings m_system_settings{}; + PrivateSettings m_private_settings{}; + DeviceSettings m_device_settings{}; + ApplnSettings m_appln_settings{}; + std::mutex m_save_needed_mutex; + std::jthread m_save_thread; + bool m_save_needed{false}; +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 4ae32a9c1..32c218638 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -3,6 +3,7 @@ #pragma once +#include <chrono> #include <memory> #include <mutex> #include <string> @@ -10,6 +11,7 @@ #include "common/concepts.h" #include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/svc.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -62,12 +64,21 @@ public: Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); template <Common::DerivedFrom<SessionRequestHandler> T> - std::shared_ptr<T> GetService(const std::string& service_name) const { + std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const { auto service = registered_services.find(service_name); - if (service == registered_services.end()) { + if (service == registered_services.end() && !block) { LOG_DEBUG(Service, "Can't find service: {}", service_name); return nullptr; + } else if (block) { + using namespace std::literals::chrono_literals; + while (service == registered_services.end()) { + Kernel::Svc::SleepThread( + kernel.System(), + std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count()); + service = registered_services.find(service_name); + } } + return std::static_pointer_cast<T>(service->second()); } diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h deleted file mode 100644 index 7149fffeb..000000000 --- a/src/core/hle/service/time/clock_types.h +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <ratio> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/time_zone_types.h" - -// Defined by WinBase.h on Windows -#ifdef GetCurrentTime -#undef GetCurrentTime -#endif - -namespace Service::Time::Clock { - -enum class TimeType : u8 { - UserSystemClock, - NetworkSystemClock, - LocalSystemClock, -}; - -/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint -struct SteadyClockTimePoint { - s64 time_point; - Common::UUID clock_source_id; - - Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const { - span = 0; - - if (clock_source_id != other.clock_source_id) { - return ERROR_TIME_MISMATCH; - } - - span = other.time_point - time_point; - - return ResultSuccess; - } - - static SteadyClockTimePoint GetRandom() { - return {0, Common::UUID::MakeRandom()}; - } -}; -static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); -static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>, - "SteadyClockTimePoint must be trivially copyable"); - -struct SteadyClockContext { - u64 internal_offset; - Common::UUID steady_time_point; -}; -static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); -static_assert(std::is_trivially_copyable_v<SteadyClockContext>, - "SteadyClockContext must be trivially copyable"); -using StandardSteadyClockTimePointType = SteadyClockContext; - -struct SystemClockContext { - s64 offset; - SteadyClockTimePoint steady_time_point; -}; -static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size"); -static_assert(std::is_trivially_copyable_v<SystemClockContext>, - "SystemClockContext must be trivially copyable"); - -struct ContinuousAdjustmentTimePoint { - s64 measurement_offset; - s64 diff_scale; - u32 shift_amount; - s64 lower; - s64 upper; - Common::UUID clock_source_id; -}; -static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38); -static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>, - "ContinuousAdjustmentTimePoint must be trivially copyable"); - -/// https://switchbrew.org/wiki/Glue_services#TimeSpanType -struct TimeSpanType { - s64 nanoseconds{}; - - s64 ToSeconds() const { - return nanoseconds / std::nano::den; - } - - static TimeSpanType FromSeconds(s64 seconds) { - return {seconds * std::nano::den}; - } - - template <u64 Frequency> - static TimeSpanType FromTicks(u64 ticks) { - using TicksToNSRatio = std::ratio<std::nano::den, Frequency>; - return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)}; - } -}; -static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); - -struct ClockSnapshot { - SystemClockContext user_context; - SystemClockContext network_context; - s64 user_time; - s64 network_time; - TimeZone::CalendarTime user_calendar_time; - TimeZone::CalendarTime network_calendar_time; - TimeZone::CalendarAdditionalInfo user_calendar_additional_time; - TimeZone::CalendarAdditionalInfo network_calendar_additional_time; - SteadyClockTimePoint steady_clock_time_point; - TimeZone::LocationName location_name; - u8 is_automatic_correction_enabled; - TimeType type; - INSERT_PADDING_BYTES_NOINIT(0x2); - - static Result GetCurrentTime(s64& current_time, - const SteadyClockTimePoint& steady_clock_time_point, - const SystemClockContext& context) { - if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) { - current_time = 0; - return ERROR_TIME_MISMATCH; - } - current_time = steady_clock_time_point.time_point + context.offset; - return ResultSuccess; - } -}; -static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size"); - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h deleted file mode 100644 index 0f928a5a5..000000000 --- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_context_update_callback.h" - -namespace Service::Time::Clock { - -class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h deleted file mode 100644 index 0a5f5aafb..000000000 --- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -class EphemeralNetworkSystemClockCore final : public SystemClockCore { -public: - explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h deleted file mode 100644 index 6655d30e1..000000000 --- a/src/core/hle/service/time/errors.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/result.h" - -namespace Service::Time { - -constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; -constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; -constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; -constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; -constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201}; -constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; -constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; -constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; -constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; -constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h deleted file mode 100644 index 1639ef2b9..000000000 --- a/src/core/hle/service/time/local_system_clock_context_writer.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time::Clock { - -class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - explicit LocalSystemClockContextWriter(SharedMemory& shared_memory_) - : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} - -protected: - Result Update() override { - shared_memory.UpdateLocalSystemClockContext(context); - return ResultSuccess; - } - -private: - SharedMemory& shared_memory; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h deleted file mode 100644 index 655e4c06d..000000000 --- a/src/core/hle/service/time/network_system_clock_context_writer.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time::Clock { - -class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory_) - : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} - -protected: - Result Update() override { - shared_memory.UpdateNetworkSystemClockContext(context); - return ResultSuccess; - } - -private: - SharedMemory& shared_memory; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h deleted file mode 100644 index ae2ff1bfd..000000000 --- a/src/core/hle/service/time/standard_local_system_clock_core.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -class StandardLocalSystemClockCore final : public SystemClockCore { -public: - explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h deleted file mode 100644 index c1ec5252b..000000000 --- a/src/core/hle/service/time/standard_network_system_clock_core.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class StandardNetworkSystemClockCore final : public SystemClockCore { -public: - explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} - - void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) { - standard_network_clock_sufficient_accuracy = value; - } - - bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const { - SystemClockContext clock_ctx{}; - if (GetClockContext(system, clock_ctx) != ResultSuccess) { - return {}; - } - - s64 span{}; - if (clock_ctx.steady_time_point.GetSpanBetween( - GetSteadyClockCore().GetCurrentTimePoint(system), span) != ResultSuccess) { - return {}; - } - - return TimeSpanType{span}.nanoseconds < - standard_network_clock_sufficient_accuracy.nanoseconds; - } - -private: - TimeSpanType standard_network_clock_sufficient_accuracy{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp deleted file mode 100644 index 5627b7003..000000000 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/service/time/standard_steady_clock_core.h" - -namespace Service::Time::Clock { - -TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; - TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; - - if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { - raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds; - } - - cached_raw_time_point = raw_time_point; - return raw_time_point; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h deleted file mode 100644 index 036463b87..000000000 --- a/src/core/hle/service/time/standard_steady_clock_core.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class StandardSteadyClockCore final : public SteadyClockCore { -public: - SteadyClockTimePoint GetTimePoint(Core::System& system) override { - return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()}; - } - - TimeSpanType GetInternalOffset() const override { - return internal_offset; - } - - void SetInternalOffset(TimeSpanType value) override { - internal_offset = value; - } - - TimeSpanType GetCurrentRawTimePoint(Core::System& system) override; - - void SetSetupValue(TimeSpanType value) { - setup_value = value; - } - -private: - TimeSpanType setup_value{}; - TimeSpanType internal_offset{}; - TimeSpanType cached_raw_time_point{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp deleted file mode 100644 index b033757ed..000000000 --- a/src/core/hle/service/time/standard_user_system_clock_core.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/time/standard_local_system_clock_core.h" -#include "core/hle/service/time/standard_network_system_clock_core.h" -#include "core/hle/service/time/standard_user_system_clock_core.h" - -namespace Service::Time::Clock { - -StandardUserSystemClockCore::StandardUserSystemClockCore( - StandardLocalSystemClockCore& local_system_clock_core_, - StandardNetworkSystemClockCore& network_system_clock_core_, Core::System& system_) - : SystemClockCore(local_system_clock_core_.GetSteadyClockCore()), - local_system_clock_core{local_system_clock_core_}, - network_system_clock_core{network_system_clock_core_}, - auto_correction_time{SteadyClockTimePoint::GetRandom()}, service_context{ - system_, - "StandardUserSystemClockCore"} { - auto_correction_event = - service_context.CreateEvent("StandardUserSystemClockCore:AutoCorrectionEvent"); -} - -StandardUserSystemClockCore::~StandardUserSystemClockCore() { - service_context.CloseEvent(auto_correction_event); -} - -Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, - bool value) { - if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { - return result; - } - - auto_correction_enabled = value; - - return ResultSuccess; -} - -Result StandardUserSystemClockCore::GetClockContext(Core::System& system, - SystemClockContext& ctx) const { - if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { - return result; - } - - return local_system_clock_core.GetClockContext(system, ctx); -} - -Result StandardUserSystemClockCore::Flush(const SystemClockContext&) { - UNIMPLEMENTED(); - return ERROR_NOT_IMPLEMENTED; -} - -Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { - UNIMPLEMENTED(); - return ERROR_NOT_IMPLEMENTED; -} - -Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, - bool value) const { - if (auto_correction_enabled == value) { - return ResultSuccess; - } - - if (!network_system_clock_core.IsClockSetup(system)) { - return ERROR_UNINITIALIZED_CLOCK; - } - - SystemClockContext ctx{}; - if (const Result result{network_system_clock_core.GetClockContext(system, ctx)}; - result != ResultSuccess) { - return result; - } - - local_system_clock_core.SetClockContext(ctx); - - return ResultSuccess; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h deleted file mode 100644 index ee6e29487..000000000 --- a/src/core/hle/service/time/standard_user_system_clock_core.h +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KEvent; -} - -namespace Service::Time::Clock { - -class StandardLocalSystemClockCore; -class StandardNetworkSystemClockCore; - -class StandardUserSystemClockCore final : public SystemClockCore { -public: - StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core_, - StandardNetworkSystemClockCore& network_system_clock_core_, - Core::System& system_); - - ~StandardUserSystemClockCore() override; - - Result SetAutomaticCorrectionEnabled(Core::System& system, bool value); - - Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override; - - bool IsAutomaticCorrectionEnabled() const { - return auto_correction_enabled; - } - - void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) { - auto_correction_time = steady_clock_time_point; - } - -protected: - Result Flush(const SystemClockContext&) override; - - Result SetClockContext(const SystemClockContext&) override; - - Result ApplyAutomaticCorrection(Core::System& system, bool value) const; - - const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const { - return auto_correction_time; - } - -private: - StandardLocalSystemClockCore& local_system_clock_core; - StandardNetworkSystemClockCore& network_system_clock_core; - bool auto_correction_enabled{}; - SteadyClockTimePoint auto_correction_time; - KernelHelpers::ServiceContext service_context; - Kernel::KEvent* auto_correction_event; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h deleted file mode 100644 index 2867c351c..000000000 --- a/src/core/hle/service/time/steady_clock_core.h +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/uuid.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class SteadyClockCore { -public: - SteadyClockCore() = default; - virtual ~SteadyClockCore() = default; - - const Common::UUID& GetClockSourceId() const { - return clock_source_id; - } - - void SetClockSourceId(const Common::UUID& value) { - clock_source_id = value; - } - - virtual TimeSpanType GetInternalOffset() const = 0; - - virtual void SetInternalOffset(TimeSpanType internal_offset) = 0; - - virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0; - - virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0; - - SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) { - SteadyClockTimePoint result{GetTimePoint(system)}; - result.time_point += GetInternalOffset().ToSeconds(); - return result; - } - - bool IsInitialized() const { - return is_initialized; - } - - void MarkAsInitialized() { - is_initialized = true; - } - -private: - Common::UUID clock_source_id{Common::UUID::MakeRandom()}; - bool is_initialized{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp deleted file mode 100644 index cafc04ee7..000000000 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" - -namespace Service::Time::Clock { - -SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default; -SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default; - -bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const { - if (has_context) { - return context.offset != value.offset || - context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id; - } - - return true; -} - -void SystemClockContextUpdateCallback::RegisterOperationEvent( - std::shared_ptr<Kernel::KEvent>&& event) { - operation_event_list.emplace_back(std::move(event)); -} - -void SystemClockContextUpdateCallback::BroadcastOperationEvent() { - for (const auto& event : operation_event_list) { - event->Signal(); - } -} - -Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { - Result result{ResultSuccess}; - - if (NeedUpdate(value)) { - context = value; - has_context = true; - - result = Update(); - - if (result == ResultSuccess) { - BroadcastOperationEvent(); - } - } - - return result; -} - -Result SystemClockContextUpdateCallback::Update() { - return ResultSuccess; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h deleted file mode 100644 index bf657acd9..000000000 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <vector> - -#include "core/hle/service/time/clock_types.h" - -namespace Kernel { -class KEvent; -} - -namespace Service::Time::Clock { - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class SystemClockContextUpdateCallback { -public: - SystemClockContextUpdateCallback(); - virtual ~SystemClockContextUpdateCallback(); - - bool NeedUpdate(const SystemClockContext& value) const; - - void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event); - - void BroadcastOperationEvent(); - - Result Update(const SystemClockContext& value); - -protected: - virtual Result Update(); - - SystemClockContext context{}; - -private: - bool has_context{}; - std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp deleted file mode 100644 index da078241f..000000000 --- a/src/core/hle/service/time/system_clock_core.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_) - : steady_clock_core{steady_clock_core_} { - context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId(); -} - -SystemClockCore::~SystemClockCore() = default; - -Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { - posix_time = 0; - - const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - - SystemClockContext clock_context{}; - if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) { - return result; - } - - if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) { - return ERROR_TIME_MISMATCH; - } - - posix_time = clock_context.offset + current_time_point.time_point; - - return ResultSuccess; -} - -Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { - const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - const SystemClockContext clock_context{posix_time - current_time_point.time_point, - current_time_point}; - - if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { - return result; - } - return Flush(clock_context); -} - -Result SystemClockCore::Flush(const SystemClockContext& clock_context) { - if (!system_clock_context_update_callback) { - return ResultSuccess; - } - return system_clock_context_update_callback->Update(clock_context); -} - -Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { - if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { - return result; - } - return Flush(clock_context); -} - -bool SystemClockCore::IsClockSetup(Core::System& system) const { - SystemClockContext value{}; - if (GetClockContext(system, value) == ResultSuccess) { - const SteadyClockTimePoint steady_clock_time_point{ - steady_clock_core.GetCurrentTimePoint(system)}; - return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id; - } - return {}; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h deleted file mode 100644 index 8cb34126f..000000000 --- a/src/core/hle/service/time/system_clock_core.h +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "common/common_types.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class SteadyClockCore; -class SystemClockContextUpdateCallback; - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class SystemClockCore { -public: - explicit SystemClockCore(SteadyClockCore& steady_clock_core_); - virtual ~SystemClockCore(); - - SteadyClockCore& GetSteadyClockCore() const { - return steady_clock_core; - } - - Result GetCurrentTime(Core::System& system, s64& posix_time) const; - - Result SetCurrentTime(Core::System& system, s64 posix_time); - - virtual Result GetClockContext([[maybe_unused]] Core::System& system, - SystemClockContext& value) const { - value = context; - return ResultSuccess; - } - - virtual Result SetClockContext(const SystemClockContext& value) { - context = value; - return ResultSuccess; - } - - virtual Result Flush(const SystemClockContext& clock_context); - - void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) { - system_clock_context_update_callback = std::move(callback); - } - - Result SetSystemClockContext(const SystemClockContext& context); - - bool IsInitialized() const { - return is_initialized; - } - - void MarkAsInitialized() { - is_initialized = true; - } - - bool IsClockSetup(Core::System& system) const; - -private: - SteadyClockCore& steady_clock_core; - SystemClockContext context{}; - bool is_initialized{}; - std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp deleted file mode 100644 index 0d9fb3143..000000000 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/service/time/tick_based_steady_clock_core.h" - -namespace Service::Time::Clock { - -SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; - - return {ticks_time_span.ToSeconds(), GetClockSourceId()}; -} - -TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - return TimeSpanType::FromSeconds(GetTimePoint(system).time_point); -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h deleted file mode 100644 index 491185dc3..000000000 --- a/src/core/hle/service/time/tick_based_steady_clock_core.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class TickBasedSteadyClockCore final : public SteadyClockCore { -public: - TimeSpanType GetInternalOffset() const override { - return {}; - } - - void SetInternalOffset(TimeSpanType internal_offset) override {} - - SteadyClockTimePoint GetTimePoint(Core::System& system) override; - - TimeSpanType GetCurrentRawTimePoint(Core::System& system) override; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp deleted file mode 100644 index 7197ca30f..000000000 --- a/src/core/hle/service/time/time.cpp +++ /dev/null @@ -1,412 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/server_manager.h" -#include "core/hle/service/time/time.h" -#include "core/hle/service/time/time_interface.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_sharedmemory.h" -#include "core/hle/service/time/time_zone_service.h" - -namespace Service::Time { - -class ISystemClock final : public ServiceFramework<ISystemClock> { -public: - explicit ISystemClock(Clock::SystemClockCore& clock_core_, Core::System& system_) - : ServiceFramework{system_, "ISystemClock"}, clock_core{clock_core_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, - {1, nullptr, "SetCurrentTime"}, - {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}, - {3, nullptr, "SetSystemClockContext"}, - {4, nullptr, "GetOperationEventReadableHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetCurrentTime(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - s64 posix_time{}; - if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<s64>(posix_time); - } - - void GetSystemClockContext(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - Clock::SystemClockContext system_clock_context{}; - if (const Result result{clock_core.GetClockContext(system, system_clock_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(system_clock_context); - } - - Clock::SystemClockCore& clock_core; -}; - -class ISteadyClock final : public ServiceFramework<ISteadyClock> { -public: - explicit ISteadyClock(Clock::SteadyClockCore& clock_core_, Core::System& system_) - : ServiceFramework{system_, "ISteadyClock"}, clock_core{clock_core_} { - static const FunctionInfo functions[] = { - {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, - {2, nullptr, "GetTestOffset"}, - {3, nullptr, "SetTestOffset"}, - {100, nullptr, "GetRtcValue"}, - {101, nullptr, "IsRtcResetDetected"}, - {102, nullptr, "GetSetupResultValue"}, - {200, nullptr, "GetInternalOffset"}, - {201, nullptr, "SetInternalOffset"}, - }; - RegisterHandlers(functions); - } - -private: - void GetCurrentTimePoint(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)}; - IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_point); - } - - Clock::SteadyClockCore& clock_core; -}; - -Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal( - Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, Clock::TimeType type, - Clock::ClockSnapshot& clock_snapshot) { - - auto& time_manager{system.GetTimeManager()}; - - clock_snapshot.steady_clock_time_point = - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system); - clock_snapshot.is_automatic_correction_enabled = - time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); - clock_snapshot.type = type; - - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName( - clock_snapshot.location_name)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.user_context = user_context; - - if (const Result result{Clock::ClockSnapshot::GetCurrentTime( - clock_snapshot.user_time, clock_snapshot.steady_clock_time_point, - clock_snapshot.user_context)}; - result != ResultSuccess) { - return result; - } - - TimeZone::CalendarInfo userCalendarInfo{}; - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( - clock_snapshot.user_time, userCalendarInfo)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.user_calendar_time = userCalendarInfo.time; - clock_snapshot.user_calendar_additional_time = userCalendarInfo.additional_info; - - clock_snapshot.network_context = network_context; - - if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, - clock_snapshot.steady_clock_time_point, - clock_snapshot.network_context) != ResultSuccess) { - clock_snapshot.network_time = 0; - } - - TimeZone::CalendarInfo networkCalendarInfo{}; - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( - clock_snapshot.network_time, networkCalendarInfo)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.network_calendar_time = networkCalendarInfo.time; - clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additional_info; - - return ResultSuccess; -} - -void Module::Interface::GetStandardUserSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(), - system); -} - -void Module::Interface::GetStandardNetworkSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(), - system); -} - -void Module::Interface::GetStandardSteadyClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system); -} - -void Module::Interface::GetTimeZoneService(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ITimeZoneService>(system, - system.GetTimeManager().GetTimeZoneContentManager()); -} - -void Module::Interface::GetStandardLocalSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(), - system); -} - -void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()}; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system)); -} - -void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()}; - if (!steady_clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - IPC::RequestParser rp{ctx}; - const auto context{rp.PopRaw<Clock::SystemClockContext>()}; - const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - - if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { - const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( - system.CoreTiming().GetClockTicks())}; - const s64 base_time_point{context.offset + current_time_point.time_point - - ticks.ToSeconds()}; - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(base_time_point); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_TIME_MISMATCH); -} - -void Module::Interface::GetClockSnapshot(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto type{rp.PopEnum<Clock::TimeType>()}; - - LOG_DEBUG(Service_Time, "called, type={}", type); - - Clock::SystemClockContext user_context{}; - if (const Result result{ - system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system, - user_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - Clock::SystemClockContext network_context{}; - if (const Result result{ - system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( - system, network_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - Clock::ClockSnapshot clock_snapshot{}; - if (const Result result{GetClockSnapshotFromSystemClockContextInternal( - &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(clock_snapshot); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto type{rp.PopEnum<Clock::TimeType>()}; - - rp.Skip(1, false); - - const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()}; - const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()}; - - LOG_DEBUG(Service_Time, "called, type={}", type); - - Clock::ClockSnapshot clock_snapshot{}; - if (const Result result{GetClockSnapshotFromSystemClockContextInternal( - &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(clock_snapshot); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - Clock::ClockSnapshot snapshot_a; - Clock::ClockSnapshot snapshot_b; - - const auto snapshot_a_data = ctx.ReadBuffer(0); - const auto snapshot_b_data = ctx.ReadBuffer(1); - - std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); - std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); - - auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset - - snapshot_a.user_context.offset)}; - - if ((snapshot_b.user_context.steady_time_point.clock_source_id != - snapshot_a.user_context.steady_time_point.clock_source_id) || - (snapshot_b.is_automatic_correction_enabled && - snapshot_a.is_automatic_correction_enabled)) { - time_span_type.nanoseconds = 0; - } - - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_span_type.nanoseconds); -} - -void Module::Interface::CalculateSpanBetween(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - Clock::ClockSnapshot snapshot_a; - Clock::ClockSnapshot snapshot_b; - - const auto snapshot_a_data = ctx.ReadBuffer(0); - const auto snapshot_b_data = ctx.ReadBuffer(1); - - std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); - std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); - - Clock::TimeSpanType time_span_type{}; - s64 span{}; - - if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween( - snapshot_b.steady_clock_time_point, span)}; - result != ResultSuccess) { - if (snapshot_a.network_time && snapshot_b.network_time) { - time_span_type = - Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_TIME_NOT_FOUND); - return; - } - } else { - time_span_type = Clock::TimeSpanType::FromSeconds(span); - } - - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_span_type.nanoseconds); -} - -void Module::Interface::GetSharedMemoryNativeHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&system.Kernel().GetTimeSharedMem()); -} - -Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)} {} - -Module::Interface::~Interface() = default; - -void LoopProcess(Core::System& system) { - auto server_manager = std::make_unique<ServerManager>(system); - auto module{std::make_shared<Module>()}; - - server_manager->RegisterNamedService("time:a", - std::make_shared<Time>(module, system, "time:a")); - server_manager->RegisterNamedService("time:s", - std::make_shared<Time>(module, system, "time:s")); - server_manager->RegisterNamedService("time:u", - std::make_shared<Time>(module, system, "time:u")); - ServerManager::RunServer(std::move(server_manager)); -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h deleted file mode 100644 index b2d754ef3..000000000 --- a/src/core/hle/service/time/time.h +++ /dev/null @@ -1,51 +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" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -class Module final { -public: - Module() = default; - - class Interface : public ServiceFramework<Interface> { - public: - explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name); - ~Interface() override; - - void GetStandardUserSystemClock(HLERequestContext& ctx); - void GetStandardNetworkSystemClock(HLERequestContext& ctx); - void GetStandardSteadyClock(HLERequestContext& ctx); - void GetTimeZoneService(HLERequestContext& ctx); - void GetStandardLocalSystemClock(HLERequestContext& ctx); - void IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); - void CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); - void GetClockSnapshot(HLERequestContext& ctx); - void GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); - void CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); - void CalculateSpanBetween(HLERequestContext& ctx); - void GetSharedMemoryNativeHandle(HLERequestContext& ctx); - - private: - Result GetClockSnapshotFromSystemClockContextInternal( - Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, Clock::TimeType type, - Clock::ClockSnapshot& cloc_snapshot); - - protected: - std::shared_ptr<Module> module; - }; -}; - -void LoopProcess(Core::System& system); - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp deleted file mode 100644 index 0c53e98ee..000000000 --- a/src/core/hle/service/time/time_interface.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/time/time_interface.h" - -namespace Service::Time { - -Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_) - : Interface{std::move(module_), system_, name_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, - {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, - {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"}, - {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, - {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, - {5, nullptr, "GetEphemeralNetworkSystemClock"}, - {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, - {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, - {50, nullptr, "SetStandardSteadyClockInternalOffset"}, - {51, nullptr, "GetStandardSteadyClockRtcValue"}, - {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, - {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, - {102, nullptr, "GetStandardUserSystemClockInitialYear"}, - {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, - {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, - {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, - {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, - {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, - {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -Time::~Time() = default; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h deleted file mode 100644 index ceeb0e5ef..000000000 --- a/src/core/hle/service/time/time_interface.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/time.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -class Time final : public Module::Interface { -public: - explicit Time(std::shared_ptr<Module> time, Core::System& system_, const char* name_); - ~Time() override; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp deleted file mode 100644 index fa0fd0531..000000000 --- a/src/core/hle/service/time/time_manager.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <ctime> - -#include "common/settings.h" -#include "common/time_zone.h" -#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h" -#include "core/hle/service/time/ephemeral_network_system_clock_core.h" -#include "core/hle/service/time/local_system_clock_context_writer.h" -#include "core/hle/service/time/network_system_clock_context_writer.h" -#include "core/hle/service/time/tick_based_steady_clock_core.h" -#include "core/hle/service/time/time_manager.h" - -namespace Service::Time { -namespace { -constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL}; - -s64 GetSecondsSinceEpoch() { - const auto time_since_epoch = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() + - Settings::values.custom_rtc_differential; -} -} // Anonymous namespace - -struct TimeManager::Impl final { - explicit Impl(Core::System& system) - : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core}, - standard_network_system_clock_core{standard_steady_clock_core}, - standard_user_system_clock_core{standard_local_system_clock_core, - standard_network_system_clock_core, system}, - ephemeral_network_system_clock_core{tick_based_steady_clock_core}, - local_system_clock_context_writer{ - std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)}, - network_system_clock_context_writer{ - std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)}, - ephemeral_network_system_clock_context_writer{ - std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()}, - time_zone_content_manager{system} { - - const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())}; - SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {}); - SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); - - Clock::SystemClockContext clock_context{}; - standard_local_system_clock_core.GetClockContext(system, clock_context); - - SetupStandardNetworkSystemClock(clock_context, standard_network_clock_accuracy); - SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom()); - SetupEphemeralNetworkSystemClock(); - } - - ~Impl() = default; - - Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() { - return standard_steady_clock_core; - } - - const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const { - return standard_steady_clock_core; - } - - Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() { - return standard_local_system_clock_core; - } - - const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const { - return standard_local_system_clock_core; - } - - Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() { - return standard_network_system_clock_core; - } - - const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const { - return standard_network_system_clock_core; - } - - Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() { - return standard_user_system_clock_core; - } - - const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const { - return standard_user_system_clock_core; - } - - TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() { - return time_zone_content_manager; - } - - const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const { - return time_zone_content_manager; - } - - SharedMemory& GetSharedMemory() { - return shared_memory; - } - - const SharedMemory& GetSharedMemory() const { - return shared_memory; - } - - void SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file) { - if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( - location_name, vfs_file) != ResultSuccess) { - ASSERT(false); - return; - } - - time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point); - time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount( - location_names.size()); - time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names); - time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion( - time_zone_rule_version); - time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized(); - } - - void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id, - Clock::TimeSpanType setup_value, - Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) { - standard_steady_clock_core.SetClockSourceId(clock_source_id); - standard_steady_clock_core.SetSetupValue(setup_value); - standard_steady_clock_core.SetInternalOffset(internal_offset); - standard_steady_clock_core.MarkAsInitialized(); - - const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system_)}; - shared_memory.SetupStandardSteadyClock(clock_source_id, current_time_point); - } - - void SetupStandardLocalSystemClock(Core::System& system_, - Clock::SystemClockContext clock_context, s64 posix_time) { - standard_local_system_clock_core.SetUpdateCallbackInstance( - local_system_clock_context_writer); - - const auto current_time_point{ - standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system_)}; - if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) { - standard_local_system_clock_core.SetSystemClockContext(clock_context); - } else { - if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != - ResultSuccess) { - ASSERT(false); - return; - } - } - - standard_local_system_clock_core.MarkAsInitialized(); - } - - void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context, - Clock::TimeSpanType sufficient_accuracy) { - standard_network_system_clock_core.SetUpdateCallbackInstance( - network_system_clock_context_writer); - - if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != - ResultSuccess) { - ASSERT(false); - return; - } - - standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy( - sufficient_accuracy); - standard_network_system_clock_core.MarkAsInitialized(); - } - - void SetupStandardUserSystemClock(Core::System& system_, bool is_automatic_correction_enabled, - Clock::SteadyClockTimePoint steady_clock_time_point) { - if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( - system_, is_automatic_correction_enabled) != ResultSuccess) { - ASSERT(false); - return; - } - - standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point); - standard_user_system_clock_core.MarkAsInitialized(); - shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled); - } - - void SetupEphemeralNetworkSystemClock() { - ephemeral_network_system_clock_core.SetUpdateCallbackInstance( - ephemeral_network_system_clock_context_writer); - ephemeral_network_system_clock_core.MarkAsInitialized(); - } - - void UpdateLocalSystemClockTime(Core::System& system_, s64 posix_time) { - const auto timespan{Clock::TimeSpanType::FromSeconds(posix_time)}; - if (GetStandardLocalSystemClockCore() - .SetCurrentTime(system_, timespan.ToSeconds()) - .IsError()) { - ASSERT(false); - return; - } - } - - SharedMemory shared_memory; - - Clock::StandardSteadyClockCore standard_steady_clock_core; - Clock::TickBasedSteadyClockCore tick_based_steady_clock_core; - Clock::StandardLocalSystemClockCore standard_local_system_clock_core; - Clock::StandardNetworkSystemClockCore standard_network_system_clock_core; - Clock::StandardUserSystemClockCore standard_user_system_clock_core; - Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core; - - std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer; - std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer; - std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter> - ephemeral_network_system_clock_context_writer; - - TimeZone::TimeZoneContentManager time_zone_content_manager; -}; - -TimeManager::TimeManager(Core::System& system_) : system{system_} {} - -TimeManager::~TimeManager() = default; - -void TimeManager::Initialize() { - impl = std::make_unique<Impl>(system); - - // Time zones can only be initialized after impl is valid - impl->time_zone_content_manager.Initialize(*this); -} - -Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() { - return impl->standard_steady_clock_core; -} - -const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const { - return impl->standard_steady_clock_core; -} - -Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() { - return impl->standard_local_system_clock_core; -} - -const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const { - return impl->standard_local_system_clock_core; -} - -Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() { - return impl->standard_network_system_clock_core; -} - -const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() - const { - return impl->standard_network_system_clock_core; -} - -Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() { - return impl->standard_user_system_clock_core; -} - -const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const { - return impl->standard_user_system_clock_core; -} - -TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() { - return impl->time_zone_content_manager; -} - -const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const { - return impl->time_zone_content_manager; -} - -SharedMemory& TimeManager::GetSharedMemory() { - return impl->shared_memory; -} - -const SharedMemory& TimeManager::GetSharedMemory() const { - return impl->shared_memory; -} - -void TimeManager::Shutdown() { - impl.reset(); -} - -void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { - impl->UpdateLocalSystemClockTime(system, posix_time); -} - -void TimeManager::SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, - u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file) { - impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names, - time_zone_rule_version, vfs_file); -} -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h deleted file mode 100644 index 84572dbfa..000000000 --- a/src/core/hle/service/time/time_manager.h +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/standard_local_system_clock_core.h" -#include "core/hle/service/time/standard_network_system_clock_core.h" -#include "core/hle/service/time/standard_steady_clock_core.h" -#include "core/hle/service/time/standard_user_system_clock_core.h" -#include "core/hle/service/time/time_sharedmemory.h" -#include "core/hle/service/time/time_zone_content_manager.h" - -namespace Service::Time { - -namespace Clock { -class EphemeralNetworkSystemClockContextWriter; -class LocalSystemClockContextWriter; -class NetworkSystemClockContextWriter; -} // namespace Clock - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class TimeManager final { -public: - explicit TimeManager(Core::System& system_); - ~TimeManager(); - - void Initialize(); - - Clock::StandardSteadyClockCore& GetStandardSteadyClockCore(); - - const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const; - - Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore(); - - const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const; - - Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore(); - - const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const; - - Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore(); - - const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const; - - TimeZone::TimeZoneContentManager& GetTimeZoneContentManager(); - - const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const; - - void UpdateLocalSystemClockTime(s64 posix_time); - - SharedMemory& GetSharedMemory(); - - const SharedMemory& GetSharedMemory() const; - - void Shutdown(); - - void SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file); - -private: - Core::System& system; - - struct Impl; - std::unique_ptr<Impl> impl; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp deleted file mode 100644 index a00676669..000000000 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time { - -static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000}; - -SharedMemory::SharedMemory(Core::System& system_) : system(system_) { - std::memset(system.Kernel().GetTimeSharedMem().GetPointer(), 0, SHARED_MEMORY_SIZE); -} - -SharedMemory::~SharedMemory() = default; - -void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, - Clock::TimeSpanType current_time_point) { - const Clock::TimeSpanType ticks_time_span{ - Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( - system.CoreTiming().GetClockTicks())}; - const Clock::SteadyClockContext context{ - static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), - clock_source_id}; - StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context); -} - -void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { - // lower and upper are related to the measurement point for the steady time point, - // and compare equal on boot - const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL; - - // This adjusts for some sort of time skew - // Both 0 on boot - const s64 diff_scale = 0; - const u32 shift_amount = 0; - - const Clock::ContinuousAdjustmentTimePoint adjustment{ - .measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(), - .diff_scale = diff_scale, - .shift_amount = shift_amount, - .lower = time_point_ns, - .upper = time_point_ns, - .clock_source_id = context.steady_time_point.clock_source_id, - }; - - StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment); - StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context); -} - -void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { - StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context); -} - -void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { - StoreToLockFreeAtomicType( - &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled); -} - -SharedMemory::Format* SharedMemory::GetFormat() { - return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer()); -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h deleted file mode 100644 index c89be9765..000000000 --- a/src/core/hle/service/time/time_sharedmemory.h +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/service/time/clock_types.h" - -namespace Service::Time { - -// Note: this type is not safe for concurrent writes. -template <typename T> -struct LockFreeAtomicType { - u32 counter_; - std::array<T, 2> value_; -}; - -template <typename T> -static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { - // Get the current counter. - auto counter = p->counter_; - - // Increment the counter. - ++counter; - - // Store the updated value. - p->value_[counter % 2] = value; - - // Fence memory. - std::atomic_thread_fence(std::memory_order_release); - - // Set the updated counter. - p->counter_ = counter; -} - -template <typename T> -static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { - while (true) { - // Get the counter. - auto counter = p->counter_; - - // Get the value. - auto value = p->value_[counter % 2]; - - // Fence memory. - std::atomic_thread_fence(std::memory_order_acquire); - - // Check that the counter matches. - if (counter == p->counter_) { - return value; - } - } -} - -class SharedMemory final { -public: - explicit SharedMemory(Core::System& system_); - ~SharedMemory(); - - // Shared memory format - struct Format { - LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint; - LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context; - LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context; - LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled; - LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint; - }; - static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0); - static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38); - static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80); - static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) == - 0xc8); - static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0); - static_assert(sizeof(Format) == 0x148, "Format is an invalid size"); - - void SetupStandardSteadyClock(const Common::UUID& clock_source_id, - Clock::TimeSpanType current_time_point); - void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); - void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); - void SetAutomaticCorrectionEnabled(bool is_enabled); - Format* GetFormat(); - -private: - Core::System& system; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp deleted file mode 100644 index 1b96de37a..000000000 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <sstream> -#include <utility> - -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/time_zone.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/file_sys/vfs.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/result.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" - -namespace Service::Time::TimeZone { - -constexpr u64 time_zone_binary_titleid{0x010000000000080E}; - -static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) { - const auto* nand{system.GetFileSystemController().GetSystemNANDContents()}; - const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)}; - - FileSys::VirtualFile romfs; - if (nca) { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid); - } - - if (!romfs) { - LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid); - return {}; - } - - return FileSys::ExtractRomFS(romfs); -} - -static std::vector<std::string> BuildLocationNameCache( - const FileSys::VirtualDir& time_zone_binary) { - if (!time_zone_binary) { - LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid); - return {}; - } - - const FileSys::VirtualFile binary_list{time_zone_binary->GetFile("binaryList.txt")}; - if (!binary_list) { - LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid); - return {}; - } - - std::vector<char> raw_data(binary_list->GetSize() + 1); - binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize()); - - std::stringstream data_stream{raw_data.data()}; - std::string name; - std::vector<std::string> location_name_cache; - while (std::getline(data_stream, name)) { - name.pop_back(); // Remove carriage return - location_name_cache.emplace_back(std::move(name)); - } - return location_name_cache; -} - -TimeZoneContentManager::TimeZoneContentManager(Core::System& system_) - : system{system_}, time_zone_binary{GetTimeZoneBinary(system)}, - location_name_cache{BuildLocationNameCache(time_zone_binary)} {} - -void TimeZoneContentManager::Initialize(TimeManager& time_manager) { - const auto timezone_setting = - Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); - - if (FileSys::VirtualFile vfs_file; - GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) { - const auto time_point{ - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; - time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {}, - vfs_file); - } else { - time_zone_manager.MarkAsInitialized(); - } -} - -Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, - const std::string& location_name) const { - FileSys::VirtualFile vfs_file; - if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)}; - result != ResultSuccess) { - return result; - } - - return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file); -} - -bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const { - return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) != - location_name_cache.end(); -} - -Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const { - if (!IsLocationNameValid(location_name)) { - return ERROR_TIME_NOT_FOUND; - } - - if (!time_zone_binary) { - LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid); - return ERROR_TIME_NOT_FOUND; - } - - const FileSys::VirtualDir zoneinfo_dir{time_zone_binary->GetSubdirectory("zoneinfo")}; - if (!zoneinfo_dir) { - LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid); - return ERROR_TIME_NOT_FOUND; - } - - vfs_file = zoneinfo_dir->GetFileRelative(location_name); - if (!vfs_file) { - LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.", - time_zone_binary_titleid, location_name); - const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()}; - vfs_file = zoneinfo_dir->GetFile(system_time_zone); - } - - if (!vfs_file) { - LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", - time_zone_binary_titleid, location_name); - vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone()); - } - - if (!vfs_file) { - LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid, - location_name); - return ERROR_TIME_NOT_FOUND; - } - - return ResultSuccess; -} - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h deleted file mode 100644 index a6f9698bc..000000000 --- a/src/core/hle/service/time/time_zone_content_manager.h +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string> -#include <vector> - -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/time_zone_manager.h" - -namespace Core { -class System; -} - -namespace Service::Time { -class TimeManager; -} - -namespace Service::Time::TimeZone { - -class TimeZoneContentManager final { -public: - explicit TimeZoneContentManager(Core::System& system_); - - void Initialize(TimeManager& time_manager); - - TimeZoneManager& GetTimeZoneManager() { - return time_zone_manager; - } - - const TimeZoneManager& GetTimeZoneManager() const { - return time_zone_manager; - } - - Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; - -private: - bool IsLocationNameValid(const std::string& location_name) const; - Result GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const; - - Core::System& system; - TimeZoneManager time_zone_manager; - const FileSys::VirtualDir time_zone_binary; - const std::vector<std::string> location_name_cache; -}; - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp deleted file mode 100644 index 205371a26..000000000 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ /dev/null @@ -1,1182 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <climits> -#include <limits> - -#include "common/assert.h" -#include "common/logging/log.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/service/time/time_zone_manager.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time::TimeZone { - -static constexpr s32 epoch_year{1970}; -static constexpr s32 year_base{1900}; -static constexpr s32 epoch_week_day{4}; -static constexpr s32 seconds_per_minute{60}; -static constexpr s32 minutes_per_hour{60}; -static constexpr s32 hours_per_day{24}; -static constexpr s32 days_per_week{7}; -static constexpr s32 days_per_normal_year{365}; -static constexpr s32 days_per_leap_year{366}; -static constexpr s32 months_per_year{12}; -static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour}; -static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day}; -static constexpr s32 years_per_repeat{400}; -static constexpr s64 average_seconds_per_year{31556952}; -static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year}; - -struct Rule { - enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek }; - Type rule_type{}; - s32 day{}; - s32 week{}; - s32 month{}; - s32 transition_time{}; -}; - -struct CalendarTimeInternal { - s64 year{}; - s8 month{}; - s8 day{}; - s8 hour{}; - s8 minute{}; - s8 second{}; - int Compare(const CalendarTimeInternal& other) const { - if (year != other.year) { - if (year < other.year) { - return -1; - } - return 1; - } - if (month != other.month) { - return month - other.month; - } - if (day != other.day) { - return day - other.day; - } - if (hour != other.hour) { - return hour - other.hour; - } - if (minute != other.minute) { - return minute - other.minute; - } - if (second != other.second) { - return second - other.second; - } - return {}; - } -}; - -template <typename TResult, typename TOperand> -static bool SafeAdd(TResult& result, TOperand op) { - result = result + op; - return true; -} - -template <typename TResult, typename TUnit, typename TBase> -static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) { - TUnit delta{}; - if (unit >= 0) { - delta = unit / base; - } else { - delta = -1 - (-1 - unit) / base; - } - unit -= delta * base; - return SafeAdd(result, delta); -} - -template <typename T> -static constexpr bool IsLeapYear(T year) { - return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0); -} - -template <typename T> -static constexpr T GetYearLengthInDays(T year) { - return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year; -} - -static constexpr s64 GetLeapDaysFromYearPositive(s64 year) { - return year / 4 - year / 100 + year / years_per_repeat; -} - -static constexpr s64 GetLeapDaysFromYear(s64 year) { - if (year < 0) { - return -1 - GetLeapDaysFromYearPositive(-1 - year); - } else { - return GetLeapDaysFromYearPositive(year); - } -} - -static constexpr s8 GetMonthLength(bool is_leap_year, int month) { - constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; -} - -static constexpr bool IsDigit(char value) { - return value >= '0' && value <= '9'; -} - -static constexpr int GetQZName(const char* name, int offset, char delimiter) { - while (name[offset] != '\0' && name[offset] != delimiter) { - offset++; - } - return offset; -} - -static constexpr int GetTZName(const char* name, int offset) { - char c; - - while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') { - ++offset; - } - return offset; -} - -static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) { - value = 0; - char temp{name[offset]}; - if (!IsDigit(temp)) { - return {}; - } - do { - value = value * 10 + (temp - '0'); - if (value > max) { - return {}; - } - offset++; - temp = name[offset]; - } while (IsDigit(temp)); - - return value >= min; -} - -static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) { - seconds = 0; - int value{}; - if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) { - return {}; - } - seconds = value * seconds_per_hour; - - if (name[offset] == ':') { - offset++; - if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) { - return {}; - } - seconds += value * seconds_per_minute; - if (name[offset] == ':') { - offset++; - if (!GetInteger(name, offset, value, 0, seconds_per_minute)) { - return {}; - } - seconds += value; - } - } - return true; -} - -static constexpr bool GetOffset(const char* name, int& offset, int& value) { - bool is_negative{}; - if (name[offset] == '-') { - is_negative = true; - offset++; - } else if (name[offset] == '+') { - offset++; - } - if (!GetSeconds(name, offset, value)) { - return {}; - } - if (is_negative) { - value = -value; - } - return true; -} - -static constexpr bool GetRule(const char* name, int& position, Rule& rule) { - bool is_valid{}; - if (name[position] == 'J') { - position++; - rule.rule_type = Rule::Type::JulianDay; - is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year); - } else if (name[position] == 'M') { - position++; - rule.rule_type = Rule::Type::MonthNthDayOfWeek; - is_valid = GetInteger(name, position, rule.month, 1, months_per_year); - if (!is_valid) { - return {}; - } - if (name[position++] != '.') { - return {}; - } - is_valid = GetInteger(name, position, rule.week, 1, 5); - if (!is_valid) { - return {}; - } - if (name[position++] != '.') { - return {}; - } - is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1); - } else if (isdigit(name[position])) { - rule.rule_type = Rule::Type::DayOfYear; - is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1); - } else { - return {}; - } - if (!is_valid) { - return {}; - } - if (name[position] == '/') { - position++; - return GetOffset(name, position, rule.transition_time); - } else { - rule.transition_time = 2 * seconds_per_hour; - } - return true; -} - -static constexpr int TransitionTime(int year, Rule rule, int offset) { - int value{}; - switch (rule.rule_type) { - case Rule::Type::JulianDay: - value = (rule.day - 1) * seconds_per_day; - if (IsLeapYear(year) && rule.day >= 60) { - value += seconds_per_day; - } - break; - case Rule::Type::DayOfYear: - value = rule.day * seconds_per_day; - break; - case Rule::Type::MonthNthDayOfWeek: { - // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to - // calculate the day of the week for any Julian or Gregorian calendar date. - const int m1{(rule.month + 9) % 12 + 1}; - const int yy0{(rule.month <= 2) ? (year - 1) : year}; - const int yy1{yy0 / 100}; - const int yy2{yy0 % 100}; - int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7}; - - if (day_of_week < 0) { - day_of_week += days_per_week; - } - int day{rule.day - day_of_week}; - if (day < 0) { - day += days_per_week; - } - for (int i{1}; i < rule.week; i++) { - if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) { - break; - } - day += days_per_week; - } - - value = day * seconds_per_day; - for (int index{}; index < rule.month - 1; ++index) { - value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day; - } - break; - } - default: - ASSERT(false); - break; - } - return value + rule.transition_time + offset; -} - -static bool ParsePosixName(const char* name, TimeZoneRule& rule) { - static constexpr char default_rule[]{",M4.1.0,M10.5.0"}; - const char* std_name{name}; - int std_len{}; - int offset{}; - int std_offset{}; - - if (name[offset] == '<') { - offset++; - std_name = name + offset; - const int std_name_offset{offset}; - offset = GetQZName(name, offset, '>'); - if (name[offset] != '>') { - return {}; - } - std_len = offset - std_name_offset; - offset++; - } else { - offset = GetTZName(name, offset); - std_len = offset; - } - if (std_len == 0) { - return {}; - } - if (!GetOffset(name, offset, std_offset)) { - return {}; - } - - int char_count{std_len + 1}; - int dest_len{}; - int dest_offset{}; - const char* dest_name{name + offset}; - if (rule.chars.size() < std::size_t(char_count)) { - return {}; - } - - if (name[offset] != '\0') { - if (name[offset] == '<') { - dest_name = name + (++offset); - const int dest_name_offset{offset}; - offset = GetQZName(name, offset, '>'); - if (name[offset] != '>') { - return {}; - } - dest_len = offset - dest_name_offset; - offset++; - } else { - dest_name = name + (offset); - offset = GetTZName(name, offset); - dest_len = offset; - } - if (dest_len == 0) { - return {}; - } - char_count += dest_len + 1; - if (rule.chars.size() < std::size_t(char_count)) { - return {}; - } - if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') { - if (!GetOffset(name, offset, dest_offset)) { - return {}; - } - } else { - dest_offset = std_offset - seconds_per_hour; - } - if (name[offset] == '\0') { - name = default_rule; - offset = 0; - } - if (name[offset] == ',' || name[offset] == ';') { - offset++; - - Rule start{}; - if (!GetRule(name, offset, start)) { - return {}; - } - if (name[offset++] != ',') { - return {}; - } - - Rule end{}; - if (!GetRule(name, offset, end)) { - return {}; - } - if (name[offset] != '\0') { - return {}; - } - - rule.type_count = 2; - rule.ttis[0].gmt_offset = -dest_offset; - rule.ttis[0].is_dst = true; - rule.ttis[0].abbreviation_list_index = std_len + 1; - rule.ttis[1].gmt_offset = -std_offset; - rule.ttis[1].is_dst = false; - rule.ttis[1].abbreviation_list_index = 0; - rule.default_type = 0; - - s64 jan_first{}; - int time_count{}; - int jan_offset{}; - int year_beginning{epoch_year}; - do { - const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day}; - year_beginning--; - if (!SafeAdd(jan_first, -year_seconds)) { - jan_offset = -year_seconds; - break; - } - } while (epoch_year - years_per_repeat / 2 < year_beginning); - - int year_limit{year_beginning + years_per_repeat + 1}; - int year{}; - for (year = year_beginning; year < year_limit; year++) { - int start_time{TransitionTime(year, start, std_offset)}; - int end_time{TransitionTime(year, end, dest_offset)}; - const int year_seconds{GetYearLengthInDays(year) * seconds_per_day}; - const bool is_reversed{end_time < start_time}; - if (is_reversed) { - int swap{start_time}; - start_time = end_time; - end_time = swap; - } - - if (is_reversed || - (start_time < end_time && - (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) { - if (rule.ats.size() - 2 < std::size_t(time_count)) { - break; - } - - rule.ats[time_count] = jan_first; - if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) { - rule.types[time_count++] = is_reversed ? 1 : 0; - } else if (jan_offset != 0) { - rule.default_type = is_reversed ? 1 : 0; - } - - rule.ats[time_count] = jan_first; - if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) { - rule.types[time_count++] = is_reversed ? 0 : 1; - year_limit = year + years_per_repeat + 1; - } else if (jan_offset != 0) { - rule.default_type = is_reversed ? 0 : 1; - } - } - if (!SafeAdd(jan_first, jan_offset + year_seconds)) { - break; - } - jan_offset = 0; - } - rule.time_count = time_count; - if (time_count == 0) { - rule.type_count = 1; - } else if (years_per_repeat < year - year_beginning) { - rule.go_back = true; - rule.go_ahead = true; - } - } else { - if (name[offset] == '\0') { - return {}; - } - - s64 their_std_offset{}; - for (int index{}; index < rule.time_count; ++index) { - const s8 type{rule.types[index]}; - if (rule.ttis[type].is_standard_time_daylight) { - their_std_offset = -rule.ttis[type].gmt_offset; - } - } - - s64 their_offset{their_std_offset}; - for (int index{}; index < rule.time_count; ++index) { - const s8 type{rule.types[index]}; - rule.types[index] = rule.ttis[type].is_dst ? 1 : 0; - if (!rule.ttis[type].is_gmt) { - if (!rule.ttis[type].is_standard_time_daylight) { - rule.ats[index] += dest_offset - their_std_offset; - } else { - rule.ats[index] += std_offset - their_std_offset; - } - } - their_offset = -rule.ttis[type].gmt_offset; - if (!rule.ttis[type].is_dst) { - their_std_offset = their_offset; - } - } - - if (rule.time_count > 0) { - UNIMPLEMENTED(); - // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329 - // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing - } - - rule.ttis[0].gmt_offset = -std_offset; - rule.ttis[0].is_dst = false; - rule.ttis[0].abbreviation_list_index = 0; - rule.ttis[1].gmt_offset = -dest_offset; - rule.ttis[1].is_dst = true; - rule.ttis[1].abbreviation_list_index = std_len + 1; - rule.type_count = 2; - rule.default_type = 0; - } - } else { - // Default is standard time - rule.type_count = 1; - rule.time_count = 0; - rule.default_type = 0; - rule.ttis[0].gmt_offset = -std_offset; - rule.ttis[0].is_dst = false; - rule.ttis[0].abbreviation_list_index = 0; - } - - rule.char_count = char_count; - for (int index{}; index < std_len; ++index) { - rule.chars[index] = std_name[index]; - } - - rule.chars[std_len++] = '\0'; - if (dest_len != 0) { - for (int index{}; index < dest_len; ++index) { - rule.chars[std_len + index] = dest_name[index]; - } - rule.chars[std_len + dest_len] = '\0'; - } - - return true; -} - -static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) { - TzifHeader header{}; - if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) { - return {}; - } - - constexpr s32 time_zone_max_leaps{50}; - constexpr s32 time_zone_max_chars{50}; - constexpr s32 time_zone_max_times{1000}; - if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && - 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) && - 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) && - 0 <= header.char_count && header.char_count < time_zone_max_chars && - (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) && - (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) { - return {}; - } - time_zone_rule.time_count = header.time_count; - time_zone_rule.type_count = header.type_count; - time_zone_rule.char_count = header.char_count; - - int time_count{}; - u64 read_offset = sizeof(TzifHeader); - for (int index{}; index < time_zone_rule.time_count; ++index) { - s64_be at{}; - vfs_file->ReadObject<s64_be>(&at, read_offset); - time_zone_rule.types[index] = 1; - if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) { - if (at < time_zone_rule.ats[time_count - 1]) { - return {}; - } - time_zone_rule.types[index - 1] = 0; - time_count--; - } - time_zone_rule.ats[time_count++] = at; - read_offset += sizeof(s64_be); - } - time_count = 0; - for (int index{}; index < time_zone_rule.time_count; ++index) { - const u8 type{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (time_zone_rule.type_count <= type) { - return {}; - } - if (time_zone_rule.types[index] != 0) { - time_zone_rule.types[time_count++] = type; - } - } - time_zone_rule.time_count = time_count; - for (int index{}; index < time_zone_rule.type_count; ++index) { - TimeTypeInfo& ttis{time_zone_rule.ttis[index]}; - u32_be gmt_offset{}; - vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset); - read_offset += sizeof(u32_be); - ttis.gmt_offset = gmt_offset; - - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - ttis.is_dst = dst != 0; - - const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (abbreviation_list_index >= time_zone_rule.char_count) { - return {}; - } - ttis.abbreviation_list_index = abbreviation_list_index; - } - - vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset); - time_zone_rule.chars[time_zone_rule.char_count] = '\0'; - read_offset += time_zone_rule.char_count; - for (int index{}; index < time_zone_rule.type_count; ++index) { - if (header.ttis_std_count == 0) { - time_zone_rule.ttis[index].is_standard_time_daylight = false; - } else { - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0; - } - } - - for (int index{}; index < time_zone_rule.type_count; ++index) { - if (header.ttis_std_count == 0) { - time_zone_rule.ttis[index].is_gmt = false; - } else { - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - time_zone_rule.ttis[index].is_gmt = dst != 0; - } - } - - const u64 position{(read_offset - sizeof(TzifHeader))}; - const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position); - if (bytes_read < 0) { - return {}; - } - constexpr s32 time_zone_name_max{255}; - if (bytes_read > (time_zone_name_max + 1)) { - return {}; - } - - std::array<char, time_zone_name_max + 1> temp_name{}; - vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset); - if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' && - std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) { - temp_name[bytes_read - 1] = '\0'; - - std::array<char, time_zone_name_max> name{}; - std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1)); - - // Fill in computed transition times with temp rule - TimeZoneRule temp_rule; - if (ParsePosixName(name.data(), temp_rule)) { - int have_abbreviation = 0; - int char_count = time_zone_rule.char_count; - - for (int i = 0; i < temp_rule.type_count; i++) { - char* temp_abbreviation = - temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index; - int j; - for (j = 0; j < char_count; j++) { - if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) { - temp_rule.ttis[i].abbreviation_list_index = j; - have_abbreviation++; - break; - } - } - if (j >= char_count) { - int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation)); - if (j + temp_abbreviation_length < time_zone_max_chars) { - std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation); - char_count = j + temp_abbreviation_length + 1; - temp_rule.ttis[i].abbreviation_list_index = j; - have_abbreviation++; - } - } - } - - if (have_abbreviation == temp_rule.type_count) { - time_zone_rule.char_count = char_count; - - // Original comment: - /* Ignore any trailing, no-op transitions generated - by zic as they don't help here and can run afoul - of bugs in zic 2016j or earlier. */ - // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic - while (1 < time_zone_rule.time_count && - (time_zone_rule.types[time_zone_rule.time_count - 1] == - time_zone_rule.types[time_zone_rule.time_count - 2])) { - time_zone_rule.time_count--; - } - - for (int i = 0; - i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times; - i++) { - const s64 transition_time = temp_rule.ats[i]; - if (0 < time_zone_rule.time_count && - transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) { - continue; - } - - time_zone_rule.ats[time_zone_rule.time_count] = transition_time; - time_zone_rule.types[time_zone_rule.time_count] = - static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]); - time_zone_rule.time_count++; - } - for (int i = 0; i < temp_rule.type_count; i++) { - time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i]; - } - } - } - } - - const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool { - if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) { - return {}; - } - - const struct TimeTypeInfo* ap = &rule.ttis[a]; - const struct TimeTypeInfo* bp = &rule.ttis[b]; - - return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst && - (std::strcmp(&rule.chars[ap->abbreviation_list_index], - &rule.chars[bp->abbreviation_list_index]) == 0)); - }; - - if (time_zone_rule.type_count == 0) { - return {}; - } - if (time_zone_rule.time_count > 1) { - if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) { - s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat; - int repeatattype = time_zone_rule.types[0]; - for (int i = 1; i < time_zone_rule.time_count; ++i) { - if (time_zone_rule.ats[i] == repeatat && - typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { - time_zone_rule.go_back = true; - break; - } - } - } - if (std::numeric_limits<s64>::min() + seconds_per_repeat <= - time_zone_rule.ats[time_zone_rule.time_count - 1]) { - s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat; - int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1]; - for (int i = time_zone_rule.time_count; i >= 0; --i) { - if (time_zone_rule.ats[i] == repeatat && - typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { - time_zone_rule.go_ahead = true; - break; - } - } - } - } - - s32 default_type{}; - - for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) { - if (time_zone_rule.types[default_type] == 0) { - break; - } - } - - default_type = default_type < time_zone_rule.time_count ? -1 : 0; - if (default_type < 0 && time_zone_rule.time_count > 0 && - time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) { - default_type = time_zone_rule.types[0]; - while (--default_type >= 0) { - if (!time_zone_rule.ttis[default_type].is_dst) { - break; - } - } - } - if (default_type < 0) { - default_type = 0; - while (time_zone_rule.ttis[default_type].is_dst) { - if (++default_type >= time_zone_rule.type_count) { - default_type = 0; - break; - } - } - } - time_zone_rule.default_type = default_type; - return true; -} - -static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { - s64 year{epoch_year}; - s64 time_days{time / seconds_per_day}; - s64 remaining_seconds{time % seconds_per_day}; - while (time_days < 0 || time_days >= GetYearLengthInDays(year)) { - s64 delta = time_days / days_per_leap_year; - if (!delta) { - delta = time_days < 0 ? -1 : 1; - } - s64 new_year{year}; - if (!SafeAdd(new_year, delta)) { - return ERROR_OUT_OF_RANGE; - } - time_days -= (new_year - year) * days_per_normal_year; - time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1); - year = new_year; - } - - s64 day_of_year{time_days}; - remaining_seconds += gmt_offset; - while (remaining_seconds < 0) { - remaining_seconds += seconds_per_day; - day_of_year--; - } - - while (remaining_seconds >= seconds_per_day) { - remaining_seconds -= seconds_per_day; - day_of_year++; - } - - while (day_of_year < 0) { - if (!SafeAdd(year, -1)) { - return ERROR_OUT_OF_RANGE; - } - day_of_year += GetYearLengthInDays(year); - } - - while (day_of_year >= GetYearLengthInDays(year)) { - day_of_year -= GetYearLengthInDays(year); - if (!SafeAdd(year, 1)) { - return ERROR_OUT_OF_RANGE; - } - } - - calendar_time.year = year; - calendar_additional_info.day_of_year = static_cast<u32>(day_of_year); - s64 day_of_week{ - (epoch_week_day + - ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) + - GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) % - days_per_week}; - if (day_of_week < 0) { - day_of_week += days_per_week; - } - - calendar_additional_info.day_of_week = static_cast<u32>(day_of_week); - calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour); - remaining_seconds %= seconds_per_hour; - calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute); - calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute); - - for (calendar_time.month = 0; - day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month); - ++calendar_time.month) { - day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month); - } - - calendar_time.day = static_cast<s8>(day_of_year + 1); - calendar_additional_info.is_dst = false; - calendar_additional_info.gmt_offset = gmt_offset; - - return ResultSuccess; -} - -static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, - CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { - ASSERT(rules.go_ahead ? rules.time_count > 0 : true); - if ((rules.go_back && time < rules.ats[0]) || - (rules.go_ahead && time > rules.ats[rules.time_count - 1])) { - s64 seconds{}; - if (time < rules.ats[0]) { - seconds = rules.ats[0] - time; - } else { - seconds = time - rules.ats[rules.time_count - 1]; - } - seconds--; - - const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat}; - seconds = years * average_seconds_per_year; - - s64 new_time{time}; - if (time < rules.ats[0]) { - new_time += seconds; - } else { - new_time -= seconds; - } - if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) { - return ERROR_TIME_NOT_FOUND; - } - if (const Result result{ - ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)}; - result != ResultSuccess) { - return result; - } - if (time < rules.ats[0]) { - calendar_time.year -= years; - } else { - calendar_time.year += years; - } - - return ResultSuccess; - } - - s32 tti_index{}; - if (rules.time_count == 0 || time < rules.ats[0]) { - tti_index = rules.default_type; - } else { - s32 low{1}; - s32 high{rules.time_count}; - while (low < high) { - s32 mid{(low + high) >> 1}; - if (time < rules.ats[mid]) { - high = mid; - } else { - low = mid + 1; - } - } - tti_index = rules.types[low - 1]; - } - - if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, - calendar_time, calendar_additional_info)}; - result != ResultSuccess) { - return result; - } - - calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst; - const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]}; - u32 index; - for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' && - index < calendar_additional_info.timezone_name.size() - 1; - ++index) { - calendar_additional_info.timezone_name[index] = time_zone[index]; - } - calendar_additional_info.timezone_name[index] = '\0'; - return ResultSuccess; -} - -static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { - CalendarTimeInternal calendar_time{}; - const Result result{ - ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)}; - calendar.time.year = static_cast<s16>(calendar_time.year); - - // Internal impl. uses 0-indexed month - calendar.time.month = static_cast<s8>(calendar_time.month + 1); - - calendar.time.day = calendar_time.day; - calendar.time.hour = calendar_time.hour; - calendar.time.minute = calendar_time.minute; - calendar.time.second = calendar_time.second; - return result; -} - -TimeZoneManager::TimeZoneManager() = default; -TimeZoneManager::~TimeZoneManager() = default; - -Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, - CalendarInfo& calendar) const { - return ToCalendarTimeImpl(rules, time, calendar); -} - -Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file) { - TimeZoneRule rule{}; - if (ParseTimeZoneBinary(rule, vfs_file)) { - device_location_name = location_name; - time_zone_rule = rule; - return ResultSuccess; - } - return ERROR_TIME_ZONE_CONVERSION_FAILED; -} - -Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { - time_zone_update_time_point = value; - return ResultSuccess; -} - -Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { - if (is_initialized) { - return ToCalendarTime(time_zone_rule, time, calendar); - } else { - return ERROR_UNINITIALIZED_CLOCK; - } -} - -Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, - FileSys::VirtualFile& vfs_file) const { - if (!ParseTimeZoneBinary(rules, vfs_file)) { - return ERROR_TIME_ZONE_CONVERSION_FAILED; - } - return ResultSuccess; -} - -Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const { - posix_time = 0; - - CalendarTimeInternal internal_time{ - .year = calendar_time.year, - // Internal impl. uses 0-indexed month - .month = static_cast<s8>(calendar_time.month - 1), - .day = calendar_time.day, - .hour = calendar_time.hour, - .minute = calendar_time.minute, - .second = calendar_time.second, - }; - - s32 hour{internal_time.hour}; - s32 minute{internal_time.minute}; - if (!SafeNormalize(hour, minute, minutes_per_hour)) { - return ERROR_OVERFLOW; - } - internal_time.minute = static_cast<s8>(minute); - - s32 day{internal_time.day}; - if (!SafeNormalize(day, hour, hours_per_day)) { - return ERROR_OVERFLOW; - } - internal_time.day = static_cast<s8>(day); - internal_time.hour = static_cast<s8>(hour); - - s64 year{internal_time.year}; - s64 month{internal_time.month}; - if (!SafeNormalize(year, month, months_per_year)) { - return ERROR_OVERFLOW; - } - internal_time.month = static_cast<s8>(month); - - if (!SafeAdd(year, year_base)) { - return ERROR_OVERFLOW; - } - - while (day <= 0) { - if (!SafeAdd(year, -1)) { - return ERROR_OVERFLOW; - } - s64 temp_year{year}; - if (1 < internal_time.month) { - ++temp_year; - } - day += static_cast<s32>(GetYearLengthInDays(temp_year)); - } - - while (day > days_per_leap_year) { - s64 temp_year{year}; - if (1 < internal_time.month) { - temp_year++; - } - day -= static_cast<s32>(GetYearLengthInDays(temp_year)); - if (!SafeAdd(year, 1)) { - return ERROR_OVERFLOW; - } - } - - while (true) { - const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)}; - if (day <= month_length) { - break; - } - day -= month_length; - internal_time.month++; - if (internal_time.month >= months_per_year) { - internal_time.month = 0; - if (!SafeAdd(year, 1)) { - return ERROR_OVERFLOW; - } - } - } - internal_time.day = static_cast<s8>(day); - - if (!SafeAdd(year, -year_base)) { - return ERROR_OVERFLOW; - } - internal_time.year = year; - - s32 saved_seconds{}; - if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) { - saved_seconds = 0; - } else if (year + year_base < epoch_year) { - s32 second{internal_time.second}; - if (!SafeAdd(second, 1 - seconds_per_minute)) { - return ERROR_OVERFLOW; - } - saved_seconds = second; - internal_time.second = 1 - seconds_per_minute; - } else { - saved_seconds = internal_time.second; - internal_time.second = 0; - } - - s64 low{LLONG_MIN}; - s64 high{LLONG_MAX}; - while (true) { - s64 pivot{low / 2 + high / 2}; - if (pivot < low) { - pivot = low; - } else if (pivot > high) { - pivot = high; - } - s32 direction{}; - CalendarTimeInternal candidate_calendar_time{}; - CalendarAdditionalInfo unused{}; - if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) != - ResultSuccess) { - if (pivot > 0) { - direction = 1; - } else { - direction = -1; - } - } else { - direction = candidate_calendar_time.Compare(internal_time); - } - if (!direction) { - const s64 time_result{pivot + saved_seconds}; - if ((time_result < pivot) != (saved_seconds < 0)) { - return ERROR_OVERFLOW; - } - posix_time = time_result; - break; - } else { - if (pivot == low) { - if (pivot == LLONG_MAX) { - return ERROR_TIME_NOT_FOUND; - } - pivot++; - low++; - } else if (pivot == high) { - if (pivot == LLONG_MIN) { - return ERROR_TIME_NOT_FOUND; - } - pivot--; - high--; - } - if (low > high) { - return ERROR_TIME_NOT_FOUND; - } - if (direction > 0) { - high = pivot; - } else { - low = pivot; - } - } - } - return ResultSuccess; -} - -Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, - s64& posix_time) const { - if (is_initialized) { - return ToPosixTime(time_zone_rule, calendar_time, posix_time); - } - posix_time = 0; - return ERROR_UNINITIALIZED_CLOCK; -} - -Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size()); - return ResultSuccess; -} - -Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - count = static_cast<u32>(total_location_name_count); - - return ResultSuccess; -} - -Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - version = time_zone_rule_version; - - return ResultSuccess; -} - -Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - - for (const auto& name : total_location_names) { - LocationName entry{}; - std::memcpy(entry.data(), name.c_str(), name.size()); - values.push_back(entry); - } - - return ResultSuccess; -} - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h deleted file mode 100644 index 8664f28d1..000000000 --- a/src/core/hle/service/time/time_zone_manager.h +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string> - -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time::TimeZone { - -class TimeZoneManager final { -public: - TimeZoneManager(); - ~TimeZoneManager(); - - void SetTotalLocationNameCount(std::size_t value) { - total_location_name_count = value; - } - - void SetLocationNames(std::vector<std::string> location_names) { - total_location_names = location_names; - } - - void SetTimeZoneRuleVersion(const u128& value) { - time_zone_rule_version = value; - } - - void MarkAsInitialized() { - is_initialized = true; - } - - Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file); - Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); - Result GetDeviceLocationName(TimeZone::LocationName& value) const; - Result GetTotalLocationNameCount(s32& count) const; - Result GetTimeZoneRuleVersion(u128& version) const; - Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const; - Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; - Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; - Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; - Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const; - Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; - -private: - bool is_initialized{}; - TimeZoneRule time_zone_rule{}; - std::string device_location_name{"GMT"}; - u128 time_zone_rule_version{}; - std::size_t total_location_name_count{}; - std::vector<std::string> total_location_names{}; - Clock::SteadyClockTimePoint time_zone_update_time_point{ - Clock::SteadyClockTimePoint::GetRandom()}; -}; - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp deleted file mode 100644 index 8171c82a5..000000000 --- a/src/core/hle/service/time/time_zone_service.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/time/time_zone_content_manager.h" -#include "core/hle/service/time/time_zone_service.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time { - -ITimeZoneService::ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_) - : ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} { - static const FunctionInfo functions[] = { - {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, - {1, nullptr, "SetDeviceLocationName"}, - {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"}, - {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"}, - {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, - {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, - {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"}, - {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, - {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, - {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, - }; - RegisterHandlers(functions); -} - -void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - TimeZone::LocationName location_name{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(location_name); -} - -void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - s32 count{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(count); -} - -void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - std::vector<TimeZone::LocationName> location_names{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(location_names); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<s32>(location_names.size())); -} -void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - u128 rule_version{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(rule_version); -} - -void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()}; - - std::string location_name; - for (const auto& byte : raw_location_name) { - // Strip extra bytes - if (byte == '\0') { - break; - } - location_name.push_back(byte); - } - - LOG_DEBUG(Service_Time, "called, location_name={}", location_name); - - TimeZone::TimeZoneRule time_zone_rule{}; - const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; - - std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); - std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); - ctx.WriteBuffer(time_zone_rule_outbuffer); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto posix_time{rp.Pop<s64>()}; - - LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); - - TimeZone::TimeZoneRule time_zone_rule{}; - const auto buffer{ctx.ReadBuffer()}; - std::memcpy(&time_zone_rule, buffer.data(), buffer.size()); - - TimeZone::CalendarInfo calendar_info{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( - time_zone_rule, posix_time, calendar_info)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)}; - rb.Push(ResultSuccess); - rb.PushRaw(calendar_info); -} - -void ITimeZoneService::ToCalendarTimeWithMyRule(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto posix_time{rp.Pop<s64>()}; - - LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); - - TimeZone::CalendarInfo calendar_info{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules( - posix_time, calendar_info)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)}; - rb.Push(ResultSuccess); - rb.PushRaw(calendar_info); -} - -void ITimeZoneService::ToPosixTime(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - IPC::RequestParser rp{ctx}; - const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; - TimeZone::TimeZoneRule time_zone_rule{}; - std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule)); - - s64 posix_time{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( - time_zone_rule, calendar_time, posix_time)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(posix_time); - - // TODO(bunnei): Handle multiple times - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(1); // Number of times we're returning -} - -void ITimeZoneService::ToPosixTimeWithMyRule(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - IPC::RequestParser rp{ctx}; - const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; - - s64 posix_time{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule( - calendar_time, posix_time)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(posix_time); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(1); // Number of times we're returning -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h deleted file mode 100644 index 952fcb0e2..000000000 --- a/src/core/hle/service/time/time_zone_service.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 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::Time { - -namespace TimeZone { -class TimeZoneContentManager; -} - -class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { -public: - explicit ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_); - -private: - void GetDeviceLocationName(HLERequestContext& ctx); - void GetTotalLocationNameCount(HLERequestContext& ctx); - void LoadLocationNameList(HLERequestContext& ctx); - void GetTimeZoneRuleVersion(HLERequestContext& ctx); - void LoadTimeZoneRule(HLERequestContext& ctx); - void ToCalendarTime(HLERequestContext& ctx); - void ToCalendarTimeWithMyRule(HLERequestContext& ctx); - void ToPosixTime(HLERequestContext& ctx); - void ToPosixTimeWithMyRule(HLERequestContext& ctx); - -private: - TimeZone::TimeZoneContentManager& time_zone_content_manager; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h deleted file mode 100644 index eb4fb52d1..000000000 --- a/src/core/hle/service/time/time_zone_types.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/swap.h" - -namespace Service::Time::TimeZone { - -using LocationName = std::array<char, 0x24>; - -/// https://switchbrew.org/wiki/Glue_services#ttinfo -struct TimeTypeInfo { - s32 gmt_offset{}; - u8 is_dst{}; - INSERT_PADDING_BYTES(3); - s32 abbreviation_list_index{}; - u8 is_standard_time_daylight{}; - u8 is_gmt{}; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule -struct TimeZoneRule { - s32 time_count{}; - s32 type_count{}; - s32 char_count{}; - u8 go_back{}; - u8 go_ahead{}; - INSERT_PADDING_BYTES(2); - std::array<s64, 1000> ats{}; - std::array<s8, 1000> types{}; - std::array<TimeTypeInfo, 128> ttis{}; - std::array<char, 512> chars{}; - s32 default_type{}; - INSERT_PADDING_BYTES(0x12C4); -}; -static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo -struct CalendarAdditionalInfo { - u32 day_of_week; - u32 day_of_year; - std::array<char, 8> timezone_name; - u32 is_dst; - s32 gmt_offset; -}; -static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#CalendarTime -struct CalendarTime { - s16 year; - s8 month; - s8 day; - s8 hour; - s8 minute; - s8 second; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size"); - -struct CalendarInfo { - CalendarTime time; - CalendarAdditionalInfo additional_info; -}; -static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size"); - -struct TzifHeader { - u32_be magic{}; - u8 version{}; - INSERT_PADDING_BYTES(15); - s32_be ttis_gmt_count{}; - s32_be ttis_std_count{}; - s32_be leap_count{}; - s32_be time_count{}; - s32_be type_count{}; - s32_be char_count{}; -}; -static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size"); - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 71ce9be50..725311c53 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -71,18 +71,7 @@ size_t Display::GetNumLayers() const { return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); }); } -Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) { - if (got_vsync_event) { - return ResultPermissionDenied; - } - - got_vsync_event = true; - - *out_vsync_event = GetVSyncEventUnchecked(); - return ResultSuccess; -} - -Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { +Kernel::KReadableEvent* Display::GetVSyncEvent() { return &vsync_event->GetReadableEvent(); } @@ -102,6 +91,10 @@ void Display::CreateLayer(u64 layer_id, u32 binder_id, 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)); } @@ -114,6 +107,13 @@ void Display::DestroyLayer(u64 layer_id) { [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) { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 1d9360b96..8eb8a5155 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -74,16 +74,8 @@ public: std::size_t GetNumLayers() const; - /** - * Gets the internal vsync event. - * - * @returns The internal Vsync event if it has not yet been retrieved, - * VI::ResultPermissionDenied otherwise. - */ - [[nodiscard]] Result GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event); - /// Gets the internal vsync event. - Kernel::KReadableEvent* GetVSyncEventUnchecked(); + Kernel::KReadableEvent* GetVSyncEvent(); /// Signals the internal vsync event. void SignalVSyncEvent(); @@ -104,9 +96,10 @@ public: /// Resets the display for a new connection. void Reset() { layers.clear(); - got_vsync_event = false; } + void Abandon(); + /// Attempts to find a layer with the given ID. /// /// @param layer_id The layer ID. @@ -133,7 +126,7 @@ private: std::vector<std::unique_ptr<Layer>> layers; Kernel::KEvent* vsync_event{}; - bool got_vsync_event{false}; + bool is_abandoned{}; }; } // 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 index 295005e23..f95e2dc71 100644 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -4,6 +4,7 @@ #pragma once #include <memory> +#include <utility> #include "common/common_types.h" @@ -75,12 +76,12 @@ public: return open; } - void Close() { - open = false; + bool Close() { + return std::exchange(open, false); } - void Open() { - open = true; + bool Open() { + return !std::exchange(open, true); } private: diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 9ab8788e3..1f3d82c57 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -15,6 +15,7 @@ #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" @@ -343,8 +344,8 @@ private: class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { public: - explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_) - : ServiceFramework{system_, "IManagerDisplayService"}, nv_flinger{nv_flinger_} { + explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) + : ServiceFramework{system_, "IManagerDisplayService"}, nvnflinger{nvnflinger_} { // clang-format off static const FunctionInfo functions[] = { {200, nullptr, "AllocateProcessHeapBlock"}, @@ -440,7 +441,7 @@ private: IPC::RequestParser rp{ctx}; const u64 display = rp.Pop<u64>(); - const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; + const Result rc = nvnflinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); @@ -457,7 +458,7 @@ private: "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", unknown, display, aruid); - const auto layer_id = nv_flinger.CreateLayer(display); + 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}; @@ -494,14 +495,14 @@ private: rb.Push(ResultSuccess); } - Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::Nvnflinger& nvnflinger; }; class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { public: - IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) - : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, + : ServiceFramework{system_, "IApplicationDisplayService"}, nvnflinger{nvnflinger_}, hos_binder_driver_server{hos_binder_driver_server_} { static const FunctionInfo functions[] = { @@ -564,7 +565,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger); + rb.PushIpcInterface<ISystemDisplayService>(system, nvnflinger); } void GetManagerDisplayService(HLERequestContext& ctx) { @@ -572,7 +573,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IManagerDisplayService>(system, nv_flinger); + rb.PushIpcInterface<IManagerDisplayService>(system, nvnflinger); } void GetIndirectDisplayTransactionService(HLERequestContext& ctx) { @@ -607,7 +608,7 @@ private: ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); - const auto display_id = nv_flinger.OpenDisplay(name); + 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}; @@ -624,7 +625,7 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop<u64>(); - const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; + const Result rc = nvnflinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); @@ -694,16 +695,14 @@ private: void OpenLayer(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - const auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - - const std::string display_name(name_buf.begin(), end); + 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 = nv_flinger.OpenDisplay(display_name); + 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}; @@ -711,7 +710,7 @@ private: return; } - const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, 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}; @@ -719,7 +718,12 @@ private: return; } - nv_flinger.OpenLayer(layer_id); + 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}); @@ -737,7 +741,12 @@ private: LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); - nv_flinger.CloseLayer(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); @@ -753,7 +762,7 @@ private: // TODO(Subv): What's the difference between a Stray and a Managed layer? - const auto layer_id = nv_flinger.CreateLayer(display_id); + 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}; @@ -761,7 +770,7 @@ private: return; } - const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *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}; @@ -785,7 +794,7 @@ private: const u64 layer_id = rp.Pop<u64>(); LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id); - nv_flinger.DestroyLayer(layer_id); + nvnflinger.DestroyLayer(layer_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -798,7 +807,7 @@ private: LOG_DEBUG(Service_VI, "called. display_id={}", display_id); Kernel::KReadableEvent* vsync_event{}; - const auto result = nv_flinger.FindVsyncEvent(&vsync_event, display_id); + 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); @@ -808,6 +817,12 @@ private: 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); @@ -899,8 +914,9 @@ private: } } - Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::Nvnflinger& nvnflinger; Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; + bool vsync_event_fetched{false}; }; static bool IsValidServiceAccess(Permission permission, Policy policy) { @@ -916,7 +932,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { } void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, - Nvnflinger::Nvnflinger& nv_flinger, + Nvnflinger::Nvnflinger& nvnflinger, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, Permission permission) { IPC::RequestParser rp{ctx}; @@ -931,19 +947,19 @@ void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server); + rb.PushIpcInterface<IApplicationDisplayService>(system, nvnflinger, hos_binder_driver_server); } -void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, +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<VI_M>(system, nv_flinger, hos_binder_driver_server)); + "vi:m", std::make_shared<VI_M>(system, nvnflinger, hos_binder_driver_server)); server_manager->RegisterNamedService( - "vi:s", std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)); + "vi:s", std::make_shared<VI_S>(system, nvnflinger, hos_binder_driver_server)); server_manager->RegisterNamedService( - "vi:u", std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)); + "vi:u", std::make_shared<VI_U>(system, nvnflinger, hos_binder_driver_server)); 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 a35b62f97..ee4bcbcfa 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -48,7 +48,7 @@ void GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, Permission permission); } // namespace detail -void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, +void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server); } // namespace Service::VI diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index c9f8707b7..9b75c660c 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -19,8 +19,54 @@ #include "core/arm/nce/patcher.h" #endif +#ifndef HAS_NCE +namespace Core::NCE { +class Patcher {}; +} // namespace Core::NCE +#endif + namespace Loader { +struct PatchCollection { + explicit PatchCollection(bool is_application_) : is_application{is_application_} { + module_patcher_indices.fill(-1); + patchers.emplace_back(); + } + + std::vector<Core::NCE::Patcher>* GetPatchers() { + if (is_application && Settings::IsNceEnabled()) { + return &patchers; + } + return nullptr; + } + + size_t GetTotalPatchSize() const { + size_t total_size{}; +#ifdef HAS_NCE + for (auto& patcher : patchers) { + total_size += patcher.GetSectionSize(); + } +#endif + return total_size; + } + + void SaveIndex(size_t module) { + module_patcher_indices[module] = static_cast<s32>(patchers.size() - 1); + } + + s32 GetIndex(size_t module) const { + return module_patcher_indices[module]; + } + + s32 GetLastIndex() const { + return static_cast<s32>(patchers.size()) - 1; + } + + bool is_application; + std::vector<Core::NCE::Patcher> patchers; + std::array<s32, 13> module_patcher_indices{}; +}; + AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, bool override_update_) : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { @@ -142,18 +188,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect std::size_t code_size{}; // Define an nce patch context for each potential module. -#ifdef HAS_NCE - std::array<Core::NCE::Patcher, 13> module_patchers; -#endif - - const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { -#ifdef HAS_NCE - if (is_application && Settings::IsNceEnabled()) { - return &module_patchers[i]; - } -#endif - return nullptr; - }; + PatchCollection patch_ctx{is_application}; // Use the NSO module loader to figure out the code layout for (size_t i = 0; i < static_modules.size(); i++) { @@ -164,13 +199,14 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; - const auto tentative_next_load_addr = - AppLoader_NSO::LoadModule(process, system, *module_file, code_size, - should_pass_arguments, false, {}, GetPatcher(i)); + const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( + process, system, *module_file, code_size, should_pass_arguments, false, {}, + patch_ctx.GetPatchers(), patch_ctx.GetLastIndex()); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; } + patch_ctx.SaveIndex(i); code_size = *tentative_next_load_addr; } @@ -184,6 +220,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect return 0; }(); + // Add patch size to the total module size + code_size += patch_ctx.GetTotalPatchSize(); + // Setup the process code layout if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; @@ -204,9 +243,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect const VAddr load_addr{next_load_addr}; const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; - const auto tentative_next_load_addr = - AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, - should_pass_arguments, true, pm, GetPatcher(i)); + const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( + process, system, *module_file, load_addr, should_pass_arguments, true, pm, + patch_ctx.GetPatchers(), patch_ctx.GetIndex(i)); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; } @@ -216,20 +255,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); } - // Find the RomFS by searching for a ".romfs" file in this directory - const auto& files = dir->GetFiles(); - const auto romfs_iter = - std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) { - return f->GetName().find(".romfs") != std::string::npos; - }); - - // Register the RomFS if a ".romfs" file was found - if (romfs_iter != files.end() && *romfs_iter != nullptr) { - romfs = *romfs_iter; - system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( - *this, system.GetContentProvider(), system.GetFileSystemController())); - } - is_loaded = true; return {ResultStatus::Success, LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}}; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index b4828f7cd..f4e932cec 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -14,7 +14,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core { class System; diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 814407535..2a32b1276 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -74,8 +74,10 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S return load_result; } - system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( - *this, system.GetContentProvider(), system.GetFileSystemController())); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), nca->GetTitleId(), + std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(), + system.GetFileSystemController())); is_loaded = true; return load_result; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index e74697cda..1d96dc4c8 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -12,7 +12,7 @@ #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/romfs_factory.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" @@ -275,10 +275,12 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S return {ResultStatus::ErrorLoadingNRO, {}}; } - if (romfs != nullptr) { - system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( - *this, system.GetContentProvider(), system.GetFileSystemController())); - } + u64 program_id{}; + ReadProgramId(program_id); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), program_id, + std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(), + system.GetFileSystemController())); is_loaded = true; return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index b053a0d14..583b7e927 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -77,7 +77,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional<FileSys::PatchManager> pm, - Core::NCE::Patcher* patch) { + std::vector<Core::NCE::Patcher>* patches, + s32 patch_index) { if (nso_file.GetSize() < sizeof(NSOHeader)) { return std::nullopt; } @@ -94,8 +95,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: // Allocate some space at the beginning if we are patching in PreText mode. const size_t module_start = [&]() -> size_t { #ifdef HAS_NCE - if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { - return patch->GetSectionSize(); + if (patches && load_into_process) { + auto* patch = &patches->operator[](patch_index); + if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { + return patch->GetSectionSize(); + } } #endif return 0; @@ -160,27 +164,24 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: #ifdef HAS_NCE // If we are computing the process code layout and using nce backend, patch. const auto& code = codeset.CodeSegment(); - if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { + auto* patch = patches ? &patches->operator[](patch_index) : nullptr; + if (patch && !load_into_process) { // Patch SVCs and MRS calls in the guest code - patch->PatchText(program_image, code); - - // Add patch section size to the module size. - image_size += static_cast<u32>(patch->GetSectionSize()); + while (!patch->PatchText(program_image, code)) { + patch = &patches->emplace_back(); + } } else if (patch) { // Relocate code patch and copy to the program_image. - patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); - - // Update patch section. - auto& patch_segment = codeset.PatchSegment(); - patch_segment.addr = - patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; - patch_segment.size = static_cast<u32>(patch->GetSectionSize()); - - // Add patch section size to the module size. In PreText mode image_size - // already contains the patch segment as part of module_start. - if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) { - image_size += patch_segment.size; + if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) { + // Update patch section. + auto& patch_segment = codeset.PatchSegment(); + patch_segment.addr = + patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; + patch_segment.size = static_cast<u32>(patch->GetSectionSize()); } + + // Refresh image_size to take account the patch section if it was added by RelocateAndCopy + image_size = static_cast<u32>(program_image.size()); } #endif diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 29b86ed4c..6356697e3 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -93,7 +93,8 @@ public: const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional<FileSys::PatchManager> pm = {}, - Core::NCE::Patcher* patch = nullptr); + std::vector<Core::NCE::Patcher>* patches = nullptr, + s32 patch_index = -1); LoadResult Load(Kernel::KProcess& process, Core::System& system) override; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index f4ab75b77..3016d5f25 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -10,6 +10,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/filesystem/filesystem.h" @@ -109,9 +110,17 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S return result; } + if (nsp->IsExtractedType()) { + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), {}, + std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(), + system.GetFileSystemController())); + } + FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), + std::move(update_raw)); } is_loaded = true; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 12d72c380..e9abb199a 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -78,7 +78,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), + std::move(update_raw)); } is_loaded = true; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8176a41be..e10a4601e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -24,6 +24,8 @@ #include "core/hle/kernel/k_process.h" #include "core/memory.h" #include "video_core/gpu.h" +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/host1x/host1x.h" #include "video_core/rasterizer_download_area.h" namespace Core::Memory { @@ -637,17 +639,6 @@ struct Memory::Impl { LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", GetInteger(target), base * YUZU_PAGESIZE, (base + size) * YUZU_PAGESIZE); - // During boot, current_page_table might not be set yet, in which case we need not flush - if (system.IsPoweredOn()) { - auto& gpu = system.GPU(); - for (u64 i = 0; i < size; i++) { - const auto page = base + i; - if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { - gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE); - } - } - } - const auto end = base + size; ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", base + page_table.pointers.size()); @@ -790,8 +781,7 @@ struct Memory::Impl { }, [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); }); if (ptr) { - const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); - return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); + return Common::AtomicCompareAndSwap(reinterpret_cast<T*>(ptr), data, expected); } return true; } @@ -805,27 +795,38 @@ struct Memory::Impl { }, [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(u128)); }); if (ptr) { - const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); - return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); + return Common::AtomicCompareAndSwap(reinterpret_cast<u64*>(ptr), data, expected); } return true; } - void HandleRasterizerDownload(VAddr address, size_t size) { + void HandleRasterizerDownload(VAddr v_address, size_t size) { + const auto* p = GetPointerImpl( + v_address, []() {}, []() {}); + if (!gpu_device_memory) [[unlikely]] { + gpu_device_memory = &system.Host1x().MemoryManager(); + } const size_t core = system.GetCurrentHostThreadID(); auto& current_area = rasterizer_read_areas[core]; - const VAddr end_address = address + size; - if (current_area.start_address <= address && end_address <= current_area.end_address) - [[likely]] { - return; - } - current_area = system.GPU().OnCPURead(address, size); + gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { + const DAddr end_address = address + size; + if (current_area.start_address <= address && end_address <= current_area.end_address) + [[likely]] { + return; + } + current_area = system.GPU().OnCPURead(address, size); + }); } - void HandleRasterizerWrite(VAddr address, size_t size) { + void HandleRasterizerWrite(VAddr v_address, size_t size) { + const auto* p = GetPointerImpl( + v_address, []() {}, []() {}); constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; const size_t core = std::min(system.GetCurrentHostThreadID(), sys_core); // any other calls threads go to syscore. + if (!gpu_device_memory) [[unlikely]] { + gpu_device_memory = &system.Host1x().MemoryManager(); + } // Guard on sys_core; if (core == sys_core) [[unlikely]] { sys_core_guard.lock(); @@ -835,36 +836,53 @@ struct Memory::Impl { sys_core_guard.unlock(); } }); - auto& current_area = rasterizer_write_areas[core]; - VAddr subaddress = address >> YUZU_PAGEBITS; - bool do_collection = current_area.last_address == subaddress; - if (!do_collection) [[unlikely]] { - do_collection = system.GPU().OnCPUWrite(address, size); - if (!do_collection) { - return; + gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { + auto& current_area = rasterizer_write_areas[core]; + PAddr subaddress = address >> YUZU_PAGEBITS; + bool do_collection = current_area.last_address == subaddress; + if (!do_collection) [[unlikely]] { + do_collection = system.GPU().OnCPUWrite(address, size); + if (!do_collection) { + return; + } + current_area.last_address = subaddress; } - current_area.last_address = subaddress; - } - gpu_dirty_managers[core].Collect(address, size); + gpu_dirty_managers[core].Collect(address, size); + }); } struct GPUDirtyState { - VAddr last_address; + PAddr last_address; }; - void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { - system.GPU().InvalidateRegion(GetInteger(dest_addr), size); - } - - void FlushRegion(Common::ProcessAddress dest_addr, size_t size) { - system.GPU().FlushRegion(GetInteger(dest_addr), size); + void InvalidateGPUMemory(u8* p, size_t size) { + constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; + const size_t core = std::min(system.GetCurrentHostThreadID(), + sys_core); // any other calls threads go to syscore. + if (!gpu_device_memory) [[unlikely]] { + gpu_device_memory = &system.Host1x().MemoryManager(); + } + // Guard on sys_core; + if (core == sys_core) [[unlikely]] { + sys_core_guard.lock(); + } + 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); }); } Core::System& system; + Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{}; Common::PageTable* current_page_table = nullptr; std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_read_areas{}; std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; + std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{}; std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; std::mutex sys_core_guard; @@ -1059,14 +1077,6 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug) impl->MarkRegionDebug(GetInteger(vaddr), size, debug); } -void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { - impl->InvalidateRegion(dest_addr, size); -} - -void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) { - impl->FlushRegion(dest_addr, size); -} - bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { [[maybe_unused]] bool mapped = true; [[maybe_unused]] bool rasterizer = false; @@ -1078,10 +1088,10 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { GetInteger(vaddr)); mapped = false; }, - [&] { - impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); - rasterizer = true; - }); + [&] { rasterizer = true; }); + if (rasterizer) { + impl->InvalidateGPUMemory(ptr, size); + } #ifdef __linux__ if (!rasterizer && mapped) { diff --git a/src/core/memory.h b/src/core/memory.h index dddfaf4a4..f7e6b297f 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -12,6 +12,7 @@ #include "common/scratch_buffer.h" #include "common/typed_address.h" +#include "core/guest_memory.h" #include "core/hle/result.h" namespace Common { @@ -486,10 +487,10 @@ public: void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); - void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); + bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); + bool InvalidateSeparateHeap(void* fault_address); - void FlushRegion(Common::ProcessAddress dest_addr, size_t size); private: Core::System& system; @@ -498,209 +499,9 @@ private: std::unique_ptr<Impl> impl; }; -enum GuestMemoryFlags : u32 { - Read = 1 << 0, - Write = 1 << 1, - Safe = 1 << 2, - Cached = 1 << 3, - - SafeRead = Read | Safe, - SafeWrite = Write | Safe, - SafeReadWrite = SafeRead | SafeWrite, - SafeReadCachedWrite = SafeReadWrite | Cached, - - UnsafeRead = Read, - UnsafeWrite = Write, - UnsafeReadWrite = UnsafeRead | UnsafeWrite, - UnsafeReadCachedWrite = UnsafeReadWrite | Cached, -}; - -namespace { -template <typename M, typename T, GuestMemoryFlags FLAGS> -class GuestMemory { - using iterator = T*; - using const_iterator = const T*; - using value_type = T; - using element_type = T; - using iterator_category = std::contiguous_iterator_tag; - -public: - GuestMemory() = delete; - explicit GuestMemory(M& memory, u64 addr, std::size_t size, - Common::ScratchBuffer<T>* backup = nullptr) - : m_memory{memory}, m_addr{addr}, m_size{size} { - static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); - if constexpr (FLAGS & GuestMemoryFlags::Read) { - Read(addr, size, backup); - } - } - - ~GuestMemory() = default; - - T* data() noexcept { - return m_data_span.data(); - } - - const T* data() const noexcept { - return m_data_span.data(); - } - - size_t size() const noexcept { - return m_size; - } - - size_t size_bytes() const noexcept { - return this->size() * sizeof(T); - } - - [[nodiscard]] T* begin() noexcept { - return this->data(); - } - - [[nodiscard]] const T* begin() const noexcept { - return this->data(); - } - - [[nodiscard]] T* end() noexcept { - return this->data() + this->size(); - } - - [[nodiscard]] const T* end() const noexcept { - return this->data() + this->size(); - } - - T& operator[](size_t index) noexcept { - return m_data_span[index]; - } - - const T& operator[](size_t index) const noexcept { - return m_data_span[index]; - } - - void SetAddressAndSize(u64 addr, std::size_t size) noexcept { - m_addr = addr; - m_size = size; - m_addr_changed = true; - } - - std::span<T> Read(u64 addr, std::size_t size, - Common::ScratchBuffer<T>* backup = nullptr) noexcept { - m_addr = addr; - m_size = size; - if (m_size == 0) { - m_is_data_copy = true; - return {}; - } - - if (this->TrySetSpan()) { - if constexpr (FLAGS & GuestMemoryFlags::Safe) { - m_memory.FlushRegion(m_addr, this->size_bytes()); - } - } else { - if (backup) { - backup->resize_destructive(this->size()); - m_data_span = *backup; - } else { - m_data_copy.resize(this->size()); - m_data_span = std::span(m_data_copy); - } - m_is_data_copy = true; - m_span_valid = true; - if constexpr (FLAGS & GuestMemoryFlags::Safe) { - m_memory.ReadBlock(m_addr, this->data(), this->size_bytes()); - } else { - m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes()); - } - } - return m_data_span; - } - - void Write(std::span<T> write_data) noexcept { - if constexpr (FLAGS & GuestMemoryFlags::Cached) { - m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes()); - } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { - m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes()); - } else { - m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes()); - } - } - - bool TrySetSpan() noexcept { - if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) { - m_data_span = {reinterpret_cast<T*>(ptr), this->size()}; - m_span_valid = true; - return true; - } - return false; - } - -protected: - bool IsDataCopy() const noexcept { - return m_is_data_copy; - } - - bool AddressChanged() const noexcept { - return m_addr_changed; - } - - M& m_memory; - u64 m_addr{}; - size_t m_size{}; - std::span<T> m_data_span{}; - std::vector<T> m_data_copy{}; - bool m_span_valid{false}; - bool m_is_data_copy{false}; - bool m_addr_changed{false}; -}; - -template <typename M, typename T, GuestMemoryFlags FLAGS> -class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { -public: - GuestMemoryScoped() = delete; - explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size, - Common::ScratchBuffer<T>* backup = nullptr) - : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) { - if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { - if (!this->TrySetSpan()) { - if (backup) { - this->m_data_span = *backup; - this->m_span_valid = true; - this->m_is_data_copy = true; - } - } - } - } - - ~GuestMemoryScoped() { - if constexpr (FLAGS & GuestMemoryFlags::Write) { - if (this->size() == 0) [[unlikely]] { - return; - } - - if (this->AddressChanged() || this->IsDataCopy()) { - ASSERT(this->m_span_valid); - if constexpr (FLAGS & GuestMemoryFlags::Cached) { - this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes()); - } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { - this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes()); - } else { - this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); - } - } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) || - (FLAGS & GuestMemoryFlags::Cached)) { - this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); - } - } - } -}; -} // namespace - template <typename T, GuestMemoryFlags FLAGS> -using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>; +using CpuGuestMemory = GuestMemory<Core::Memory::Memory, T, FLAGS>; template <typename T, GuestMemoryFlags FLAGS> -using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>; -template <typename T, GuestMemoryFlags FLAGS> -using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>; -template <typename T, GuestMemoryFlags FLAGS> -using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>; +using CpuGuestMemoryScoped = GuestMemoryScoped<Core::Memory::Memory, T, FLAGS>; + } // namespace Core::Memory diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 7bc5b5ae5..96fa7fa3a 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -9,12 +9,12 @@ #include "core/core_timing.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/hid_server.h" -#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/sm/sm.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" +#include "hid_core/resource_manager.h" +#include "hid_core/resources/npad/npad.h" namespace Core::Memory { namespace { diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index dc3883528..1a0138697 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -68,8 +68,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta auto out = json{ {"title_id", fmt::format("{:016X}", title_id)}, {"result_raw", fmt::format("{:08X}", result.raw)}, - {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))}, - {"result_description", fmt::format("{:08X}", result.description.Value())}, + {"result_module", fmt::format("{:08X}", static_cast<u32>(result.GetModule()))}, + {"result_description", fmt::format("{:08X}", result.GetDescription())}, {"timestamp", timestamp}, }; |