summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt388
-rw-r--r--src/core/arm/nce/patcher.cpp83
-rw-r--r--src/core/arm/nce/patcher.h27
-rw-r--r--src/core/core.cpp118
-rw-r--r--src/core/core.h15
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/cpu_manager.h2
-rw-r--r--src/core/crypto/aes_util.h2
-rw-r--r--src/core/crypto/encryption_layer.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp6
-rw-r--r--src/core/crypto/partition_data_manager.h2
-rw-r--r--src/core/debugger/debugger.cpp39
-rw-r--r--src/core/debugger/gdbstub.cpp110
-rw-r--r--src/core/debugger/gdbstub.h15
-rw-r--r--src/core/device_memory.h16
-rw-r--r--src/core/device_memory_manager.h211
-rw-r--r--src/core/device_memory_manager.inc581
-rw-r--r--src/core/file_sys/bis_factory.cpp5
-rw-r--r--src/core/file_sys/bis_factory.h2
-rw-r--r--src/core/file_sys/card_image.cpp4
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp2
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/control_metadata.cpp2
-rw-r--r--src/core/file_sys/control_metadata.h2
-rw-r--r--src/core/file_sys/directory.h39
-rw-r--r--src/core/file_sys/errors.h26
-rw-r--r--src/core/file_sys/fs_directory.h33
-rw-r--r--src/core/file_sys/fs_file.h65
-rw-r--r--src/core/file_sys/fs_filesystem.h39
-rw-r--r--src/core/file_sys/fs_memory_management.h40
-rw-r--r--src/core/file_sys/fs_operate_range.h22
-rw-r--r--src/core/file_sys/fs_path.h566
-rw-r--r--src/core/file_sys/fs_path_utility.h1239
-rw-r--r--src/core/file_sys/fs_string_util.h226
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp4
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h2
-rw-r--r--src/core/file_sys/fssystem/fs_i_storage.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp2
-rw-r--r--src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_bucket_tree.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_compressed_storage.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp2
-rw-r--r--src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_indirect_storage.h4
-rw-r--r--src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp4
-rw-r--r--src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h2
-rw-r--r--src/core/file_sys/fssystem/fssystem_nca_reader.cpp2
-rw-r--r--src/core/file_sys/ips_layer.cpp2
-rw-r--r--src/core/file_sys/ips_layer.h2
-rw-r--r--src/core/file_sys/kernel_executable.cpp2
-rw-r--r--src/core/file_sys/kernel_executable.h2
-rw-r--r--src/core/file_sys/mode.h23
-rw-r--r--src/core/file_sys/nca_metadata.cpp2
-rw-r--r--src/core/file_sys/nca_metadata.h2
-rw-r--r--src/core/file_sys/partition_filesystem.cpp2
-rw-r--r--src/core/file_sys/partition_filesystem.h2
-rw-r--r--src/core/file_sys/patch_manager.cpp51
-rw-r--r--src/core/file_sys/patch_manager.h19
-rw-r--r--src/core/file_sys/program_metadata.cpp2
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/romfs.cpp10
-rw-r--r--src/core/file_sys/romfs.h2
-rw-r--r--src/core/file_sys/romfs_factory.h2
-rw-r--r--src/core/file_sys/savedata_factory.cpp28
-rw-r--r--src/core/file_sys/savedata_factory.h13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp2
-rw-r--r--src/core/file_sys/sdmc_factory.h2
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp2
-rw-r--r--src/core/file_sys/system_archive/mii_model.h2
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp2
-rw-r--r--src/core/file_sys/system_archive/ng_word.h2
-rw-r--r--src/core/file_sys/system_archive/shared_font.cpp2
-rw-r--r--src/core/file_sys/system_archive/shared_font.h2
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp12
-rw-r--r--src/core/file_sys/system_archive/system_archive.h2
-rw-r--r--src/core/file_sys/system_archive/system_version.cpp2
-rw-r--r--src/core/file_sys/system_archive/system_version.h2
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp3
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.h2
-rw-r--r--src/core/file_sys/vfs.cpp552
-rw-r--r--src/core/file_sys/vfs.h327
-rw-r--r--src/core/file_sys/vfs/vfs.cpp551
-rw-r--r--src/core/file_sys/vfs/vfs.h326
-rw-r--r--src/core/file_sys/vfs/vfs_cached.cpp63
-rw-r--r--src/core/file_sys/vfs/vfs_cached.h31
-rw-r--r--src/core/file_sys/vfs/vfs_concat.cpp192
-rw-r--r--src/core/file_sys/vfs/vfs_concat.h57
-rw-r--r--src/core/file_sys/vfs/vfs_layered.cpp132
-rw-r--r--src/core/file_sys/vfs/vfs_layered.h46
-rw-r--r--src/core/file_sys/vfs/vfs_offset.cpp98
-rw-r--r--src/core/file_sys/vfs/vfs_offset.h50
-rw-r--r--src/core/file_sys/vfs/vfs_real.cpp536
-rw-r--r--src/core/file_sys/vfs/vfs_real.h148
-rw-r--r--src/core/file_sys/vfs/vfs_static.h80
-rw-r--r--src/core/file_sys/vfs/vfs_types.h (renamed from src/core/file_sys/vfs_types.h)0
-rw-r--r--src/core/file_sys/vfs/vfs_vector.cpp133
-rw-r--r--src/core/file_sys/vfs/vfs_vector.h131
-rw-r--r--src/core/file_sys/vfs_cached.cpp63
-rw-r--r--src/core/file_sys/vfs_cached.h31
-rw-r--r--src/core/file_sys/vfs_concat.cpp192
-rw-r--r--src/core/file_sys/vfs_concat.h57
-rw-r--r--src/core/file_sys/vfs_layered.cpp132
-rw-r--r--src/core/file_sys/vfs_layered.h46
-rw-r--r--src/core/file_sys/vfs_offset.cpp98
-rw-r--r--src/core/file_sys/vfs_offset.h50
-rw-r--r--src/core/file_sys/vfs_real.cpp528
-rw-r--r--src/core/file_sys/vfs_real.h145
-rw-r--r--src/core/file_sys/vfs_static.h80
-rw-r--r--src/core/file_sys/vfs_vector.cpp133
-rw-r--r--src/core/file_sys/vfs_vector.h131
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/file_sys/xts_archive.h2
-rw-r--r--src/core/frontend/applets/controller.cpp8
-rw-r--r--src/core/frontend/applets/error.cpp6
-rw-r--r--src/core/gpu_dirty_memory_manager.h14
-rw-r--r--src/core/guest_memory.h214
-rw-r--r--src/core/hid/emulated_console.cpp324
-rw-r--r--src/core/hid/emulated_console.h192
-rw-r--r--src/core/hid/emulated_controller.cpp1972
-rw-r--r--src/core/hid/emulated_controller.h619
-rw-r--r--src/core/hid/emulated_devices.cpp483
-rw-r--r--src/core/hid/emulated_devices.h212
-rw-r--r--src/core/hid/hid_core.cpp222
-rw-r--r--src/core/hid/hid_core.h89
-rw-r--r--src/core/hid/hid_types.h735
-rw-r--r--src/core/hid/input_converter.cpp436
-rw-r--r--src/core/hid/input_converter.h119
-rw-r--r--src/core/hid/input_interpreter.cpp64
-rw-r--r--src/core/hid/input_interpreter.h111
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hid/motion_input.cpp357
-rw-r--r--src/core/hid/motion_input.h119
-rw-r--r--src/core/hle/kernel/k_memory_block.h14
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp4
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp59
-rw-r--r--src/core/hle/kernel/k_page_table_base.h1
-rw-r--r--src/core/hle/kernel/k_process.cpp24
-rw-r--r--src/core/hle/kernel/k_process.h4
-rw-r--r--src/core/hle/kernel/k_thread.cpp11
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp8
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc_types.h4
-rw-r--r--src/core/hle/result.h38
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp36
-rw-r--r--src/core/hle/service/acc/profile_manager.h2
-rw-r--r--src/core/hle/service/am/am.cpp11
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp8
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.h2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp8
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.h2
-rw-r--r--src/core/hle/service/audio/audctl.cpp84
-rw-r--r--src/core/hle/service/audio/audctl.h20
-rw-r--r--src/core/hle/service/audio/audin_u.cpp36
-rw-r--r--src/core/hle/service/audio/audout_u.cpp26
-rw-r--r--src/core/hle/service/audio/audren_u.cpp19
-rw-r--r--src/core/hle/service/bcat/backend/backend.h2
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp2
-rw-r--r--src/core/hle/service/btm/btm.cpp9
-rw-r--r--src/core/hle/service/caps/caps_a.cpp13
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp57
-rw-r--r--src/core/hle/service/caps/caps_manager.h4
-rw-r--r--src/core/hle/service/caps/caps_result.h2
-rw-r--r--src/core/hle/service/cmif_serialization.h336
-rw-r--r--src/core/hle/service/cmif_types.h294
-rw-r--r--src/core/hle/service/fatal/fatal.cpp4
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp303
-rw-r--r--src/core/hle/service/filesystem/filesystem.h75
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_directory.cpp84
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_directory.h30
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_file.cpp127
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_file.h25
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp262
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_filesystem.h38
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_storage.cpp62
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_storage.h23
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_ldr.cpp22
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_ldr.h (renamed from src/core/hle/service/filesystem/fsp_ldr.h)0
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_pr.cpp23
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_pr.h (renamed from src/core/hle/service/filesystem/fsp_pr.h)0
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.cpp727
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.h78
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_util.h22
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.cpp22
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.cpp23
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp1250
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h72
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.cpp37
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.h31
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.cpp99
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.h35
-rw-r--r--src/core/hle/service/friend/friend.cpp2
-rw-r--r--src/core/hle/service/glue/arp.cpp7
-rw-r--r--src/core/hle/service/glue/glue.cpp19
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.cpp82
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.h53
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.cpp23
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.h28
-rw-r--r--src/core/hle/service/glue/time/manager.cpp277
-rw-r--r--src/core/hle/service/glue/time/manager.h42
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.cpp13
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.h18
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.cpp123
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.h41
-rw-r--r--src/core/hle/service/glue/time/static.cpp448
-rw-r--r--src/core/hle/service/glue/time/static.h110
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp377
-rw-r--r--src/core/hle/service/glue/time/time_zone.h95
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.cpp221
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.h32
-rw-r--r--src/core/hle/service/glue/time/worker.cpp338
-rw-r--r--src/core/hle/service/glue/time/worker.h64
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.cpp329
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.h122
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp44
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h30
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h53
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.cpp63
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.h34
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp58
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h36
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.h27
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp364
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h87
-rw-r--r--src/core/hle/service/hid/controllers/home_button.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/home_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp55
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h28
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp63
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h34
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1346
-rw-r--r--src/core/hle/service/hid/controllers/npad.h197
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp226
-rw-r--r--src/core/hle/service/hid/controllers/palma.h162
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.cpp66
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.h65
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.cpp54
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.h44
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp420
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.h111
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp132
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h43
-rw-r--r--src/core/hle/service/hid/controllers/types/debug_pad_types.h31
-rw-r--r--src/core/hle/service/hid/controllers/types/gesture_types.h77
-rw-r--r--src/core/hle/service/hid/controllers/types/keyboard_types.h20
-rw-r--r--src/core/hle/service/hid/controllers/types/mouse_types.h8
-rw-r--r--src/core/hle/service/hid/controllers/types/npad_types.h254
-rw-r--r--src/core/hle/service/hid/controllers/types/shared_memory_format.h240
-rw-r--r--src/core/hle/service/hid/controllers/types/touch_types.h90
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.h27
-rw-r--r--src/core/hle/service/hid/errors.h39
-rw-r--r--src/core/hle/service/hid/hid.cpp24
-rw-r--r--src/core/hle/service/hid/hid_debug_server.cpp2
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.cpp99
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.h54
-rw-r--r--src/core/hle/service/hid/hid_server.cpp587
-rw-r--r--src/core/hle/service/hid/hid_server.h1
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp333
-rw-r--r--src/core/hle/service/hid/hid_system_server.h24
-rw-r--r--src/core/hle/service/hid/hid_util.h146
-rw-r--r--src/core/hle/service/hid/hidbus.cpp16
-rw-r--r--src/core/hle/service/hid/hidbus.h4
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp73
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h183
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp292
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h253
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp50
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h37
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp50
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h37
-rw-r--r--src/core/hle/service/hid/irs.cpp22
-rw-r--r--src/core/hle/service/hid/irs.h6
-rw-r--r--src/core/hle/service/hid/irs_ring_lifo.h47
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp267
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h115
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp155
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.h77
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.cpp27
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.h47
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp149
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h91
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.cpp26
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.cpp67
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.h33
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.h53
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp358
-rw-r--r--src/core/hle/service/hid/resource_manager.h149
-rw-r--r--src/core/hle/service/hid/ring_lifo.h53
-rw-r--r--src/core/hle/service/hle_ipc.cpp97
-rw-r--r--src/core/hle/service/hle_ipc.h25
-rw-r--r--src/core/hle/service/jit/jit.cpp291
-rw-r--r--src/core/hle/service/kernel_helpers.cpp3
-rw-r--r--src/core/hle/service/mii/mii.cpp563
-rw-r--r--src/core/hle/service/mii/mii.h15
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp10
-rw-r--r--src/core/hle/service/nfc/common/device.cpp59
-rw-r--r--src/core/hle/service/nfc/common/device.h9
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp41
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h7
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp9
-rw-r--r--src/core/hle/service/nfp/nfp_interface.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp_types.h14
-rw-r--r--src/core/hle/service/ns/language.cpp4
-rw-r--r--src/core/hle/service/ns/language.h5
-rw-r--r--src/core/hle/service/ns/ns.cpp4
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp115
-rw-r--r--src/core/hle/service/nvdrv/core/container.h32
-rw-r--r--src/core/hle/service/nvdrv/core/heap_mapper.cpp175
-rw-r--r--src/core/hle/service/nvdrv/core/heap_mapper.h49
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp133
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.h27
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h17
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h7
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp27
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h6
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp37
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h1
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp25
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.h5
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp21
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h4
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp2
-rw-r--r--src/core/hle/service/pcv/pcv.cpp10
-rw-r--r--src/core/hle/service/pm/pm.cpp85
-rw-r--r--src/core/hle/service/psc/psc.cpp17
-rw-r--r--src/core/hle/service/psc/time/alarms.cpp209
-rw-r--r--src/core/hle/service/psc/time/alarms.h139
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.cpp83
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.h79
-rw-r--r--src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h21
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp20
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h23
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp42
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h30
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp101
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h54
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp63
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/steady_clock_core.h81
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.cpp75
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp43
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h41
-rw-r--r--src/core/hle/service/psc/time/common.cpp16
-rw-r--r--src/core/hle/service/psc/time/common.h168
-rw-r--r--src/core/hle/service/psc/time/errors.h24
-rw-r--r--src/core/hle/service/psc/time/manager.h56
-rw-r--r--src/core/hle/service/psc/time/power_state_request_manager.cpp50
-rw-r--r--src/core/hle/service/psc/time/power_state_request_manager.h42
-rw-r--r--src/core/hle/service/psc/time/power_state_service.cpp49
-rw-r--r--src/core/hle/service/psc/time/power_state_service.h32
-rw-r--r--src/core/hle/service/psc/time/service_manager.cpp494
-rw-r--r--src/core/hle/service/psc/time/service_manager.h101
-rw-r--r--src/core/hle/service/psc/time/shared_memory.cpp84
-rw-r--r--src/core/hle/service/psc/time/shared_memory.h70
-rw-r--r--src/core/hle/service/psc/time/static.cpp500
-rw-r--r--src/core/hle/service/psc/time/static.h95
-rw-r--r--src/core/hle/service/psc/time/steady_clock.cpp164
-rw-r--r--src/core/hle/service/psc/time/steady_clock.h49
-rw-r--r--src/core/hle/service/psc/time/system_clock.cpp127
-rw-r--r--src/core/hle/service/psc/time/system_clock.h46
-rw-r--r--src/core/hle/service/psc/time/time_zone.cpp280
-rw-r--r--src/core/hle/service/psc/time/time_zone.h62
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.cpp289
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.h69
-rw-r--r--src/core/hle/service/ro/ro.cpp191
-rw-r--r--src/core/hle/service/server_manager.cpp9
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/service.h16
-rw-r--r--src/core/hle/service/set/appln_settings.cpp12
-rw-r--r--src/core/hle/service/set/appln_settings.h36
-rw-r--r--src/core/hle/service/set/device_settings.cpp12
-rw-r--r--src/core/hle/service/set/device_settings.h54
-rw-r--r--src/core/hle/service/set/factory_settings_server.cpp63
-rw-r--r--src/core/hle/service/set/factory_settings_server.h20
-rw-r--r--src/core/hle/service/set/firmware_debug_settings_server.cpp29
-rw-r--r--src/core/hle/service/set/firmware_debug_settings_server.h20
-rw-r--r--src/core/hle/service/set/private_settings.cpp12
-rw-r--r--src/core/hle/service/set/private_settings.h6
-rw-r--r--src/core/hle/service/set/set.cpp166
-rw-r--r--src/core/hle/service/set/set.h95
-rw-r--r--src/core/hle/service/set/set_cal.cpp62
-rw-r--r--src/core/hle/service/set/set_cal.h20
-rw-r--r--src/core/hle/service/set/set_fd.cpp28
-rw-r--r--src/core/hle/service/set/set_fd.h20
-rw-r--r--src/core/hle/service/set/set_sys.cpp1272
-rw-r--r--src/core/hle/service/set/set_sys.h153
-rw-r--r--src/core/hle/service/set/setting_formats/appln_settings.cpp16
-rw-r--r--src/core/hle/service/set/setting_formats/appln_settings.h35
-rw-r--r--src/core/hle/service/set/setting_formats/device_settings.cpp12
-rw-r--r--src/core/hle/service/set/setting_formats/device_settings.h54
-rw-r--r--src/core/hle/service/set/setting_formats/private_settings.cpp12
-rw-r--r--src/core/hle/service/set/setting_formats/private_settings.h38
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.cpp70
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.h391
-rw-r--r--src/core/hle/service/set/settings.cpp19
-rw-r--r--src/core/hle/service/set/settings_server.cpp166
-rw-r--r--src/core/hle/service/set/settings_server.h36
-rw-r--r--src/core/hle/service/set/settings_types.h475
-rw-r--r--src/core/hle/service/set/system_settings.cpp51
-rw-r--r--src/core/hle/service/set/system_settings.h699
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp1673
-rw-r--r--src/core/hle/service/set/system_settings_server.h178
-rw-r--r--src/core/hle/service/sm/sm.h15
-rw-r--r--src/core/hle/service/time/clock_types.h129
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h15
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_core.h16
-rw-r--r--src/core/hle/service/time/errors.h21
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h26
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h27
-rw-r--r--src/core/hle/service/time/standard_local_system_clock_core.h16
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h45
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp24
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.h41
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp81
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h63
-rw-r--r--src/core/hle/service/time/steady_clock_core.h55
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp54
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h43
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp71
-rw-r--r--src/core/hle/service/time/system_clock_core.h72
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp22
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.h28
-rw-r--r--src/core/hle/service/time/time.cpp412
-rw-r--r--src/core/hle/service/time/time.h51
-rw-r--r--src/core/hle/service/time/time_interface.cpp41
-rw-r--r--src/core/hle/service/time/time_interface.h20
-rw-r--r--src/core/hle/service/time/time_manager.cpp293
-rw-r--r--src/core/hle/service/time/time_manager.h74
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp69
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h89
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp151
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h49
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1182
-rw-r--r--src/core/hle/service/time/time_zone_manager.h61
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp217
-rw-r--r--src/core/hle/service/time/time_zone_service.h38
-rw-r--r--src/core/hle/service/time/time_zone_types.h86
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp24
-rw-r--r--src/core/hle/service/vi/display/vi_display.h15
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h9
-rw-r--r--src/core/hle/service/vi/vi.cpp74
-rw-r--r--src/core/hle/service/vi/vi.h2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp89
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/core/loader/nca.cpp6
-rw-r--r--src/core/loader/nro.cpp12
-rw-r--r--src/core/loader/nso.cpp41
-rw-r--r--src/core/loader/nso.h3
-rw-r--r--src/core/loader/nsp.cpp11
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/core/memory.cpp114
-rw-r--r--src/core/memory.h211
-rw-r--r--src/core/memory/cheat_engine.cpp4
-rw-r--r--src/core/reporter.cpp4
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(), [&param](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(), [&param](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..7231ff78e
--- /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 = Service::PSC::Time::LocationName{"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},
};