summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--externals/nx_tzdb/CMakeLists.txt2
-rw-r--r--externals/nx_tzdb/NxTzdbCreateHeader.cmake4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt54
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt33
-rw-r--r--src/android/app/src/main/jni/game_metadata.cpp12
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp7
-rw-r--r--src/android/app/src/main/jni/id_cache.h1
-rw-r--r--src/android/app/src/main/jni/native.cpp49
-rw-r--r--src/android/app/src/main/jni/native.h7
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml4
-rw-r--r--src/common/overflow.h18
-rw-r--r--src/core/CMakeLists.txt161
-rw-r--r--src/core/core.cpp12
-rw-r--r--src/core/core.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/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.cpp6
-rw-r--r--src/core/file_sys/patch_manager.h2
-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.cpp2
-rw-r--r--src/core/file_sys/savedata_factory.h2
-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.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.cpp2
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.h2
-rw-r--r--src/core/file_sys/vfs/vfs.cpp (renamed from src/core/file_sys/vfs.cpp)29
-rw-r--r--src/core/file_sys/vfs/vfs.h (renamed from src/core/file_sys/vfs.h)13
-rw-r--r--src/core/file_sys/vfs/vfs_cached.cpp (renamed from src/core/file_sys/vfs_cached.cpp)4
-rw-r--r--src/core/file_sys/vfs/vfs_cached.h (renamed from src/core/file_sys/vfs_cached.h)2
-rw-r--r--src/core/file_sys/vfs/vfs_concat.cpp (renamed from src/core/file_sys/vfs_concat.cpp)4
-rw-r--r--src/core/file_sys/vfs/vfs_concat.h (renamed from src/core/file_sys/vfs_concat.h)2
-rw-r--r--src/core/file_sys/vfs/vfs_layered.cpp (renamed from src/core/file_sys/vfs_layered.cpp)2
-rw-r--r--src/core/file_sys/vfs/vfs_layered.h (renamed from src/core/file_sys/vfs_layered.h)2
-rw-r--r--src/core/file_sys/vfs/vfs_offset.cpp (renamed from src/core/file_sys/vfs_offset.cpp)2
-rw-r--r--src/core/file_sys/vfs/vfs_offset.h (renamed from src/core/file_sys/vfs_offset.h)2
-rw-r--r--src/core/file_sys/vfs/vfs_real.cpp (renamed from src/core/file_sys/vfs_real.cpp)64
-rw-r--r--src/core/file_sys/vfs/vfs_real.h (renamed from src/core/file_sys/vfs_real.h)27
-rw-r--r--src/core/file_sys/vfs/vfs_static.h (renamed from src/core/file_sys/vfs_static.h)2
-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.cpp (renamed from src/core/file_sys/vfs_vector.cpp)2
-rw-r--r--src/core/file_sys/vfs/vfs_vector.h (renamed from src/core/file_sys/vfs_vector.h)2
-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/error.cpp6
-rw-r--r--src/core/hle/result.h38
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp4
-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/audren_u.cpp3
-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/fatal/fatal.cpp4
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp78
-rw-r--r--src/core/hle/service/filesystem/filesystem.h18
-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.cpp (renamed from src/core/hle/service/filesystem/fsp_ldr.cpp)2
-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.cpp (renamed from src/core/hle/service/filesystem/fsp_pr.cpp)2
-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.cpp (renamed from src/core/hle/service/filesystem/fsp_srv.cpp)546
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.h (renamed from src/core/hle/service/filesystem/fsp_srv.h)0
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_util.h22
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.h2
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.cpp6
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.h2
-rw-r--r--src/core/hle/service/glue/time/manager.cpp2
-rw-r--r--src/core/hle/service/glue/time/manager.h2
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/hid/hid_server.cpp4
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp54
-rw-r--r--src/core/hle/service/hid/hid_system_server.h9
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp2
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.cpp1
-rw-r--r--src/core/hle/service/set/settings_types.h9
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp179
-rw-r--r--src/core/hle/service/set/system_settings_server.h36
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/reporter.cpp4
-rw-r--r--src/frontend_common/content_manager.h11
-rw-r--r--src/hid_core/frontend/emulated_controller.cpp18
-rw-r--r--src/hid_core/frontend/emulated_controller.h3
-rw-r--r--src/hid_core/hid_types.h9
-rw-r--r--src/hid_core/resource_manager.cpp12
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp2
-rw-r--r--src/hid_core/resources/hid_firmware_settings.cpp12
-rw-r--r--src/hid_core/resources/hid_firmware_settings.h3
-rw-r--r--src/hid_core/resources/npad/npad.cpp5
-rw-r--r--src/hid_core/resources/npad/npad.h7
-rw-r--r--src/hid_core/resources/npad/npad_vibration.cpp36
-rw-r--r--src/hid_core/resources/npad/npad_vibration.h7
-rw-r--r--src/hid_core/resources/vibration/vibration_base.cpp4
-rw-r--r--src/hid_core/resources/vibration/vibration_base.h1
-rw-r--r--src/yuzu/applets/qt_error.cpp12
-rw-r--r--src/yuzu/configuration/configure_per_game.h2
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.h2
-rw-r--r--src/yuzu/game_list_worker.cpp4
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
173 files changed, 3817 insertions, 1104 deletions
diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt
index 0fad24642..13723f175 100644
--- a/externals/nx_tzdb/CMakeLists.txt
+++ b/externals/nx_tzdb/CMakeLists.txt
@@ -32,7 +32,7 @@ set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
32 32
33set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") 33set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
34 34
35if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ARCHIVE}) 35if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ROMFS_DIR})
36 set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") 36 set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")
37 37
38 message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...") 38 message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...")
diff --git a/externals/nx_tzdb/NxTzdbCreateHeader.cmake b/externals/nx_tzdb/NxTzdbCreateHeader.cmake
index 8c29e1167..95606d862 100644
--- a/externals/nx_tzdb/NxTzdbCreateHeader.cmake
+++ b/externals/nx_tzdb/NxTzdbCreateHeader.cmake
@@ -11,6 +11,10 @@ execute_process(
11 WORKING_DIRECTORY ${ZONE_PATH} 11 WORKING_DIRECTORY ${ZONE_PATH}
12 OUTPUT_VARIABLE FILE_LIST) 12 OUTPUT_VARIABLE FILE_LIST)
13 13
14if (NOT FILE_LIST)
15 message(FATAL_ERROR "No timezone files found in directory ${ZONE_PATH}, did the download fail?")
16endif()
17
14set(DIRECTORY_NAME ${HEADER_NAME}) 18set(DIRECTORY_NAME ${HEADER_NAME})
15 19
16set(FILE_DATA "") 20set(FILE_DATA "")
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 55abba093..53137b2e2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -261,7 +261,7 @@ object NativeLibrary {
261 /** 261 /**
262 * Begins emulation. 262 * Begins emulation.
263 */ 263 */
264 external fun run(path: String?) 264 external fun run(path: String?, programIndex: Int = 0)
265 265
266 // Surface Handling 266 // Surface Handling
267 external fun surfaceChanged(surf: Surface?) 267 external fun surfaceChanged(surf: Surface?)
@@ -489,6 +489,12 @@ object NativeLibrary {
489 sEmulationActivity.get()!!.onEmulationStopped(status) 489 sEmulationActivity.get()!!.onEmulationStopped(status)
490 } 490 }
491 491
492 @Keep
493 @JvmStatic
494 fun onProgramChanged(programIndex: Int) {
495 sEmulationActivity.get()!!.onProgramChanged(programIndex)
496 }
497
492 /** 498 /**
493 * Logs the Yuzu version, Android version and, CPU. 499 * Logs the Yuzu version, Android version and, CPU.
494 */ 500 */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 26cddecf4..564aaf305 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -76,7 +76,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
76 76
77 override fun onDestroy() { 77 override fun onDestroy() {
78 stopForegroundService(this) 78 stopForegroundService(this)
79 emulationViewModel.clear()
80 super.onDestroy() 79 super.onDestroy()
81 } 80 }
82 81
@@ -446,9 +445,14 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
446 } 445 }
447 446
448 fun onEmulationStopped(status: Int) { 447 fun onEmulationStopped(status: Int) {
449 if (status == 0) { 448 if (status == 0 && emulationViewModel.programChanged.value == -1) {
450 finish() 449 finish()
451 } 450 }
451 emulationViewModel.setEmulationStopped(true)
452 }
453
454 fun onProgramChanged(programIndex: Int) {
455 emulationViewModel.setProgramChanged(programIndex)
452 } 456 }
453 457
454 private fun startMotionSensorListener() { 458 private fun startMotionSensorListener() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 22da1d0e5..1f591ced1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -301,6 +301,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
301 301
302 R.id.menu_exit -> { 302 R.id.menu_exit -> {
303 emulationState.stop() 303 emulationState.stop()
304 NativeConfig.reloadGlobalConfig()
304 emulationViewModel.setIsEmulationStopping(true) 305 emulationViewModel.setIsEmulationStopping(true)
305 binding.drawerLayout.close() 306 binding.drawerLayout.close()
306 binding.inGameMenu.requestFocus() 307 binding.inGameMenu.requestFocus()
@@ -423,10 +424,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
423 } 424 }
424 } 425 }
425 } 426 }
427 launch {
428 repeatOnLifecycle(Lifecycle.State.CREATED) {
429 emulationViewModel.programChanged.collect {
430 if (it != 0) {
431 emulationViewModel.setEmulationStarted(false)
432 binding.drawerLayout.close()
433 binding.drawerLayout
434 .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
435 ViewUtils.hideView(binding.surfaceInputOverlay)
436 ViewUtils.showView(binding.loadingIndicator)
437 }
438 }
439 }
440 }
441 launch {
442 repeatOnLifecycle(Lifecycle.State.CREATED) {
443 emulationViewModel.emulationStopped.collect {
444 if (it && emulationViewModel.programChanged.value != -1) {
445 if (perfStatsUpdater != null) {
446 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
447 }
448 emulationState.changeProgram(emulationViewModel.programChanged.value)
449 emulationViewModel.setProgramChanged(-1)
450 emulationViewModel.setEmulationStopped(false)
451 }
452 }
453 }
454 }
426 } 455 }
427 } 456 }
428 457
429 private fun startEmulation() { 458 private fun startEmulation(programIndex: Int = 0) {
430 if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) { 459 if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
431 if (!DirectoryInitialization.areDirectoriesReady) { 460 if (!DirectoryInitialization.areDirectoriesReady) {
432 DirectoryInitialization.start() 461 DirectoryInitialization.start()
@@ -434,7 +463,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
434 463
435 updateScreenLayout() 464 updateScreenLayout()
436 465
437 emulationState.run(emulationActivity!!.isActivityRecreated) 466 emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
438 } 467 }
439 } 468 }
440 469
@@ -832,6 +861,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
832 ) { 861 ) {
833 private var state: State 862 private var state: State
834 private var surface: Surface? = null 863 private var surface: Surface? = null
864 lateinit var emulationThread: Thread
835 865
836 init { 866 init {
837 // Starting state is stopped. 867 // Starting state is stopped.
@@ -877,7 +907,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
877 } 907 }
878 908
879 @Synchronized 909 @Synchronized
880 fun run(isActivityRecreated: Boolean) { 910 fun run(isActivityRecreated: Boolean, programIndex: Int = 0) {
881 if (isActivityRecreated) { 911 if (isActivityRecreated) {
882 if (NativeLibrary.isRunning()) { 912 if (NativeLibrary.isRunning()) {
883 state = State.PAUSED 913 state = State.PAUSED
@@ -888,10 +918,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
888 918
889 // If the surface is set, run now. Otherwise, wait for it to get set. 919 // If the surface is set, run now. Otherwise, wait for it to get set.
890 if (surface != null) { 920 if (surface != null) {
891 runWithValidSurface() 921 runWithValidSurface(programIndex)
892 } 922 }
893 } 923 }
894 924
925 @Synchronized
926 fun changeProgram(programIndex: Int) {
927 emulationThread.join()
928 emulationThread = Thread({
929 Log.debug("[EmulationFragment] Starting emulation thread.")
930 NativeLibrary.run(gamePath, programIndex)
931 }, "NativeEmulation")
932 emulationThread.start()
933 }
934
895 // Surface callbacks 935 // Surface callbacks
896 @Synchronized 936 @Synchronized
897 fun newSurface(surface: Surface?) { 937 fun newSurface(surface: Surface?) {
@@ -931,7 +971,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
931 } 971 }
932 } 972 }
933 973
934 private fun runWithValidSurface() { 974 private fun runWithValidSurface(programIndex: Int = 0) {
935 NativeLibrary.surfaceChanged(surface) 975 NativeLibrary.surfaceChanged(surface)
936 if (!emulationCanStart.invoke()) { 976 if (!emulationCanStart.invoke()) {
937 return 977 return
@@ -939,9 +979,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
939 979
940 when (state) { 980 when (state) {
941 State.STOPPED -> { 981 State.STOPPED -> {
942 val emulationThread = Thread({ 982 emulationThread = Thread({
943 Log.debug("[EmulationFragment] Starting emulation thread.") 983 Log.debug("[EmulationFragment] Starting emulation thread.")
944 NativeLibrary.run(gamePath) 984 NativeLibrary.run(gamePath, programIndex)
945 }, "NativeEmulation") 985 }, "NativeEmulation")
946 emulationThread.start() 986 emulationThread.start()
947 } 987 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index aefae2938..1f3578b22 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -153,7 +153,13 @@ class HomeSettingsFragment : Fragment() {
153 cancellable = true 153 cancellable = true
154 ) { progressCallback, _ -> 154 ) { progressCallback, _ ->
155 val result = NativeLibrary.verifyInstalledContents(progressCallback) 155 val result = NativeLibrary.verifyInstalledContents(progressCallback)
156 return@newInstance if (result.isEmpty()) { 156 return@newInstance if (progressCallback.invoke(100, 100)) {
157 // Invoke the progress callback to check if the process was cancelled
158 MessageDialogFragment.newInstance(
159 titleId = R.string.verify_no_result,
160 descriptionId = R.string.verify_no_result_description
161 )
162 } else if (result.isEmpty()) {
157 MessageDialogFragment.newInstance( 163 MessageDialogFragment.newInstance(
158 titleId = R.string.verify_success, 164 titleId = R.string.verify_success,
159 descriptionId = R.string.operation_completed_successfully 165 descriptionId = R.string.operation_completed_successfully
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
index b66f47fe7..d024493cd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -15,6 +15,12 @@ class EmulationViewModel : ViewModel() {
15 val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping 15 val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping
16 private val _isEmulationStopping = MutableStateFlow(false) 16 private val _isEmulationStopping = MutableStateFlow(false)
17 17
18 private val _emulationStopped = MutableStateFlow(false)
19 val emulationStopped = _emulationStopped.asStateFlow()
20
21 private val _programChanged = MutableStateFlow(-1)
22 val programChanged = _programChanged.asStateFlow()
23
18 val shaderProgress: StateFlow<Int> get() = _shaderProgress 24 val shaderProgress: StateFlow<Int> get() = _shaderProgress
19 private val _shaderProgress = MutableStateFlow(0) 25 private val _shaderProgress = MutableStateFlow(0)
20 26
@@ -35,6 +41,17 @@ class EmulationViewModel : ViewModel() {
35 _isEmulationStopping.value = value 41 _isEmulationStopping.value = value
36 } 42 }
37 43
44 fun setEmulationStopped(value: Boolean) {
45 if (value) {
46 _emulationStarted.value = false
47 }
48 _emulationStopped.value = value
49 }
50
51 fun setProgramChanged(programIndex: Int) {
52 _programChanged.value = programIndex
53 }
54
38 fun setShaderProgress(progress: Int) { 55 fun setShaderProgress(progress: Int) {
39 _shaderProgress.value = progress 56 _shaderProgress.value = progress
40 } 57 }
@@ -56,20 +73,4 @@ class EmulationViewModel : ViewModel() {
56 fun setDrawerOpen(value: Boolean) { 73 fun setDrawerOpen(value: Boolean) {
57 _drawerOpen.value = value 74 _drawerOpen.value = value
58 } 75 }
59
60 fun clear() {
61 setEmulationStarted(false)
62 setIsEmulationStopping(false)
63 setShaderProgress(0)
64 setTotalShaders(0)
65 setShaderMessage("")
66 }
67
68 companion object {
69 const val KEY_EMULATION_STARTED = "EmulationStarted"
70 const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting"
71 const val KEY_SHADER_PROGRESS = "ShaderProgress"
72 const val KEY_TOTAL_SHADERS = "TotalShaders"
73 const val KEY_SHADER_MESSAGE = "ShaderMessage"
74 }
75} 76}
diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp
index 78f604c70..8f0da1413 100644
--- a/src/android/app/src/main/jni/game_metadata.cpp
+++ b/src/android/app/src/main/jni/game_metadata.cpp
@@ -1,12 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <core/core.h> 4#include "core/core.h"
5#include <core/file_sys/mode.h> 5#include "core/file_sys/fs_filesystem.h"
6#include <core/file_sys/patch_manager.h> 6#include "core/file_sys/patch_manager.h"
7#include <core/loader/nro.h>
8#include <jni.h>
9#include "core/loader/loader.h" 7#include "core/loader/loader.h"
8#include "core/loader/nro.h"
9#include "jni.h"
10#include "jni/android_common/android_common.h" 10#include "jni/android_common/android_common.h"
11#include "native.h" 11#include "native.h"
12 12
@@ -79,7 +79,7 @@ extern "C" {
79jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, 79jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
80 jstring jpath) { 80 jstring jpath) {
81 const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( 81 const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
82 GetJString(env, jpath), FileSys::Mode::Read); 82 GetJString(env, jpath), FileSys::OpenMode::Read);
83 if (!file) { 83 if (!file) {
84 return false; 84 return false;
85 } 85 }
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 96f2ad3d4..f30100bd8 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -19,6 +19,7 @@ static jmethodID s_exit_emulation_activity;
19static jmethodID s_disk_cache_load_progress; 19static jmethodID s_disk_cache_load_progress;
20static jmethodID s_on_emulation_started; 20static jmethodID s_on_emulation_started;
21static jmethodID s_on_emulation_stopped; 21static jmethodID s_on_emulation_stopped;
22static jmethodID s_on_program_changed;
22 23
23static jclass s_game_class; 24static jclass s_game_class;
24static jmethodID s_game_constructor; 25static jmethodID s_game_constructor;
@@ -123,6 +124,10 @@ jmethodID GetOnEmulationStopped() {
123 return s_on_emulation_stopped; 124 return s_on_emulation_stopped;
124} 125}
125 126
127jmethodID GetOnProgramChanged() {
128 return s_on_program_changed;
129}
130
126jclass GetGameClass() { 131jclass GetGameClass() {
127 return s_game_class; 132 return s_game_class;
128} 133}
@@ -306,6 +311,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
306 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); 311 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
307 s_on_emulation_stopped = 312 s_on_emulation_stopped =
308 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); 313 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
314 s_on_program_changed =
315 env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
309 316
310 const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); 317 const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
311 s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); 318 s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index a002e705d..00e48afc0 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -19,6 +19,7 @@ jmethodID GetExitEmulationActivity();
19jmethodID GetDiskCacheLoadProgress(); 19jmethodID GetDiskCacheLoadProgress();
20jmethodID GetOnEmulationStarted(); 20jmethodID GetOnEmulationStarted();
21jmethodID GetOnEmulationStopped(); 21jmethodID GetOnEmulationStopped();
22jmethodID GetOnProgramChanged();
22 23
23jclass GetGameClass(); 24jclass GetGameClass();
24jmethodID GetGameConstructor(); 25jmethodID GetGameConstructor();
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 247f2c2b3..958a77ac7 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -35,9 +35,10 @@
35#include "core/crypto/key_manager.h" 35#include "core/crypto/key_manager.h"
36#include "core/file_sys/card_image.h" 36#include "core/file_sys/card_image.h"
37#include "core/file_sys/content_archive.h" 37#include "core/file_sys/content_archive.h"
38#include "core/file_sys/fs_filesystem.h"
38#include "core/file_sys/submission_package.h" 39#include "core/file_sys/submission_package.h"
39#include "core/file_sys/vfs.h" 40#include "core/file_sys/vfs/vfs.h"
40#include "core/file_sys/vfs_real.h" 41#include "core/file_sys/vfs/vfs_real.h"
41#include "core/frontend/applets/cabinet.h" 42#include "core/frontend/applets/cabinet.h"
42#include "core/frontend/applets/controller.h" 43#include "core/frontend/applets/controller.h"
43#include "core/frontend/applets/error.h" 44#include "core/frontend/applets/error.h"
@@ -154,7 +155,7 @@ void EmulationSession::SurfaceChanged() {
154} 155}
155 156
156void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) { 157void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
157 const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); 158 const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::OpenMode::Read);
158 if (!file) { 159 if (!file) {
159 return; 160 return;
160 } 161 }
@@ -207,7 +208,8 @@ void EmulationSession::InitializeSystem(bool reload) {
207 m_system.GetFileSystemController().CreateFactories(*m_vfs); 208 m_system.GetFileSystemController().CreateFactories(*m_vfs);
208} 209}
209 210
210Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { 211Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
212 const std::size_t program_index) {
211 std::scoped_lock lock(m_mutex); 213 std::scoped_lock lock(m_mutex);
212 214
213 // Create the render window. 215 // Create the render window.
@@ -237,7 +239,8 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
237 ConfigureFilesystemProvider(filepath); 239 ConfigureFilesystemProvider(filepath);
238 240
239 // Load the ROM. 241 // Load the ROM.
240 m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); 242 m_load_result =
243 m_system.Load(EmulationSession::GetInstance().Window(), filepath, 0, program_index);
241 if (m_load_result != Core::SystemResultStatus::Success) { 244 if (m_load_result != Core::SystemResultStatus::Success) {
242 return m_load_result; 245 return m_load_result;
243 } 246 }
@@ -247,6 +250,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
247 m_system.GetCpuManager().OnGpuReady(); 250 m_system.GetCpuManager().OnGpuReady();
248 m_system.RegisterExitCallback([&] { HaltEmulation(); }); 251 m_system.RegisterExitCallback([&] { HaltEmulation(); });
249 252
253 // Register an ExecuteProgram callback such that Core can execute a sub-program
254 m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
255 m_next_program_index = program_index_;
256 EmulationSession::GetInstance().HaltEmulation();
257 });
258
250 OnEmulationStarted(); 259 OnEmulationStarted();
251 return Core::SystemResultStatus::Success; 260 return Core::SystemResultStatus::Success;
252} 261}
@@ -254,6 +263,11 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
254void EmulationSession::ShutdownEmulation() { 263void EmulationSession::ShutdownEmulation() {
255 std::scoped_lock lock(m_mutex); 264 std::scoped_lock lock(m_mutex);
256 265
266 if (m_next_program_index != -1) {
267 ChangeProgram(m_next_program_index);
268 m_next_program_index = -1;
269 }
270
257 m_is_running = false; 271 m_is_running = false;
258 272
259 // Unload user input. 273 // Unload user input.
@@ -401,6 +415,12 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
401 static_cast<jint>(result)); 415 static_cast<jint>(result));
402} 416}
403 417
418void EmulationSession::ChangeProgram(std::size_t program_index) {
419 JNIEnv* env = IDCache::GetEnvForThread();
420 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(),
421 static_cast<jint>(program_index));
422}
423
404u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { 424u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
405 auto program_id_string = GetJString(env, jprogramId); 425 auto program_id_string = GetJString(env, jprogramId);
406 try { 426 try {
@@ -410,7 +430,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
410 } 430 }
411} 431}
412 432
413static Core::SystemResultStatus RunEmulation(const std::string& filepath) { 433static Core::SystemResultStatus RunEmulation(const std::string& filepath,
434 const size_t program_index = 0) {
414 MicroProfileOnThreadCreate("EmuThread"); 435 MicroProfileOnThreadCreate("EmuThread");
415 SCOPE_EXIT({ MicroProfileShutdown(); }); 436 SCOPE_EXIT({ MicroProfileShutdown(); });
416 437
@@ -423,7 +444,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
423 444
424 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); 445 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
425 446
426 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); 447 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index);
427 if (result != Core::SystemResultStatus::Success) { 448 if (result != Core::SystemResultStatus::Success) {
428 return result; 449 return result;
429 } 450 }
@@ -475,8 +496,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
475 u64 program_id = EmulationSession::GetProgramId(env, jprogramId); 496 u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
476 std::string updatePath = GetJString(env, jupdatePath); 497 std::string updatePath = GetJString(env, jupdatePath);
477 std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( 498 std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
478 EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath, 499 EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
479 FileSys::Mode::Read)); 500 updatePath, FileSys::OpenMode::Read));
480 for (const auto& item : nsp->GetNCAs()) { 501 for (const auto& item : nsp->GetNCAs()) {
481 for (const auto& nca_details : item.second) { 502 for (const auto& nca_details : item.second) {
482 if (nca_details.second->GetName().ends_with(".cnmt.nca")) { 503 if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
@@ -688,11 +709,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
688 Settings::LogSettings(); 709 Settings::LogSettings();
689} 710}
690 711
691void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, 712void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
692 jstring j_path) { 713 jint j_program_index) {
693 const std::string path = GetJString(env, j_path); 714 const std::string path = GetJString(env, j_path);
694 715
695 const Core::SystemResultStatus result{RunEmulation(path)}; 716 const Core::SystemResultStatus result{RunEmulation(path, j_program_index)};
696 if (result != Core::SystemResultStatus::Success) { 717 if (result != Core::SystemResultStatus::Success) {
697 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), 718 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
698 IDCache::GetExitEmulationActivity(), static_cast<int>(result)); 719 IDCache::GetExitEmulationActivity(), static_cast<int>(result));
@@ -719,7 +740,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
719 jobject instance) { 740 jobject instance) {
720 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); 741 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
721 auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory( 742 auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
722 Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); 743 Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
723 744
724 const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser( 745 const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
725 static_cast<std::size_t>(0)); 746 static_cast<std::size_t>(0));
@@ -889,7 +910,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
889 910
890 const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); 911 const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
891 auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir), 912 auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
892 FileSys::Mode::Read); 913 FileSys::OpenMode::Read);
893 914
894 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 915 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
895 {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 916 {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index dadb138ad..bfe3fccca 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -45,7 +45,8 @@ public:
45 const Core::PerfStatsResults& PerfStats(); 45 const Core::PerfStatsResults& PerfStats();
46 void ConfigureFilesystemProvider(const std::string& filepath); 46 void ConfigureFilesystemProvider(const std::string& filepath);
47 void InitializeSystem(bool reload); 47 void InitializeSystem(bool reload);
48 Core::SystemResultStatus InitializeEmulation(const std::string& filepath); 48 Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
49 const std::size_t program_index = 0);
49 50
50 bool IsHandheldOnly(); 51 bool IsHandheldOnly();
51 void SetDeviceType([[maybe_unused]] int index, int type); 52 void SetDeviceType([[maybe_unused]] int index, int type);
@@ -60,6 +61,7 @@ public:
60private: 61private:
61 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); 62 static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
62 static void OnEmulationStopped(Core::SystemResultStatus result); 63 static void OnEmulationStopped(Core::SystemResultStatus result);
64 static void ChangeProgram(std::size_t program_index);
63 65
64private: 66private:
65 // Window management 67 // Window management
@@ -84,4 +86,7 @@ private:
84 // Synchronization 86 // Synchronization
85 std::condition_variable_any m_cv; 87 std::condition_variable_any m_cv;
86 mutable std::mutex m_mutex; 88 mutable std::mutex m_mutex;
89
90 // Program index for next boot
91 std::atomic<s32> m_next_program_index = -1;
87}; 92};
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index c01117d14..0d2bfe8d6 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -34,8 +34,10 @@
34 android:layout_width="wrap_content" 34 android:layout_width="wrap_content"
35 android:layout_height="wrap_content" 35 android:layout_height="wrap_content"
36 android:layout_gravity="center" 36 android:layout_gravity="center"
37 android:focusable="false"
37 android:defaultFocusHighlightEnabled="false" 38 android:defaultFocusHighlightEnabled="false"
38 android:clickable="false"> 39 android:clickable="false"
40 app:rippleColor="@android:color/transparent">
39 41
40 <androidx.constraintlayout.widget.ConstraintLayout 42 <androidx.constraintlayout.widget.ConstraintLayout
41 android:id="@+id/loading_layout" 43 android:id="@+id/loading_layout"
diff --git a/src/common/overflow.h b/src/common/overflow.h
index 44d8e7e73..e184ead95 100644
--- a/src/common/overflow.h
+++ b/src/common/overflow.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <algorithm>
6#include <type_traits> 7#include <type_traits>
7#include "bit_cast.h" 8#include "bit_cast.h"
8 9
@@ -19,4 +20,21 @@ inline T WrappingAdd(T lhs, T rhs) {
19 return BitCast<T>(lhs_u + rhs_u); 20 return BitCast<T>(lhs_u + rhs_u);
20} 21}
21 22
23template <typename T>
24 requires(std::is_integral_v<T> && std::is_signed_v<T>)
25inline bool CanAddWithoutOverflow(T lhs, T rhs) {
26#ifdef _MSC_VER
27 if (lhs >= 0 && rhs >= 0) {
28 return WrappingAdd(lhs, rhs) >= std::max(lhs, rhs);
29 } else if (lhs < 0 && rhs < 0) {
30 return WrappingAdd(lhs, rhs) <= std::min(lhs, rhs);
31 } else {
32 return true;
33 }
34#else
35 T res;
36 return !__builtin_add_overflow(lhs, rhs, &res);
37#endif
38}
39
22} // namespace Common 40} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 45a0d8746..347bbf2d0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -20,28 +20,49 @@ add_library(core STATIC
20 cpu_manager.h 20 cpu_manager.h
21 crypto/aes_util.cpp 21 crypto/aes_util.cpp
22 crypto/aes_util.h 22 crypto/aes_util.h
23 crypto/ctr_encryption_layer.cpp
24 crypto/ctr_encryption_layer.h
23 crypto/encryption_layer.cpp 25 crypto/encryption_layer.cpp
24 crypto/encryption_layer.h 26 crypto/encryption_layer.h
25 crypto/key_manager.cpp 27 crypto/key_manager.cpp
26 crypto/key_manager.h 28 crypto/key_manager.h
27 crypto/partition_data_manager.cpp 29 crypto/partition_data_manager.cpp
28 crypto/partition_data_manager.h 30 crypto/partition_data_manager.h
29 crypto/ctr_encryption_layer.cpp
30 crypto/ctr_encryption_layer.h
31 crypto/xts_encryption_layer.cpp 31 crypto/xts_encryption_layer.cpp
32 crypto/xts_encryption_layer.h 32 crypto/xts_encryption_layer.h
33 debugger/debugger_interface.h
34 debugger/debugger.cpp 33 debugger/debugger.cpp
35 debugger/debugger.h 34 debugger/debugger.h
36 debugger/gdbstub_arch.cpp 35 debugger/debugger_interface.h
37 debugger/gdbstub_arch.h
38 debugger/gdbstub.cpp 36 debugger/gdbstub.cpp
39 debugger/gdbstub.h 37 debugger/gdbstub.h
38 debugger/gdbstub_arch.cpp
39 debugger/gdbstub_arch.h
40 device_memory_manager.h 40 device_memory_manager.h
41 device_memory_manager.inc 41 device_memory_manager.inc
42 device_memory.cpp 42 device_memory.cpp
43 device_memory.h 43 device_memory.h
44 file_sys/bis_factory.cpp
45 file_sys/bis_factory.h
46 file_sys/card_image.cpp
47 file_sys/card_image.h
48 file_sys/common_funcs.h
49 file_sys/content_archive.cpp
50 file_sys/content_archive.h
51 file_sys/control_metadata.cpp
52 file_sys/control_metadata.h
53 file_sys/errors.h
54 file_sys/fs_directory.h
55 file_sys/fs_file.h
56 file_sys/fs_filesystem.h
57 file_sys/fs_memory_management.h
58 file_sys/fs_operate_range.h
59 file_sys/fs_path.h
60 file_sys/fs_path_utility.h
61 file_sys/fs_string_util.h
62 file_sys/fsmitm_romfsbuild.cpp
63 file_sys/fsmitm_romfsbuild.h
44 file_sys/fssystem/fs_i_storage.h 64 file_sys/fssystem/fs_i_storage.h
65 file_sys/fssystem/fs_types.h
45 file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp 66 file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
46 file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h 67 file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h
47 file_sys/fssystem/fssystem_aes_ctr_storage.cpp 68 file_sys/fssystem/fssystem_aes_ctr_storage.cpp
@@ -83,25 +104,10 @@ add_library(core STATIC
83 file_sys/fssystem/fssystem_switch_storage.h 104 file_sys/fssystem/fssystem_switch_storage.h
84 file_sys/fssystem/fssystem_utility.cpp 105 file_sys/fssystem/fssystem_utility.cpp
85 file_sys/fssystem/fssystem_utility.h 106 file_sys/fssystem/fssystem_utility.h
86 file_sys/fssystem/fs_types.h
87 file_sys/bis_factory.cpp
88 file_sys/bis_factory.h
89 file_sys/card_image.cpp
90 file_sys/card_image.h
91 file_sys/common_funcs.h
92 file_sys/content_archive.cpp
93 file_sys/content_archive.h
94 file_sys/control_metadata.cpp
95 file_sys/control_metadata.h
96 file_sys/directory.h
97 file_sys/errors.h
98 file_sys/fsmitm_romfsbuild.cpp
99 file_sys/fsmitm_romfsbuild.h
100 file_sys/ips_layer.cpp 107 file_sys/ips_layer.cpp
101 file_sys/ips_layer.h 108 file_sys/ips_layer.h
102 file_sys/kernel_executable.cpp 109 file_sys/kernel_executable.cpp
103 file_sys/kernel_executable.h 110 file_sys/kernel_executable.h
104 file_sys/mode.h
105 file_sys/nca_metadata.cpp 111 file_sys/nca_metadata.cpp
106 file_sys/nca_metadata.h 112 file_sys/nca_metadata.h
107 file_sys/partition_filesystem.cpp 113 file_sys/partition_filesystem.cpp
@@ -146,22 +152,22 @@ add_library(core STATIC
146 file_sys/system_archive/system_version.h 152 file_sys/system_archive/system_version.h
147 file_sys/system_archive/time_zone_binary.cpp 153 file_sys/system_archive/time_zone_binary.cpp
148 file_sys/system_archive/time_zone_binary.h 154 file_sys/system_archive/time_zone_binary.h
149 file_sys/vfs.cpp 155 file_sys/vfs/vfs.cpp
150 file_sys/vfs.h 156 file_sys/vfs/vfs.h
151 file_sys/vfs_cached.cpp 157 file_sys/vfs/vfs_cached.cpp
152 file_sys/vfs_cached.h 158 file_sys/vfs/vfs_cached.h
153 file_sys/vfs_concat.cpp 159 file_sys/vfs/vfs_concat.cpp
154 file_sys/vfs_concat.h 160 file_sys/vfs/vfs_concat.h
155 file_sys/vfs_layered.cpp 161 file_sys/vfs/vfs_layered.cpp
156 file_sys/vfs_layered.h 162 file_sys/vfs/vfs_layered.h
157 file_sys/vfs_offset.cpp 163 file_sys/vfs/vfs_offset.cpp
158 file_sys/vfs_offset.h 164 file_sys/vfs/vfs_offset.h
159 file_sys/vfs_real.cpp 165 file_sys/vfs/vfs_real.cpp
160 file_sys/vfs_real.h 166 file_sys/vfs/vfs_real.h
161 file_sys/vfs_static.h 167 file_sys/vfs/vfs_static.h
162 file_sys/vfs_types.h 168 file_sys/vfs/vfs_types.h
163 file_sys/vfs_vector.cpp 169 file_sys/vfs/vfs_vector.cpp
164 file_sys/vfs_vector.h 170 file_sys/vfs/vfs_vector.h
165 file_sys/xts_archive.cpp 171 file_sys/xts_archive.cpp
166 file_sys/xts_archive.h 172 file_sys/xts_archive.h
167 frontend/applets/cabinet.cpp 173 frontend/applets/cabinet.cpp
@@ -194,7 +200,6 @@ add_library(core STATIC
194 hle/kernel/board/nintendo/nx/secure_monitor.h 200 hle/kernel/board/nintendo/nx/secure_monitor.h
195 hle/kernel/code_set.cpp 201 hle/kernel/code_set.cpp
196 hle/kernel/code_set.h 202 hle/kernel/code_set.h
197 hle/kernel/svc_results.h
198 hle/kernel/global_scheduler_context.cpp 203 hle/kernel/global_scheduler_context.cpp
199 hle/kernel/global_scheduler_context.h 204 hle/kernel/global_scheduler_context.h
200 hle/kernel/init/init_slab_setup.cpp 205 hle/kernel/init/init_slab_setup.cpp
@@ -204,11 +209,11 @@ add_library(core STATIC
204 hle/kernel/k_address_arbiter.h 209 hle/kernel/k_address_arbiter.h
205 hle/kernel/k_address_space_info.cpp 210 hle/kernel/k_address_space_info.cpp
206 hle/kernel/k_address_space_info.h 211 hle/kernel/k_address_space_info.h
212 hle/kernel/k_affinity_mask.h
207 hle/kernel/k_auto_object.cpp 213 hle/kernel/k_auto_object.cpp
208 hle/kernel/k_auto_object.h 214 hle/kernel/k_auto_object.h
209 hle/kernel/k_auto_object_container.cpp 215 hle/kernel/k_auto_object_container.cpp
210 hle/kernel/k_auto_object_container.h 216 hle/kernel/k_auto_object_container.h
211 hle/kernel/k_affinity_mask.h
212 hle/kernel/k_capabilities.cpp 217 hle/kernel/k_capabilities.cpp
213 hle/kernel/k_capabilities.h 218 hle/kernel/k_capabilities.h
214 hle/kernel/k_class_token.cpp 219 hle/kernel/k_class_token.cpp
@@ -232,9 +237,9 @@ add_library(core STATIC
232 hle/kernel/k_event_info.h 237 hle/kernel/k_event_info.h
233 hle/kernel/k_handle_table.cpp 238 hle/kernel/k_handle_table.cpp
234 hle/kernel/k_handle_table.h 239 hle/kernel/k_handle_table.h
235 hle/kernel/k_hardware_timer_base.h
236 hle/kernel/k_hardware_timer.cpp 240 hle/kernel/k_hardware_timer.cpp
237 hle/kernel/k_hardware_timer.h 241 hle/kernel/k_hardware_timer.h
242 hle/kernel/k_hardware_timer_base.h
238 hle/kernel/k_interrupt_manager.cpp 243 hle/kernel/k_interrupt_manager.cpp
239 hle/kernel/k_interrupt_manager.h 244 hle/kernel/k_interrupt_manager.h
240 hle/kernel/k_light_client_session.cpp 245 hle/kernel/k_light_client_session.cpp
@@ -261,10 +266,10 @@ add_library(core STATIC
261 hle/kernel/k_page_bitmap.h 266 hle/kernel/k_page_bitmap.h
262 hle/kernel/k_page_buffer.cpp 267 hle/kernel/k_page_buffer.cpp
263 hle/kernel/k_page_buffer.h 268 hle/kernel/k_page_buffer.h
264 hle/kernel/k_page_heap.cpp
265 hle/kernel/k_page_heap.h
266 hle/kernel/k_page_group.cpp 269 hle/kernel/k_page_group.cpp
267 hle/kernel/k_page_group.h 270 hle/kernel/k_page_group.h
271 hle/kernel/k_page_heap.cpp
272 hle/kernel/k_page_heap.h
268 hle/kernel/k_page_table.h 273 hle/kernel/k_page_table.h
269 hle/kernel/k_page_table_base.cpp 274 hle/kernel/k_page_table_base.cpp
270 hle/kernel/k_page_table_base.h 275 hle/kernel/k_page_table_base.h
@@ -329,8 +334,6 @@ add_library(core STATIC
329 hle/kernel/slab_helpers.h 334 hle/kernel/slab_helpers.h
330 hle/kernel/svc.cpp 335 hle/kernel/svc.cpp
331 hle/kernel/svc.h 336 hle/kernel/svc.h
332 hle/kernel/svc_common.h
333 hle/kernel/svc_types.h
334 hle/kernel/svc/svc_activity.cpp 337 hle/kernel/svc/svc_activity.cpp
335 hle/kernel/svc/svc_address_arbiter.cpp 338 hle/kernel/svc/svc_address_arbiter.cpp
336 hle/kernel/svc/svc_address_translation.cpp 339 hle/kernel/svc/svc_address_translation.cpp
@@ -368,6 +371,9 @@ add_library(core STATIC
368 hle/kernel/svc/svc_thread_profiler.cpp 371 hle/kernel/svc/svc_thread_profiler.cpp
369 hle/kernel/svc/svc_tick.cpp 372 hle/kernel/svc/svc_tick.cpp
370 hle/kernel/svc/svc_transfer_memory.cpp 373 hle/kernel/svc/svc_transfer_memory.cpp
374 hle/kernel/svc_common.h
375 hle/kernel/svc_results.h
376 hle/kernel/svc_types.h
371 hle/result.h 377 hle/result.h
372 hle/service/acc/acc.cpp 378 hle/service/acc/acc.cpp
373 hle/service/acc/acc.h 379 hle/service/acc/acc.h
@@ -486,14 +492,25 @@ add_library(core STATIC
486 hle/service/fatal/fatal_p.h 492 hle/service/fatal/fatal_p.h
487 hle/service/fatal/fatal_u.cpp 493 hle/service/fatal/fatal_u.cpp
488 hle/service/fatal/fatal_u.h 494 hle/service/fatal/fatal_u.h
495 hle/service/fgm/fgm.cpp
496 hle/service/fgm/fgm.h
489 hle/service/filesystem/filesystem.cpp 497 hle/service/filesystem/filesystem.cpp
490 hle/service/filesystem/filesystem.h 498 hle/service/filesystem/filesystem.h
491 hle/service/filesystem/fsp_ldr.cpp 499 hle/service/filesystem/fsp/fs_i_directory.cpp
492 hle/service/filesystem/fsp_ldr.h 500 hle/service/filesystem/fsp/fs_i_directory.h
493 hle/service/filesystem/fsp_pr.cpp 501 hle/service/filesystem/fsp/fs_i_file.cpp
494 hle/service/filesystem/fsp_pr.h 502 hle/service/filesystem/fsp/fs_i_file.h
495 hle/service/filesystem/fsp_srv.cpp 503 hle/service/filesystem/fsp/fs_i_filesystem.cpp
496 hle/service/filesystem/fsp_srv.h 504 hle/service/filesystem/fsp/fs_i_filesystem.h
505 hle/service/filesystem/fsp/fs_i_storage.cpp
506 hle/service/filesystem/fsp/fs_i_storage.h
507 hle/service/filesystem/fsp/fsp_ldr.cpp
508 hle/service/filesystem/fsp/fsp_ldr.h
509 hle/service/filesystem/fsp/fsp_pr.cpp
510 hle/service/filesystem/fsp/fsp_pr.h
511 hle/service/filesystem/fsp/fsp_srv.cpp
512 hle/service/filesystem/fsp/fsp_srv.h
513 hle/service/filesystem/fsp/fsp_util.h
497 hle/service/filesystem/romfs_controller.cpp 514 hle/service/filesystem/romfs_controller.cpp
498 hle/service/filesystem/romfs_controller.h 515 hle/service/filesystem/romfs_controller.h
499 hle/service/filesystem/save_data_controller.cpp 516 hle/service/filesystem/save_data_controller.cpp
@@ -551,13 +568,18 @@ add_library(core STATIC
551 hle/service/hid/irs.h 568 hle/service/hid/irs.h
552 hle/service/hid/xcd.cpp 569 hle/service/hid/xcd.cpp
553 hle/service/hid/xcd.h 570 hle/service/hid/xcd.h
571 hle/service/hle_ipc.cpp
572 hle/service/hle_ipc.h
573 hle/service/ipc_helpers.h
574 hle/service/kernel_helpers.cpp
575 hle/service/kernel_helpers.h
554 hle/service/lbl/lbl.cpp 576 hle/service/lbl/lbl.cpp
555 hle/service/lbl/lbl.h 577 hle/service/lbl/lbl.h
556 hle/service/ldn/lan_discovery.cpp 578 hle/service/ldn/lan_discovery.cpp
557 hle/service/ldn/lan_discovery.h 579 hle/service/ldn/lan_discovery.h
558 hle/service/ldn/ldn_results.h
559 hle/service/ldn/ldn.cpp 580 hle/service/ldn/ldn.cpp
560 hle/service/ldn/ldn.h 581 hle/service/ldn/ldn.h
582 hle/service/ldn/ldn_results.h
561 hle/service/ldn/ldn_types.h 583 hle/service/ldn/ldn_types.h
562 hle/service/ldr/ldr.cpp 584 hle/service/ldr/ldr.cpp
563 hle/service/ldr/ldr.h 585 hle/service/ldr/ldr.h
@@ -565,16 +587,6 @@ add_library(core STATIC
565 hle/service/lm/lm.h 587 hle/service/lm/lm.h
566 hle/service/mig/mig.cpp 588 hle/service/mig/mig.cpp
567 hle/service/mig/mig.h 589 hle/service/mig/mig.h
568 hle/service/mii/types/char_info.cpp
569 hle/service/mii/types/char_info.h
570 hle/service/mii/types/core_data.cpp
571 hle/service/mii/types/core_data.h
572 hle/service/mii/types/raw_data.cpp
573 hle/service/mii/types/raw_data.h
574 hle/service/mii/types/store_data.cpp
575 hle/service/mii/types/store_data.h
576 hle/service/mii/types/ver3_store_data.cpp
577 hle/service/mii/types/ver3_store_data.h
578 hle/service/mii/mii.cpp 590 hle/service/mii/mii.cpp
579 hle/service/mii/mii.h 591 hle/service/mii/mii.h
580 hle/service/mii/mii_database.cpp 592 hle/service/mii/mii_database.cpp
@@ -586,10 +598,22 @@ add_library(core STATIC
586 hle/service/mii/mii_result.h 598 hle/service/mii/mii_result.h
587 hle/service/mii/mii_types.h 599 hle/service/mii/mii_types.h
588 hle/service/mii/mii_util.h 600 hle/service/mii/mii_util.h
601 hle/service/mii/types/char_info.cpp
602 hle/service/mii/types/char_info.h
603 hle/service/mii/types/core_data.cpp
604 hle/service/mii/types/core_data.h
605 hle/service/mii/types/raw_data.cpp
606 hle/service/mii/types/raw_data.h
607 hle/service/mii/types/store_data.cpp
608 hle/service/mii/types/store_data.h
609 hle/service/mii/types/ver3_store_data.cpp
610 hle/service/mii/types/ver3_store_data.h
589 hle/service/mm/mm_u.cpp 611 hle/service/mm/mm_u.cpp
590 hle/service/mm/mm_u.h 612 hle/service/mm/mm_u.h
591 hle/service/mnpp/mnpp_app.cpp 613 hle/service/mnpp/mnpp_app.cpp
592 hle/service/mnpp/mnpp_app.h 614 hle/service/mnpp/mnpp_app.h
615 hle/service/mutex.cpp
616 hle/service/mutex.h
593 hle/service/ncm/ncm.cpp 617 hle/service/ncm/ncm.cpp
594 hle/service/ncm/ncm.h 618 hle/service/ncm/ncm.h
595 hle/service/nfc/common/amiibo_crypto.cpp 619 hle/service/nfc/common/amiibo_crypto.cpp
@@ -759,19 +783,12 @@ add_library(core STATIC
759 hle/service/ptm/ptm.h 783 hle/service/ptm/ptm.h
760 hle/service/ptm/ts.cpp 784 hle/service/ptm/ts.cpp
761 hle/service/ptm/ts.h 785 hle/service/ptm/ts.h
762 hle/service/hle_ipc.cpp 786 hle/service/ro/ro.cpp
763 hle/service/hle_ipc.h 787 hle/service/ro/ro.h
764 hle/service/ipc_helpers.h
765 hle/service/kernel_helpers.cpp
766 hle/service/kernel_helpers.h
767 hle/service/mutex.cpp
768 hle/service/mutex.h
769 hle/service/ro/ro_nro_utils.cpp 788 hle/service/ro/ro_nro_utils.cpp
770 hle/service/ro/ro_nro_utils.h 789 hle/service/ro/ro_nro_utils.h
771 hle/service/ro/ro_results.h 790 hle/service/ro/ro_results.h
772 hle/service/ro/ro_types.h 791 hle/service/ro/ro_types.h
773 hle/service/ro/ro.cpp
774 hle/service/ro/ro.h
775 hle/service/server_manager.cpp 792 hle/service/server_manager.cpp
776 hle/service/server_manager.h 793 hle/service/server_manager.h
777 hle/service/service.cpp 794 hle/service/service.cpp
@@ -838,9 +855,9 @@ add_library(core STATIC
838 internal_network/network.h 855 internal_network/network.h
839 internal_network/network_interface.cpp 856 internal_network/network_interface.cpp
840 internal_network/network_interface.h 857 internal_network/network_interface.h
841 internal_network/sockets.h
842 internal_network/socket_proxy.cpp 858 internal_network/socket_proxy.cpp
843 internal_network/socket_proxy.h 859 internal_network/socket_proxy.h
860 internal_network/sockets.h
844 loader/deconstructed_rom_directory.cpp 861 loader/deconstructed_rom_directory.cpp
845 loader/deconstructed_rom_directory.h 862 loader/deconstructed_rom_directory.h
846 loader/kip.cpp 863 loader/kip.cpp
@@ -859,13 +876,13 @@ add_library(core STATIC
859 loader/nsp.h 876 loader/nsp.h
860 loader/xci.cpp 877 loader/xci.cpp
861 loader/xci.h 878 loader/xci.h
879 memory.cpp
880 memory.h
862 memory/cheat_engine.cpp 881 memory/cheat_engine.cpp
863 memory/cheat_engine.h 882 memory/cheat_engine.h
864 memory/dmnt_cheat_types.h 883 memory/dmnt_cheat_types.h
865 memory/dmnt_cheat_vm.cpp 884 memory/dmnt_cheat_vm.cpp
866 memory/dmnt_cheat_vm.h 885 memory/dmnt_cheat_vm.h
867 memory.cpp
868 memory.h
869 perf_stats.cpp 886 perf_stats.cpp
870 perf_stats.h 887 perf_stats.h
871 precompiled_headers.h 888 precompiled_headers.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index dd9de948c..1b412ac98 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -21,13 +21,13 @@
21#include "core/debugger/debugger.h" 21#include "core/debugger/debugger.h"
22#include "core/device_memory.h" 22#include "core/device_memory.h"
23#include "core/file_sys/bis_factory.h" 23#include "core/file_sys/bis_factory.h"
24#include "core/file_sys/mode.h" 24#include "core/file_sys/fs_filesystem.h"
25#include "core/file_sys/patch_manager.h" 25#include "core/file_sys/patch_manager.h"
26#include "core/file_sys/registered_cache.h" 26#include "core/file_sys/registered_cache.h"
27#include "core/file_sys/romfs_factory.h" 27#include "core/file_sys/romfs_factory.h"
28#include "core/file_sys/savedata_factory.h" 28#include "core/file_sys/savedata_factory.h"
29#include "core/file_sys/vfs_concat.h" 29#include "core/file_sys/vfs/vfs_concat.h"
30#include "core/file_sys/vfs_real.h" 30#include "core/file_sys/vfs/vfs_real.h"
31#include "core/gpu_dirty_memory_manager.h" 31#include "core/gpu_dirty_memory_manager.h"
32#include "core/hle/kernel/k_memory_manager.h" 32#include "core/hle/kernel/k_memory_manager.h"
33#include "core/hle/kernel/k_process.h" 33#include "core/hle/kernel/k_process.h"
@@ -102,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
102 Common::SplitPath(path, &dir_name, &filename, nullptr); 102 Common::SplitPath(path, &dir_name, &filename, nullptr);
103 103
104 if (filename == "00") { 104 if (filename == "00") {
105 const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read); 105 const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read);
106 std::vector<FileSys::VirtualFile> concat; 106 std::vector<FileSys::VirtualFile> concat;
107 107
108 for (u32 i = 0; i < 0x10; ++i) { 108 for (u32 i = 0; i < 0x10; ++i) {
@@ -127,10 +127,10 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
127 } 127 }
128 128
129 if (Common::FS::IsDir(path)) { 129 if (Common::FS::IsDir(path)) {
130 return vfs->OpenFile(path + "/main", FileSys::Mode::Read); 130 return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read);
131 } 131 }
132 132
133 return vfs->OpenFile(path, FileSys::Mode::Read); 133 return vfs->OpenFile(path, FileSys::OpenMode::Read);
134} 134}
135 135
136struct System::Impl { 136struct System::Impl {
diff --git a/src/core/core.h b/src/core/core.h
index 183410602..d8862e9ce 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -13,7 +13,7 @@
13#include <vector> 13#include <vector>
14 14
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/file_sys/vfs_types.h" 16#include "core/file_sys/vfs/vfs_types.h"
17 17
18namespace Core::Frontend { 18namespace Core::Frontend {
19class EmuWindow; 19class EmuWindow;
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 @@
7#include <span> 7#include <span>
8#include <type_traits> 8#include <type_traits>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11 11
12namespace Core::Crypto { 12namespace Core::Crypto {
13 13
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 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/file_sys/vfs.h" 7#include "core/file_sys/vfs/vfs.h"
8 8
9namespace Core::Crypto { 9namespace Core::Crypto {
10 10
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 @@
21#include "core/crypto/partition_data_manager.h" 21#include "core/crypto/partition_data_manager.h"
22#include "core/crypto/xts_encryption_layer.h" 22#include "core/crypto/xts_encryption_layer.h"
23#include "core/file_sys/kernel_executable.h" 23#include "core/file_sys/kernel_executable.h"
24#include "core/file_sys/vfs.h" 24#include "core/file_sys/vfs/vfs.h"
25#include "core/file_sys/vfs_offset.h" 25#include "core/file_sys/vfs/vfs_offset.h"
26#include "core/file_sys/vfs_vector.h" 26#include "core/file_sys/vfs/vfs_vector.h"
27#include "core/loader/loader.h" 27#include "core/loader/loader.h"
28 28
29using Common::AsArray; 29using 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 @@
5 5
6#include <vector> 6#include <vector>
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/file_sys/vfs_types.h" 8#include "core/file_sys/vfs/vfs_types.h"
9 9
10namespace Core::Crypto { 10namespace Core::Crypto {
11 11
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 @@
4#include <fmt/format.h> 4#include <fmt/format.h>
5#include "common/fs/path_util.h" 5#include "common/fs/path_util.h"
6#include "core/file_sys/bis_factory.h" 6#include "core/file_sys/bis_factory.h"
7#include "core/file_sys/mode.h"
8#include "core/file_sys/registered_cache.h" 7#include "core/file_sys/registered_cache.h"
9#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs/vfs.h"
10 9
11namespace FileSys { 10namespace FileSys {
12 11
@@ -84,7 +83,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
84 VirtualFilesystem file_system) const { 83 VirtualFilesystem file_system) const {
85 auto& keys = Core::Crypto::KeyManager::Instance(); 84 auto& keys = Core::Crypto::KeyManager::Instance();
86 Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( 85 Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
87 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)}; 86 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), OpenMode::Read)};
88 keys.PopulateFromPartitionData(pdm); 87 keys.PopulateFromPartitionData(pdm);
89 88
90 switch (id) { 89 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 @@
6#include <memory> 6#include <memory>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h" 9#include "core/file_sys/vfs/vfs_types.h"
10 10
11namespace FileSys { 11namespace FileSys {
12 12
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 @@
13#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/partition_filesystem.h" 14#include "core/file_sys/partition_filesystem.h"
15#include "core/file_sys/submission_package.h" 15#include "core/file_sys/submission_package.h"
16#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs/vfs_offset.h"
17#include "core/file_sys/vfs_vector.h" 17#include "core/file_sys/vfs/vfs_vector.h"
18#include "core/loader/loader.h" 18#include "core/loader/loader.h"
19 19
20namespace FileSys { 20namespace 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 @@
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12 12
13namespace Core::Crypto { 13namespace Core::Crypto {
14class KeyManager; 14class 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 @@
13#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
14#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
15#include "core/file_sys/partition_filesystem.h" 15#include "core/file_sys/partition_filesystem.h"
16#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs/vfs_offset.h"
17#include "core/loader/loader.h" 17#include "core/loader/loader.h"
18 18
19#include "core/file_sys/fssystem/fssystem_compression_configuration.h" 19#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 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/swap.h" 14#include "common/swap.h"
15#include "core/crypto/key_manager.h" 15#include "core/crypto/key_manager.h"
16#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs/vfs.h"
17 17
18namespace Loader { 18namespace Loader {
19enum class ResultStatus : u16; 19enum 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 @@
5#include "common/string_util.h" 5#include "common/string_util.h"
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs/vfs.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
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 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/file_sys/vfs_types.h" 11#include "core/file_sys/vfs/vfs_types.h"
12 12
13namespace FileSys { 13namespace FileSys {
14 14
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 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <cstddef>
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9
10////////////////////////////////////////////////////////////////////////////////////////////////////
11// FileSys namespace
12
13namespace FileSys {
14
15enum class EntryType : u8 {
16 Directory = 0,
17 File = 1,
18};
19
20// Structure of a directory entry, from
21// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
22struct Entry {
23 Entry(std::string_view view, EntryType entry_type, u64 entry_size)
24 : type{entry_type}, file_size{entry_size} {
25 const std::size_t copy_size = view.copy(filename, std::size(filename) - 1);
26 filename[copy_size] = '\0';
27 }
28
29 char filename[0x301];
30 INSERT_PADDING_BYTES(3);
31 EntryType type;
32 INSERT_PADDING_BYTES(3);
33 u64 file_size;
34};
35static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!");
36static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
37static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
38
39} // 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 @@
7 7
8namespace FileSys { 8namespace FileSys {
9 9
10constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; 10constexpr Result ResultPathNotFound{ErrorModule::FS, 1};
11constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2}; 11constexpr Result ResultPathAlreadyExists{ErrorModule::FS, 2};
12constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
13constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
14constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
15constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
16constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
17constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
18constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
19
20constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50}; 12constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50};
21constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001}; 13constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001};
14constexpr Result ResultTargetNotFound{ErrorModule::FS, 1002};
15constexpr Result ResultPortSdCardNoDevice{ErrorModule::FS, 2001};
16constexpr Result ResultNotImplemented{ErrorModule::FS, 3001};
22constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002}; 17constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002};
23constexpr Result ResultOutOfRange{ErrorModule::FS, 3005}; 18constexpr Result ResultOutOfRange{ErrorModule::FS, 3005};
24constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294}; 19constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294};
@@ -78,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324};
78constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325}; 73constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325};
79constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326}; 74constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326};
80constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327}; 75constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327};
76constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328};
81constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001}; 77constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001};
78constexpr Result ResultInvalidPath{ErrorModule::FS, 6002};
79constexpr Result ResultTooLongPath{ErrorModule::FS, 6003};
80constexpr Result ResultInvalidCharacter{ErrorModule::FS, 6004};
81constexpr Result ResultInvalidPathFormat{ErrorModule::FS, 6005};
82constexpr Result ResultDirectoryUnobtainable{ErrorModule::FS, 6006};
83constexpr Result ResultNotNormalized{ErrorModule::FS, 6007};
82constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061}; 84constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061};
83constexpr Result ResultInvalidSize{ErrorModule::FS, 6062}; 85constexpr Result ResultInvalidSize{ErrorModule::FS, 6062};
84constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063}; 86constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063};
87constexpr Result ResultInvalidOpenMode{ErrorModule::FS, 6072};
88constexpr Result ResultFileExtensionWithoutOpenModeAllowAppend{ErrorModule::FS, 6201};
89constexpr Result ResultReadNotPermitted{ErrorModule::FS, 6202};
90constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
85constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; 91constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
86constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; 92constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
87constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; 93constexpr 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace FileSys {
7
8constexpr inline size_t EntryNameLengthMax = 0x300;
9
10struct DirectoryEntry {
11 DirectoryEntry(std::string_view view, s8 entry_type, u64 entry_size)
12 : type{entry_type}, file_size{static_cast<s64>(entry_size)} {
13 const std::size_t copy_size = view.copy(name, std::size(name) - 1);
14 name[copy_size] = '\0';
15 }
16
17 char name[EntryNameLengthMax + 1];
18 INSERT_PADDING_BYTES(3);
19 s8 type;
20 INSERT_PADDING_BYTES(3);
21 s64 file_size;
22};
23
24static_assert(sizeof(DirectoryEntry) == 0x310,
25 "Directory Entry struct isn't exactly 0x310 bytes long!");
26static_assert(offsetof(DirectoryEntry, type) == 0x304, "Wrong offset for type in Entry.");
27static_assert(offsetof(DirectoryEntry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
28
29struct DirectoryHandle {
30 void* handle;
31};
32
33} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace FileSys {
9
10struct ReadOption {
11 u32 value;
12
13 static const ReadOption None;
14};
15
16enum ReadOptionFlag : u32 {
17 ReadOptionFlag_None = (0 << 0),
18};
19
20inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None};
21
22inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) {
23 return lhs.value == rhs.value;
24}
25
26inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) {
27 return !(lhs == rhs);
28}
29
30static_assert(sizeof(ReadOption) == sizeof(u32));
31
32enum WriteOptionFlag : u32 {
33 WriteOptionFlag_None = (0 << 0),
34 WriteOptionFlag_Flush = (1 << 0),
35};
36
37struct WriteOption {
38 u32 value;
39
40 constexpr inline bool HasFlushFlag() const {
41 return value & WriteOptionFlag_Flush;
42 }
43
44 static const WriteOption None;
45 static const WriteOption Flush;
46};
47
48inline constexpr const WriteOption WriteOption::None = {WriteOptionFlag_None};
49inline constexpr const WriteOption WriteOption::Flush = {WriteOptionFlag_Flush};
50
51inline constexpr bool operator==(const WriteOption& lhs, const WriteOption& rhs) {
52 return lhs.value == rhs.value;
53}
54
55inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) {
56 return !(lhs == rhs);
57}
58
59static_assert(sizeof(WriteOption) == sizeof(u32));
60
61struct FileHandle {
62 void* handle;
63};
64
65} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace FileSys {
10
11enum class OpenMode : u32 {
12 Read = (1 << 0),
13 Write = (1 << 1),
14 AllowAppend = (1 << 2),
15
16 ReadWrite = (Read | Write),
17 All = (ReadWrite | AllowAppend),
18};
19DECLARE_ENUM_FLAG_OPERATORS(OpenMode)
20
21enum class OpenDirectoryMode : u64 {
22 Directory = (1 << 0),
23 File = (1 << 1),
24
25 All = (Directory | File),
26};
27DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
28
29enum class DirectoryEntryType : u8 {
30 Directory = 0,
31 File = 1,
32};
33
34enum class CreateOption : u8 {
35 None = (0 << 0),
36 BigFile = (1 << 0),
37};
38
39} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7#include "common/alignment.h"
8
9namespace FileSys {
10
11constexpr size_t RequiredAlignment = alignof(u64);
12
13void* AllocateUnsafe(size_t size) {
14 // Allocate
15 void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
16
17 // Check alignment
18 ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(ptr), RequiredAlignment));
19
20 // Return allocated pointer
21 return ptr;
22}
23
24void DeallocateUnsafe(void* ptr, size_t size) {
25 // Deallocate the pointer
26 ::operator delete(ptr, std::align_val_t{RequiredAlignment});
27}
28
29void* Allocate(size_t size) {
30 return AllocateUnsafe(size);
31}
32
33void Deallocate(void* ptr, size_t size) {
34 // If the pointer is non-null, deallocate it
35 if (ptr != nullptr) {
36 DeallocateUnsafe(ptr, size);
37 }
38}
39
40} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace FileSys {
9
10enum class OperationId : s64 {
11 FillZero = 0,
12 DestroySignature = 1,
13 Invalidate = 2,
14 QueryRange = 3,
15 QueryUnpreparedRange = 4,
16 QueryLazyLoadCompletionRate = 5,
17 SetLazyLoadPriority = 6,
18
19 ReadLazyLoadFileForciblyForDebug = 10001,
20};
21
22} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/alignment.h"
7#include "common/common_funcs.h"
8#include "core/file_sys/errors.h"
9#include "core/file_sys/fs_memory_management.h"
10#include "core/file_sys/fs_path_utility.h"
11#include "core/file_sys/fs_string_util.h"
12#include "core/hle/result.h"
13
14namespace FileSys {
15class DirectoryPathParser;
16
17class Path {
18 YUZU_NON_COPYABLE(Path);
19 YUZU_NON_MOVEABLE(Path);
20
21private:
22 static constexpr const char* EmptyPath = "";
23 static constexpr size_t WriteBufferAlignmentLength = 8;
24
25private:
26 friend class DirectoryPathParser;
27
28public:
29 class WriteBuffer {
30 YUZU_NON_COPYABLE(WriteBuffer);
31
32 private:
33 char* m_buffer;
34 size_t m_length_and_is_normalized;
35
36 public:
37 constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) {}
38
39 constexpr ~WriteBuffer() {
40 if (m_buffer != nullptr) {
41 Deallocate(m_buffer, this->GetLength());
42 this->ResetBuffer();
43 }
44 }
45
46 constexpr WriteBuffer(WriteBuffer&& rhs)
47 : m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) {
48 rhs.ResetBuffer();
49 }
50
51 constexpr WriteBuffer& operator=(WriteBuffer&& rhs) {
52 if (m_buffer != nullptr) {
53 Deallocate(m_buffer, this->GetLength());
54 }
55
56 m_buffer = rhs.m_buffer;
57 m_length_and_is_normalized = rhs.m_length_and_is_normalized;
58
59 rhs.ResetBuffer();
60
61 return *this;
62 }
63
64 constexpr void ResetBuffer() {
65 m_buffer = nullptr;
66 this->SetLength(0);
67 }
68
69 constexpr char* Get() const {
70 return m_buffer;
71 }
72
73 constexpr size_t GetLength() const {
74 return m_length_and_is_normalized >> 1;
75 }
76
77 constexpr bool IsNormalized() const {
78 return static_cast<bool>(m_length_and_is_normalized & 1);
79 }
80
81 constexpr void SetNormalized() {
82 m_length_and_is_normalized |= static_cast<size_t>(1);
83 }
84
85 constexpr void SetNotNormalized() {
86 m_length_and_is_normalized &= ~static_cast<size_t>(1);
87 }
88
89 private:
90 constexpr WriteBuffer(char* buffer, size_t length)
91 : m_buffer(buffer), m_length_and_is_normalized(0) {
92 this->SetLength(length);
93 }
94
95 public:
96 static WriteBuffer Make(size_t length) {
97 if (void* alloc = Allocate(length); alloc != nullptr) {
98 return WriteBuffer(static_cast<char*>(alloc), length);
99 } else {
100 return WriteBuffer();
101 }
102 }
103
104 private:
105 constexpr void SetLength(size_t size) {
106 m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1);
107 }
108 };
109
110private:
111 const char* m_str;
112 WriteBuffer m_write_buffer;
113
114public:
115 constexpr Path() : m_str(EmptyPath), m_write_buffer() {}
116
117 constexpr Path(const char* s) : m_str(s), m_write_buffer() {
118 m_write_buffer.SetNormalized();
119 }
120
121 constexpr ~Path() = default;
122
123 constexpr Result SetShallowBuffer(const char* buffer) {
124 // Check pre-conditions
125 ASSERT(m_write_buffer.GetLength() == 0);
126
127 // Check the buffer is valid
128 R_UNLESS(buffer != nullptr, ResultNullptrArgument);
129
130 // Set buffer
131 this->SetReadOnlyBuffer(buffer);
132
133 // Note that we're normalized
134 this->SetNormalized();
135
136 R_SUCCEED();
137 }
138
139 constexpr const char* GetString() const {
140 // Check pre-conditions
141 ASSERT(this->IsNormalized());
142
143 return m_str;
144 }
145
146 constexpr size_t GetLength() const {
147 if (std::is_constant_evaluated()) {
148 return Strlen(this->GetString());
149 } else {
150 return std::strlen(this->GetString());
151 }
152 }
153
154 constexpr bool IsEmpty() const {
155 return *m_str == '\x00';
156 }
157
158 constexpr bool IsMatchHead(const char* p, size_t len) const {
159 return Strncmp(this->GetString(), p, len) == 0;
160 }
161
162 Result Initialize(const Path& rhs) {
163 // Check the other path is normalized
164 const bool normalized = rhs.IsNormalized();
165 R_UNLESS(normalized, ResultNotNormalized);
166
167 // Allocate buffer for our path
168 const auto len = rhs.GetLength();
169 R_TRY(this->Preallocate(len + 1));
170
171 // Copy the path
172 const size_t copied = Strlcpy<char>(m_write_buffer.Get(), rhs.GetString(), len + 1);
173 R_UNLESS(copied == len, ResultUnexpectedInPathA);
174
175 // Set normalized
176 this->SetNormalized();
177 R_SUCCEED();
178 }
179
180 Result Initialize(const char* path, size_t len) {
181 // Check the path is valid
182 R_UNLESS(path != nullptr, ResultNullptrArgument);
183
184 // Initialize
185 R_TRY(this->InitializeImpl(path, len));
186
187 // Set not normalized
188 this->SetNotNormalized();
189
190 R_SUCCEED();
191 }
192
193 Result Initialize(const char* path) {
194 // Check the path is valid
195 R_UNLESS(path != nullptr, ResultNullptrArgument);
196
197 R_RETURN(this->Initialize(path, std::strlen(path)));
198 }
199
200 Result InitializeWithReplaceBackslash(const char* path) {
201 // Check the path is valid
202 R_UNLESS(path != nullptr, ResultNullptrArgument);
203
204 // Initialize
205 R_TRY(this->InitializeImpl(path, std::strlen(path)));
206
207 // Replace slashes as desired
208 if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) {
209 Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/');
210 }
211
212 // Set not normalized
213 this->SetNotNormalized();
214
215 R_SUCCEED();
216 }
217
218 Result InitializeWithReplaceForwardSlashes(const char* path) {
219 // Check the path is valid
220 R_UNLESS(path != nullptr, ResultNullptrArgument);
221
222 // Initialize
223 R_TRY(this->InitializeImpl(path, std::strlen(path)));
224
225 // Replace slashes as desired
226 if (m_write_buffer.GetLength() > 1) {
227 if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') {
228 p[0] = '\\';
229 p[1] = '\\';
230 }
231 }
232
233 // Set not normalized
234 this->SetNotNormalized();
235
236 R_SUCCEED();
237 }
238
239 Result InitializeWithNormalization(const char* path, size_t size) {
240 // Check the path is valid
241 R_UNLESS(path != nullptr, ResultNullptrArgument);
242
243 // Initialize
244 R_TRY(this->InitializeImpl(path, size));
245
246 // Set not normalized
247 this->SetNotNormalized();
248
249 // Perform normalization
250 PathFlags path_flags;
251 if (IsPathRelative(m_str)) {
252 path_flags.AllowRelativePath();
253 } else if (IsWindowsPath(m_str, true)) {
254 path_flags.AllowWindowsPath();
255 } else {
256 /* NOTE: In this case, Nintendo checks is normalized, then sets is normalized, then
257 * returns success. */
258 /* This seems like a bug. */
259 size_t dummy;
260 bool normalized;
261 R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy),
262 m_str));
263
264 this->SetNormalized();
265 R_SUCCEED();
266 }
267
268 // Normalize
269 R_TRY(this->Normalize(path_flags));
270
271 this->SetNormalized();
272 R_SUCCEED();
273 }
274
275 Result InitializeWithNormalization(const char* path) {
276 // Check the path is valid
277 R_UNLESS(path != nullptr, ResultNullptrArgument);
278
279 R_RETURN(this->InitializeWithNormalization(path, std::strlen(path)));
280 }
281
282 Result InitializeAsEmpty() {
283 // Clear our buffer
284 this->ClearBuffer();
285
286 // Set normalized
287 this->SetNormalized();
288
289 R_SUCCEED();
290 }
291
292 Result AppendChild(const char* child) {
293 // Check the path is valid
294 R_UNLESS(child != nullptr, ResultNullptrArgument);
295
296 // Basic checks. If we have a path and the child is empty, we have nothing to do
297 const char* c = child;
298 if (m_str[0]) {
299 // Skip an early separator
300 if (*c == '/') {
301 ++c;
302 }
303
304 R_SUCCEED_IF(*c == '\x00');
305 }
306
307 // If we don't have a string, we can just initialize
308 auto cur_len = std::strlen(m_str);
309 if (cur_len == 0) {
310 R_RETURN(this->Initialize(child));
311 }
312
313 // Remove a trailing separator
314 if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') {
315 --cur_len;
316 }
317
318 // Get the child path's length
319 auto child_len = std::strlen(c);
320
321 // Reset our write buffer
322 WriteBuffer old_write_buffer;
323 if (m_write_buffer.Get() != nullptr) {
324 old_write_buffer = std::move(m_write_buffer);
325 this->ClearBuffer();
326 }
327
328 // Pre-allocate the new buffer
329 R_TRY(this->Preallocate(cur_len + 1 + child_len + 1));
330
331 // Get our write buffer
332 auto* dst = m_write_buffer.Get();
333 if (old_write_buffer.Get() != nullptr && cur_len > 0) {
334 Strlcpy<char>(dst, old_write_buffer.Get(), cur_len + 1);
335 }
336
337 // Add separator
338 dst[cur_len] = '/';
339
340 // Copy the child path
341 const size_t copied = Strlcpy<char>(dst + cur_len + 1, c, child_len + 1);
342 R_UNLESS(copied == child_len, ResultUnexpectedInPathA);
343
344 R_SUCCEED();
345 }
346
347 Result AppendChild(const Path& rhs) {
348 R_RETURN(this->AppendChild(rhs.GetString()));
349 }
350
351 Result Combine(const Path& parent, const Path& child) {
352 // Get the lengths
353 const auto p_len = parent.GetLength();
354 const auto c_len = child.GetLength();
355
356 // Allocate our buffer
357 R_TRY(this->Preallocate(p_len + c_len + 1));
358
359 // Initialize as parent
360 R_TRY(this->Initialize(parent));
361
362 // If we're empty, we can just initialize as child
363 if (this->IsEmpty()) {
364 R_TRY(this->Initialize(child));
365 } else {
366 // Otherwise, we should append the child
367 R_TRY(this->AppendChild(child));
368 }
369
370 R_SUCCEED();
371 }
372
373 Result RemoveChild() {
374 // If we don't have a write-buffer, ensure that we have one
375 if (m_write_buffer.Get() == nullptr) {
376 if (const auto len = std::strlen(m_str); len > 0) {
377 R_TRY(this->Preallocate(len));
378 Strlcpy<char>(m_write_buffer.Get(), m_str, len + 1);
379 }
380 }
381
382 // Check that it's possible for us to remove a child
383 auto* p = m_write_buffer.Get();
384 s32 len = std::strlen(p);
385 R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
386
387 // Handle a trailing separator
388 if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) {
389 --len;
390 }
391
392 // Remove the child path segment
393 while ((--len) >= 0 && p[len]) {
394 if (p[len] == '/' || p[len] == '\\') {
395 if (len > 0) {
396 p[len] = 0;
397 } else {
398 p[1] = 0;
399 len = 1;
400 }
401 break;
402 }
403 }
404
405 // Check that length remains > 0
406 R_UNLESS(len > 0, ResultNotImplemented);
407
408 R_SUCCEED();
409 }
410
411 Result Normalize(const PathFlags& flags) {
412 // If we're already normalized, nothing to do
413 R_SUCCEED_IF(this->IsNormalized());
414
415 // Check if we're normalized
416 bool normalized;
417 size_t dummy;
418 R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), m_str,
419 flags));
420
421 // If we're not normalized, normalize
422 if (!normalized) {
423 // Determine necessary buffer length
424 auto len = m_write_buffer.GetLength();
425 if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) {
426 len += 2;
427 }
428 if (flags.IsWindowsPathAllowed() && IsWindowsPath(m_str, true)) {
429 len += 1;
430 }
431
432 // Allocate a new buffer
433 const size_t size = Common::AlignUp(len, WriteBufferAlignmentLength);
434 auto buf = WriteBuffer::Make(size);
435 R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique);
436
437 // Normalize into it
438 R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(),
439 m_write_buffer.GetLength(), flags));
440
441 // Set the normalized buffer as our buffer
442 this->SetModifiableBuffer(std::move(buf));
443 }
444
445 // Set normalized
446 this->SetNormalized();
447 R_SUCCEED();
448 }
449
450private:
451 void ClearBuffer() {
452 m_write_buffer.ResetBuffer();
453 m_str = EmptyPath;
454 }
455
456 void SetModifiableBuffer(WriteBuffer&& buffer) {
457 // Check pre-conditions
458 ASSERT(buffer.Get() != nullptr);
459 ASSERT(buffer.GetLength() > 0);
460 ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength));
461
462 // Get whether we're normalized
463 if (m_write_buffer.IsNormalized()) {
464 buffer.SetNormalized();
465 } else {
466 buffer.SetNotNormalized();
467 }
468
469 // Set write buffer
470 m_write_buffer = std::move(buffer);
471 m_str = m_write_buffer.Get();
472 }
473
474 constexpr void SetReadOnlyBuffer(const char* buffer) {
475 m_str = buffer;
476 m_write_buffer.ResetBuffer();
477 }
478
479 Result Preallocate(size_t length) {
480 // Allocate additional space, if needed
481 if (length > m_write_buffer.GetLength()) {
482 // Allocate buffer
483 const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength);
484 auto buf = WriteBuffer::Make(size);
485 R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique);
486
487 // Set write buffer
488 this->SetModifiableBuffer(std::move(buf));
489 }
490
491 R_SUCCEED();
492 }
493
494 Result InitializeImpl(const char* path, size_t size) {
495 if (size > 0 && path[0]) {
496 // Pre allocate a buffer for the path
497 R_TRY(this->Preallocate(size + 1));
498
499 // Copy the path
500 const size_t copied = Strlcpy<char>(m_write_buffer.Get(), path, size + 1);
501 R_UNLESS(copied >= size, ResultUnexpectedInPathA);
502 } else {
503 // We can just clear the buffer
504 this->ClearBuffer();
505 }
506
507 R_SUCCEED();
508 }
509
510 constexpr char* GetWriteBuffer() {
511 ASSERT(m_write_buffer.Get() != nullptr);
512 return m_write_buffer.Get();
513 }
514
515 constexpr size_t GetWriteBufferLength() const {
516 return m_write_buffer.GetLength();
517 }
518
519 constexpr bool IsNormalized() const {
520 return m_write_buffer.IsNormalized();
521 }
522
523 constexpr void SetNormalized() {
524 m_write_buffer.SetNormalized();
525 }
526
527 constexpr void SetNotNormalized() {
528 m_write_buffer.SetNotNormalized();
529 }
530
531public:
532 bool operator==(const FileSys::Path& rhs) const {
533 return std::strcmp(this->GetString(), rhs.GetString()) == 0;
534 }
535 bool operator!=(const FileSys::Path& rhs) const {
536 return !(*this == rhs);
537 }
538 bool operator==(const char* p) const {
539 return std::strcmp(this->GetString(), p) == 0;
540 }
541 bool operator!=(const char* p) const {
542 return !(*this == p);
543 }
544};
545
546inline Result SetUpFixedPath(FileSys::Path* out, const char* s) {
547 // Verify the path is normalized
548 bool normalized;
549 size_t dummy;
550 R_TRY(PathNormalizer::IsNormalized(std::addressof(normalized), std::addressof(dummy), s));
551
552 R_UNLESS(normalized, ResultInvalidPathFormat);
553
554 // Set the fixed path
555 R_RETURN(out->SetShallowBuffer(s));
556}
557
558constexpr inline bool IsWindowsDriveRootPath(const FileSys::Path& path) {
559 const char* const str = path.GetString();
560 return IsWindowsDrive(str) &&
561 (str[2] == StringTraits::DirectorySeparator ||
562 str[2] == StringTraits::AlternateDirectorySeparator) &&
563 str[3] == StringTraits::NullTerminator;
564}
565
566} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/assert.h"
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/scope_exit.h"
10#include "core/file_sys/fs_directory.h"
11#include "core/file_sys/fs_memory_management.h"
12#include "core/file_sys/fs_string_util.h"
13#include "core/hle/result.h"
14
15namespace FileSys {
16
17constexpr inline size_t MountNameLengthMax = 15;
18
19namespace StringTraits {
20
21constexpr inline char DirectorySeparator = '/';
22constexpr inline char DriveSeparator = ':';
23constexpr inline char Dot = '.';
24constexpr inline char NullTerminator = '\x00';
25
26constexpr inline char AlternateDirectorySeparator = '\\';
27
28constexpr inline const char InvalidCharacters[6] = {':', '*', '?', '<', '>', '|'};
29constexpr inline const char InvalidCharactersForHostName[6] = {':', '*', '<', '>', '|', '$'};
30constexpr inline const char InvalidCharactersForMountName[5] = {'*', '?', '<', '>', '|'};
31
32namespace impl {
33
34template <const char* InvalidCharacterSet, size_t NumInvalidCharacters>
35consteval u64 MakeInvalidCharacterMask(size_t n) {
36 u64 mask = 0;
37 for (size_t i = 0; i < NumInvalidCharacters; ++i) {
38 if ((static_cast<u64>(InvalidCharacterSet[i]) >> 6) == n) {
39 mask |= static_cast<u64>(1) << (static_cast<u64>(InvalidCharacterSet[i]) & 0x3F);
40 }
41 }
42 return mask;
43}
44
45template <const char* InvalidCharacterSet, size_t NumInvalidCharacters>
46constexpr bool IsInvalidCharacterImpl(char c) {
47 constexpr u64 Masks[4] = {
48 MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(0),
49 MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(1),
50 MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(2),
51 MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(3)};
52
53 return (Masks[static_cast<u64>(c) >> 6] &
54 (static_cast<u64>(1) << (static_cast<u64>(c) & 0x3F))) != 0;
55}
56
57} // namespace impl
58
59constexpr bool IsInvalidCharacter(char c) {
60 return impl::IsInvalidCharacterImpl<InvalidCharacters, Common::Size(InvalidCharacters)>(c);
61}
62constexpr bool IsInvalidCharacterForHostName(char c) {
63 return impl::IsInvalidCharacterImpl<InvalidCharactersForHostName,
64 Common::Size(InvalidCharactersForHostName)>(c);
65}
66constexpr bool IsInvalidCharacterForMountName(char c) {
67 return impl::IsInvalidCharacterImpl<InvalidCharactersForMountName,
68 Common::Size(InvalidCharactersForMountName)>(c);
69}
70
71} // namespace StringTraits
72
73constexpr inline size_t WindowsDriveLength = 2;
74constexpr inline size_t UncPathPrefixLength = 2;
75constexpr inline size_t DosDevicePathPrefixLength = 4;
76
77class PathFlags {
78private:
79 static constexpr u32 WindowsPathFlag = (1 << 0);
80 static constexpr u32 RelativePathFlag = (1 << 1);
81 static constexpr u32 EmptyPathFlag = (1 << 2);
82 static constexpr u32 MountNameFlag = (1 << 3);
83 static constexpr u32 BackslashFlag = (1 << 4);
84 static constexpr u32 AllCharactersFlag = (1 << 5);
85
86private:
87 u32 m_value;
88
89public:
90 constexpr PathFlags() : m_value(0) { /* ... */
91 }
92
93#define DECLARE_PATH_FLAG_HANDLER(__WHICH__) \
94 constexpr bool Is##__WHICH__##Allowed() const { return (m_value & __WHICH__##Flag) != 0; } \
95 constexpr void Allow##__WHICH__() { m_value |= __WHICH__##Flag; }
96
97 DECLARE_PATH_FLAG_HANDLER(WindowsPath)
98 DECLARE_PATH_FLAG_HANDLER(RelativePath)
99 DECLARE_PATH_FLAG_HANDLER(EmptyPath)
100 DECLARE_PATH_FLAG_HANDLER(MountName)
101 DECLARE_PATH_FLAG_HANDLER(Backslash)
102 DECLARE_PATH_FLAG_HANDLER(AllCharacters)
103
104#undef DECLARE_PATH_FLAG_HANDLER
105};
106
107template <typename T>
108 requires(std::same_as<T, char> || std::same_as<T, wchar_t>)
109constexpr inline bool IsDosDevicePath(const T* path) {
110 ASSERT(path != nullptr);
111
112 using namespace StringTraits;
113
114 return path[0] == AlternateDirectorySeparator && path[1] == AlternateDirectorySeparator &&
115 (path[2] == Dot || path[2] == '?') &&
116 (path[3] == DirectorySeparator || path[3] == AlternateDirectorySeparator);
117}
118
119template <typename T>
120 requires(std::same_as<T, char> || std::same_as<T, wchar_t>)
121constexpr inline bool IsUncPath(const T* path, bool allow_forward_slash = true,
122 bool allow_back_slash = true) {
123 ASSERT(path != nullptr);
124
125 using namespace StringTraits;
126
127 return (allow_forward_slash && path[0] == DirectorySeparator &&
128 path[1] == DirectorySeparator) ||
129 (allow_back_slash && path[0] == AlternateDirectorySeparator &&
130 path[1] == AlternateDirectorySeparator);
131}
132
133constexpr inline bool IsWindowsDrive(const char* path) {
134 ASSERT(path != nullptr);
135
136 return (('a' <= path[0] && path[0] <= 'z') || ('A' <= path[0] && path[0] <= 'Z')) &&
137 path[1] == StringTraits::DriveSeparator;
138}
139
140constexpr inline bool IsWindowsPath(const char* path, bool allow_forward_slash_unc) {
141 return IsWindowsDrive(path) || IsDosDevicePath(path) ||
142 IsUncPath(path, allow_forward_slash_unc, true);
143}
144
145constexpr inline int GetWindowsSkipLength(const char* path) {
146 if (IsDosDevicePath(path)) {
147 return DosDevicePathPrefixLength;
148 } else if (IsWindowsDrive(path)) {
149 return WindowsDriveLength;
150 } else if (IsUncPath(path)) {
151 return UncPathPrefixLength;
152 } else {
153 return 0;
154 }
155}
156
157constexpr inline bool IsPathAbsolute(const char* path) {
158 return IsWindowsPath(path, false) || path[0] == StringTraits::DirectorySeparator;
159}
160
161constexpr inline bool IsPathRelative(const char* path) {
162 return path[0] && !IsPathAbsolute(path);
163}
164
165constexpr inline bool IsCurrentDirectory(const char* path) {
166 return path[0] == StringTraits::Dot &&
167 (path[1] == StringTraits::NullTerminator || path[1] == StringTraits::DirectorySeparator);
168}
169
170constexpr inline bool IsParentDirectory(const char* path) {
171 return path[0] == StringTraits::Dot && path[1] == StringTraits::Dot &&
172 (path[2] == StringTraits::NullTerminator || path[2] == StringTraits::DirectorySeparator);
173}
174
175constexpr inline bool IsPathStartWithCurrentDirectory(const char* path) {
176 return IsCurrentDirectory(path) || IsParentDirectory(path);
177}
178
179constexpr inline bool IsSubPath(const char* lhs, const char* rhs) {
180 // Check pre-conditions
181 ASSERT(lhs != nullptr);
182 ASSERT(rhs != nullptr);
183
184 // Import StringTraits names for current scope
185 using namespace StringTraits;
186
187 // Special case certain paths
188 if (IsUncPath(lhs) && !IsUncPath(rhs)) {
189 return false;
190 }
191 if (!IsUncPath(lhs) && IsUncPath(rhs)) {
192 return false;
193 }
194
195 if (lhs[0] == DirectorySeparator && lhs[1] == NullTerminator && rhs[0] == DirectorySeparator &&
196 rhs[1] != NullTerminator) {
197 return true;
198 }
199 if (rhs[0] == DirectorySeparator && rhs[1] == NullTerminator && lhs[0] == DirectorySeparator &&
200 lhs[1] != NullTerminator) {
201 return true;
202 }
203
204 // Check subpath
205 for (size_t i = 0; /* ... */; ++i) {
206 if (lhs[i] == NullTerminator) {
207 return rhs[i] == DirectorySeparator;
208 } else if (rhs[i] == NullTerminator) {
209 return lhs[i] == DirectorySeparator;
210 } else if (lhs[i] != rhs[i]) {
211 return false;
212 }
213 }
214}
215
216// Path utilities
217constexpr inline void Replace(char* dst, size_t dst_size, char old_char, char new_char) {
218 ASSERT(dst != nullptr);
219 for (char* cur = dst; cur < dst + dst_size && *cur; ++cur) {
220 if (*cur == old_char) {
221 *cur = new_char;
222 }
223 }
224}
225
226constexpr inline Result CheckUtf8(const char* s) {
227 // Check pre-conditions
228 ASSERT(s != nullptr);
229
230 // Iterate, checking for utf8-validity
231 while (*s) {
232 char utf8_buf[4] = {};
233
234 const auto pick_res = PickOutCharacterFromUtf8String(utf8_buf, std::addressof(s));
235 R_UNLESS(pick_res == CharacterEncodingResult_Success, ResultInvalidPathFormat);
236
237 u32 dummy;
238 const auto cvt_res = ConvertCharacterUtf8ToUtf32(std::addressof(dummy), utf8_buf);
239 R_UNLESS(cvt_res == CharacterEncodingResult_Success, ResultInvalidPathFormat);
240 }
241
242 R_SUCCEED();
243}
244
245// Path formatting
246class PathNormalizer {
247private:
248 enum class PathState {
249 Start,
250 Normal,
251 FirstSeparator,
252 Separator,
253 CurrentDir,
254 ParentDir,
255 };
256
257private:
258 static constexpr void ReplaceParentDirectoryPath(char* dst, const char* src) {
259 // Use StringTraits names for remainder of scope
260 using namespace StringTraits;
261
262 // Start with a dir-separator
263 dst[0] = DirectorySeparator;
264
265 auto i = 1;
266 while (src[i] != NullTerminator) {
267 if ((src[i - 1] == DirectorySeparator || src[i - 1] == AlternateDirectorySeparator) &&
268 src[i + 0] == Dot && src[i + 1] == Dot &&
269 (src[i + 2] == DirectorySeparator || src[i + 2] == AlternateDirectorySeparator)) {
270 dst[i - 1] = DirectorySeparator;
271 dst[i + 0] = Dot;
272 dst[i + 1] = Dot;
273 dst[i + 2] = DirectorySeparator;
274 i += 3;
275 } else {
276 if (src[i - 1] == AlternateDirectorySeparator && src[i + 0] == Dot &&
277 src[i + 1] == Dot && src[i + 2] == NullTerminator) {
278 dst[i - 1] = DirectorySeparator;
279 dst[i + 0] = Dot;
280 dst[i + 1] = Dot;
281 i += 2;
282 break;
283 }
284
285 dst[i] = src[i];
286 ++i;
287 }
288 }
289
290 dst[i] = StringTraits::NullTerminator;
291 }
292
293public:
294 static constexpr bool IsParentDirectoryPathReplacementNeeded(const char* path) {
295 // Use StringTraits names for remainder of scope
296 using namespace StringTraits;
297
298 if (path[0] != DirectorySeparator && path[0] != AlternateDirectorySeparator) {
299 return false;
300 }
301
302 // Check to find a parent reference using alternate separators
303 if (path[0] != NullTerminator && path[1] != NullTerminator && path[2] != NullTerminator) {
304 size_t i;
305 for (i = 0; path[i + 3] != NullTerminator; ++path) {
306 if (path[i + 1] != Dot || path[i + 2] != Dot) {
307 continue;
308 }
309
310 const char c0 = path[i + 0];
311 const char c3 = path[i + 3];
312
313 if (c0 == AlternateDirectorySeparator &&
314 (c3 == DirectorySeparator || c3 == AlternateDirectorySeparator ||
315 c3 == NullTerminator)) {
316 return true;
317 }
318
319 if (c3 == AlternateDirectorySeparator &&
320 (c0 == DirectorySeparator || c0 == AlternateDirectorySeparator)) {
321 return true;
322 }
323 }
324
325 if (path[i + 0] == AlternateDirectorySeparator && path[i + 1] == Dot &&
326 path[i + 2] == Dot /* && path[i + 3] == NullTerminator */) {
327 return true;
328 }
329 }
330
331 return false;
332 }
333
334 static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path,
335 bool allow_all_characters = false) {
336 // Use StringTraits names for remainder of scope
337 using namespace StringTraits;
338
339 // Parse the path
340 auto state = PathState::Start;
341 size_t len = 0;
342 while (path[len] != NullTerminator) {
343 // Get the current character
344 const char c = path[len++];
345
346 // Check the current character is valid
347 if (!allow_all_characters && state != PathState::Start) {
348 R_UNLESS(!IsInvalidCharacter(c), ResultInvalidCharacter);
349 }
350
351 // Process depending on current state
352 switch (state) {
353 // Import the PathState enums for convenience
354 using enum PathState;
355
356 case Start:
357 R_UNLESS(c == DirectorySeparator, ResultInvalidPathFormat);
358 state = FirstSeparator;
359 break;
360 case Normal:
361 if (c == DirectorySeparator) {
362 state = Separator;
363 }
364 break;
365 case FirstSeparator:
366 case Separator:
367 if (c == DirectorySeparator) {
368 *out = false;
369 R_SUCCEED();
370 }
371
372 if (c == Dot) {
373 state = CurrentDir;
374 } else {
375 state = Normal;
376 }
377 break;
378 case CurrentDir:
379 if (c == DirectorySeparator) {
380 *out = false;
381 R_SUCCEED();
382 }
383
384 if (c == Dot) {
385 state = ParentDir;
386 } else {
387 state = Normal;
388 }
389 break;
390 case ParentDir:
391 if (c == DirectorySeparator) {
392 *out = false;
393 R_SUCCEED();
394 }
395
396 state = Normal;
397 break;
398 default:
399 UNREACHABLE();
400 break;
401 }
402 }
403
404 // Check the final state
405 switch (state) {
406 // Import the PathState enums for convenience
407 using enum PathState;
408 case Start:
409 R_THROW(ResultInvalidPathFormat);
410 case Normal:
411 case FirstSeparator:
412 *out = true;
413 break;
414 case Separator:
415 case CurrentDir:
416 case ParentDir:
417 *out = false;
418 break;
419 default:
420 UNREACHABLE();
421 break;
422 }
423
424 // Set the output length
425 *out_len = len;
426 R_SUCCEED();
427 }
428
429 static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size,
430 bool is_windows_path, bool is_drive_relative_path,
431 bool allow_all_characters = false) {
432 // Use StringTraits names for remainder of scope
433 using namespace StringTraits;
434
435 // Prepare to iterate
436 const char* cur_path = path;
437 size_t total_len = 0;
438
439 // If path begins with a separator, check that we're not drive relative
440 if (cur_path[0] != DirectorySeparator) {
441 R_UNLESS(is_drive_relative_path, ResultInvalidPathFormat);
442
443 dst[total_len++] = DirectorySeparator;
444 }
445
446 // We're going to need to do path replacement, potentially
447 char* replacement_path = nullptr;
448 size_t replacement_path_size = 0;
449
450 SCOPE_EXIT({
451 if (replacement_path != nullptr) {
452 if (std::is_constant_evaluated()) {
453 delete[] replacement_path;
454 } else {
455 Deallocate(replacement_path, replacement_path_size);
456 }
457 }
458 });
459
460 // Perform path replacement, if necessary
461 if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
462 if (std::is_constant_evaluated()) {
463 replacement_path_size = EntryNameLengthMax + 1;
464 replacement_path = new char[replacement_path_size];
465 } else {
466 replacement_path_size = EntryNameLengthMax + 1;
467 replacement_path = static_cast<char*>(Allocate(replacement_path_size));
468 }
469
470 ReplaceParentDirectoryPath(replacement_path, cur_path);
471
472 cur_path = replacement_path;
473 }
474
475 // Iterate, normalizing path components
476 bool skip_next_sep = false;
477 size_t i = 0;
478
479 while (cur_path[i] != NullTerminator) {
480 // Process a directory separator, if we run into one
481 if (cur_path[i] == DirectorySeparator) {
482 // Swallow separators
483 do {
484 ++i;
485 } while (cur_path[i] == DirectorySeparator);
486
487 // Check if we hit end of string
488 if (cur_path[i] == NullTerminator) {
489 break;
490 }
491
492 // If we aren't skipping the separator, write it, checking that we remain in bounds.
493 if (!skip_next_sep) {
494 if (total_len + 1 == max_out_size) {
495 dst[total_len] = NullTerminator;
496 *out_len = total_len;
497 R_THROW(ResultTooLongPath);
498 }
499
500 dst[total_len++] = DirectorySeparator;
501 }
502
503 // Don't skip the next separator
504 skip_next_sep = false;
505 }
506
507 // Get the length of the current directory component
508 size_t dir_len = 0;
509 while (cur_path[i + dir_len] != DirectorySeparator &&
510 cur_path[i + dir_len] != NullTerminator) {
511 // Check for validity
512 if (!allow_all_characters) {
513 R_UNLESS(!IsInvalidCharacter(cur_path[i + dir_len]), ResultInvalidCharacter);
514 }
515
516 ++dir_len;
517 }
518
519 // Handle the current dir component
520 if (IsCurrentDirectory(cur_path + i)) {
521 skip_next_sep = true;
522 } else if (IsParentDirectory(cur_path + i)) {
523 // We should have just written a separator
524 ASSERT(dst[total_len - 1] == DirectorySeparator);
525
526 // We should have started with a separator, for non-windows paths
527 if (!is_windows_path) {
528 ASSERT(dst[0] == DirectorySeparator);
529 }
530
531 // Remove the previous component
532 if (total_len == 1) {
533 R_UNLESS(is_windows_path, ResultDirectoryUnobtainable);
534
535 --total_len;
536 } else {
537 total_len -= 2;
538
539 do {
540 if (dst[total_len] == DirectorySeparator) {
541 break;
542 }
543 } while ((--total_len) != 0);
544 }
545
546 // We should be pointing to a directory separator, for non-windows paths
547 if (!is_windows_path) {
548 ASSERT(dst[total_len] == DirectorySeparator);
549 }
550
551 // We should remain in bounds
552 ASSERT(total_len < max_out_size);
553 } else {
554 // Copy, possibly truncating
555 if (total_len + dir_len + 1 > max_out_size) {
556 const size_t copy_len = max_out_size - (total_len + 1);
557
558 for (size_t j = 0; j < copy_len; ++j) {
559 dst[total_len++] = cur_path[i + j];
560 }
561
562 dst[total_len] = NullTerminator;
563 *out_len = total_len;
564 R_THROW(ResultTooLongPath);
565 }
566
567 for (size_t j = 0; j < dir_len; ++j) {
568 dst[total_len++] = cur_path[i + j];
569 }
570 }
571
572 // Advance past the current directory component
573 i += dir_len;
574 }
575
576 if (skip_next_sep) {
577 --total_len;
578 }
579
580 if (total_len == 0 && max_out_size != 0) {
581 total_len = 1;
582 dst[0] = DirectorySeparator;
583 }
584
585 // NOTE: Probable nintendo bug, as max_out_size must be at least total_len + 1 for the null
586 // terminator.
587 R_UNLESS(max_out_size >= total_len - 1, ResultTooLongPath);
588
589 dst[total_len] = NullTerminator;
590
591 // Check that the result path is normalized
592 bool is_normalized;
593 size_t dummy;
594 R_TRY(IsNormalized(std::addressof(is_normalized), std::addressof(dummy), dst,
595 allow_all_characters));
596
597 // Assert that the result path is normalized
598 ASSERT(is_normalized);
599
600 // Set the output length
601 *out_len = total_len;
602 R_SUCCEED();
603 }
604};
605
606class PathFormatter {
607private:
608 static constexpr Result CheckSharedName(const char* name, size_t len) {
609 // Use StringTraits names for remainder of scope
610 using namespace StringTraits;
611
612 if (len == 1) {
613 R_UNLESS(name[0] != Dot, ResultInvalidPathFormat);
614 } else if (len == 2) {
615 R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat);
616 }
617
618 for (size_t i = 0; i < len; ++i) {
619 R_UNLESS(!IsInvalidCharacter(name[i]), ResultInvalidCharacter);
620 }
621
622 R_SUCCEED();
623 }
624
625 static constexpr Result CheckHostName(const char* name, size_t len) {
626 // Use StringTraits names for remainder of scope
627 using namespace StringTraits;
628
629 if (len == 2) {
630 R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat);
631 }
632
633 for (size_t i = 0; i < len; ++i) {
634 R_UNLESS(!IsInvalidCharacterForHostName(name[i]), ResultInvalidCharacter);
635 }
636
637 R_SUCCEED();
638 }
639
640 static constexpr Result CheckInvalidBackslash(bool* out_contains_backslash, const char* path,
641 bool allow_backslash) {
642 // Use StringTraits names for remainder of scope
643 using namespace StringTraits;
644
645 // Default to no backslashes, so we can just write if we see one
646 *out_contains_backslash = false;
647
648 while (*path != NullTerminator) {
649 if (*(path++) == AlternateDirectorySeparator) {
650 *out_contains_backslash = true;
651
652 R_UNLESS(allow_backslash, ResultInvalidCharacter);
653 }
654 }
655
656 R_SUCCEED();
657 }
658
659public:
660 static constexpr Result CheckPathFormat(const char* path, const PathFlags& flags) {
661 bool normalized;
662 size_t len;
663 R_RETURN(IsNormalized(std::addressof(normalized), std::addressof(len), path, flags));
664 }
665
666 static constexpr Result SkipMountName(const char** out, size_t* out_len, const char* path) {
667 R_RETURN(ParseMountName(out, out_len, nullptr, 0, path));
668 }
669
670 static constexpr Result ParseMountName(const char** out, size_t* out_len, char* out_mount_name,
671 size_t out_mount_name_buffer_size, const char* path) {
672 // Check pre-conditions
673 ASSERT(path != nullptr);
674 ASSERT(out_len != nullptr);
675 ASSERT(out != nullptr);
676 ASSERT((out_mount_name == nullptr) == (out_mount_name_buffer_size == 0));
677
678 // Use StringTraits names for remainder of scope
679 using namespace StringTraits;
680
681 // Determine max mount length
682 const auto max_mount_len =
683 out_mount_name_buffer_size == 0
684 ? MountNameLengthMax + 1
685 : std::min(MountNameLengthMax + 1, out_mount_name_buffer_size);
686
687 // Parse the path until we see a drive separator
688 size_t mount_len = 0;
689 for (/* ... */; mount_len < max_mount_len && path[mount_len]; ++mount_len) {
690 const char c = path[mount_len];
691
692 // If we see a drive separator, advance, then we're done with the pre-drive separator
693 // part of the mount.
694 if (c == DriveSeparator) {
695 ++mount_len;
696 break;
697 }
698
699 // If we see a directory separator, we're not in a mount name
700 if (c == DirectorySeparator || c == AlternateDirectorySeparator) {
701 *out = path;
702 *out_len = 0;
703 R_SUCCEED();
704 }
705 }
706
707 // Check to be sure we're actually looking at a mount name
708 if (mount_len <= 2 || path[mount_len - 1] != DriveSeparator) {
709 *out = path;
710 *out_len = 0;
711 R_SUCCEED();
712 }
713
714 // Check that all characters in the mount name are allowable
715 for (size_t i = 0; i < mount_len; ++i) {
716 R_UNLESS(!IsInvalidCharacterForMountName(path[i]), ResultInvalidCharacter);
717 }
718
719 // Copy out the mount name
720 if (out_mount_name_buffer_size > 0) {
721 R_UNLESS(mount_len < out_mount_name_buffer_size, ResultTooLongPath);
722
723 for (size_t i = 0; i < mount_len; ++i) {
724 out_mount_name[i] = path[i];
725 }
726 out_mount_name[mount_len] = NullTerminator;
727 }
728
729 // Set the output
730 *out = path + mount_len;
731 *out_len = mount_len;
732 R_SUCCEED();
733 }
734
735 static constexpr Result SkipRelativeDotPath(const char** out, size_t* out_len,
736 const char* path) {
737 R_RETURN(ParseRelativeDotPath(out, out_len, nullptr, 0, path));
738 }
739
740 static constexpr Result ParseRelativeDotPath(const char** out, size_t* out_len,
741 char* out_relative,
742 size_t out_relative_buffer_size,
743 const char* path) {
744 // Check pre-conditions
745 ASSERT(path != nullptr);
746 ASSERT(out_len != nullptr);
747 ASSERT(out != nullptr);
748 ASSERT((out_relative == nullptr) == (out_relative_buffer_size == 0));
749
750 // Use StringTraits names for remainder of scope
751 using namespace StringTraits;
752
753 // Initialize the output buffer, if we have one
754 if (out_relative_buffer_size > 0) {
755 out_relative[0] = NullTerminator;
756 }
757
758 // Check if the path is relative
759 if (path[0] == Dot && (path[1] == NullTerminator || path[1] == DirectorySeparator ||
760 path[1] == AlternateDirectorySeparator)) {
761 if (out_relative_buffer_size > 0) {
762 R_UNLESS(out_relative_buffer_size >= 2, ResultTooLongPath);
763
764 out_relative[0] = Dot;
765 out_relative[1] = NullTerminator;
766 }
767
768 *out = path + 1;
769 *out_len = 1;
770 R_SUCCEED();
771 }
772
773 // Ensure the path isn't a parent directory
774 R_UNLESS(!(path[0] == Dot && path[1] == Dot), ResultDirectoryUnobtainable);
775
776 // There was no relative dot path
777 *out = path;
778 *out_len = 0;
779 R_SUCCEED();
780 }
781
782 static constexpr Result SkipWindowsPath(const char** out, size_t* out_len, bool* out_normalized,
783 const char* path, bool has_mount_name) {
784 // We're normalized if and only if the parsing doesn't throw ResultNotNormalized()
785 *out_normalized = true;
786
787 R_TRY_CATCH(ParseWindowsPath(out, out_len, nullptr, 0, path, has_mount_name)) {
788 R_CATCH(ResultNotNormalized) {
789 *out_normalized = false;
790 }
791 }
792 R_END_TRY_CATCH;
793 ON_RESULT_INCLUDED(ResultNotNormalized) {
794 *out_normalized = false;
795 };
796
797 R_SUCCEED();
798 }
799
800 static constexpr Result ParseWindowsPath(const char** out, size_t* out_len, char* out_win,
801 size_t out_win_buffer_size, const char* path,
802 bool has_mount_name) {
803 // Check pre-conditions
804 ASSERT(path != nullptr);
805 ASSERT(out_len != nullptr);
806 ASSERT(out != nullptr);
807 ASSERT((out_win == nullptr) == (out_win_buffer_size == 0));
808
809 // Use StringTraits names for remainder of scope
810 using namespace StringTraits;
811
812 // Initialize the output buffer, if we have one
813 if (out_win_buffer_size > 0) {
814 out_win[0] = NullTerminator;
815 }
816
817 // Handle path start
818 const char* cur_path = path;
819 if (has_mount_name && path[0] == DirectorySeparator) {
820 if (path[1] == AlternateDirectorySeparator && path[2] == AlternateDirectorySeparator) {
821 R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized);
822
823 ++cur_path;
824 } else if (IsWindowsDrive(path + 1)) {
825 R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized);
826
827 ++cur_path;
828 }
829 }
830
831 // Handle windows drive
832 if (IsWindowsDrive(cur_path)) {
833 // Parse up to separator
834 size_t win_path_len = WindowsDriveLength;
835 for (/* ... */; cur_path[win_path_len] != NullTerminator; ++win_path_len) {
836 R_UNLESS(!IsInvalidCharacter(cur_path[win_path_len]), ResultInvalidCharacter);
837
838 if (cur_path[win_path_len] == DirectorySeparator ||
839 cur_path[win_path_len] == AlternateDirectorySeparator) {
840 break;
841 }
842 }
843
844 // Ensure that we're normalized, if we're required to be
845 if (out_win_buffer_size == 0) {
846 for (size_t i = 0; i < win_path_len; ++i) {
847 R_UNLESS(cur_path[i] != AlternateDirectorySeparator, ResultNotNormalized);
848 }
849 } else {
850 // Ensure we can copy into the normalized buffer
851 R_UNLESS(win_path_len < out_win_buffer_size, ResultTooLongPath);
852
853 for (size_t i = 0; i < win_path_len; ++i) {
854 out_win[i] = cur_path[i];
855 }
856 out_win[win_path_len] = NullTerminator;
857
858 Replace(out_win, win_path_len, AlternateDirectorySeparator, DirectorySeparator);
859 }
860
861 *out = cur_path + win_path_len;
862 *out_len = win_path_len;
863 R_SUCCEED();
864 }
865
866 // Handle DOS device
867 if (IsDosDevicePath(cur_path)) {
868 size_t dos_prefix_len = DosDevicePathPrefixLength;
869
870 if (IsWindowsDrive(cur_path + dos_prefix_len)) {
871 dos_prefix_len += WindowsDriveLength;
872 } else {
873 --dos_prefix_len;
874 }
875
876 if (out_win_buffer_size > 0) {
877 // Ensure we can copy into the normalized buffer
878 R_UNLESS(dos_prefix_len < out_win_buffer_size, ResultTooLongPath);
879
880 for (size_t i = 0; i < dos_prefix_len; ++i) {
881 out_win[i] = cur_path[i];
882 }
883 out_win[dos_prefix_len] = NullTerminator;
884
885 Replace(out_win, dos_prefix_len, DirectorySeparator, AlternateDirectorySeparator);
886 }
887
888 *out = cur_path + dos_prefix_len;
889 *out_len = dos_prefix_len;
890 R_SUCCEED();
891 }
892
893 // Handle UNC path
894 if (IsUncPath(cur_path, false, true)) {
895 const char* final_path = cur_path;
896
897 R_UNLESS(cur_path[UncPathPrefixLength] != DirectorySeparator, ResultInvalidPathFormat);
898 R_UNLESS(cur_path[UncPathPrefixLength] != AlternateDirectorySeparator,
899 ResultInvalidPathFormat);
900
901 size_t cur_component_offset = 0;
902 size_t pos = UncPathPrefixLength;
903 for (/* ... */; cur_path[pos] != NullTerminator; ++pos) {
904 if (cur_path[pos] == DirectorySeparator ||
905 cur_path[pos] == AlternateDirectorySeparator) {
906 if (cur_component_offset != 0) {
907 R_TRY(CheckSharedName(cur_path + cur_component_offset,
908 pos - cur_component_offset));
909
910 final_path = cur_path + pos;
911 break;
912 }
913
914 R_UNLESS(cur_path[pos + 1] != DirectorySeparator, ResultInvalidPathFormat);
915 R_UNLESS(cur_path[pos + 1] != AlternateDirectorySeparator,
916 ResultInvalidPathFormat);
917
918 R_TRY(CheckHostName(cur_path + 2, pos - 2));
919
920 cur_component_offset = pos + 1;
921 }
922 }
923
924 R_UNLESS(cur_component_offset != pos, ResultInvalidPathFormat);
925
926 if (cur_component_offset != 0 && final_path == cur_path) {
927 R_TRY(CheckSharedName(cur_path + cur_component_offset, pos - cur_component_offset));
928
929 final_path = cur_path + pos;
930 }
931
932 size_t unc_prefix_len = final_path - cur_path;
933
934 // Ensure that we're normalized, if we're required to be
935 if (out_win_buffer_size == 0) {
936 for (size_t i = 0; i < unc_prefix_len; ++i) {
937 R_UNLESS(cur_path[i] != DirectorySeparator, ResultNotNormalized);
938 }
939 } else {
940 // Ensure we can copy into the normalized buffer
941 R_UNLESS(unc_prefix_len < out_win_buffer_size, ResultTooLongPath);
942
943 for (size_t i = 0; i < unc_prefix_len; ++i) {
944 out_win[i] = cur_path[i];
945 }
946 out_win[unc_prefix_len] = NullTerminator;
947
948 Replace(out_win, unc_prefix_len, DirectorySeparator, AlternateDirectorySeparator);
949 }
950
951 *out = cur_path + unc_prefix_len;
952 *out_len = unc_prefix_len;
953 R_SUCCEED();
954 }
955
956 // There's no windows path to parse
957 *out = path;
958 *out_len = 0;
959 R_SUCCEED();
960 }
961
962 static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path,
963 const PathFlags& flags = {}) {
964 // Ensure nothing is null
965 R_UNLESS(out != nullptr, ResultNullptrArgument);
966 R_UNLESS(out_len != nullptr, ResultNullptrArgument);
967 R_UNLESS(path != nullptr, ResultNullptrArgument);
968
969 // Verify that the path is valid utf-8
970 R_TRY(CheckUtf8(path));
971
972 // Use StringTraits names for remainder of scope
973 using namespace StringTraits;
974
975 // Handle the case where the path is empty
976 if (path[0] == NullTerminator) {
977 R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat);
978
979 *out = true;
980 *out_len = 0;
981 R_SUCCEED();
982 }
983
984 // All normalized paths start with a directory separator...unless they're windows paths,
985 // relative paths, or have mount names.
986 if (path[0] != DirectorySeparator) {
987 R_UNLESS(flags.IsWindowsPathAllowed() || flags.IsRelativePathAllowed() ||
988 flags.IsMountNameAllowed(),
989 ResultInvalidPathFormat);
990 }
991
992 // Check that the path is allowed to be a windows path, if it is
993 if (IsWindowsPath(path, false)) {
994 R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat);
995 }
996
997 // Skip past the mount name, if one is present
998 size_t total_len = 0;
999 size_t mount_name_len = 0;
1000 R_TRY(SkipMountName(std::addressof(path), std::addressof(mount_name_len), path));
1001
1002 // If we had a mount name, check that that was allowed
1003 if (mount_name_len > 0) {
1004 R_UNLESS(flags.IsMountNameAllowed(), ResultInvalidPathFormat);
1005
1006 total_len += mount_name_len;
1007 }
1008
1009 // Check that the path starts as a normalized path should
1010 if (path[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(path) &&
1011 !IsWindowsPath(path, false)) {
1012 R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat);
1013 R_UNLESS(!IsInvalidCharacter(path[0]), ResultInvalidPathFormat);
1014
1015 *out = false;
1016 R_SUCCEED();
1017 }
1018
1019 // Process relative path
1020 size_t relative_len = 0;
1021 R_TRY(SkipRelativeDotPath(std::addressof(path), std::addressof(relative_len), path));
1022
1023 // If we have a relative path, check that was allowed
1024 if (relative_len > 0) {
1025 R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat);
1026
1027 total_len += relative_len;
1028
1029 if (path[0] == NullTerminator) {
1030 *out = true;
1031 *out_len = total_len;
1032 R_SUCCEED();
1033 }
1034 }
1035
1036 // Process windows path
1037 size_t windows_len = 0;
1038 bool normalized_win = false;
1039 R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(windows_len),
1040 std::addressof(normalized_win), path, mount_name_len > 0));
1041
1042 // If the windows path wasn't normalized, we're not normalized
1043 if (!normalized_win) {
1044 R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat);
1045
1046 *out = false;
1047 R_SUCCEED();
1048 }
1049
1050 // If we had a windows path, check that was allowed
1051 if (windows_len > 0) {
1052 R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat);
1053
1054 total_len += windows_len;
1055
1056 // We can't have both a relative path and a windows path
1057 R_UNLESS(relative_len == 0, ResultInvalidPathFormat);
1058
1059 // A path ending in a windows path isn't normalized
1060 if (path[0] == NullTerminator) {
1061 *out = false;
1062 R_SUCCEED();
1063 }
1064
1065 // Check that there are no windows directory separators in the path
1066 for (size_t i = 0; path[i] != NullTerminator; ++i) {
1067 if (path[i] == AlternateDirectorySeparator) {
1068 *out = false;
1069 R_SUCCEED();
1070 }
1071 }
1072 }
1073
1074 // Check that parent directory replacement is not needed if backslashes are allowed
1075 if (flags.IsBackslashAllowed() &&
1076 PathNormalizer::IsParentDirectoryPathReplacementNeeded(path)) {
1077 *out = false;
1078 R_SUCCEED();
1079 }
1080
1081 // Check that the backslash state is valid
1082 bool is_backslash_contained = false;
1083 R_TRY(CheckInvalidBackslash(std::addressof(is_backslash_contained), path,
1084 flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed()));
1085
1086 // Check that backslashes are contained only if allowed
1087 if (is_backslash_contained && !flags.IsBackslashAllowed()) {
1088 *out = false;
1089 R_SUCCEED();
1090 }
1091
1092 // Check that the final result path is normalized
1093 size_t normal_len = 0;
1094 R_TRY(PathNormalizer::IsNormalized(out, std::addressof(normal_len), path,
1095 flags.IsAllCharactersAllowed()));
1096
1097 // Add the normal length
1098 total_len += normal_len;
1099
1100 // Set the output length
1101 *out_len = total_len;
1102 R_SUCCEED();
1103 }
1104
1105 static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
1106 const PathFlags& flags) {
1107 // Use StringTraits names for remainder of scope
1108 using namespace StringTraits;
1109
1110 // Prepare to iterate
1111 const char* src = path;
1112 size_t cur_pos = 0;
1113 bool is_windows_path = false;
1114
1115 // Check if the path is empty
1116 if (src[0] == NullTerminator) {
1117 if (dst_size != 0) {
1118 dst[0] = NullTerminator;
1119 }
1120
1121 R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat);
1122
1123 R_SUCCEED();
1124 }
1125
1126 // Handle a mount name
1127 size_t mount_name_len = 0;
1128 if (flags.IsMountNameAllowed()) {
1129 R_TRY(ParseMountName(std::addressof(src), std::addressof(mount_name_len), dst + cur_pos,
1130 dst_size - cur_pos, src));
1131
1132 cur_pos += mount_name_len;
1133 }
1134
1135 // Handle a drive-relative prefix
1136 bool is_drive_relative = false;
1137 if (src[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(src) &&
1138 !IsWindowsPath(src, false)) {
1139 R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat);
1140 R_UNLESS(!IsInvalidCharacter(src[0]), ResultInvalidPathFormat);
1141
1142 dst[cur_pos++] = Dot;
1143 is_drive_relative = true;
1144 }
1145
1146 size_t relative_len = 0;
1147 if (flags.IsRelativePathAllowed()) {
1148 R_UNLESS(cur_pos < dst_size, ResultTooLongPath);
1149
1150 R_TRY(ParseRelativeDotPath(std::addressof(src), std::addressof(relative_len),
1151 dst + cur_pos, dst_size - cur_pos, src));
1152
1153 cur_pos += relative_len;
1154
1155 if (src[0] == NullTerminator) {
1156 R_UNLESS(cur_pos < dst_size, ResultTooLongPath);
1157
1158 dst[cur_pos] = NullTerminator;
1159 R_SUCCEED();
1160 }
1161 }
1162
1163 // Handle a windows path
1164 if (flags.IsWindowsPathAllowed()) {
1165 const char* const orig = src;
1166
1167 R_UNLESS(cur_pos < dst_size, ResultTooLongPath);
1168
1169 size_t windows_len = 0;
1170 R_TRY(ParseWindowsPath(std::addressof(src), std::addressof(windows_len), dst + cur_pos,
1171 dst_size - cur_pos, src, mount_name_len != 0));
1172
1173 cur_pos += windows_len;
1174
1175 if (src[0] == NullTerminator) {
1176 /* NOTE: Bug in original code here repeated, should be checking cur_pos + 2. */
1177 R_UNLESS(cur_pos + 1 < dst_size, ResultTooLongPath);
1178
1179 dst[cur_pos + 0] = DirectorySeparator;
1180 dst[cur_pos + 1] = NullTerminator;
1181 R_SUCCEED();
1182 }
1183
1184 if ((src - orig) > 0) {
1185 is_windows_path = true;
1186 }
1187 }
1188
1189 // Check for invalid backslash
1190 bool backslash_contained = false;
1191 R_TRY(CheckInvalidBackslash(std::addressof(backslash_contained), src,
1192 flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed()));
1193
1194 // Handle backslash replacement as necessary
1195 if (backslash_contained && flags.IsWindowsPathAllowed()) {
1196 // Create a temporary buffer holding a slash-replaced version of the path.
1197 // NOTE: Nintendo unnecessarily allocates and replaces here a fully copy of the path,
1198 // despite having skipped some of it already.
1199 const size_t replaced_src_len = path_len - (src - path);
1200
1201 char* replaced_src = nullptr;
1202 SCOPE_EXIT({
1203 if (replaced_src != nullptr) {
1204 if (std::is_constant_evaluated()) {
1205 delete[] replaced_src;
1206 } else {
1207 Deallocate(replaced_src, replaced_src_len);
1208 }
1209 }
1210 });
1211
1212 if (std::is_constant_evaluated()) {
1213 replaced_src = new char[replaced_src_len];
1214 } else {
1215 replaced_src = static_cast<char*>(Allocate(replaced_src_len));
1216 }
1217
1218 Strlcpy<char>(replaced_src, src, replaced_src_len);
1219
1220 Replace(replaced_src, replaced_src_len, AlternateDirectorySeparator,
1221 DirectorySeparator);
1222
1223 size_t dummy;
1224 R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), replaced_src,
1225 dst_size - cur_pos, is_windows_path, is_drive_relative,
1226 flags.IsAllCharactersAllowed()));
1227 } else {
1228 // We can just do normalization
1229 size_t dummy;
1230 R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), src,
1231 dst_size - cur_pos, is_windows_path, is_drive_relative,
1232 flags.IsAllCharactersAllowed()));
1233 }
1234
1235 R_SUCCEED();
1236 }
1237};
1238
1239} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/assert.h"
7
8namespace FileSys {
9
10template <typename T>
11constexpr int Strlen(const T* str) {
12 ASSERT(str != nullptr);
13
14 int length = 0;
15 while (*str++) {
16 ++length;
17 }
18
19 return length;
20}
21
22template <typename T>
23constexpr int Strnlen(const T* str, int count) {
24 ASSERT(str != nullptr);
25 ASSERT(count >= 0);
26
27 int length = 0;
28 while (count-- && *str++) {
29 ++length;
30 }
31
32 return length;
33}
34
35template <typename T>
36constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
37 ASSERT(lhs != nullptr);
38 ASSERT(rhs != nullptr);
39 ASSERT(count >= 0);
40
41 if (count == 0) {
42 return 0;
43 }
44
45 T l, r;
46 do {
47 l = *(lhs++);
48 r = *(rhs++);
49 } while (l && (l == r) && (--count));
50
51 return l - r;
52}
53
54template <typename T>
55static constexpr int Strlcpy(T* dst, const T* src, int count) {
56 ASSERT(dst != nullptr);
57 ASSERT(src != nullptr);
58
59 const T* cur = src;
60 if (count > 0) {
61 while ((--count) && *cur) {
62 *(dst++) = *(cur++);
63 }
64 *dst = 0;
65 }
66
67 while (*cur) {
68 cur++;
69 }
70
71 return static_cast<int>(cur - src);
72}
73
74enum CharacterEncodingResult {
75 CharacterEncodingResult_Success = 0,
76 CharacterEncodingResult_InsufficientLength = 1,
77 CharacterEncodingResult_InvalidFormat = 2,
78};
79
80namespace impl {
81
82class CharacterEncodingHelper {
83public:
84 static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = {
85 -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,
86 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,
87 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,
88 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,
89 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,
90 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,
91 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,
92 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,
93 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,
94 };
95
96 static constexpr char GetUtf8NBytes(size_t i) {
97 return static_cast<char>(Utf8NBytesInnerTable[1 + i]);
98 }
99};
100
101} // namespace impl
102
103constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) {
104 // Check pre-conditions
105 ASSERT(dst != nullptr);
106 ASSERT(src != nullptr);
107
108 // Perform the conversion
109 const auto* p = src;
110 switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) {
111 case 1:
112 *dst = static_cast<u32>(p[0]);
113 return CharacterEncodingResult_Success;
114 case 2:
115 if ((static_cast<u32>(p[0]) & 0x1E) != 0) {
116 if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) ==
117 0) {
118 *dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
119 return CharacterEncodingResult_Success;
120 }
121 }
122 break;
123 case 3:
124 if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
125 impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
126 const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) |
127 (static_cast<u32>(p[1] & 0x3F) << 6) |
128 (static_cast<u32>(p[2] & 0x3F) << 0);
129 if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
130 *dst = c;
131 return CharacterEncodingResult_Success;
132 }
133 }
134 return CharacterEncodingResult_InvalidFormat;
135 case 4:
136 if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
137 impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 &&
138 impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
139 const u32 c =
140 (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) |
141 (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
142 if (c >= 0x10000 && c < 0x110000) {
143 *dst = c;
144 return CharacterEncodingResult_Success;
145 }
146 }
147 return CharacterEncodingResult_InvalidFormat;
148 default:
149 break;
150 }
151
152 // We failed to convert
153 return CharacterEncodingResult_InvalidFormat;
154}
155
156constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst,
157 const char** str) {
158 // Check pre-conditions
159 ASSERT(dst != nullptr);
160 ASSERT(str != nullptr);
161 ASSERT(*str != nullptr);
162
163 // Clear the output
164 dst[0] = 0;
165 dst[1] = 0;
166 dst[2] = 0;
167 dst[3] = 0;
168
169 // Perform the conversion
170 const auto* p = *str;
171 u32 c = static_cast<u32>(*p);
172 switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) {
173 case 1:
174 dst[0] = (*str)[0];
175 ++(*str);
176 break;
177 case 2:
178 if ((p[0] & 0x1E) != 0) {
179 if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) ==
180 0) {
181 c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
182 dst[0] = (*str)[0];
183 dst[1] = (*str)[1];
184 (*str) += 2;
185 break;
186 }
187 }
188 return CharacterEncodingResult_InvalidFormat;
189 case 3:
190 if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
191 impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
192 c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) |
193 (static_cast<u32>(p[2] & 0x3F) << 0);
194 if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
195 dst[0] = (*str)[0];
196 dst[1] = (*str)[1];
197 dst[2] = (*str)[2];
198 (*str) += 3;
199 break;
200 }
201 }
202 return CharacterEncodingResult_InvalidFormat;
203 case 4:
204 if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
205 impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 &&
206 impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
207 c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) |
208 (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
209 if (c >= 0x10000 && c < 0x110000) {
210 dst[0] = (*str)[0];
211 dst[1] = (*str)[1];
212 dst[2] = (*str)[2];
213 dst[3] = (*str)[3];
214 (*str) += 4;
215 break;
216 }
217 }
218 return CharacterEncodingResult_InvalidFormat;
219 default:
220 return CharacterEncodingResult_InvalidFormat;
221 }
222
223 return CharacterEncodingResult_Success;
224}
225
226} // 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 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "core/file_sys/fsmitm_romfsbuild.h" 9#include "core/file_sys/fsmitm_romfsbuild.h"
10#include "core/file_sys/ips_layer.h" 10#include "core/file_sys/ips_layer.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12#include "core/file_sys/vfs_vector.h" 12#include "core/file_sys/vfs/vfs_vector.h"
13 13
14namespace FileSys { 14namespace FileSys {
15 15
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 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11 11
12namespace FileSys { 12namespace FileSys {
13 13
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 @@
5 5
6#include "common/overflow.h" 6#include "common/overflow.h"
7#include "core/file_sys/errors.h" 7#include "core/file_sys/errors.h"
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs/vfs.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
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 @@
4#include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h" 4#include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h"
5#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h" 5#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h"
6#include "core/file_sys/fssystem/fssystem_nca_header.h" 6#include "core/file_sys/fssystem/fssystem_nca_header.h"
7#include "core/file_sys/vfs_offset.h" 7#include "core/file_sys/vfs/vfs_offset.h"
8 8
9namespace FileSys { 9namespace FileSys {
10 10
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 @@
9#include "core/crypto/key_manager.h" 9#include "core/crypto/key_manager.h"
10#include "core/file_sys/errors.h" 10#include "core/file_sys/errors.h"
11#include "core/file_sys/fssystem/fs_i_storage.h" 11#include "core/file_sys/fssystem/fs_i_storage.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs/vfs.h"
13 13
14namespace FileSys { 14namespace FileSys {
15 15
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 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/literals.h" 11#include "common/literals.h"
12 12
13#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs/vfs.h"
14#include "core/hle/result.h" 14#include "core/hle/result.h"
15 15
16namespace FileSys { 16namespace 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 @@
10#include "core/file_sys/fssystem/fssystem_bucket_tree.h" 10#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
11#include "core/file_sys/fssystem/fssystem_compression_common.h" 11#include "core/file_sys/fssystem/fssystem_compression_common.h"
12#include "core/file_sys/fssystem/fssystem_pooled_buffer.h" 12#include "core/file_sys/fssystem/fssystem_pooled_buffer.h"
13#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs/vfs.h"
14 14
15namespace FileSys { 15namespace FileSys {
16 16
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 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h" 4#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
5#include "core/file_sys/vfs_offset.h" 5#include "core/file_sys/vfs/vfs_offset.h"
6 6
7namespace FileSys { 7namespace FileSys {
8 8
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 @@
8#include "core/file_sys/fssystem/fs_types.h" 8#include "core/file_sys/fssystem/fs_types.h"
9#include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h" 9#include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h"
10#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h" 10#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h"
11#include "core/file_sys/vfs_offset.h" 11#include "core/file_sys/vfs/vfs_offset.h"
12 12
13namespace FileSys { 13namespace FileSys {
14 14
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 @@
7 7
8#include "core/file_sys/errors.h" 8#include "core/file_sys/errors.h"
9#include "core/file_sys/fssystem/fs_i_storage.h" 9#include "core/file_sys/fssystem/fs_i_storage.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11 11
12namespace FileSys { 12namespace FileSys {
13 13
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 @@
7#include "core/file_sys/fssystem/fs_i_storage.h" 7#include "core/file_sys/fssystem/fs_i_storage.h"
8#include "core/file_sys/fssystem/fssystem_bucket_tree.h" 8#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
9#include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h" 9#include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11#include "core/file_sys/vfs_offset.h" 11#include "core/file_sys/vfs/vfs_offset.h"
12 12
13namespace FileSys { 13namespace FileSys {
14 14
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 @@
5 5
6#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h" 6#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
7#include "core/file_sys/fssystem/fssystem_nca_header.h" 7#include "core/file_sys/fssystem/fssystem_nca_header.h"
8#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs/vfs_vector.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
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 @@
14#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" 14#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
15#include "core/file_sys/fssystem/fssystem_sparse_storage.h" 15#include "core/file_sys/fssystem/fssystem_sparse_storage.h"
16#include "core/file_sys/fssystem/fssystem_switch_storage.h" 16#include "core/file_sys/fssystem/fssystem_switch_storage.h"
17#include "core/file_sys/vfs_offset.h" 17#include "core/file_sys/vfs/vfs_offset.h"
18#include "core/file_sys/vfs_vector.h" 18#include "core/file_sys/vfs/vfs_vector.h"
19 19
20namespace FileSys { 20namespace FileSys {
21 21
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 @@
5 5
6#include "core/file_sys/fssystem/fssystem_compression_common.h" 6#include "core/file_sys/fssystem/fssystem_compression_common.h"
7#include "core/file_sys/fssystem/fssystem_nca_header.h" 7#include "core/file_sys/fssystem/fssystem_nca_header.h"
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs/vfs.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
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 @@
3 3
4#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h" 4#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h"
5#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" 5#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
6#include "core/file_sys/vfs_offset.h" 6#include "core/file_sys/vfs/vfs_offset.h"
7 7
8namespace FileSys { 8namespace FileSys {
9 9
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 @@
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/swap.h" 13#include "common/swap.h"
14#include "core/file_sys/ips_layer.h" 14#include "core/file_sys/ips_layer.h"
15#include "core/file_sys/vfs_vector.h" 15#include "core/file_sys/vfs/vfs_vector.h"
16 16
17namespace FileSys { 17namespace FileSys {
18 18
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 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12 12
13namespace FileSys { 13namespace FileSys {
14 14
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 @@
5 5
6#include "common/string_util.h" 6#include "common/string_util.h"
7#include "core/file_sys/kernel_executable.h" 7#include "core/file_sys/kernel_executable.h"
8#include "core/file_sys/vfs_offset.h" 8#include "core/file_sys/vfs/vfs_offset.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace FileSys { 11namespace 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 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/file_sys/vfs_types.h" 13#include "core/file_sys/vfs/vfs_types.h"
14 14
15namespace Loader { 15namespace Loader {
16enum class ResultStatus : u16; 16enum 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 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace FileSys {
10
11enum class Mode : u32 {
12 Read = 1 << 0,
13 Write = 1 << 1,
14 ReadWrite = Read | Write,
15 Append = 1 << 2,
16 ReadAppend = Read | Append,
17 WriteAppend = Write | Append,
18 All = ReadWrite | Append,
19};
20
21DECLARE_ENUM_FLAG_OPERATORS(Mode)
22
23} // 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 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/swap.h" 7#include "common/swap.h"
8#include "core/file_sys/nca_metadata.h" 8#include "core/file_sys/nca_metadata.h"
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs/vfs.h"
10 10
11namespace FileSys { 11namespace FileSys {
12 12
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 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/file_sys/vfs_types.h" 11#include "core/file_sys/vfs/vfs_types.h"
12 12
13namespace FileSys { 13namespace FileSys {
14class CNMT; 14class 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 @@
9 9
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/file_sys/partition_filesystem.h" 11#include "core/file_sys/partition_filesystem.h"
12#include "core/file_sys/vfs_offset.h" 12#include "core/file_sys/vfs/vfs_offset.h"
13#include "core/loader/loader.h" 13#include "core/loader/loader.h"
14 14
15namespace FileSys { 15namespace 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 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs/vfs.h"
13 13
14namespace Loader { 14namespace Loader {
15enum class ResultStatus : u16; 15enum class ResultStatus : u16;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 612122224..21d45235e 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -21,9 +21,9 @@
21#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
22#include "core/file_sys/registered_cache.h" 22#include "core/file_sys/registered_cache.h"
23#include "core/file_sys/romfs.h" 23#include "core/file_sys/romfs.h"
24#include "core/file_sys/vfs_cached.h" 24#include "core/file_sys/vfs/vfs_cached.h"
25#include "core/file_sys/vfs_layered.h" 25#include "core/file_sys/vfs/vfs_layered.h"
26#include "core/file_sys/vfs_vector.h" 26#include "core/file_sys/vfs/vfs_vector.h"
27#include "core/hle/service/filesystem/filesystem.h" 27#include "core/hle/service/filesystem/filesystem.h"
28#include "core/hle/service/ns/language.h" 28#include "core/hle/service/ns/language.h"
29#include "core/hle/service/set/settings_server.h" 29#include "core/hle/service/set/settings_server.h"
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 2601b8217..552c0fbe2 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -9,7 +9,7 @@
9#include <string> 9#include <string>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/vfs_types.h" 12#include "core/file_sys/vfs/vfs_types.h"
13#include "core/memory/dmnt_cheat_types.h" 13#include "core/memory/dmnt_cheat_types.h"
14 14
15namespace Core { 15namespace Core {
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 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/scope_exit.h" 8#include "common/scope_exit.h"
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace FileSys { 13namespace 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 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/file_sys/vfs_types.h" 13#include "core/file_sys/vfs/vfs_types.h"
14 14
15namespace Loader { 15namespace Loader {
16enum class ResultStatus : u16; 16enum 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 @@
17#include "core/file_sys/nca_metadata.h" 17#include "core/file_sys/nca_metadata.h"
18#include "core/file_sys/registered_cache.h" 18#include "core/file_sys/registered_cache.h"
19#include "core/file_sys/submission_package.h" 19#include "core/file_sys/submission_package.h"
20#include "core/file_sys/vfs_concat.h" 20#include "core/file_sys/vfs/vfs_concat.h"
21#include "core/loader/loader.h" 21#include "core/loader/loader.h"
22 22
23namespace FileSys { 23namespace 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 @@
11#include <boost/container/flat_map.hpp> 11#include <boost/container/flat_map.hpp>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
14#include "core/file_sys/vfs.h" 14#include "core/file_sys/vfs/vfs.h"
15 15
16namespace FileSys { 16namespace FileSys {
17class CNMT; 17class 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 @@
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/file_sys/fsmitm_romfsbuild.h" 10#include "core/file_sys/fsmitm_romfsbuild.h"
11#include "core/file_sys/romfs.h" 11#include "core/file_sys/romfs.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs/vfs.h"
13#include "core/file_sys/vfs_cached.h" 13#include "core/file_sys/vfs/vfs_cached.h"
14#include "core/file_sys/vfs_concat.h" 14#include "core/file_sys/vfs/vfs_concat.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs/vfs_offset.h"
16#include "core/file_sys/vfs_vector.h" 16#include "core/file_sys/vfs/vfs_vector.h"
17 17
18namespace FileSys { 18namespace FileSys {
19namespace { 19namespace {
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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/vfs.h" 6#include "core/file_sys/vfs/vfs.h"
7 7
8namespace FileSys { 8namespace FileSys {
9 9
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 @@
6#include <memory> 6#include <memory>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h" 9#include "core/file_sys/vfs/vfs_types.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11 11
12namespace Loader { 12namespace Loader {
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 23196cd5f..cbf411a20 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -8,7 +8,7 @@
8#include "common/uuid.h" 8#include "common/uuid.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/savedata_factory.h" 10#include "core/file_sys/savedata_factory.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12 12
13namespace FileSys { 13namespace FileSys {
14 14
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index 30d96928e..5ab7e4d32 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12 12
13namespace Core { 13namespace Core {
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 @@
4#include <memory> 4#include <memory>
5#include "core/file_sys/registered_cache.h" 5#include "core/file_sys/registered_cache.h"
6#include "core/file_sys/sdmc_factory.h" 6#include "core/file_sys/sdmc_factory.h"
7#include "core/file_sys/vfs.h" 7#include "core/file_sys/vfs/vfs.h"
8#include "core/file_sys/xts_archive.h" 8#include "core/file_sys/xts_archive.h"
9 9
10namespace FileSys { 10namespace 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 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include "core/file_sys/vfs_types.h" 7#include "core/file_sys/vfs/vfs_types.h"
8#include "core/hle/result.h" 8#include "core/hle/result.h"
9 9
10namespace FileSys { 10namespace 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 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs/vfs.h"
13 13
14namespace Core::Crypto { 14namespace Core::Crypto {
15class KeyManager; 15class 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 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/file_sys/system_archive/mii_model.h" 4#include "core/file_sys/system_archive/mii_model.h"
5#include "core/file_sys/vfs_vector.h" 5#include "core/file_sys/vfs/vfs_vector.h"
6 6
7namespace FileSys::SystemArchive { 7namespace FileSys::SystemArchive {
8 8
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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/vfs_types.h" 6#include "core/file_sys/vfs/vfs_types.h"
7 7
8namespace FileSys::SystemArchive { 8namespace FileSys::SystemArchive {
9 9
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 @@
4#include <fmt/format.h> 4#include <fmt/format.h>
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "core/file_sys/system_archive/ng_word.h" 6#include "core/file_sys/system_archive/ng_word.h"
7#include "core/file_sys/vfs_vector.h" 7#include "core/file_sys/vfs/vfs_vector.h"
8 8
9namespace FileSys::SystemArchive { 9namespace FileSys::SystemArchive {
10 10
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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/vfs_types.h" 6#include "core/file_sys/vfs/vfs_types.h"
7 7
8namespace FileSys::SystemArchive { 8namespace FileSys::SystemArchive {
9 9
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 @@
8#include "core/file_sys/system_archive/data/font_nintendo_extended.h" 8#include "core/file_sys/system_archive/data/font_nintendo_extended.h"
9#include "core/file_sys/system_archive/data/font_standard.h" 9#include "core/file_sys/system_archive/data/font_standard.h"
10#include "core/file_sys/system_archive/shared_font.h" 10#include "core/file_sys/system_archive/shared_font.h"
11#include "core/file_sys/vfs_vector.h" 11#include "core/file_sys/vfs/vfs_vector.h"
12#include "core/hle/service/ns/iplatform_service_manager.h" 12#include "core/hle/service/ns/iplatform_service_manager.h"
13 13
14namespace FileSys::SystemArchive { 14namespace 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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/vfs_types.h" 6#include "core/file_sys/vfs/vfs_types.h"
7 7
8namespace FileSys::SystemArchive { 8namespace FileSys::SystemArchive {
9 9
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 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/file_sys/vfs_types.h" 7#include "core/file_sys/vfs/vfs_types.h"
8 8
9namespace FileSys::SystemArchive { 9namespace FileSys::SystemArchive {
10 10
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 @@
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "core/file_sys/system_archive/system_version.h" 5#include "core/file_sys/system_archive/system_version.h"
6#include "core/file_sys/vfs_vector.h" 6#include "core/file_sys/vfs/vfs_vector.h"
7#include "core/hle/api_version.h" 7#include "core/hle/api_version.h"
8 8
9namespace FileSys::SystemArchive { 9namespace 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 @@
4#pragma once 4#pragma once
5 5
6#include <string> 6#include <string>
7#include "core/file_sys/vfs_types.h" 7#include "core/file_sys/vfs/vfs_types.h"
8 8
9namespace FileSys::SystemArchive { 9namespace FileSys::SystemArchive {
10 10
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 d4d2eae76..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,7 +5,7 @@
5 5
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/system_archive/time_zone_binary.h" 7#include "core/file_sys/system_archive/time_zone_binary.h"
8#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs/vfs_vector.h"
9 9
10#include "nx_tzdb.h" 10#include "nx_tzdb.h"
11 11
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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/vfs_types.h" 6#include "core/file_sys/vfs/vfs_types.h"
7 7
8namespace FileSys::SystemArchive { 8namespace FileSys::SystemArchive {
9 9
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp
index b7105c8ff..a04292760 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs/vfs.cpp
@@ -5,8 +5,7 @@
5#include <numeric> 5#include <numeric>
6#include <string> 6#include <string>
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "core/file_sys/mode.h" 8#include "core/file_sys/vfs/vfs.h"
9#include "core/file_sys/vfs.h"
10 9
11namespace FileSys { 10namespace FileSys {
12 11
@@ -36,12 +35,12 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
36 return VfsEntryType::None; 35 return VfsEntryType::None;
37} 36}
38 37
39VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 38VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
40 const auto path = Common::FS::SanitizePath(path_); 39 const auto path = Common::FS::SanitizePath(path_);
41 return root->GetFileRelative(path); 40 return root->GetFileRelative(path);
42} 41}
43 42
44VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 43VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
45 const auto path = Common::FS::SanitizePath(path_); 44 const auto path = Common::FS::SanitizePath(path_);
46 return root->CreateFileRelative(path); 45 return root->CreateFileRelative(path);
47} 46}
@@ -54,17 +53,17 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
54 if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { 53 if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
55 if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) 54 if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
56 return nullptr; 55 return nullptr;
57 return OpenFile(new_path, Mode::ReadWrite); 56 return OpenFile(new_path, OpenMode::ReadWrite);
58 } 57 }
59 58
60 // Do it using RawCopy. Non-default impls are encouraged to optimize this. 59 // Do it using RawCopy. Non-default impls are encouraged to optimize this.
61 const auto old_file = OpenFile(old_path, Mode::Read); 60 const auto old_file = OpenFile(old_path, OpenMode::Read);
62 if (old_file == nullptr) 61 if (old_file == nullptr)
63 return nullptr; 62 return nullptr;
64 auto new_file = OpenFile(new_path, Mode::Read); 63 auto new_file = OpenFile(new_path, OpenMode::Read);
65 if (new_file != nullptr) 64 if (new_file != nullptr)
66 return nullptr; 65 return nullptr;
67 new_file = CreateFile(new_path, Mode::Write); 66 new_file = CreateFile(new_path, OpenMode::Write);
68 if (new_file == nullptr) 67 if (new_file == nullptr)
69 return nullptr; 68 return nullptr;
70 if (!VfsRawCopy(old_file, new_file)) 69 if (!VfsRawCopy(old_file, new_file))
@@ -87,18 +86,18 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
87 86
88bool VfsFilesystem::DeleteFile(std::string_view path_) { 87bool VfsFilesystem::DeleteFile(std::string_view path_) {
89 const auto path = Common::FS::SanitizePath(path_); 88 const auto path = Common::FS::SanitizePath(path_);
90 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); 89 auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write);
91 if (parent == nullptr) 90 if (parent == nullptr)
92 return false; 91 return false;
93 return parent->DeleteFile(Common::FS::GetFilename(path)); 92 return parent->DeleteFile(Common::FS::GetFilename(path));
94} 93}
95 94
96VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 95VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
97 const auto path = Common::FS::SanitizePath(path_); 96 const auto path = Common::FS::SanitizePath(path_);
98 return root->GetDirectoryRelative(path); 97 return root->GetDirectoryRelative(path);
99} 98}
100 99
101VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 100VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
102 const auto path = Common::FS::SanitizePath(path_); 101 const auto path = Common::FS::SanitizePath(path_);
103 return root->CreateDirectoryRelative(path); 102 return root->CreateDirectoryRelative(path);
104} 103}
@@ -108,13 +107,13 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
108 const auto new_path = Common::FS::SanitizePath(new_path_); 107 const auto new_path = Common::FS::SanitizePath(new_path_);
109 108
110 // Non-default impls are highly encouraged to provide a more optimized version of this. 109 // Non-default impls are highly encouraged to provide a more optimized version of this.
111 auto old_dir = OpenDirectory(old_path, Mode::Read); 110 auto old_dir = OpenDirectory(old_path, OpenMode::Read);
112 if (old_dir == nullptr) 111 if (old_dir == nullptr)
113 return nullptr; 112 return nullptr;
114 auto new_dir = OpenDirectory(new_path, Mode::Read); 113 auto new_dir = OpenDirectory(new_path, OpenMode::Read);
115 if (new_dir != nullptr) 114 if (new_dir != nullptr)
116 return nullptr; 115 return nullptr;
117 new_dir = CreateDirectory(new_path, Mode::Write); 116 new_dir = CreateDirectory(new_path, OpenMode::Write);
118 if (new_dir == nullptr) 117 if (new_dir == nullptr)
119 return nullptr; 118 return nullptr;
120 119
@@ -149,7 +148,7 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
149 148
150bool VfsFilesystem::DeleteDirectory(std::string_view path_) { 149bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
151 const auto path = Common::FS::SanitizePath(path_); 150 const auto path = Common::FS::SanitizePath(path_);
152 auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); 151 auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write);
153 if (parent == nullptr) 152 if (parent == nullptr)
154 return false; 153 return false;
155 return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); 154 return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs/vfs.h
index a7cd1cae3..f846a9669 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs/vfs.h
@@ -13,12 +13,11 @@
13 13
14#include "common/common_funcs.h" 14#include "common/common_funcs.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/file_sys/vfs_types.h" 16#include "core/file_sys/fs_filesystem.h"
17#include "core/file_sys/vfs/vfs_types.h"
17 18
18namespace FileSys { 19namespace FileSys {
19 20
20enum class Mode : u32;
21
22// An enumeration representing what can be at the end of a path in a VfsFilesystem 21// An enumeration representing what can be at the end of a path in a VfsFilesystem
23enum class VfsEntryType { 22enum class VfsEntryType {
24 None, 23 None,
@@ -49,9 +48,9 @@ public:
49 virtual VfsEntryType GetEntryType(std::string_view path) const; 48 virtual VfsEntryType GetEntryType(std::string_view path) const;
50 49
51 // Opens the file with path relative to root. If it doesn't exist, returns nullptr. 50 // Opens the file with path relative to root. If it doesn't exist, returns nullptr.
52 virtual VirtualFile OpenFile(std::string_view path, Mode perms); 51 virtual VirtualFile OpenFile(std::string_view path, OpenMode perms);
53 // Creates a new, empty file at path 52 // Creates a new, empty file at path
54 virtual VirtualFile CreateFile(std::string_view path, Mode perms); 53 virtual VirtualFile CreateFile(std::string_view path, OpenMode perms);
55 // Copies the file from old_path to new_path, returning the new file on success and nullptr on 54 // Copies the file from old_path to new_path, returning the new file on success and nullptr on
56 // failure. 55 // failure.
57 virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); 56 virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
@@ -62,9 +61,9 @@ public:
62 virtual bool DeleteFile(std::string_view path); 61 virtual bool DeleteFile(std::string_view path);
63 62
64 // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. 63 // Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
65 virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); 64 virtual VirtualDir OpenDirectory(std::string_view path, OpenMode perms);
66 // Creates a new, empty directory at path 65 // Creates a new, empty directory at path
67 virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); 66 virtual VirtualDir CreateDirectory(std::string_view path, OpenMode perms);
68 // Copies the directory from old_path to new_path, returning the new directory on success and 67 // Copies the directory from old_path to new_path, returning the new directory on success and
69 // nullptr on failure. 68 // nullptr on failure.
70 virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); 69 virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs/vfs_cached.cpp
index 7ee5300e5..01cd0f1e0 100644
--- a/src/core/file_sys/vfs_cached.cpp
+++ b/src/core/file_sys/vfs/vfs_cached.cpp
@@ -1,8 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/file_sys/vfs_cached.h" 4#include "core/file_sys/vfs/vfs_cached.h"
5#include "core/file_sys/vfs_types.h" 5#include "core/file_sys/vfs/vfs_types.h"
6 6
7namespace FileSys { 7namespace FileSys {
8 8
diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs/vfs_cached.h
index 1e5300784..47dff7224 100644
--- a/src/core/file_sys/vfs_cached.h
+++ b/src/core/file_sys/vfs/vfs_cached.h
@@ -5,7 +5,7 @@
5 5
6#include <string_view> 6#include <string_view>
7#include <vector> 7#include <vector>
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs/vfs.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs/vfs_concat.cpp
index 7c7298527..b5cc9a9e9 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs/vfs_concat.cpp
@@ -5,8 +5,8 @@
5#include <utility> 5#include <utility>
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/file_sys/vfs_concat.h" 8#include "core/file_sys/vfs/vfs_concat.h"
9#include "core/file_sys/vfs_static.h" 9#include "core/file_sys/vfs/vfs_static.h"
10 10
11namespace FileSys { 11namespace FileSys {
12 12
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs/vfs_concat.h
index b5f3d72e3..6d12af762 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs/vfs_concat.h
@@ -6,7 +6,7 @@
6#include <compare> 6#include <compare>
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs/vfs.h"
10 10
11namespace FileSys { 11namespace FileSys {
12 12
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs/vfs_layered.cpp
index 5551743fb..47b2a3c78 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs/vfs_layered.cpp
@@ -5,7 +5,7 @@
5#include <set> 5#include <set>
6#include <unordered_set> 6#include <unordered_set>
7#include <utility> 7#include <utility>
8#include "core/file_sys/vfs_layered.h" 8#include "core/file_sys/vfs/vfs_layered.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs/vfs_layered.h
index a62112e9d..0027ffa9a 100644
--- a/src/core/file_sys/vfs_layered.h
+++ b/src/core/file_sys/vfs/vfs_layered.h
@@ -4,7 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include "core/file_sys/vfs.h" 7#include "core/file_sys/vfs/vfs.h"
8 8
9namespace FileSys { 9namespace FileSys {
10 10
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs/vfs_offset.cpp
index d950a6633..1a37d2670 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs/vfs_offset.cpp
@@ -4,7 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5#include <utility> 5#include <utility>
6 6
7#include "core/file_sys/vfs_offset.h" 7#include "core/file_sys/vfs/vfs_offset.h"
8 8
9namespace FileSys { 9namespace FileSys {
10 10
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs/vfs_offset.h
index 6c051ca00..4abe41d8e 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs/vfs_offset.h
@@ -5,7 +5,7 @@
5 5
6#include <memory> 6#include <memory>
7 7
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs/vfs.h"
9 9
10namespace FileSys { 10namespace FileSys {
11 11
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp
index cd9b79786..3ad073e4a 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs/vfs_real.cpp
@@ -10,8 +10,8 @@
10#include "common/fs/fs.h" 10#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs/vfs.h"
14#include "core/file_sys/vfs_real.h" 14#include "core/file_sys/vfs/vfs_real.h"
15 15
16// For FileTimeStampRaw 16// For FileTimeStampRaw
17#include <sys/stat.h> 17#include <sys/stat.h>
@@ -20,6 +20,10 @@
20#define stat _stat64 20#define stat _stat64
21#endif 21#endif
22 22
23#ifdef ANDROID
24#include "common/fs/fs_android.h"
25#endif
26
23namespace FileSys { 27namespace FileSys {
24 28
25namespace FS = Common::FS; 29namespace FS = Common::FS;
@@ -28,16 +32,14 @@ namespace {
28 32
29constexpr size_t MaxOpenFiles = 512; 33constexpr size_t MaxOpenFiles = 512;
30 34
31constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { 35constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
32 switch (mode) { 36 switch (mode) {
33 case Mode::Read: 37 case OpenMode::Read:
34 return FS::FileAccessMode::Read; 38 return FS::FileAccessMode::Read;
35 case Mode::Write: 39 case OpenMode::Write:
36 case Mode::ReadWrite: 40 case OpenMode::ReadWrite:
37 case Mode::Append: 41 case OpenMode::AllowAppend:
38 case Mode::ReadAppend: 42 case OpenMode::All:
39 case Mode::WriteAppend:
40 case Mode::All:
41 return FS::FileAccessMode::ReadWrite; 43 return FS::FileAccessMode::ReadWrite;
42 default: 44 default:
43 return {}; 45 return {};
@@ -74,7 +76,7 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
74} 76}
75 77
76VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, 78VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size,
77 Mode perms) { 79 OpenMode perms) {
78 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 80 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
79 std::scoped_lock lk{list_lock}; 81 std::scoped_lock lk{list_lock};
80 82
@@ -98,11 +100,11 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
98 return file; 100 return file;
99} 101}
100 102
101VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 103VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
102 return OpenFileFromEntry(path_, {}, perms); 104 return OpenFileFromEntry(path_, {}, perms);
103} 105}
104 106
105VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { 107VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
106 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 108 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
107 { 109 {
108 std::scoped_lock lk{list_lock}; 110 std::scoped_lock lk{list_lock};
@@ -145,7 +147,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
145 if (!FS::RenameFile(old_path, new_path)) { 147 if (!FS::RenameFile(old_path, new_path)) {
146 return nullptr; 148 return nullptr;
147 } 149 }
148 return OpenFile(new_path, Mode::ReadWrite); 150 return OpenFile(new_path, OpenMode::ReadWrite);
149} 151}
150 152
151bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 153bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
@@ -157,12 +159,12 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
157 return FS::RemoveFile(path); 159 return FS::RemoveFile(path);
158} 160}
159 161
160VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { 162VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
161 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 163 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
162 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); 164 return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
163} 165}
164 166
165VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { 167VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
166 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 168 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
167 if (!FS::CreateDirs(path)) { 169 if (!FS::CreateDirs(path)) {
168 return nullptr; 170 return nullptr;
@@ -184,7 +186,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
184 if (!FS::RenameDir(old_path, new_path)) { 186 if (!FS::RenameDir(old_path, new_path)) {
185 return nullptr; 187 return nullptr;
186 } 188 }
187 return OpenDirectory(new_path, Mode::ReadWrite); 189 return OpenDirectory(new_path, OpenMode::ReadWrite);
188} 190}
189 191
190bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 192bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
@@ -193,7 +195,7 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
193} 195}
194 196
195std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, 197std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path,
196 Mode perms, 198 OpenMode perms,
197 FileReference& reference) { 199 FileReference& reference) {
198 std::unique_lock lk{list_lock}; 200 std::unique_lock lk{list_lock};
199 201
@@ -266,7 +268,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference)
266} 268}
267 269
268RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, 270RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
269 const std::string& path_, Mode perms_, std::optional<u64> size_) 271 const std::string& path_, OpenMode perms_, std::optional<u64> size_)
270 : base(base_), reference(std::move(reference_)), path(path_), 272 : base(base_), reference(std::move(reference_)), path(path_),
271 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), 273 parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
272 size(size_), perms(perms_) {} 274 size(size_), perms(perms_) {}
@@ -276,6 +278,11 @@ RealVfsFile::~RealVfsFile() {
276} 278}
277 279
278std::string RealVfsFile::GetName() const { 280std::string RealVfsFile::GetName() const {
281#ifdef ANDROID
282 if (path[0] != '/') {
283 return FS::Android::GetFilename(path);
284 }
285#endif
279 return path_components.empty() ? "" : std::string(path_components.back()); 286 return path_components.empty() ? "" : std::string(path_components.back());
280} 287}
281 288
@@ -298,11 +305,11 @@ VirtualDir RealVfsFile::GetContainingDirectory() const {
298} 305}
299 306
300bool RealVfsFile::IsWritable() const { 307bool RealVfsFile::IsWritable() const {
301 return True(perms & Mode::Write); 308 return True(perms & OpenMode::Write);
302} 309}
303 310
304bool RealVfsFile::IsReadable() const { 311bool RealVfsFile::IsReadable() const {
305 return True(perms & Mode::Read); 312 return True(perms & OpenMode::Read);
306} 313}
307 314
308std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 315std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
@@ -331,7 +338,7 @@ bool RealVfsFile::Rename(std::string_view name) {
331 338
332template <> 339template <>
333std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { 340std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
334 if (perms == Mode::Append) { 341 if (perms == OpenMode::AllowAppend) {
335 return {}; 342 return {};
336 } 343 }
337 344
@@ -353,7 +360,7 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
353 360
354template <> 361template <>
355std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { 362std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
356 if (perms == Mode::Append) { 363 if (perms == OpenMode::AllowAppend) {
357 return {}; 364 return {};
358 } 365 }
359 366
@@ -373,10 +380,11 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
373 return out; 380 return out;
374} 381}
375 382
376RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) 383RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_,
384 OpenMode perms_)
377 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), 385 : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
378 path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { 386 path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
379 if (!FS::Exists(path) && True(perms & Mode::Write)) { 387 if (!FS::Exists(path) && True(perms & OpenMode::Write)) {
380 void(FS::CreateDirs(path)); 388 void(FS::CreateDirs(path));
381 } 389 }
382} 390}
@@ -456,11 +464,11 @@ std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
456} 464}
457 465
458bool RealVfsDirectory::IsWritable() const { 466bool RealVfsDirectory::IsWritable() const {
459 return True(perms & Mode::Write); 467 return True(perms & OpenMode::Write);
460} 468}
461 469
462bool RealVfsDirectory::IsReadable() const { 470bool RealVfsDirectory::IsReadable() const {
463 return True(perms & Mode::Read); 471 return True(perms & OpenMode::Read);
464} 472}
465 473
466std::string RealVfsDirectory::GetName() const { 474std::string RealVfsDirectory::GetName() const {
@@ -507,7 +515,7 @@ std::string RealVfsDirectory::GetFullPath() const {
507} 515}
508 516
509std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { 517std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
510 if (perms == Mode::Append) { 518 if (perms == OpenMode::AllowAppend) {
511 return {}; 519 return {};
512 } 520 }
513 521
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h
index 26ea7df62..5c2172cce 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs/vfs_real.h
@@ -8,8 +8,8 @@
8#include <optional> 8#include <optional>
9#include <string_view> 9#include <string_view>
10#include "common/intrusive_list.h" 10#include "common/intrusive_list.h"
11#include "core/file_sys/mode.h" 11#include "core/file_sys/fs_filesystem.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs/vfs.h"
13 13
14namespace Common::FS { 14namespace Common::FS {
15class IOFile; 15class IOFile;
@@ -33,13 +33,14 @@ public:
33 bool IsReadable() const override; 33 bool IsReadable() const override;
34 bool IsWritable() const override; 34 bool IsWritable() const override;
35 VfsEntryType GetEntryType(std::string_view path) const override; 35 VfsEntryType GetEntryType(std::string_view path) const override;
36 VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; 36 VirtualFile OpenFile(std::string_view path, OpenMode perms = OpenMode::Read) override;
37 VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; 37 VirtualFile CreateFile(std::string_view path, OpenMode perms = OpenMode::ReadWrite) override;
38 VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; 38 VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
39 VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; 39 VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
40 bool DeleteFile(std::string_view path) override; 40 bool DeleteFile(std::string_view path) override;
41 VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; 41 VirtualDir OpenDirectory(std::string_view path, OpenMode perms = OpenMode::Read) override;
42 VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; 42 VirtualDir CreateDirectory(std::string_view path,
43 OpenMode perms = OpenMode::ReadWrite) override;
43 VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; 44 VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
44 VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; 45 VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
45 bool DeleteDirectory(std::string_view path) override; 46 bool DeleteDirectory(std::string_view path) override;
@@ -54,14 +55,14 @@ private:
54 55
55private: 56private:
56 friend class RealVfsFile; 57 friend class RealVfsFile;
57 std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms, 58 std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
58 FileReference& reference); 59 FileReference& reference);
59 void DropReference(std::unique_ptr<FileReference>&& reference); 60 void DropReference(std::unique_ptr<FileReference>&& reference);
60 61
61private: 62private:
62 friend class RealVfsDirectory; 63 friend class RealVfsDirectory;
63 VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, 64 VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size,
64 Mode perms = Mode::Read); 65 OpenMode perms = OpenMode::Read);
65 66
66private: 67private:
67 void EvictSingleReferenceLocked(); 68 void EvictSingleReferenceLocked();
@@ -89,7 +90,8 @@ public:
89 90
90private: 91private:
91 RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, 92 RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
92 const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {}); 93 const std::string& path, OpenMode perms = OpenMode::Read,
94 std::optional<u64> size = {});
93 95
94 RealVfsFilesystem& base; 96 RealVfsFilesystem& base;
95 std::unique_ptr<FileReference> reference; 97 std::unique_ptr<FileReference> reference;
@@ -97,7 +99,7 @@ private:
97 std::string parent_path; 99 std::string parent_path;
98 std::vector<std::string> path_components; 100 std::vector<std::string> path_components;
99 std::optional<u64> size; 101 std::optional<u64> size;
100 Mode perms; 102 OpenMode perms;
101}; 103};
102 104
103// An implementation of VfsDirectory that represents a directory on the user's computer. 105// An implementation of VfsDirectory that represents a directory on the user's computer.
@@ -130,7 +132,8 @@ public:
130 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; 132 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
131 133
132private: 134private:
133 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); 135 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
136 OpenMode perms = OpenMode::Read);
134 137
135 template <typename T, typename R> 138 template <typename T, typename R>
136 std::vector<std::shared_ptr<R>> IterateEntries() const; 139 std::vector<std::shared_ptr<R>> IterateEntries() const;
@@ -139,7 +142,7 @@ private:
139 std::string path; 142 std::string path;
140 std::string parent_path; 143 std::string parent_path;
141 std::vector<std::string> path_components; 144 std::vector<std::string> path_components;
142 Mode perms; 145 OpenMode perms;
143}; 146};
144 147
145} // namespace FileSys 148} // namespace FileSys
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs/vfs_static.h
index ca3f989ef..bb53560ac 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs/vfs_static.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string_view> 8#include <string_view>
9 9
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11 11
12namespace FileSys { 12namespace FileSys {
13 13
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_vector.cpp b/src/core/file_sys/vfs/vfs_vector.cpp
index 251d9d7c9..0d54461c8 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs/vfs_vector.cpp
@@ -3,7 +3,7 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include <utility> 5#include <utility>
6#include "core/file_sys/vfs_vector.h" 6#include "core/file_sys/vfs/vfs_vector.h"
7 7
8namespace FileSys { 8namespace FileSys {
9VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name_, VirtualDir parent_) 9VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name_, VirtualDir parent_)
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs/vfs_vector.h
index bfedb6e42..587187dd2 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs/vfs_vector.h
@@ -8,7 +8,7 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <vector> 10#include <vector>
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12 12
13namespace FileSys { 13namespace FileSys {
14 14
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 @@
17#include "core/crypto/key_manager.h" 17#include "core/crypto/key_manager.h"
18#include "core/crypto/xts_encryption_layer.h" 18#include "core/crypto/xts_encryption_layer.h"
19#include "core/file_sys/content_archive.h" 19#include "core/file_sys/content_archive.h"
20#include "core/file_sys/vfs_offset.h" 20#include "core/file_sys/vfs/vfs_offset.h"
21#include "core/file_sys/xts_archive.h" 21#include "core/file_sys/xts_archive.h"
22#include "core/loader/loader.h" 22#include "core/loader/loader.h"
23 23
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 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/crypto/key_manager.h" 10#include "core/crypto/key_manager.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12 12
13namespace Loader { 13namespace Loader {
14enum class ResultStatus : u16; 14enum class ResultStatus : u16;
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 {}
12 12
13void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const { 13void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const {
14 LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", 14 LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
15 error.module.Value(), error.description.Value(), error.raw); 15 error.GetModule(), error.GetDescription(), error.raw);
16} 16}
17 17
18void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, 18void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
@@ -20,7 +20,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon
20 LOG_CRITICAL( 20 LOG_CRITICAL(
21 Service_Fatal, 21 Service_Fatal,
22 "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", 22 "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
23 error.module.Value(), error.description.Value(), error.raw, time.count()); 23 error.GetModule(), error.GetDescription(), error.raw, time.count());
24} 24}
25 25
26void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, 26void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text,
@@ -28,7 +28,7 @@ void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text
28 FinishedCallback finished) const { 28 FinishedCallback finished) const {
29 LOG_CRITICAL(Service_Fatal, 29 LOG_CRITICAL(Service_Fatal,
30 "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", 30 "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
31 error.module.Value(), error.description.Value(), error.raw); 31 error.GetModule(), error.GetDescription(), error.raw);
32 LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text); 32 LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
33 LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text); 33 LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
34} 34}
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 {
189union Result { 189union Result {
190 u32 raw; 190 u32 raw;
191 191
192 BitField<0, 9, ErrorModule> module; 192 using Module = BitField<0, 9, ErrorModule>;
193 BitField<9, 13, u32> description; 193 using Description = BitField<9, 13, u32>;
194 194
195 Result() = default; 195 Result() = default;
196 constexpr explicit Result(u32 raw_) : raw(raw_) {} 196 constexpr explicit Result(u32 raw_) : raw(raw_) {}
197 197
198 constexpr Result(ErrorModule module_, u32 description_) 198 constexpr Result(ErrorModule module_, u32 description_)
199 : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} 199 : raw(Module::FormatValue(module_) | Description::FormatValue(description_)) {}
200 200
201 [[nodiscard]] constexpr bool IsSuccess() const { 201 [[nodiscard]] constexpr bool IsSuccess() const {
202 return raw == 0; 202 return raw == 0;
@@ -211,7 +211,15 @@ union Result {
211 } 211 }
212 212
213 [[nodiscard]] constexpr u32 GetInnerValue() const { 213 [[nodiscard]] constexpr u32 GetInnerValue() const {
214 return static_cast<u32>(module.Value()) | (description << module.bits); 214 return raw;
215 }
216
217 [[nodiscard]] constexpr ErrorModule GetModule() const {
218 return Module::ExtractValue(raw);
219 }
220
221 [[nodiscard]] constexpr u32 GetDescription() const {
222 return Description::ExtractValue(raw);
215 } 223 }
216 224
217 [[nodiscard]] constexpr bool Includes(Result result) const { 225 [[nodiscard]] constexpr bool Includes(Result result) const {
@@ -274,8 +282,9 @@ public:
274 } 282 }
275 283
276 [[nodiscard]] constexpr bool Includes(Result other) const { 284 [[nodiscard]] constexpr bool Includes(Result other) const {
277 return code.module == other.module && code.description <= other.description && 285 return code.GetModule() == other.GetModule() &&
278 other.description <= description_end; 286 code.GetDescription() <= other.GetDescription() &&
287 other.GetDescription() <= description_end;
279 } 288 }
280 289
281private: 290private:
@@ -330,6 +339,16 @@ constexpr bool EvaluateResultFailure(const Result& r) {
330 return R_FAILED(r); 339 return R_FAILED(r);
331} 340}
332 341
342template <auto... R>
343constexpr bool EvaluateAnyResultIncludes(const Result& r) {
344 return ((r == R) || ...);
345}
346
347template <auto... R>
348constexpr bool EvaluateResultNotIncluded(const Result& r) {
349 return !EvaluateAnyResultIncludes<R...>(r);
350}
351
333template <typename T> 352template <typename T>
334constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete; 353constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete;
335// Intentionally not defined 354// Intentionally not defined
@@ -371,6 +390,13 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc
371 DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ 390 DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
372 ON_RESULT_SUCCESS_2 391 ON_RESULT_SUCCESS_2
373 392
393#define ON_RESULT_INCLUDED_2(...) \
394 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateAnyResultIncludes<__VA_ARGS__>)
395
396#define ON_RESULT_INCLUDED(...) \
397 DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
398 ON_RESULT_INCLUDED_2(__VA_ARGS__)
399
374constexpr inline Result __TmpCurrentResultReference = ResultSuccess; 400constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
375 401
376/// Returns a result. 402/// Returns a result.
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 {
27 27
28 static constexpr ErrorCode FromResult(Result result) { 28 static constexpr ErrorCode FromResult(Result result) {
29 return { 29 return {
30 .error_category{2000 + static_cast<u32>(result.module.Value())}, 30 .error_category{2000 + static_cast<u32>(result.GetModule())},
31 .error_number{result.description.Value()}, 31 .error_number{result.GetDescription()},
32 }; 32 };
33 } 33 }
34 34
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 @@
9#include "common/string_util.h" 9#include "common/string_util.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/mode.h" 12#include "core/file_sys/fs_filesystem.h"
13#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/patch_manager.h" 14#include "core/file_sys/patch_manager.h"
15#include "core/file_sys/registered_cache.h" 15#include "core/file_sys/registered_cache.h"
16#include "core/file_sys/romfs.h" 16#include "core/file_sys/romfs.h"
17#include "core/file_sys/system_archive/system_archive.h" 17#include "core/file_sys/system_archive/system_archive.h"
18#include "core/file_sys/vfs_vector.h" 18#include "core/file_sys/vfs/vfs_vector.h"
19#include "core/frontend/applets/web_browser.h" 19#include "core/frontend/applets/web_browser.h"
20#include "core/hle/result.h" 20#include "core/hle/result.h"
21#include "core/hle/service/am/am.h" 21#include "core/hle/service/am/am.h"
@@ -213,7 +213,7 @@ void ExtractSharedFonts(Core::System& system) {
213 std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); 213 std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]);
214 214
215 const auto temp_dir = system.GetFilesystem()->CreateDirectory( 215 const auto temp_dir = system.GetFilesystem()->CreateDirectory(
216 Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); 216 Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite);
217 217
218 const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); 218 const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]);
219 219
@@ -333,7 +333,7 @@ void WebBrowser::ExtractOfflineRomFS() {
333 const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); 333 const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
334 334
335 const auto temp_dir = system.GetFilesystem()->CreateDirectory( 335 const auto temp_dir = system.GetFilesystem()->CreateDirectory(
336 Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); 336 Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite);
337 337
338 FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); 338 FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir);
339} 339}
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 @@
7#include <optional> 7#include <optional>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h" 10#include "core/file_sys/vfs/vfs_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/hle/service/am/applets/applet_web_browser_types.h" 12#include "core/hle/service/am/applets/applet_web_browser_types.h"
13#include "core/hle/service/am/applets/applets.h" 13#include "core/hle/service/am/applets/applets.h"
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index bd4ca753b..05581e6e0 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -139,7 +139,8 @@ private:
139 ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1); 139 ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1);
140 } 140 }
141 } else { 141 } else {
142 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); 142 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!",
143 result.GetDescription());
143 } 144 }
144 145
145 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
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 @@
8#include <string> 8#include <string>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/vfs_types.h" 11#include "core/file_sys/vfs/vfs_types.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13#include "core/hle/service/kernel_helpers.h" 13#include "core/hle/service/kernel_helpers.h"
14 14
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 @@
8#include "common/settings.h" 8#include "common/settings.h"
9#include "common/string_util.h" 9#include "common/string_util.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12#include "core/hle/kernel/k_readable_event.h" 12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/service/bcat/backend/backend.h" 13#include "core/hle/service/bcat/backend/backend.h"
14#include "core/hle/service/bcat/bcat.h" 14#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:
283 {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, 283 {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
284 {18, nullptr, "DisconnectAudioDevice"}, 284 {18, nullptr, "DisconnectAudioDevice"},
285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, 285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
286 {20, nullptr, "GetPairedAudioDevices"}, 286 {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"},
287 {21, nullptr, "RemoveAudioDevicePairing"}, 287 {21, nullptr, "RemoveAudioDevicePairing"},
288 {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, 288 {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
289 {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} 289 {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
@@ -327,6 +327,13 @@ private:
327 rb.Push<u32>(0); 327 rb.Push<u32>(0);
328 } 328 }
329 329
330 void GetPairedAudioDevices(HLERequestContext& ctx) {
331 LOG_WARNING(Service_BTM, "(STUBBED) called");
332 IPC::ResponseBuilder rb{ctx, 3};
333 rb.Push(ResultSuccess);
334 rb.Push<u32>(0);
335 }
336
330 void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { 337 void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
331 LOG_WARNING(Service_BTM, "(STUBBED) called"); 338 LOG_WARNING(Service_BTM, "(STUBBED) called");
332 IPC::ResponseBuilder rb{ctx, 2}; 339 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) {
202 } 202 }
203 203
204 if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) { 204 if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) {
205 if (in_result.description - 0x514 < 100) { 205 if (in_result.GetDescription() - 0x514 < 100) {
206 return ResultInvalidFileData; 206 return ResultInvalidFileData;
207 } 207 }
208 if (in_result.description - 0x5dc < 100) { 208 if (in_result.GetDescription() - 0x5dc < 100) {
209 return ResultInvalidFileData; 209 return ResultInvalidFileData;
210 } 210 }
211 211
212 if (in_result.description - 0x578 < 100) { 212 if (in_result.GetDescription() - 0x578 < 100) {
213 if (in_result == ResultFileCountLimit) { 213 if (in_result == ResultFileCountLimit) {
214 return ResultUnknown22; 214 return ResultUnknown22;
215 } 215 }
@@ -244,9 +244,10 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
244 return ResultUnknown1024; 244 return ResultUnknown1024;
245 } 245 }
246 246
247 if (in_result.module == ErrorModule::FS) { 247 if (in_result.GetModule() == ErrorModule::FS) {
248 if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) || 248 if ((in_result.GetDescription() >> 0xc < 0x7d) ||
249 (((in_result.description - 3000) >> 3) < 0x271)) { 249 (in_result.GetDescription() - 1000 < 2000) ||
250 (((in_result.GetDescription() - 3000) >> 3) < 0x271)) {
250 // TODO: Translate FS error 251 // TODO: Translate FS error
251 return in_result; 252 return in_result;
252 } 253 }
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
73 "Program entry point: 0x{:16X}\n" 73 "Program entry point: 0x{:16X}\n"
74 "\n", 74 "\n",
75 Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, 75 Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
76 2000 + static_cast<u32>(error_code.module.Value()), 76 2000 + static_cast<u32>(error_code.GetModule()),
77 static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point); 77 static_cast<u32>(error_code.GetDescription()), info.set_flags, info.program_entry_point);
78 if (info.backtrace_size != 0x0) { 78 if (info.backtrace_size != 0x0) {
79 crash_report += "Registers:\n"; 79 crash_report += "Registers:\n";
80 for (size_t i = 0; i < info.registers.size(); i++) { 80 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 ca6d8d607..ae230afc0 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -12,18 +12,17 @@
12#include "core/file_sys/card_image.h" 12#include "core/file_sys/card_image.h"
13#include "core/file_sys/control_metadata.h" 13#include "core/file_sys/control_metadata.h"
14#include "core/file_sys/errors.h" 14#include "core/file_sys/errors.h"
15#include "core/file_sys/mode.h"
16#include "core/file_sys/patch_manager.h" 15#include "core/file_sys/patch_manager.h"
17#include "core/file_sys/registered_cache.h" 16#include "core/file_sys/registered_cache.h"
18#include "core/file_sys/romfs_factory.h" 17#include "core/file_sys/romfs_factory.h"
19#include "core/file_sys/savedata_factory.h" 18#include "core/file_sys/savedata_factory.h"
20#include "core/file_sys/sdmc_factory.h" 19#include "core/file_sys/sdmc_factory.h"
21#include "core/file_sys/vfs.h" 20#include "core/file_sys/vfs/vfs.h"
22#include "core/file_sys/vfs_offset.h" 21#include "core/file_sys/vfs/vfs_offset.h"
23#include "core/hle/service/filesystem/filesystem.h" 22#include "core/hle/service/filesystem/filesystem.h"
24#include "core/hle/service/filesystem/fsp_ldr.h" 23#include "core/hle/service/filesystem/fsp/fsp_ldr.h"
25#include "core/hle/service/filesystem/fsp_pr.h" 24#include "core/hle/service/filesystem/fsp/fsp_pr.h"
26#include "core/hle/service/filesystem/fsp_srv.h" 25#include "core/hle/service/filesystem/fsp/fsp_srv.h"
27#include "core/hle/service/filesystem/romfs_controller.h" 26#include "core/hle/service/filesystem/romfs_controller.h"
28#include "core/hle/service/filesystem/save_data_controller.h" 27#include "core/hle/service/filesystem/save_data_controller.h"
29#include "core/hle/service/server_manager.h" 28#include "core/hle/service/server_manager.h"
@@ -53,12 +52,12 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size
53 std::string path(Common::FS::SanitizePath(path_)); 52 std::string path(Common::FS::SanitizePath(path_));
54 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); 53 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
55 if (dir == nullptr) { 54 if (dir == nullptr) {
56 return FileSys::ERROR_PATH_NOT_FOUND; 55 return FileSys::ResultPathNotFound;
57 } 56 }
58 57
59 FileSys::EntryType entry_type{}; 58 FileSys::DirectoryEntryType entry_type{};
60 if (GetEntryType(&entry_type, path) == ResultSuccess) { 59 if (GetEntryType(&entry_type, path) == ResultSuccess) {
61 return FileSys::ERROR_PATH_ALREADY_EXISTS; 60 return FileSys::ResultPathAlreadyExists;
62 } 61 }
63 62
64 auto file = dir->CreateFile(Common::FS::GetFilename(path)); 63 auto file = dir->CreateFile(Common::FS::GetFilename(path));
@@ -82,7 +81,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
82 81
83 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); 82 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
84 if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) { 83 if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
85 return FileSys::ERROR_PATH_NOT_FOUND; 84 return FileSys::ResultPathNotFound;
86 } 85 }
87 if (!dir->DeleteFile(Common::FS::GetFilename(path))) { 86 if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
88 // TODO(DarkLordZach): Find a better error code for this 87 // TODO(DarkLordZach): Find a better error code for this
@@ -153,12 +152,12 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
153 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { 152 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
154 // Use more-optimized vfs implementation rename. 153 // Use more-optimized vfs implementation rename.
155 if (src == nullptr) { 154 if (src == nullptr) {
156 return FileSys::ERROR_PATH_NOT_FOUND; 155 return FileSys::ResultPathNotFound;
157 } 156 }
158 157
159 if (dst && Common::FS::Exists(dst->GetFullPath())) { 158 if (dst && Common::FS::Exists(dst->GetFullPath())) {
160 LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath()); 159 LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
161 return FileSys::ERROR_PATH_ALREADY_EXISTS; 160 return FileSys::ResultPathAlreadyExists;
162 } 161 }
163 162
164 if (!src->Rename(Common::FS::GetFilename(dest_path))) { 163 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
@@ -195,7 +194,7 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
195 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { 194 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
196 // Use more-optimized vfs implementation rename. 195 // Use more-optimized vfs implementation rename.
197 if (src == nullptr) 196 if (src == nullptr)
198 return FileSys::ERROR_PATH_NOT_FOUND; 197 return FileSys::ResultPathNotFound;
199 if (!src->Rename(Common::FS::GetFilename(dest_path))) { 198 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
200 // TODO(DarkLordZach): Find a better error code for this 199 // TODO(DarkLordZach): Find a better error code for this
201 return ResultUnknown; 200 return ResultUnknown;
@@ -214,7 +213,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
214} 213}
215 214
216Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file, 215Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
217 const std::string& path_, FileSys::Mode mode) const { 216 const std::string& path_,
217 FileSys::OpenMode mode) const {
218 const std::string path(Common::FS::SanitizePath(path_)); 218 const std::string path(Common::FS::SanitizePath(path_));
219 std::string_view npath = path; 219 std::string_view npath = path;
220 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { 220 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
@@ -223,10 +223,10 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
223 223
224 auto file = backing->GetFileRelative(npath); 224 auto file = backing->GetFileRelative(npath);
225 if (file == nullptr) { 225 if (file == nullptr) {
226 return FileSys::ERROR_PATH_NOT_FOUND; 226 return FileSys::ResultPathNotFound;
227 } 227 }
228 228
229 if (mode == FileSys::Mode::Append) { 229 if (mode == FileSys::OpenMode::AllowAppend) {
230 *out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()); 230 *out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize());
231 } else { 231 } else {
232 *out_file = file; 232 *out_file = file;
@@ -241,50 +241,50 @@ Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_direct
241 auto dir = GetDirectoryRelativeWrapped(backing, path); 241 auto dir = GetDirectoryRelativeWrapped(backing, path);
242 if (dir == nullptr) { 242 if (dir == nullptr) {
243 // TODO(DarkLordZach): Find a better error code for this 243 // TODO(DarkLordZach): Find a better error code for this
244 return FileSys::ERROR_PATH_NOT_FOUND; 244 return FileSys::ResultPathNotFound;
245 } 245 }
246 *out_directory = dir; 246 *out_directory = dir;
247 return ResultSuccess; 247 return ResultSuccess;
248} 248}
249 249
250Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::EntryType* out_entry_type, 250Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::DirectoryEntryType* out_entry_type,
251 const std::string& path_) const { 251 const std::string& path_) const {
252 std::string path(Common::FS::SanitizePath(path_)); 252 std::string path(Common::FS::SanitizePath(path_));
253 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); 253 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
254 if (dir == nullptr) { 254 if (dir == nullptr) {
255 return FileSys::ERROR_PATH_NOT_FOUND; 255 return FileSys::ResultPathNotFound;
256 } 256 }
257 257
258 auto filename = Common::FS::GetFilename(path); 258 auto filename = Common::FS::GetFilename(path);
259 // TODO(Subv): Some games use the '/' path, find out what this means. 259 // TODO(Subv): Some games use the '/' path, find out what this means.
260 if (filename.empty()) { 260 if (filename.empty()) {
261 *out_entry_type = FileSys::EntryType::Directory; 261 *out_entry_type = FileSys::DirectoryEntryType::Directory;
262 return ResultSuccess; 262 return ResultSuccess;
263 } 263 }
264 264
265 if (dir->GetFile(filename) != nullptr) { 265 if (dir->GetFile(filename) != nullptr) {
266 *out_entry_type = FileSys::EntryType::File; 266 *out_entry_type = FileSys::DirectoryEntryType::File;
267 return ResultSuccess; 267 return ResultSuccess;
268 } 268 }
269 269
270 if (dir->GetSubdirectory(filename) != nullptr) { 270 if (dir->GetSubdirectory(filename) != nullptr) {
271 *out_entry_type = FileSys::EntryType::Directory; 271 *out_entry_type = FileSys::DirectoryEntryType::Directory;
272 return ResultSuccess; 272 return ResultSuccess;
273 } 273 }
274 274
275 return FileSys::ERROR_PATH_NOT_FOUND; 275 return FileSys::ResultPathNotFound;
276} 276}
277 277
278Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw( 278Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
279 FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const { 279 FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const {
280 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); 280 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
281 if (dir == nullptr) { 281 if (dir == nullptr) {
282 return FileSys::ERROR_PATH_NOT_FOUND; 282 return FileSys::ResultPathNotFound;
283 } 283 }
284 284
285 FileSys::EntryType entry_type; 285 FileSys::DirectoryEntryType entry_type;
286 if (GetEntryType(&entry_type, path) != ResultSuccess) { 286 if (GetEntryType(&entry_type, path) != ResultSuccess) {
287 return FileSys::ERROR_PATH_NOT_FOUND; 287 return FileSys::ResultPathNotFound;
288 } 288 }
289 289
290 *out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path)); 290 *out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path));
@@ -317,7 +317,7 @@ Result FileSystemController::OpenProcess(
317 317
318 const auto it = registrations.find(process_id); 318 const auto it = registrations.find(process_id);
319 if (it == registrations.end()) { 319 if (it == registrations.end()) {
320 return FileSys::ERROR_ENTITY_NOT_FOUND; 320 return FileSys::ResultTargetNotFound;
321 } 321 }
322 322
323 *out_program_id = it->second.program_id; 323 *out_program_id = it->second.program_id;
@@ -347,7 +347,7 @@ std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController
347std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory( 347std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
348 ProgramId program_id) { 348 ProgramId program_id) {
349 using YuzuPath = Common::FS::YuzuPath; 349 using YuzuPath = Common::FS::YuzuPath;
350 const auto rw_mode = FileSys::Mode::ReadWrite; 350 const auto rw_mode = FileSys::OpenMode::ReadWrite;
351 351
352 auto vfs = system.GetFilesystem(); 352 auto vfs = system.GetFilesystem();
353 const auto nand_directory = 353 const auto nand_directory =
@@ -360,12 +360,12 @@ Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
360 LOG_TRACE(Service_FS, "Opening SDMC"); 360 LOG_TRACE(Service_FS, "Opening SDMC");
361 361
362 if (sdmc_factory == nullptr) { 362 if (sdmc_factory == nullptr) {
363 return FileSys::ERROR_SD_CARD_NOT_FOUND; 363 return FileSys::ResultPortSdCardNoDevice;
364 } 364 }
365 365
366 auto sdmc = sdmc_factory->Open(); 366 auto sdmc = sdmc_factory->Open();
367 if (sdmc == nullptr) { 367 if (sdmc == nullptr) {
368 return FileSys::ERROR_SD_CARD_NOT_FOUND; 368 return FileSys::ResultPortSdCardNoDevice;
369 } 369 }
370 370
371 *out_sdmc = sdmc; 371 *out_sdmc = sdmc;
@@ -377,12 +377,12 @@ Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_parti
377 LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id); 377 LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);
378 378
379 if (bis_factory == nullptr) { 379 if (bis_factory == nullptr) {
380 return FileSys::ERROR_ENTITY_NOT_FOUND; 380 return FileSys::ResultTargetNotFound;
381 } 381 }
382 382
383 auto part = bis_factory->OpenPartition(id); 383 auto part = bis_factory->OpenPartition(id);
384 if (part == nullptr) { 384 if (part == nullptr) {
385 return FileSys::ERROR_INVALID_ARGUMENT; 385 return FileSys::ResultInvalidArgument;
386 } 386 }
387 387
388 *out_bis_partition = part; 388 *out_bis_partition = part;
@@ -394,12 +394,12 @@ Result FileSystemController::OpenBISPartitionStorage(
394 LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id); 394 LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);
395 395
396 if (bis_factory == nullptr) { 396 if (bis_factory == nullptr) {
397 return FileSys::ERROR_ENTITY_NOT_FOUND; 397 return FileSys::ResultTargetNotFound;
398 } 398 }
399 399
400 auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem()); 400 auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
401 if (part == nullptr) { 401 if (part == nullptr) {
402 return FileSys::ERROR_INVALID_ARGUMENT; 402 return FileSys::ResultInvalidArgument;
403 } 403 }
404 404
405 *out_bis_partition_storage = part; 405 *out_bis_partition_storage = part;
@@ -686,15 +686,15 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
686 using YuzuPath = Common::FS::YuzuPath; 686 using YuzuPath = Common::FS::YuzuPath;
687 const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir); 687 const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir);
688 const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents"; 688 const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
689 const auto rw_mode = FileSys::Mode::ReadWrite; 689 const auto rw_mode = FileSys::OpenMode::ReadWrite;
690 690
691 auto nand_directory = 691 auto nand_directory =
692 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); 692 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
693 auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode); 693 auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode);
694 auto load_directory = 694 auto load_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir),
695 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); 695 FileSys::OpenMode::Read);
696 auto sd_load_directory = 696 auto sd_load_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path),
697 vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read); 697 FileSys::OpenMode::Read);
698 auto dump_directory = 698 auto dump_directory =
699 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); 699 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode);
700 700
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 48f37d289..718500385 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -4,9 +4,11 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <mutex>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/directory.h" 9#include "core/file_sys/fs_directory.h"
9#include "core/file_sys/vfs.h" 10#include "core/file_sys/fs_filesystem.h"
11#include "core/file_sys/vfs/vfs.h"
10#include "core/hle/result.h" 12#include "core/hle/result.h"
11 13
12namespace Core { 14namespace Core {
@@ -26,7 +28,6 @@ class XCI;
26 28
27enum class BisPartitionId : u32; 29enum class BisPartitionId : u32;
28enum class ContentRecordType : u8; 30enum class ContentRecordType : u8;
29enum class Mode : u32;
30enum class SaveDataSpaceId : u8; 31enum class SaveDataSpaceId : u8;
31enum class SaveDataType : u8; 32enum class SaveDataType : u8;
32enum class StorageId : u8; 33enum class StorageId : u8;
@@ -57,13 +58,6 @@ enum class ImageDirectoryId : u32 {
57 SdCard, 58 SdCard,
58}; 59};
59 60
60enum class OpenDirectoryMode : u64 {
61 Directory = (1 << 0),
62 File = (1 << 1),
63 All = Directory | File
64};
65DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
66
67using ProcessId = u64; 61using ProcessId = u64;
68using ProgramId = u64; 62using ProgramId = u64;
69 63
@@ -237,7 +231,7 @@ public:
237 * @return Opened file, or error code 231 * @return Opened file, or error code
238 */ 232 */
239 Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path, 233 Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path,
240 FileSys::Mode mode) const; 234 FileSys::OpenMode mode) const;
241 235
242 /** 236 /**
243 * Open a directory specified by its path 237 * Open a directory specified by its path
@@ -250,7 +244,7 @@ public:
250 * Get the type of the specified path 244 * Get the type of the specified path
251 * @return The type of the specified path or error code 245 * @return The type of the specified path or error code
252 */ 246 */
253 Result GetEntryType(FileSys::EntryType* out_entry_type, const std::string& path) const; 247 Result GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path) const;
254 248
255 /** 249 /**
256 * Get the timestamp of the specified path 250 * 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/file_sys/fs_filesystem.h"
5#include "core/file_sys/savedata_factory.h"
6#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
7#include "core/hle/service/ipc_helpers.h"
8
9namespace Service::FileSystem {
10
11template <typename T>
12static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
13 const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
14 entries.reserve(entries.size() + new_data.size());
15
16 for (const auto& new_entry : new_data) {
17 auto name = new_entry->GetName();
18
19 if (type == FileSys::DirectoryEntryType::File &&
20 name == FileSys::GetSaveDataSizeFileName()) {
21 continue;
22 }
23
24 entries.emplace_back(name, static_cast<s8>(type),
25 type == FileSys::DirectoryEntryType::Directory ? 0
26 : new_entry->GetSize());
27 }
28}
29
30IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
31 FileSys::OpenDirectoryMode mode)
32 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
33 static const FunctionInfo functions[] = {
34 {0, &IDirectory::Read, "Read"},
35 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
36 };
37 RegisterHandlers(functions);
38
39 // TODO(DarkLordZach): Verify that this is the correct behavior.
40 // Build entry index now to save time later.
41 if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
42 BuildEntryIndex(entries, backend->GetSubdirectories(),
43 FileSys::DirectoryEntryType::Directory);
44 }
45 if (True(mode & FileSys::OpenDirectoryMode::File)) {
46 BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
47 }
48}
49
50void IDirectory::Read(HLERequestContext& ctx) {
51 LOG_DEBUG(Service_FS, "called.");
52
53 // Calculate how many entries we can fit in the output buffer
54 const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
55
56 // Cap at total number of entries.
57 const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
58
59 // Determine data start and end
60 const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
61 const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
62 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
63
64 next_entry_index += actual_entries;
65
66 // Write the data to memory
67 ctx.WriteBuffer(begin, range_size);
68
69 IPC::ResponseBuilder rb{ctx, 4};
70 rb.Push(ResultSuccess);
71 rb.Push(actual_entries);
72}
73
74void IDirectory::GetEntryCount(HLERequestContext& ctx) {
75 LOG_DEBUG(Service_FS, "called");
76
77 u64 count = entries.size() - next_entry_index;
78
79 IPC::ResponseBuilder rb{ctx, 4};
80 rb.Push(ResultSuccess);
81 rb.Push(count);
82}
83
84} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/service.h"
9
10namespace FileSys {
11struct DirectoryEntry;
12}
13
14namespace Service::FileSystem {
15
16class IDirectory final : public ServiceFramework<IDirectory> {
17public:
18 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
19 FileSys::OpenDirectoryMode mode);
20
21private:
22 FileSys::VirtualDir backend;
23 std::vector<FileSys::DirectoryEntry> entries;
24 u64 next_entry_index = 0;
25
26 void Read(HLERequestContext& ctx);
27 void GetEntryCount(HLERequestContext& ctx);
28};
29
30} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/file_sys/errors.h"
5#include "core/hle/service/filesystem/fsp/fs_i_file.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::FileSystem {
9
10IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
11 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
12 static const FunctionInfo functions[] = {
13 {0, &IFile::Read, "Read"},
14 {1, &IFile::Write, "Write"},
15 {2, &IFile::Flush, "Flush"},
16 {3, &IFile::SetSize, "SetSize"},
17 {4, &IFile::GetSize, "GetSize"},
18 {5, nullptr, "OperateRange"},
19 {6, nullptr, "OperateRangeWithBuffer"},
20 };
21 RegisterHandlers(functions);
22}
23
24void IFile::Read(HLERequestContext& ctx) {
25 IPC::RequestParser rp{ctx};
26 const u64 option = rp.Pop<u64>();
27 const s64 offset = rp.Pop<s64>();
28 const s64 length = rp.Pop<s64>();
29
30 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
31
32 // Error checking
33 if (length < 0) {
34 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
35 IPC::ResponseBuilder rb{ctx, 2};
36 rb.Push(FileSys::ResultInvalidSize);
37 return;
38 }
39 if (offset < 0) {
40 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
41 IPC::ResponseBuilder rb{ctx, 2};
42 rb.Push(FileSys::ResultInvalidOffset);
43 return;
44 }
45
46 // Read the data from the Storage backend
47 std::vector<u8> output = backend->ReadBytes(length, offset);
48
49 // Write the data to memory
50 ctx.WriteBuffer(output);
51
52 IPC::ResponseBuilder rb{ctx, 4};
53 rb.Push(ResultSuccess);
54 rb.Push(static_cast<u64>(output.size()));
55}
56
57void IFile::Write(HLERequestContext& ctx) {
58 IPC::RequestParser rp{ctx};
59 const u64 option = rp.Pop<u64>();
60 const s64 offset = rp.Pop<s64>();
61 const s64 length = rp.Pop<s64>();
62
63 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
64
65 // Error checking
66 if (length < 0) {
67 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
68 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(FileSys::ResultInvalidSize);
70 return;
71 }
72 if (offset < 0) {
73 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
74 IPC::ResponseBuilder rb{ctx, 2};
75 rb.Push(FileSys::ResultInvalidOffset);
76 return;
77 }
78
79 const auto data = ctx.ReadBuffer();
80
81 ASSERT_MSG(static_cast<s64>(data.size()) <= length,
82 "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
83 length, data.size());
84
85 // Write the data to the Storage backend
86 const auto write_size =
87 static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
88 const std::size_t written = backend->Write(data.data(), write_size, offset);
89
90 ASSERT_MSG(static_cast<s64>(written) == length,
91 "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
92 written);
93
94 IPC::ResponseBuilder rb{ctx, 2};
95 rb.Push(ResultSuccess);
96}
97
98void IFile::Flush(HLERequestContext& ctx) {
99 LOG_DEBUG(Service_FS, "called");
100
101 // Exists for SDK compatibiltity -- No need to flush file.
102
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(ResultSuccess);
105}
106
107void IFile::SetSize(HLERequestContext& ctx) {
108 IPC::RequestParser rp{ctx};
109 const u64 size = rp.Pop<u64>();
110 LOG_DEBUG(Service_FS, "called, size={}", size);
111
112 backend->Resize(size);
113
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(ResultSuccess);
116}
117
118void IFile::GetSize(HLERequestContext& ctx) {
119 const u64 size = backend->GetSize();
120 LOG_DEBUG(Service_FS, "called, size={}", size);
121
122 IPC::ResponseBuilder rb{ctx, 4};
123 rb.Push(ResultSuccess);
124 rb.Push<u64>(size);
125}
126
127} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/filesystem/filesystem.h"
7#include "core/hle/service/service.h"
8
9namespace Service::FileSystem {
10
11class IFile final : public ServiceFramework<IFile> {
12public:
13 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_);
14
15private:
16 FileSys::VirtualFile backend;
17
18 void Read(HLERequestContext& ctx);
19 void Write(HLERequestContext& ctx);
20 void Flush(HLERequestContext& ctx);
21 void SetSize(HLERequestContext& ctx);
22 void GetSize(HLERequestContext& ctx);
23};
24
25} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/string_util.h"
5#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
6#include "core/hle/service/filesystem/fsp/fs_i_file.h"
7#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
8#include "core/hle/service/ipc_helpers.h"
9
10namespace Service::FileSystem {
11
12IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
13 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
14 size_)} {
15 static const FunctionInfo functions[] = {
16 {0, &IFileSystem::CreateFile, "CreateFile"},
17 {1, &IFileSystem::DeleteFile, "DeleteFile"},
18 {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
19 {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
20 {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
21 {5, &IFileSystem::RenameFile, "RenameFile"},
22 {6, nullptr, "RenameDirectory"},
23 {7, &IFileSystem::GetEntryType, "GetEntryType"},
24 {8, &IFileSystem::OpenFile, "OpenFile"},
25 {9, &IFileSystem::OpenDirectory, "OpenDirectory"},
26 {10, &IFileSystem::Commit, "Commit"},
27 {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
28 {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
29 {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
30 {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
31 {15, nullptr, "QueryEntry"},
32 {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
33 };
34 RegisterHandlers(functions);
35}
36
37void IFileSystem::CreateFile(HLERequestContext& ctx) {
38 IPC::RequestParser rp{ctx};
39
40 const auto file_buffer = ctx.ReadBuffer();
41 const std::string name = Common::StringFromBuffer(file_buffer);
42
43 const u64 file_mode = rp.Pop<u64>();
44 const u32 file_size = rp.Pop<u32>();
45
46 LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
47 file_size);
48
49 IPC::ResponseBuilder rb{ctx, 2};
50 rb.Push(backend.CreateFile(name, file_size));
51}
52
53void IFileSystem::DeleteFile(HLERequestContext& ctx) {
54 const auto file_buffer = ctx.ReadBuffer();
55 const std::string name = Common::StringFromBuffer(file_buffer);
56
57 LOG_DEBUG(Service_FS, "called. file={}", name);
58
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(backend.DeleteFile(name));
61}
62
63void IFileSystem::CreateDirectory(HLERequestContext& ctx) {
64 const auto file_buffer = ctx.ReadBuffer();
65 const std::string name = Common::StringFromBuffer(file_buffer);
66
67 LOG_DEBUG(Service_FS, "called. directory={}", name);
68
69 IPC::ResponseBuilder rb{ctx, 2};
70 rb.Push(backend.CreateDirectory(name));
71}
72
73void IFileSystem::DeleteDirectory(HLERequestContext& ctx) {
74 const auto file_buffer = ctx.ReadBuffer();
75 const std::string name = Common::StringFromBuffer(file_buffer);
76
77 LOG_DEBUG(Service_FS, "called. directory={}", name);
78
79 IPC::ResponseBuilder rb{ctx, 2};
80 rb.Push(backend.DeleteDirectory(name));
81}
82
83void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) {
84 const auto file_buffer = ctx.ReadBuffer();
85 const std::string name = Common::StringFromBuffer(file_buffer);
86
87 LOG_DEBUG(Service_FS, "called. directory={}", name);
88
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(backend.DeleteDirectoryRecursively(name));
91}
92
93void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) {
94 const auto file_buffer = ctx.ReadBuffer();
95 const std::string name = Common::StringFromBuffer(file_buffer);
96
97 LOG_DEBUG(Service_FS, "called. Directory: {}", name);
98
99 IPC::ResponseBuilder rb{ctx, 2};
100 rb.Push(backend.CleanDirectoryRecursively(name));
101}
102
103void IFileSystem::RenameFile(HLERequestContext& ctx) {
104 const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
105 const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
106
107 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
108
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(backend.RenameFile(src_name, dst_name));
111}
112
113void IFileSystem::OpenFile(HLERequestContext& ctx) {
114 IPC::RequestParser rp{ctx};
115
116 const auto file_buffer = ctx.ReadBuffer();
117 const std::string name = Common::StringFromBuffer(file_buffer);
118
119 const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
120
121 LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
122
123 FileSys::VirtualFile vfs_file{};
124 auto result = backend.OpenFile(&vfs_file, name, mode);
125 if (result != ResultSuccess) {
126 IPC::ResponseBuilder rb{ctx, 2};
127 rb.Push(result);
128 return;
129 }
130
131 auto file = std::make_shared<IFile>(system, vfs_file);
132
133 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
134 rb.Push(ResultSuccess);
135 rb.PushIpcInterface<IFile>(std::move(file));
136}
137
138void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
139 IPC::RequestParser rp{ctx};
140
141 const auto file_buffer = ctx.ReadBuffer();
142 const std::string name = Common::StringFromBuffer(file_buffer);
143 const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
144
145 LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
146
147 FileSys::VirtualDir vfs_dir{};
148 auto result = backend.OpenDirectory(&vfs_dir, name);
149 if (result != ResultSuccess) {
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(result);
152 return;
153 }
154
155 auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
156
157 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
158 rb.Push(ResultSuccess);
159 rb.PushIpcInterface<IDirectory>(std::move(directory));
160}
161
162void IFileSystem::GetEntryType(HLERequestContext& ctx) {
163 const auto file_buffer = ctx.ReadBuffer();
164 const std::string name = Common::StringFromBuffer(file_buffer);
165
166 LOG_DEBUG(Service_FS, "called. file={}", name);
167
168 FileSys::DirectoryEntryType vfs_entry_type{};
169 auto result = backend.GetEntryType(&vfs_entry_type, name);
170 if (result != ResultSuccess) {
171 IPC::ResponseBuilder rb{ctx, 2};
172 rb.Push(result);
173 return;
174 }
175
176 IPC::ResponseBuilder rb{ctx, 3};
177 rb.Push(ResultSuccess);
178 rb.Push<u32>(static_cast<u32>(vfs_entry_type));
179}
180
181void IFileSystem::Commit(HLERequestContext& ctx) {
182 LOG_WARNING(Service_FS, "(STUBBED) called");
183
184 IPC::ResponseBuilder rb{ctx, 2};
185 rb.Push(ResultSuccess);
186}
187
188void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) {
189 LOG_DEBUG(Service_FS, "called");
190
191 IPC::ResponseBuilder rb{ctx, 4};
192 rb.Push(ResultSuccess);
193 rb.Push(size.get_free_size());
194}
195
196void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) {
197 LOG_DEBUG(Service_FS, "called");
198
199 IPC::ResponseBuilder rb{ctx, 4};
200 rb.Push(ResultSuccess);
201 rb.Push(size.get_total_size());
202}
203
204void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) {
205 const auto file_buffer = ctx.ReadBuffer();
206 const std::string name = Common::StringFromBuffer(file_buffer);
207
208 LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
209
210 FileSys::FileTimeStampRaw vfs_timestamp{};
211 auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
212 if (result != ResultSuccess) {
213 IPC::ResponseBuilder rb{ctx, 2};
214 rb.Push(result);
215 return;
216 }
217
218 IPC::ResponseBuilder rb{ctx, 10};
219 rb.Push(ResultSuccess);
220 rb.PushRaw(vfs_timestamp);
221}
222
223void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
224 LOG_WARNING(Service_FS, "(STUBBED) called");
225
226 struct FileSystemAttribute {
227 u8 dir_entry_name_length_max_defined;
228 u8 file_entry_name_length_max_defined;
229 u8 dir_path_name_length_max_defined;
230 u8 file_path_name_length_max_defined;
231 INSERT_PADDING_BYTES_NOINIT(0x5);
232 u8 utf16_dir_entry_name_length_max_defined;
233 u8 utf16_file_entry_name_length_max_defined;
234 u8 utf16_dir_path_name_length_max_defined;
235 u8 utf16_file_path_name_length_max_defined;
236 INSERT_PADDING_BYTES_NOINIT(0x18);
237 s32 dir_entry_name_length_max;
238 s32 file_entry_name_length_max;
239 s32 dir_path_name_length_max;
240 s32 file_path_name_length_max;
241 INSERT_PADDING_WORDS_NOINIT(0x5);
242 s32 utf16_dir_entry_name_length_max;
243 s32 utf16_file_entry_name_length_max;
244 s32 utf16_dir_path_name_length_max;
245 s32 utf16_file_path_name_length_max;
246 INSERT_PADDING_WORDS_NOINIT(0x18);
247 INSERT_PADDING_WORDS_NOINIT(0x1);
248 };
249 static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
250
251 FileSystemAttribute savedata_attribute{};
252 savedata_attribute.dir_entry_name_length_max_defined = true;
253 savedata_attribute.file_entry_name_length_max_defined = true;
254 savedata_attribute.dir_entry_name_length_max = 0x40;
255 savedata_attribute.file_entry_name_length_max = 0x40;
256
257 IPC::ResponseBuilder rb{ctx, 50};
258 rb.Push(ResultSuccess);
259 rb.PushRaw(savedata_attribute);
260}
261
262} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/filesystem/fsp/fsp_util.h"
9#include "core/hle/service/service.h"
10
11namespace Service::FileSystem {
12
13class IFileSystem final : public ServiceFramework<IFileSystem> {
14public:
15 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_);
16
17 void CreateFile(HLERequestContext& ctx);
18 void DeleteFile(HLERequestContext& ctx);
19 void CreateDirectory(HLERequestContext& ctx);
20 void DeleteDirectory(HLERequestContext& ctx);
21 void DeleteDirectoryRecursively(HLERequestContext& ctx);
22 void CleanDirectoryRecursively(HLERequestContext& ctx);
23 void RenameFile(HLERequestContext& ctx);
24 void OpenFile(HLERequestContext& ctx);
25 void OpenDirectory(HLERequestContext& ctx);
26 void GetEntryType(HLERequestContext& ctx);
27 void Commit(HLERequestContext& ctx);
28 void GetFreeSpaceSize(HLERequestContext& ctx);
29 void GetTotalSpaceSize(HLERequestContext& ctx);
30 void GetFileTimeStampRaw(HLERequestContext& ctx);
31 void GetFileSystemAttribute(HLERequestContext& ctx);
32
33private:
34 VfsDirectoryServiceWrapper backend;
35 SizeGetter size;
36};
37
38} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/file_sys/errors.h"
5#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::FileSystem {
9
10IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_)
11 : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
12 static const FunctionInfo functions[] = {
13 {0, &IStorage::Read, "Read"},
14 {1, nullptr, "Write"},
15 {2, nullptr, "Flush"},
16 {3, nullptr, "SetSize"},
17 {4, &IStorage::GetSize, "GetSize"},
18 {5, nullptr, "OperateRange"},
19 };
20 RegisterHandlers(functions);
21}
22
23void IStorage::Read(HLERequestContext& ctx) {
24 IPC::RequestParser rp{ctx};
25 const s64 offset = rp.Pop<s64>();
26 const s64 length = rp.Pop<s64>();
27
28 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
29
30 // Error checking
31 if (length < 0) {
32 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
33 IPC::ResponseBuilder rb{ctx, 2};
34 rb.Push(FileSys::ResultInvalidSize);
35 return;
36 }
37 if (offset < 0) {
38 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
39 IPC::ResponseBuilder rb{ctx, 2};
40 rb.Push(FileSys::ResultInvalidOffset);
41 return;
42 }
43
44 // Read the data from the Storage backend
45 std::vector<u8> output = backend->ReadBytes(length, offset);
46 // Write the data to memory
47 ctx.WriteBuffer(output);
48
49 IPC::ResponseBuilder rb{ctx, 2};
50 rb.Push(ResultSuccess);
51}
52
53void IStorage::GetSize(HLERequestContext& ctx) {
54 const u64 size = backend->GetSize();
55 LOG_DEBUG(Service_FS, "called, size={}", size);
56
57 IPC::ResponseBuilder rb{ctx, 4};
58 rb.Push(ResultSuccess);
59 rb.Push<u64>(size);
60}
61
62} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/service.h"
9
10namespace Service::FileSystem {
11
12class IStorage final : public ServiceFramework<IStorage> {
13public:
14 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_);
15
16private:
17 FileSys::VirtualFile backend;
18
19 void Read(HLERequestContext& ctx);
20 void GetSize(HLERequestContext& ctx);
21};
22
23} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp
index 1e3366e71..8ee733f47 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/filesystem/fsp_ldr.h" 4#include "core/hle/service/filesystem/fsp/fsp_ldr.h"
5 5
6namespace Service::FileSystem { 6namespace Service::FileSystem {
7 7
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_pr.cpp b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp
index 4ffc31977..7c03ebaea 100644
--- a/src/core/hle/service/filesystem/fsp_pr.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/filesystem/fsp_pr.h" 4#include "core/hle/service/filesystem/fsp/fsp_pr.h"
5 5
6namespace Service::FileSystem { 6namespace Service::FileSystem {
7 7
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_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
index a2397bec4..2be72b021 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
@@ -15,18 +15,20 @@
15#include "common/settings.h" 15#include "common/settings.h"
16#include "common/string_util.h" 16#include "common/string_util.h"
17#include "core/core.h" 17#include "core/core.h"
18#include "core/file_sys/directory.h"
19#include "core/file_sys/errors.h" 18#include "core/file_sys/errors.h"
20#include "core/file_sys/mode.h" 19#include "core/file_sys/fs_directory.h"
20#include "core/file_sys/fs_filesystem.h"
21#include "core/file_sys/nca_metadata.h" 21#include "core/file_sys/nca_metadata.h"
22#include "core/file_sys/patch_manager.h" 22#include "core/file_sys/patch_manager.h"
23#include "core/file_sys/romfs_factory.h" 23#include "core/file_sys/romfs_factory.h"
24#include "core/file_sys/savedata_factory.h" 24#include "core/file_sys/savedata_factory.h"
25#include "core/file_sys/system_archive/system_archive.h" 25#include "core/file_sys/system_archive/system_archive.h"
26#include "core/file_sys/vfs.h" 26#include "core/file_sys/vfs/vfs.h"
27#include "core/hle/result.h" 27#include "core/hle/result.h"
28#include "core/hle/service/filesystem/filesystem.h" 28#include "core/hle/service/filesystem/filesystem.h"
29#include "core/hle/service/filesystem/fsp_srv.h" 29#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
30#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
31#include "core/hle/service/filesystem/fsp/fsp_srv.h"
30#include "core/hle/service/filesystem/romfs_controller.h" 32#include "core/hle/service/filesystem/romfs_controller.h"
31#include "core/hle/service/filesystem/save_data_controller.h" 33#include "core/hle/service/filesystem/save_data_controller.h"
32#include "core/hle/service/hle_ipc.h" 34#include "core/hle/service/hle_ipc.h"
@@ -34,19 +36,6 @@
34#include "core/reporter.h" 36#include "core/reporter.h"
35 37
36namespace Service::FileSystem { 38namespace Service::FileSystem {
37
38struct SizeGetter {
39 std::function<u64()> get_free_size;
40 std::function<u64()> get_total_size;
41
42 static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
43 return {
44 [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
45 [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
46 };
47 }
48};
49
50enum class FileSystemType : u8 { 39enum class FileSystemType : u8 {
51 Invalid0 = 0, 40 Invalid0 = 0,
52 Invalid1 = 1, 41 Invalid1 = 1,
@@ -58,525 +47,6 @@ enum class FileSystemType : u8 {
58 ApplicationPackage = 7, 47 ApplicationPackage = 7,
59}; 48};
60 49
61class IStorage final : public ServiceFramework<IStorage> {
62public:
63 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
64 : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
65 static const FunctionInfo functions[] = {
66 {0, &IStorage::Read, "Read"},
67 {1, nullptr, "Write"},
68 {2, nullptr, "Flush"},
69 {3, nullptr, "SetSize"},
70 {4, &IStorage::GetSize, "GetSize"},
71 {5, nullptr, "OperateRange"},
72 };
73 RegisterHandlers(functions);
74 }
75
76private:
77 FileSys::VirtualFile backend;
78
79 void Read(HLERequestContext& ctx) {
80 IPC::RequestParser rp{ctx};
81 const s64 offset = rp.Pop<s64>();
82 const s64 length = rp.Pop<s64>();
83
84 LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
85
86 // Error checking
87 if (length < 0) {
88 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(FileSys::ERROR_INVALID_SIZE);
91 return;
92 }
93 if (offset < 0) {
94 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
95 IPC::ResponseBuilder rb{ctx, 2};
96 rb.Push(FileSys::ERROR_INVALID_OFFSET);
97 return;
98 }
99
100 // Read the data from the Storage backend
101 std::vector<u8> output = backend->ReadBytes(length, offset);
102 // Write the data to memory
103 ctx.WriteBuffer(output);
104
105 IPC::ResponseBuilder rb{ctx, 2};
106 rb.Push(ResultSuccess);
107 }
108
109 void GetSize(HLERequestContext& ctx) {
110 const u64 size = backend->GetSize();
111 LOG_DEBUG(Service_FS, "called, size={}", size);
112
113 IPC::ResponseBuilder rb{ctx, 4};
114 rb.Push(ResultSuccess);
115 rb.Push<u64>(size);
116 }
117};
118
119class IFile final : public ServiceFramework<IFile> {
120public:
121 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
122 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
123 static const FunctionInfo functions[] = {
124 {0, &IFile::Read, "Read"},
125 {1, &IFile::Write, "Write"},
126 {2, &IFile::Flush, "Flush"},
127 {3, &IFile::SetSize, "SetSize"},
128 {4, &IFile::GetSize, "GetSize"},
129 {5, nullptr, "OperateRange"},
130 {6, nullptr, "OperateRangeWithBuffer"},
131 };
132 RegisterHandlers(functions);
133 }
134
135private:
136 FileSys::VirtualFile backend;
137
138 void Read(HLERequestContext& ctx) {
139 IPC::RequestParser rp{ctx};
140 const u64 option = rp.Pop<u64>();
141 const s64 offset = rp.Pop<s64>();
142 const s64 length = rp.Pop<s64>();
143
144 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
145 length);
146
147 // Error checking
148 if (length < 0) {
149 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(FileSys::ERROR_INVALID_SIZE);
152 return;
153 }
154 if (offset < 0) {
155 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
156 IPC::ResponseBuilder rb{ctx, 2};
157 rb.Push(FileSys::ERROR_INVALID_OFFSET);
158 return;
159 }
160
161 // Read the data from the Storage backend
162 std::vector<u8> output = backend->ReadBytes(length, offset);
163
164 // Write the data to memory
165 ctx.WriteBuffer(output);
166
167 IPC::ResponseBuilder rb{ctx, 4};
168 rb.Push(ResultSuccess);
169 rb.Push(static_cast<u64>(output.size()));
170 }
171
172 void Write(HLERequestContext& ctx) {
173 IPC::RequestParser rp{ctx};
174 const u64 option = rp.Pop<u64>();
175 const s64 offset = rp.Pop<s64>();
176 const s64 length = rp.Pop<s64>();
177
178 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
179 length);
180
181 // Error checking
182 if (length < 0) {
183 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
184 IPC::ResponseBuilder rb{ctx, 2};
185 rb.Push(FileSys::ERROR_INVALID_SIZE);
186 return;
187 }
188 if (offset < 0) {
189 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
190 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(FileSys::ERROR_INVALID_OFFSET);
192 return;
193 }
194
195 const auto data = ctx.ReadBuffer();
196
197 ASSERT_MSG(
198 static_cast<s64>(data.size()) <= length,
199 "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
200 length, data.size());
201
202 // Write the data to the Storage backend
203 const auto write_size =
204 static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
205 const std::size_t written = backend->Write(data.data(), write_size, offset);
206
207 ASSERT_MSG(static_cast<s64>(written) == length,
208 "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
209 written);
210
211 IPC::ResponseBuilder rb{ctx, 2};
212 rb.Push(ResultSuccess);
213 }
214
215 void Flush(HLERequestContext& ctx) {
216 LOG_DEBUG(Service_FS, "called");
217
218 // Exists for SDK compatibiltity -- No need to flush file.
219
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(ResultSuccess);
222 }
223
224 void SetSize(HLERequestContext& ctx) {
225 IPC::RequestParser rp{ctx};
226 const u64 size = rp.Pop<u64>();
227 LOG_DEBUG(Service_FS, "called, size={}", size);
228
229 backend->Resize(size);
230
231 IPC::ResponseBuilder rb{ctx, 2};
232 rb.Push(ResultSuccess);
233 }
234
235 void GetSize(HLERequestContext& ctx) {
236 const u64 size = backend->GetSize();
237 LOG_DEBUG(Service_FS, "called, size={}", size);
238
239 IPC::ResponseBuilder rb{ctx, 4};
240 rb.Push(ResultSuccess);
241 rb.Push<u64>(size);
242 }
243};
244
245template <typename T>
246static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
247 FileSys::EntryType type) {
248 entries.reserve(entries.size() + new_data.size());
249
250 for (const auto& new_entry : new_data) {
251 auto name = new_entry->GetName();
252
253 if (type == FileSys::EntryType::File && name == FileSys::GetSaveDataSizeFileName()) {
254 continue;
255 }
256
257 entries.emplace_back(name, type,
258 type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
259 }
260}
261
262class IDirectory final : public ServiceFramework<IDirectory> {
263public:
264 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, OpenDirectoryMode mode)
265 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
266 static const FunctionInfo functions[] = {
267 {0, &IDirectory::Read, "Read"},
268 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
269 };
270 RegisterHandlers(functions);
271
272 // TODO(DarkLordZach): Verify that this is the correct behavior.
273 // Build entry index now to save time later.
274 if (True(mode & OpenDirectoryMode::Directory)) {
275 BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
276 }
277 if (True(mode & OpenDirectoryMode::File)) {
278 BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
279 }
280 }
281
282private:
283 FileSys::VirtualDir backend;
284 std::vector<FileSys::Entry> entries;
285 u64 next_entry_index = 0;
286
287 void Read(HLERequestContext& ctx) {
288 LOG_DEBUG(Service_FS, "called.");
289
290 // Calculate how many entries we can fit in the output buffer
291 const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>();
292
293 // Cap at total number of entries.
294 const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
295
296 // Determine data start and end
297 const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
298 const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
299 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
300
301 next_entry_index += actual_entries;
302
303 // Write the data to memory
304 ctx.WriteBuffer(begin, range_size);
305
306 IPC::ResponseBuilder rb{ctx, 4};
307 rb.Push(ResultSuccess);
308 rb.Push(actual_entries);
309 }
310
311 void GetEntryCount(HLERequestContext& ctx) {
312 LOG_DEBUG(Service_FS, "called");
313
314 u64 count = entries.size() - next_entry_index;
315
316 IPC::ResponseBuilder rb{ctx, 4};
317 rb.Push(ResultSuccess);
318 rb.Push(count);
319 }
320};
321
322class IFileSystem final : public ServiceFramework<IFileSystem> {
323public:
324 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
325 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
326 size_)} {
327 static const FunctionInfo functions[] = {
328 {0, &IFileSystem::CreateFile, "CreateFile"},
329 {1, &IFileSystem::DeleteFile, "DeleteFile"},
330 {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
331 {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
332 {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
333 {5, &IFileSystem::RenameFile, "RenameFile"},
334 {6, nullptr, "RenameDirectory"},
335 {7, &IFileSystem::GetEntryType, "GetEntryType"},
336 {8, &IFileSystem::OpenFile, "OpenFile"},
337 {9, &IFileSystem::OpenDirectory, "OpenDirectory"},
338 {10, &IFileSystem::Commit, "Commit"},
339 {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
340 {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
341 {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
342 {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
343 {15, nullptr, "QueryEntry"},
344 {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
345 };
346 RegisterHandlers(functions);
347 }
348
349 void CreateFile(HLERequestContext& ctx) {
350 IPC::RequestParser rp{ctx};
351
352 const auto file_buffer = ctx.ReadBuffer();
353 const std::string name = Common::StringFromBuffer(file_buffer);
354
355 const u64 file_mode = rp.Pop<u64>();
356 const u32 file_size = rp.Pop<u32>();
357
358 LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
359 file_size);
360
361 IPC::ResponseBuilder rb{ctx, 2};
362 rb.Push(backend.CreateFile(name, file_size));
363 }
364
365 void DeleteFile(HLERequestContext& ctx) {
366 const auto file_buffer = ctx.ReadBuffer();
367 const std::string name = Common::StringFromBuffer(file_buffer);
368
369 LOG_DEBUG(Service_FS, "called. file={}", name);
370
371 IPC::ResponseBuilder rb{ctx, 2};
372 rb.Push(backend.DeleteFile(name));
373 }
374
375 void CreateDirectory(HLERequestContext& ctx) {
376 const auto file_buffer = ctx.ReadBuffer();
377 const std::string name = Common::StringFromBuffer(file_buffer);
378
379 LOG_DEBUG(Service_FS, "called. directory={}", name);
380
381 IPC::ResponseBuilder rb{ctx, 2};
382 rb.Push(backend.CreateDirectory(name));
383 }
384
385 void DeleteDirectory(HLERequestContext& ctx) {
386 const auto file_buffer = ctx.ReadBuffer();
387 const std::string name = Common::StringFromBuffer(file_buffer);
388
389 LOG_DEBUG(Service_FS, "called. directory={}", name);
390
391 IPC::ResponseBuilder rb{ctx, 2};
392 rb.Push(backend.DeleteDirectory(name));
393 }
394
395 void DeleteDirectoryRecursively(HLERequestContext& ctx) {
396 const auto file_buffer = ctx.ReadBuffer();
397 const std::string name = Common::StringFromBuffer(file_buffer);
398
399 LOG_DEBUG(Service_FS, "called. directory={}", name);
400
401 IPC::ResponseBuilder rb{ctx, 2};
402 rb.Push(backend.DeleteDirectoryRecursively(name));
403 }
404
405 void CleanDirectoryRecursively(HLERequestContext& ctx) {
406 const auto file_buffer = ctx.ReadBuffer();
407 const std::string name = Common::StringFromBuffer(file_buffer);
408
409 LOG_DEBUG(Service_FS, "called. Directory: {}", name);
410
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(backend.CleanDirectoryRecursively(name));
413 }
414
415 void RenameFile(HLERequestContext& ctx) {
416 const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
417 const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
418
419 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
420
421 IPC::ResponseBuilder rb{ctx, 2};
422 rb.Push(backend.RenameFile(src_name, dst_name));
423 }
424
425 void OpenFile(HLERequestContext& ctx) {
426 IPC::RequestParser rp{ctx};
427
428 const auto file_buffer = ctx.ReadBuffer();
429 const std::string name = Common::StringFromBuffer(file_buffer);
430
431 const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
432
433 LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
434
435 FileSys::VirtualFile vfs_file{};
436 auto result = backend.OpenFile(&vfs_file, name, mode);
437 if (result != ResultSuccess) {
438 IPC::ResponseBuilder rb{ctx, 2};
439 rb.Push(result);
440 return;
441 }
442
443 auto file = std::make_shared<IFile>(system, vfs_file);
444
445 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
446 rb.Push(ResultSuccess);
447 rb.PushIpcInterface<IFile>(std::move(file));
448 }
449
450 void OpenDirectory(HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452
453 const auto file_buffer = ctx.ReadBuffer();
454 const std::string name = Common::StringFromBuffer(file_buffer);
455 const auto mode = rp.PopRaw<OpenDirectoryMode>();
456
457 LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
458
459 FileSys::VirtualDir vfs_dir{};
460 auto result = backend.OpenDirectory(&vfs_dir, name);
461 if (result != ResultSuccess) {
462 IPC::ResponseBuilder rb{ctx, 2};
463 rb.Push(result);
464 return;
465 }
466
467 auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
468
469 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
470 rb.Push(ResultSuccess);
471 rb.PushIpcInterface<IDirectory>(std::move(directory));
472 }
473
474 void GetEntryType(HLERequestContext& ctx) {
475 const auto file_buffer = ctx.ReadBuffer();
476 const std::string name = Common::StringFromBuffer(file_buffer);
477
478 LOG_DEBUG(Service_FS, "called. file={}", name);
479
480 FileSys::EntryType vfs_entry_type{};
481 auto result = backend.GetEntryType(&vfs_entry_type, name);
482 if (result != ResultSuccess) {
483 IPC::ResponseBuilder rb{ctx, 2};
484 rb.Push(result);
485 return;
486 }
487
488 IPC::ResponseBuilder rb{ctx, 3};
489 rb.Push(ResultSuccess);
490 rb.Push<u32>(static_cast<u32>(vfs_entry_type));
491 }
492
493 void Commit(HLERequestContext& ctx) {
494 LOG_WARNING(Service_FS, "(STUBBED) called");
495
496 IPC::ResponseBuilder rb{ctx, 2};
497 rb.Push(ResultSuccess);
498 }
499
500 void GetFreeSpaceSize(HLERequestContext& ctx) {
501 LOG_DEBUG(Service_FS, "called");
502
503 IPC::ResponseBuilder rb{ctx, 4};
504 rb.Push(ResultSuccess);
505 rb.Push(size.get_free_size());
506 }
507
508 void GetTotalSpaceSize(HLERequestContext& ctx) {
509 LOG_DEBUG(Service_FS, "called");
510
511 IPC::ResponseBuilder rb{ctx, 4};
512 rb.Push(ResultSuccess);
513 rb.Push(size.get_total_size());
514 }
515
516 void GetFileTimeStampRaw(HLERequestContext& ctx) {
517 const auto file_buffer = ctx.ReadBuffer();
518 const std::string name = Common::StringFromBuffer(file_buffer);
519
520 LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
521
522 FileSys::FileTimeStampRaw vfs_timestamp{};
523 auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
524 if (result != ResultSuccess) {
525 IPC::ResponseBuilder rb{ctx, 2};
526 rb.Push(result);
527 return;
528 }
529
530 IPC::ResponseBuilder rb{ctx, 10};
531 rb.Push(ResultSuccess);
532 rb.PushRaw(vfs_timestamp);
533 }
534
535 void GetFileSystemAttribute(HLERequestContext& ctx) {
536 LOG_WARNING(Service_FS, "(STUBBED) called");
537
538 struct FileSystemAttribute {
539 u8 dir_entry_name_length_max_defined;
540 u8 file_entry_name_length_max_defined;
541 u8 dir_path_name_length_max_defined;
542 u8 file_path_name_length_max_defined;
543 INSERT_PADDING_BYTES_NOINIT(0x5);
544 u8 utf16_dir_entry_name_length_max_defined;
545 u8 utf16_file_entry_name_length_max_defined;
546 u8 utf16_dir_path_name_length_max_defined;
547 u8 utf16_file_path_name_length_max_defined;
548 INSERT_PADDING_BYTES_NOINIT(0x18);
549 s32 dir_entry_name_length_max;
550 s32 file_entry_name_length_max;
551 s32 dir_path_name_length_max;
552 s32 file_path_name_length_max;
553 INSERT_PADDING_WORDS_NOINIT(0x5);
554 s32 utf16_dir_entry_name_length_max;
555 s32 utf16_file_entry_name_length_max;
556 s32 utf16_dir_path_name_length_max;
557 s32 utf16_file_path_name_length_max;
558 INSERT_PADDING_WORDS_NOINIT(0x18);
559 INSERT_PADDING_WORDS_NOINIT(0x1);
560 };
561 static_assert(sizeof(FileSystemAttribute) == 0xc0,
562 "FileSystemAttribute has incorrect size");
563
564 FileSystemAttribute savedata_attribute{};
565 savedata_attribute.dir_entry_name_length_max_defined = true;
566 savedata_attribute.file_entry_name_length_max_defined = true;
567 savedata_attribute.dir_entry_name_length_max = 0x40;
568 savedata_attribute.file_entry_name_length_max = 0x40;
569
570 IPC::ResponseBuilder rb{ctx, 50};
571 rb.Push(ResultSuccess);
572 rb.PushRaw(savedata_attribute);
573 }
574
575private:
576 VfsDirectoryServiceWrapper backend;
577 SizeGetter size;
578};
579
580class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { 50class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
581public: 51public:
582 explicit ISaveDataInfoReader(Core::System& system_, 52 explicit ISaveDataInfoReader(Core::System& system_,
@@ -960,7 +430,7 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
960 save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute); 430 save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
961 if (result != ResultSuccess) { 431 if (result != ResultSuccess) {
962 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 432 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
963 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 433 rb.Push(FileSys::ResultTargetNotFound);
964 return; 434 return;
965 } 435 }
966 436
@@ -1127,7 +597,7 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx) {
1127 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id); 597 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id);
1128 598
1129 IPC::ResponseBuilder rb{ctx, 2}; 599 IPC::ResponseBuilder rb{ctx, 2};
1130 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 600 rb.Push(FileSys::ResultTargetNotFound);
1131} 601}
1132 602
1133void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { 603void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h
index 26980af99..26980af99 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h
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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/filesystem/filesystem.h"
7
8namespace Service::FileSystem {
9
10struct SizeGetter {
11 std::function<u64()> get_free_size;
12 std::function<u64()> get_total_size;
13
14 static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
15 return {
16 [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
17 [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
18 };
19 }
20};
21
22} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h
index 9a478f71d..3c3ead344 100644
--- a/src/core/hle/service/filesystem/romfs_controller.h
+++ b/src/core/hle/service/filesystem/romfs_controller.h
@@ -5,7 +5,7 @@
5 5
6#include "core/file_sys/nca_metadata.h" 6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/romfs_factory.h" 7#include "core/file_sys/romfs_factory.h"
8#include "core/file_sys/vfs_types.h" 8#include "core/file_sys/vfs/vfs_types.h"
9 9
10namespace Service::FileSystem { 10namespace Service::FileSystem {
11 11
diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp
index d19b3ea1e..03e45f7f9 100644
--- a/src/core/hle/service/filesystem/save_data_controller.cpp
+++ b/src/core/hle/service/filesystem/save_data_controller.cpp
@@ -44,7 +44,7 @@ Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
44 44
45 auto save_data = factory->Create(space, attribute); 45 auto save_data = factory->Create(space, attribute);
46 if (save_data == nullptr) { 46 if (save_data == nullptr) {
47 return FileSys::ERROR_ENTITY_NOT_FOUND; 47 return FileSys::ResultTargetNotFound;
48 } 48 }
49 49
50 *out_save_data = save_data; 50 *out_save_data = save_data;
@@ -56,7 +56,7 @@ Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
56 const FileSys::SaveDataAttribute& attribute) { 56 const FileSys::SaveDataAttribute& attribute) {
57 auto save_data = factory->Open(space, attribute); 57 auto save_data = factory->Open(space, attribute);
58 if (save_data == nullptr) { 58 if (save_data == nullptr) {
59 return FileSys::ERROR_ENTITY_NOT_FOUND; 59 return FileSys::ResultTargetNotFound;
60 } 60 }
61 61
62 *out_save_data = save_data; 62 *out_save_data = save_data;
@@ -67,7 +67,7 @@ Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_
67 FileSys::SaveDataSpaceId space) { 67 FileSys::SaveDataSpaceId space) {
68 auto save_data_space = factory->GetSaveDataSpaceDirectory(space); 68 auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
69 if (save_data_space == nullptr) { 69 if (save_data_space == nullptr) {
70 return FileSys::ERROR_ENTITY_NOT_FOUND; 70 return FileSys::ResultTargetNotFound;
71 } 71 }
72 72
73 *out_save_data_space = save_data_space; 73 *out_save_data_space = save_data_space;
diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h
index 863188e4c..dc9d713df 100644
--- a/src/core/hle/service/filesystem/save_data_controller.h
+++ b/src/core/hle/service/filesystem/save_data_controller.h
@@ -5,7 +5,7 @@
5 5
6#include "core/file_sys/nca_metadata.h" 6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/savedata_factory.h" 7#include "core/file_sys/savedata_factory.h"
8#include "core/file_sys/vfs_types.h" 8#include "core/file_sys/vfs/vfs_types.h"
9 9
10namespace Service::FileSystem { 10namespace Service::FileSystem {
11 11
diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp
index 6423e5089..b56762941 100644
--- a/src/core/hle/service/glue/time/manager.cpp
+++ b/src/core/hle/service/glue/time/manager.cpp
@@ -8,7 +8,7 @@
8 8
9#include "common/settings.h" 9#include "common/settings.h"
10#include "common/time_zone.h" 10#include "common/time_zone.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs/vfs.h"
12#include "core/hle/kernel/svc.h" 12#include "core/hle/kernel/svc.h"
13#include "core/hle/service/glue/time/manager.h" 13#include "core/hle/service/glue/time/manager.h"
14#include "core/hle/service/glue/time/time_zone_binary.h" 14#include "core/hle/service/glue/time/time_zone_binary.h"
diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h
index a46ec6364..1de93f8f9 100644
--- a/src/core/hle/service/glue/time/manager.h
+++ b/src/core/hle/service/glue/time/manager.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h" 10#include "core/file_sys/vfs/vfs_types.h"
11#include "core/hle/service/glue/time/file_timestamp_worker.h" 11#include "core/hle/service/glue/time/file_timestamp_worker.h"
12#include "core/hle/service/glue/time/standard_steady_clock_resource.h" 12#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
13#include "core/hle/service/glue/time/worker.h" 13#include "core/hle/service/glue/time/worker.h"
diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp
index 67969aa3f..d33f784c0 100644
--- a/src/core/hle/service/glue/time/time_zone_binary.cpp
+++ b/src/core/hle/service/glue/time/time_zone_binary.cpp
@@ -7,7 +7,7 @@
7#include "core/file_sys/registered_cache.h" 7#include "core/file_sys/registered_cache.h"
8#include "core/file_sys/romfs.h" 8#include "core/file_sys/romfs.h"
9#include "core/file_sys/system_archive/system_archive.h" 9#include "core/file_sys/system_archive/system_archive.h"
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs/vfs.h"
11#include "core/hle/service/filesystem/filesystem.h" 11#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/glue/time/time_zone_binary.h" 12#include "core/hle/service/glue/time/time_zone_binary.h"
13 13
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 595a3372e..5b28be577 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -33,7 +33,7 @@ void LoopProcess(Core::System& system) {
33 server_manager->RegisterNamedService( 33 server_manager->RegisterNamedService(
34 "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager)); 34 "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager));
35 server_manager->RegisterNamedService( 35 server_manager->RegisterNamedService(
36 "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager)); 36 "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
37 37
38 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); 38 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
39 39
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 30afed812..09c47b5e3 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -1419,8 +1419,8 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct
1419 1419
1420 const auto parameters{rp.PopRaw<Parameters>()}; 1420 const auto parameters{rp.PopRaw<Parameters>()};
1421 1421
1422 LOG_INFO(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", 1422 LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
1423 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); 1423 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
1424 1424
1425 if (!IsNpadIdValid(parameters.npad_id)) { 1425 if (!IsNpadIdValid(parameters.npad_id)) {
1426 IPC::ResponseBuilder rb{ctx, 3}; 1426 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index bf27ddfbf..d1ec42edc 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -3,8 +3,10 @@
3 3
4#include "core/hle/service/hid/hid_system_server.h" 4#include "core/hle/service/hid/hid_system_server.h"
5#include "core/hle/service/ipc_helpers.h" 5#include "core/hle/service/ipc_helpers.h"
6#include "core/hle/service/set/settings_types.h"
6#include "hid_core/hid_result.h" 7#include "hid_core/hid_result.h"
7#include "hid_core/resource_manager.h" 8#include "hid_core/resource_manager.h"
9#include "hid_core/resources/hid_firmware_settings.h"
8#include "hid_core/resources/npad/npad.h" 10#include "hid_core/resources/npad/npad.h"
9#include "hid_core/resources/npad/npad_types.h" 11#include "hid_core/resources/npad/npad_types.h"
10#include "hid_core/resources/npad/npad_vibration.h" 12#include "hid_core/resources/npad/npad_vibration.h"
@@ -13,9 +15,10 @@
13 15
14namespace Service::HID { 16namespace Service::HID {
15 17
16IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) 18IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
19 std::shared_ptr<HidFirmwareSettings> settings)
17 : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, 20 : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
18 resource_manager{resource} { 21 resource_manager{resource}, firmware_settings{settings} {
19 // clang-format off 22 // clang-format off
20 static const FunctionInfo functions[] = { 23 static const FunctionInfo functions[] = {
21 {31, nullptr, "SendKeyboardLockKeyEvent"}, 24 {31, nullptr, "SendKeyboardLockKeyEvent"},
@@ -25,7 +28,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
25 {131, nullptr, "ActivateSleepButton"}, 28 {131, nullptr, "ActivateSleepButton"},
26 {141, nullptr, "AcquireCaptureButtonEventHandle"}, 29 {141, nullptr, "AcquireCaptureButtonEventHandle"},
27 {151, nullptr, "ActivateCaptureButton"}, 30 {151, nullptr, "ActivateCaptureButton"},
28 {161, nullptr, "GetPlatformConfig"}, 31 {161, &IHidSystemServer::GetPlatformConfig, "GetPlatformConfig"},
29 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, 32 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
30 {211, nullptr, "GetNpadsWithNfc"}, 33 {211, nullptr, "GetNpadsWithNfc"},
31 {212, nullptr, "AcquireNfcActivateEventHandle"}, 34 {212, nullptr, "AcquireNfcActivateEventHandle"},
@@ -80,7 +83,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
80 {520, nullptr, "EnableHandheldHids"}, 83 {520, nullptr, "EnableHandheldHids"},
81 {521, nullptr, "DisableHandheldHids"}, 84 {521, nullptr, "DisableHandheldHids"},
82 {522, nullptr, "SetJoyConRailEnabled"}, 85 {522, nullptr, "SetJoyConRailEnabled"},
83 {523, nullptr, "IsJoyConRailEnabled"}, 86 {523, &IHidSystemServer::IsJoyConRailEnabled, "IsJoyConRailEnabled"},
84 {524, nullptr, "IsHandheldHidsEnabled"}, 87 {524, nullptr, "IsHandheldHidsEnabled"},
85 {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"}, 88 {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"},
86 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, 89 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
@@ -123,7 +126,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
123 {831, nullptr, "SetNotificationLedPatternWithTimeout"}, 126 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
124 {832, nullptr, "PrepareHidsForNotificationWake"}, 127 {832, nullptr, "PrepareHidsForNotificationWake"},
125 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, 128 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
126 {851, nullptr, "EnableUsbFullKeyController"}, 129 {851, &IHidSystemServer::EnableUsbFullKeyController, "EnableUsbFullKeyController"},
127 {852, nullptr, "IsUsbConnected"}, 130 {852, nullptr, "IsUsbConnected"},
128 {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"}, 131 {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
129 {900, nullptr, "ActivateInputDetector"}, 132 {900, nullptr, "ActivateInputDetector"},
@@ -148,7 +151,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
148 {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"}, 151 {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"},
149 {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"}, 152 {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"},
150 {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"}, 153 {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"},
151 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, 154 {1132, &IHidSystemServer::CheckUsbFirmwareUpdateRequired, "CheckUsbFirmwareUpdateRequired"},
152 {1133, nullptr, "StartUsbFirmwareUpdate"}, 155 {1133, nullptr, "StartUsbFirmwareUpdate"},
153 {1134, nullptr, "GetUsbFirmwareUpdateState"}, 156 {1134, nullptr, "GetUsbFirmwareUpdateState"},
154 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, 157 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
@@ -239,6 +242,16 @@ IHidSystemServer::~IHidSystemServer() {
239 service_context.CloseEvent(unique_pad_connection_event); 242 service_context.CloseEvent(unique_pad_connection_event);
240}; 243};
241 244
245void IHidSystemServer::GetPlatformConfig(HLERequestContext& ctx) {
246 const auto platform_config = firmware_settings->GetPlatformConfig();
247
248 LOG_INFO(Service_HID, "called, platform_config={}", platform_config.raw);
249
250 IPC::ResponseBuilder rb{ctx, 3};
251 rb.Push(ResultSuccess);
252 rb.PushRaw(platform_config);
253}
254
242void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { 255void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
243 IPC::RequestParser rp{ctx}; 256 IPC::RequestParser rp{ctx};
244 const auto applet_resource_user_id{rp.Pop<u64>()}; 257 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -674,6 +687,16 @@ void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) {
674 rb.Push(result); 687 rb.Push(result);
675} 688}
676 689
690void IHidSystemServer::IsJoyConRailEnabled(HLERequestContext& ctx) {
691 const bool is_attached = true;
692
693 LOG_WARNING(Service_HID, "(STUBBED) called, is_attached={}", is_attached);
694
695 IPC::ResponseBuilder rb{ctx, 3};
696 rb.Push(ResultSuccess);
697 rb.Push(is_attached);
698}
699
677void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) { 700void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
678 const bool is_attached = true; 701 const bool is_attached = true;
679 702
@@ -727,7 +750,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
727} 750}
728 751
729void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { 752void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
730 LOG_WARNING(Service_HID, "(STUBBED) called"); 753 LOG_DEBUG(Service_HID, "(STUBBED) called");
731 754
732 IPC::ResponseBuilder rb{ctx, 4}; 755 IPC::ResponseBuilder rb{ctx, 4};
733 rb.Push(ResultSuccess); 756 rb.Push(ResultSuccess);
@@ -752,6 +775,16 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
752 rb.Push(is_enabled); 775 rb.Push(is_enabled);
753} 776}
754 777
778void IHidSystemServer::EnableUsbFullKeyController(HLERequestContext& ctx) {
779 IPC::RequestParser rp{ctx};
780 const auto is_enabled{rp.Pop<bool>()};
781
782 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
783
784 IPC::ResponseBuilder rb{ctx, 2};
785 rb.Push(ResultSuccess);
786}
787
755void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { 788void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
756 const bool button_pressed = false; 789 const bool button_pressed = false;
757 790
@@ -798,6 +831,13 @@ void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) {
798 rb.Push(ResultSuccess); 831 rb.Push(ResultSuccess);
799} 832}
800 833
834void IHidSystemServer::CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx) {
835 LOG_WARNING(Service_HID, "(STUBBED) called");
836
837 IPC::ResponseBuilder rb{ctx, 2};
838 rb.Push(ResultSuccess);
839}
840
801void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { 841void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
802 LOG_WARNING(Service_HID, "(STUBBED) called"); 842 LOG_WARNING(Service_HID, "(STUBBED) called");
803 843
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index 90a719f02..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;
16 16
17namespace Service::HID { 17namespace Service::HID {
18class ResourceManager; 18class ResourceManager;
19class HidFirmwareSettings;
19 20
20class IHidSystemServer final : public ServiceFramework<IHidSystemServer> { 21class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
21public: 22public:
22 explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); 23 explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
24 std::shared_ptr<HidFirmwareSettings> settings);
23 ~IHidSystemServer() override; 25 ~IHidSystemServer() override;
24 26
25private: 27private:
28 void GetPlatformConfig(HLERequestContext& ctx);
26 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); 29 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
27 void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); 30 void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
28 void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); 31 void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
@@ -50,6 +53,7 @@ private:
50 void GetVibrationMasterVolume(HLERequestContext& ctx); 53 void GetVibrationMasterVolume(HLERequestContext& ctx);
51 void BeginPermitVibrationSession(HLERequestContext& ctx); 54 void BeginPermitVibrationSession(HLERequestContext& ctx);
52 void EndPermitVibrationSession(HLERequestContext& ctx); 55 void EndPermitVibrationSession(HLERequestContext& ctx);
56 void IsJoyConRailEnabled(HLERequestContext& ctx);
53 void IsJoyConAttachedOnAllRail(HLERequestContext& ctx); 57 void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
54 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); 58 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
55 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); 59 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
@@ -58,12 +62,14 @@ private:
58 void GetUniquePadIds(HLERequestContext& ctx); 62 void GetUniquePadIds(HLERequestContext& ctx);
59 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); 63 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
60 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); 64 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
65 void EnableUsbFullKeyController(HLERequestContext& ctx);
61 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); 66 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
62 void InitializeFirmwareUpdate(HLERequestContext& ctx); 67 void InitializeFirmwareUpdate(HLERequestContext& ctx);
63 void CheckFirmwareUpdateRequired(HLERequestContext& ctx); 68 void CheckFirmwareUpdateRequired(HLERequestContext& ctx);
64 void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx); 69 void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx);
65 void InitializeUsbFirmwareUpdate(HLERequestContext& ctx); 70 void InitializeUsbFirmwareUpdate(HLERequestContext& ctx);
66 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); 71 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
72 void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
67 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); 73 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
68 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); 74 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
69 void SetForceHandheldStyleVibration(HLERequestContext& ctx); 75 void SetForceHandheldStyleVibration(HLERequestContext& ctx);
@@ -77,6 +83,7 @@ private:
77 Kernel::KEvent* unique_pad_connection_event; 83 Kernel::KEvent* unique_pad_connection_event;
78 KernelHelpers::ServiceContext service_context; 84 KernelHelpers::ServiceContext service_context;
79 std::shared_ptr<ResourceManager> resource_manager; 85 std::shared_ptr<ResourceManager> resource_manager;
86 std::shared_ptr<HidFirmwareSettings> firmware_settings;
80}; 87};
81 88
82} // namespace Service::HID 89} // namespace Service::HID
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index 207ac4efe..3e2c7deab 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -301,7 +301,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const {
301 return result; 301 return result;
302 } 302 }
303 303
304 if (result.module != ErrorModule::NFC) { 304 if (result.GetModule() != ErrorModule::NFC) {
305 return result; 305 return result;
306 } 306 }
307 307
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index a25b79513..2258ee609 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -6,7 +6,7 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
8#include "core/file_sys/patch_manager.h" 8#include "core/file_sys/patch_manager.h"
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs/vfs.h"
10#include "core/hle/service/filesystem/filesystem.h" 10#include "core/hle/service/filesystem/filesystem.h"
11#include "core/hle/service/glue/glue_manager.h" 11#include "core/hle/service/glue/glue_manager.h"
12#include "core/hle/service/ipc_helpers.h" 12#include "core/hle/service/ipc_helpers.h"
diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp
index ec00b90a6..88a305f03 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/system_settings.cpp
@@ -50,6 +50,7 @@ SystemSettings DefaultSystemSettings() {
50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard; 50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard;
51 settings.battery_percentage_flag = true; 51 settings.battery_percentage_flag = true;
52 settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0; 52 settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0;
53 settings.vibration_master_volume = 1.0f;
53 54
54 return settings; 55 return settings;
55} 56}
diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h
index f6f227fde..968425319 100644
--- a/src/core/hle/service/set/settings_types.h
+++ b/src/core/hle/service/set/settings_types.h
@@ -323,6 +323,15 @@ struct NotificationFlag {
323}; 323};
324static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); 324static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size");
325 325
326struct PlatformConfig {
327 union {
328 u32 raw{};
329 BitField<0, 1, u32> has_rail_interface;
330 BitField<1, 1, u32> has_sio_mcu;
331 };
332};
333static_assert(sizeof(PlatformConfig) == 0x4, "PlatformConfig is an invalid size");
334
326/// This is nn::settings::system::TvFlag 335/// This is nn::settings::system::TvFlag
327struct TvFlag { 336struct TvFlag {
328 union { 337 union {
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index cc93a3654..b80655d2f 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -67,13 +67,13 @@ Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System&
67 const auto ver_file = romfs->GetFile("file"); 67 const auto ver_file = romfs->GetFile("file");
68 if (ver_file == nullptr) { 68 if (ver_file == nullptr) {
69 return early_exit_failure("The system version archive didn't contain the file 'file'.", 69 return early_exit_failure("The system version archive didn't contain the file 'file'.",
70 FileSys::ERROR_INVALID_ARGUMENT); 70 FileSys::ResultInvalidArgument);
71 } 71 }
72 72
73 auto data = ver_file->ReadAllBytes(); 73 auto data = ver_file->ReadAllBytes();
74 if (data.size() != sizeof(FirmwareVersionFormat)) { 74 if (data.size() != sizeof(FirmwareVersionFormat)) {
75 return early_exit_failure("The system version file 'file' was not the correct size.", 75 return early_exit_failure("The system version file 'file' was not the correct size.",
76 FileSys::ERROR_OUT_OF_BOUNDS); 76 FileSys::ResultOutOfRange);
77 } 77 }
78 78
79 std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); 79 std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
@@ -123,8 +123,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
123 {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"}, 123 {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"},
124 {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"}, 124 {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"},
125 {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"}, 125 {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"},
126 {35, nullptr, "GetVibrationMasterVolume"}, 126 {35, &ISystemSettingsServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
127 {36, nullptr, "SetVibrationMasterVolume"}, 127 {36, &ISystemSettingsServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
128 {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, 128 {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
129 {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"}, 129 {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"},
130 {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"}, 130 {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"},
@@ -133,10 +133,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
133 {42, nullptr, "SetEdid"}, 133 {42, nullptr, "SetEdid"},
134 {43, nullptr, "GetAudioOutputMode"}, 134 {43, nullptr, "GetAudioOutputMode"},
135 {44, nullptr, "SetAudioOutputMode"}, 135 {44, nullptr, "SetAudioOutputMode"},
136 {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, 136 {45, &ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved, "IsForceMuteOnHeadphoneRemoved"},
137 {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, 137 {46, &ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved, "SetForceMuteOnHeadphoneRemoved"},
138 {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"}, 138 {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"},
139 {48, nullptr, "SetQuestFlag"}, 139 {48, &ISystemSettingsServer::SetQuestFlag, "SetQuestFlag"},
140 {49, nullptr, "GetDataDeletionSettings"}, 140 {49, nullptr, "GetDataDeletionSettings"},
141 {50, nullptr, "SetDataDeletionSettings"}, 141 {50, nullptr, "SetDataDeletionSettings"},
142 {51, nullptr, "GetInitialSystemAppletProgramId"}, 142 {51, nullptr, "GetInitialSystemAppletProgramId"},
@@ -152,7 +152,7 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
152 {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, 152 {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
153 {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"}, 153 {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"},
154 {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, 154 {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
155 {64, nullptr, "SetPrimaryAlbumStorage"}, 155 {64, &ISystemSettingsServer::SetPrimaryAlbumStorage, "SetPrimaryAlbumStorage"},
156 {65, nullptr, "GetUsb30EnableFlag"}, 156 {65, nullptr, "GetUsb30EnableFlag"},
157 {66, nullptr, "SetUsb30EnableFlag"}, 157 {66, nullptr, "SetUsb30EnableFlag"},
158 {67, nullptr, "GetBatteryLot"}, 158 {67, nullptr, "GetBatteryLot"},
@@ -467,7 +467,7 @@ void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ct
467 LOG_INFO(Service_SET, "called"); 467 LOG_INFO(Service_SET, "called");
468 468
469 Common::UUID id{}; 469 Common::UUID id{};
470 auto res = GetExternalSteadyClockSourceId(id); 470 const auto res = GetExternalSteadyClockSourceId(id);
471 471
472 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; 472 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)};
473 rb.Push(res); 473 rb.Push(res);
@@ -478,9 +478,9 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct
478 LOG_INFO(Service_SET, "called"); 478 LOG_INFO(Service_SET, "called");
479 479
480 IPC::RequestParser rp{ctx}; 480 IPC::RequestParser rp{ctx};
481 auto id{rp.PopRaw<Common::UUID>()}; 481 const auto id{rp.PopRaw<Common::UUID>()};
482 482
483 auto res = SetExternalSteadyClockSourceId(id); 483 const auto res = SetExternalSteadyClockSourceId(id);
484 484
485 IPC::ResponseBuilder rb{ctx, 2}; 485 IPC::ResponseBuilder rb{ctx, 2};
486 rb.Push(res); 486 rb.Push(res);
@@ -490,7 +490,7 @@ void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) {
490 LOG_INFO(Service_SET, "called"); 490 LOG_INFO(Service_SET, "called");
491 491
492 Service::PSC::Time::SystemClockContext context{}; 492 Service::PSC::Time::SystemClockContext context{};
493 auto res = GetUserSystemClockContext(context); 493 const auto res = GetUserSystemClockContext(context);
494 494
495 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; 495 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
496 rb.Push(res); 496 rb.Push(res);
@@ -501,9 +501,9 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) {
501 LOG_INFO(Service_SET, "called"); 501 LOG_INFO(Service_SET, "called");
502 502
503 IPC::RequestParser rp{ctx}; 503 IPC::RequestParser rp{ctx};
504 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; 504 const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
505 505
506 auto res = SetUserSystemClockContext(context); 506 const auto res = SetUserSystemClockContext(context);
507 507
508 IPC::ResponseBuilder rb{ctx, 2}; 508 IPC::ResponseBuilder rb{ctx, 2};
509 rb.Push(res); 509 rb.Push(res);
@@ -652,6 +652,29 @@ void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ct
652 rb.Push(ResultSuccess); 652 rb.Push(ResultSuccess);
653} 653}
654 654
655void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
656 f32 vibration_master_volume = {};
657 const auto result = GetVibrationMasterVolume(vibration_master_volume);
658
659 LOG_INFO(Service_SET, "called, master_volume={}", vibration_master_volume);
660
661 IPC::ResponseBuilder rb{ctx, 3};
662 rb.Push(result);
663 rb.Push(vibration_master_volume);
664}
665
666void ISystemSettingsServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
667 IPC::RequestParser rp{ctx};
668 const auto vibration_master_volume = rp.PopRaw<f32>();
669
670 LOG_INFO(Service_SET, "called, elements={}", m_system_settings.vibration_master_volume);
671
672 const auto result = SetVibrationMasterVolume(vibration_master_volume);
673
674 IPC::ResponseBuilder rb{ctx, 2};
675 rb.Push(result);
676}
677
655// FIXME: implement support for the real system_settings.ini 678// FIXME: implement support for the real system_settings.ini
656 679
657template <typename T> 680template <typename T>
@@ -683,6 +706,8 @@ static Settings GetSettings() {
683 ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); 706 ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023});
684 707
685 // HID 708 // HID
709 ret["hid"]["has_rail_interface"] = ToBytes(bool{true});
710 ret["hid"]["has_sio_mcu"] = ToBytes(bool{true});
686 ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true}); 711 ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true});
687 ret["hid_debug"]["manages_devices"] = ToBytes(bool{true}); 712 ret["hid_debug"]["manages_devices"] = ToBytes(bool{true});
688 ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true}); 713 ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true});
@@ -703,6 +728,9 @@ static Settings GetSettings() {
703 // Settings 728 // Settings
704 ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false}); 729 ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false});
705 730
731 // Error
732 ret["err"]["applet_auto_close"] = ToBytes(bool{false});
733
706 return ret; 734 return ret;
707} 735}
708 736
@@ -789,15 +817,25 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) {
789 rb.Push(ResultSuccess); 817 rb.Push(ResultSuccess);
790} 818}
791 819
792void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { 820void ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) {
793 bool is_debug_mode_enabled = false; 821 LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}",
794 GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled"); 822 m_system_settings.force_mute_on_headphone_removed);
795
796 LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled);
797 823
798 IPC::ResponseBuilder rb{ctx, 3}; 824 IPC::ResponseBuilder rb{ctx, 3};
799 rb.Push(ResultSuccess); 825 rb.Push(ResultSuccess);
800 rb.Push(is_debug_mode_enabled); 826 rb.PushRaw(m_system_settings.force_mute_on_headphone_removed);
827}
828
829void ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) {
830 IPC::RequestParser rp{ctx};
831 m_system_settings.force_mute_on_headphone_removed = rp.PopRaw<bool>();
832 SetSaveNeeded();
833
834 LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}",
835 m_system_settings.force_mute_on_headphone_removed);
836
837 IPC::ResponseBuilder rb{ctx, 2};
838 rb.Push(ResultSuccess);
801} 839}
802 840
803void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { 841void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
@@ -808,11 +846,22 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
808 rb.PushEnum(m_system_settings.quest_flag); 846 rb.PushEnum(m_system_settings.quest_flag);
809} 847}
810 848
849void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) {
850 IPC::RequestParser rp{ctx};
851 m_system_settings.quest_flag = rp.PopEnum<QuestFlag>();
852 SetSaveNeeded();
853
854 LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag);
855
856 IPC::ResponseBuilder rb{ctx, 2};
857 rb.Push(ResultSuccess);
858}
859
811void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 860void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
812 LOG_INFO(Service_SET, "called"); 861 LOG_INFO(Service_SET, "called");
813 862
814 Service::PSC::Time::LocationName name{}; 863 Service::PSC::Time::LocationName name{};
815 auto res = GetDeviceTimeZoneLocationName(name); 864 const auto res = GetDeviceTimeZoneLocationName(name);
816 865
817 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; 866 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
818 rb.Push(res); 867 rb.Push(res);
@@ -825,7 +874,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx
825 IPC::RequestParser rp{ctx}; 874 IPC::RequestParser rp{ctx};
826 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; 875 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
827 876
828 auto res = SetDeviceTimeZoneLocationName(name); 877 const auto res = SetDeviceTimeZoneLocationName(name);
829 878
830 IPC::ResponseBuilder rb{ctx, 2}; 879 IPC::ResponseBuilder rb{ctx, 2};
831 rb.Push(res); 880 rb.Push(res);
@@ -846,7 +895,7 @@ void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx)
846 LOG_INFO(Service_SET, "called"); 895 LOG_INFO(Service_SET, "called");
847 896
848 Service::PSC::Time::SystemClockContext context{}; 897 Service::PSC::Time::SystemClockContext context{};
849 auto res = GetNetworkSystemClockContext(context); 898 const auto res = GetNetworkSystemClockContext(context);
850 899
851 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; 900 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
852 rb.Push(res); 901 rb.Push(res);
@@ -857,9 +906,9 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx)
857 LOG_INFO(Service_SET, "called"); 906 LOG_INFO(Service_SET, "called");
858 907
859 IPC::RequestParser rp{ctx}; 908 IPC::RequestParser rp{ctx};
860 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; 909 const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
861 910
862 auto res = SetNetworkSystemClockContext(context); 911 const auto res = SetNetworkSystemClockContext(context);
863 912
864 IPC::ResponseBuilder rb{ctx, 2}; 913 IPC::ResponseBuilder rb{ctx, 2};
865 rb.Push(res); 914 rb.Push(res);
@@ -869,7 +918,7 @@ void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLEReque
869 LOG_INFO(Service_SET, "called"); 918 LOG_INFO(Service_SET, "called");
870 919
871 bool enabled{}; 920 bool enabled{};
872 auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); 921 const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled);
873 922
874 IPC::ResponseBuilder rb{ctx, 3}; 923 IPC::ResponseBuilder rb{ctx, 3};
875 rb.Push(res); 924 rb.Push(res);
@@ -882,12 +931,23 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequ
882 IPC::RequestParser rp{ctx}; 931 IPC::RequestParser rp{ctx};
883 auto enabled{rp.Pop<bool>()}; 932 auto enabled{rp.Pop<bool>()};
884 933
885 auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); 934 const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled);
886 935
887 IPC::ResponseBuilder rb{ctx, 2}; 936 IPC::ResponseBuilder rb{ctx, 2};
888 rb.Push(res); 937 rb.Push(res);
889} 938}
890 939
940void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) {
941 bool is_debug_mode_enabled = false;
942 GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled");
943
944 LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled);
945
946 IPC::ResponseBuilder rb{ctx, 3};
947 rb.Push(ResultSuccess);
948 rb.Push(is_debug_mode_enabled);
949}
950
891void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { 951void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
892 LOG_INFO(Service_SET, "called, primary_album_storage={}", 952 LOG_INFO(Service_SET, "called, primary_album_storage={}",
893 m_system_settings.primary_album_storage); 953 m_system_settings.primary_album_storage);
@@ -897,6 +957,18 @@ void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
897 rb.PushEnum(m_system_settings.primary_album_storage); 957 rb.PushEnum(m_system_settings.primary_album_storage);
898} 958}
899 959
960void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) {
961 IPC::RequestParser rp{ctx};
962 m_system_settings.primary_album_storage = rp.PopEnum<PrimaryAlbumStorage>();
963 SetSaveNeeded();
964
965 LOG_INFO(Service_SET, "called, primary_album_storage={}",
966 m_system_settings.primary_album_storage);
967
968 IPC::ResponseBuilder rb{ctx, 2};
969 rb.Push(ResultSuccess);
970}
971
900void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) { 972void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) {
901 LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag); 973 LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag);
902 974
@@ -1075,7 +1147,7 @@ void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestConte
1075 IPC::RequestParser rp{ctx}; 1147 IPC::RequestParser rp{ctx};
1076 auto offset{rp.Pop<s64>()}; 1148 auto offset{rp.Pop<s64>()};
1077 1149
1078 auto res = SetExternalSteadyClockInternalOffset(offset); 1150 const auto res = SetExternalSteadyClockInternalOffset(offset);
1079 1151
1080 IPC::ResponseBuilder rb{ctx, 2}; 1152 IPC::ResponseBuilder rb{ctx, 2};
1081 rb.Push(res); 1153 rb.Push(res);
@@ -1085,7 +1157,7 @@ void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestConte
1085 LOG_DEBUG(Service_SET, "called."); 1157 LOG_DEBUG(Service_SET, "called.");
1086 1158
1087 s64 offset{}; 1159 s64 offset{};
1088 auto res = GetExternalSteadyClockInternalOffset(offset); 1160 const auto res = GetExternalSteadyClockInternalOffset(offset);
1089 1161
1090 IPC::ResponseBuilder rb{ctx, 4}; 1162 IPC::ResponseBuilder rb{ctx, 4};
1091 rb.Push(res); 1163 rb.Push(res);
@@ -1143,7 +1215,7 @@ void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestConte
1143 LOG_INFO(Service_SET, "called"); 1215 LOG_INFO(Service_SET, "called");
1144 1216
1145 Service::PSC::Time::SteadyClockTimePoint time_point{}; 1217 Service::PSC::Time::SteadyClockTimePoint time_point{};
1146 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); 1218 const auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
1147 1219
1148 IPC::ResponseBuilder rb{ctx, 4}; 1220 IPC::ResponseBuilder rb{ctx, 4};
1149 rb.Push(res); 1221 rb.Push(res);
@@ -1156,7 +1228,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestConte
1156 IPC::RequestParser rp{ctx}; 1228 IPC::RequestParser rp{ctx};
1157 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; 1229 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
1158 1230
1159 auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); 1231 const auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
1160 1232
1161 IPC::ResponseBuilder rb{ctx, 2}; 1233 IPC::ResponseBuilder rb{ctx, 2};
1162 rb.Push(res); 1234 rb.Push(res);
@@ -1167,7 +1239,7 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1167 LOG_INFO(Service_SET, "called"); 1239 LOG_INFO(Service_SET, "called");
1168 1240
1169 Service::PSC::Time::SteadyClockTimePoint time_point{}; 1241 Service::PSC::Time::SteadyClockTimePoint time_point{};
1170 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1242 const auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
1171 1243
1172 IPC::ResponseBuilder rb{ctx, 4}; 1244 IPC::ResponseBuilder rb{ctx, 4};
1173 rb.Push(res); 1245 rb.Push(res);
@@ -1179,9 +1251,9 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1179 LOG_INFO(Service_SET, "called"); 1251 LOG_INFO(Service_SET, "called");
1180 1252
1181 IPC::RequestParser rp{ctx}; 1253 IPC::RequestParser rp{ctx};
1182 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; 1254 const auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
1183 1255
1184 auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1256 const auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
1185 1257
1186 IPC::ResponseBuilder rb{ctx, 2}; 1258 IPC::ResponseBuilder rb{ctx, 2};
1187 rb.Push(res); 1259 rb.Push(res);
@@ -1307,57 +1379,68 @@ Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value,
1307 R_SUCCEED(); 1379 R_SUCCEED();
1308} 1380}
1309 1381
1310Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) { 1382Result ISystemSettingsServer::GetVibrationMasterVolume(f32& out_volume) const {
1383 out_volume = m_system_settings.vibration_master_volume;
1384 R_SUCCEED();
1385}
1386
1387Result ISystemSettingsServer::SetVibrationMasterVolume(f32 volume) {
1388 m_system_settings.vibration_master_volume = volume;
1389 SetSaveNeeded();
1390 R_SUCCEED();
1391}
1392
1393Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) const {
1311 out_id = m_private_settings.external_clock_source_id; 1394 out_id = m_private_settings.external_clock_source_id;
1312 R_SUCCEED(); 1395 R_SUCCEED();
1313} 1396}
1314 1397
1315Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) { 1398Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& id) {
1316 m_private_settings.external_clock_source_id = id; 1399 m_private_settings.external_clock_source_id = id;
1317 SetSaveNeeded(); 1400 SetSaveNeeded();
1318 R_SUCCEED(); 1401 R_SUCCEED();
1319} 1402}
1320 1403
1321Result ISystemSettingsServer::GetUserSystemClockContext( 1404Result ISystemSettingsServer::GetUserSystemClockContext(
1322 Service::PSC::Time::SystemClockContext& out_context) { 1405 Service::PSC::Time::SystemClockContext& out_context) const {
1323 out_context = m_system_settings.user_system_clock_context; 1406 out_context = m_system_settings.user_system_clock_context;
1324 R_SUCCEED(); 1407 R_SUCCEED();
1325} 1408}
1326 1409
1327Result ISystemSettingsServer::SetUserSystemClockContext( 1410Result ISystemSettingsServer::SetUserSystemClockContext(
1328 Service::PSC::Time::SystemClockContext& context) { 1411 const Service::PSC::Time::SystemClockContext& context) {
1329 m_system_settings.user_system_clock_context = context; 1412 m_system_settings.user_system_clock_context = context;
1330 SetSaveNeeded(); 1413 SetSaveNeeded();
1331 R_SUCCEED(); 1414 R_SUCCEED();
1332} 1415}
1333 1416
1334Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( 1417Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
1335 Service::PSC::Time::LocationName& out_name) { 1418 Service::PSC::Time::LocationName& out_name) const {
1336 out_name = m_system_settings.device_time_zone_location_name; 1419 out_name = m_system_settings.device_time_zone_location_name;
1337 R_SUCCEED(); 1420 R_SUCCEED();
1338} 1421}
1339 1422
1340Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( 1423Result ISystemSettingsServer::SetDeviceTimeZoneLocationName(
1341 Service::PSC::Time::LocationName& name) { 1424 const Service::PSC::Time::LocationName& name) {
1342 m_system_settings.device_time_zone_location_name = name; 1425 m_system_settings.device_time_zone_location_name = name;
1343 SetSaveNeeded(); 1426 SetSaveNeeded();
1344 R_SUCCEED(); 1427 R_SUCCEED();
1345} 1428}
1346 1429
1347Result ISystemSettingsServer::GetNetworkSystemClockContext( 1430Result ISystemSettingsServer::GetNetworkSystemClockContext(
1348 Service::PSC::Time::SystemClockContext& out_context) { 1431 Service::PSC::Time::SystemClockContext& out_context) const {
1349 out_context = m_system_settings.network_system_clock_context; 1432 out_context = m_system_settings.network_system_clock_context;
1350 R_SUCCEED(); 1433 R_SUCCEED();
1351} 1434}
1352 1435
1353Result ISystemSettingsServer::SetNetworkSystemClockContext( 1436Result ISystemSettingsServer::SetNetworkSystemClockContext(
1354 Service::PSC::Time::SystemClockContext& context) { 1437 const Service::PSC::Time::SystemClockContext& context) {
1355 m_system_settings.network_system_clock_context = context; 1438 m_system_settings.network_system_clock_context = context;
1356 SetSaveNeeded(); 1439 SetSaveNeeded();
1357 R_SUCCEED(); 1440 R_SUCCEED();
1358} 1441}
1359 1442
1360Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) { 1443Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const {
1361 out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; 1444 out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled;
1362 R_SUCCEED(); 1445 R_SUCCEED();
1363} 1446}
@@ -1374,32 +1457,32 @@ Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) {
1374 R_SUCCEED(); 1457 R_SUCCEED();
1375} 1458}
1376 1459
1377Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) { 1460Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) const {
1378 out_offset = m_private_settings.external_steady_clock_internal_offset; 1461 out_offset = m_private_settings.external_steady_clock_internal_offset;
1379 R_SUCCEED(); 1462 R_SUCCEED();
1380} 1463}
1381 1464
1382Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( 1465Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
1383 Service::PSC::Time::SteadyClockTimePoint& out_time_point) { 1466 Service::PSC::Time::SteadyClockTimePoint& out_time_point) const {
1384 out_time_point = m_system_settings.device_time_zone_location_updated_time; 1467 out_time_point = m_system_settings.device_time_zone_location_updated_time;
1385 R_SUCCEED(); 1468 R_SUCCEED();
1386} 1469}
1387 1470
1388Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( 1471Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(
1389 Service::PSC::Time::SteadyClockTimePoint& time_point) { 1472 const Service::PSC::Time::SteadyClockTimePoint& time_point) {
1390 m_system_settings.device_time_zone_location_updated_time = time_point; 1473 m_system_settings.device_time_zone_location_updated_time = time_point;
1391 SetSaveNeeded(); 1474 SetSaveNeeded();
1392 R_SUCCEED(); 1475 R_SUCCEED();
1393} 1476}
1394 1477
1395Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( 1478Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1396 Service::PSC::Time::SteadyClockTimePoint& out_time_point) { 1479 Service::PSC::Time::SteadyClockTimePoint& out_time_point) const {
1397 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; 1480 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
1398 R_SUCCEED(); 1481 R_SUCCEED();
1399} 1482}
1400 1483
1401Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( 1484Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1402 Service::PSC::Time::SteadyClockTimePoint out_time_point) { 1485 const Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
1403 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; 1486 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
1404 SetSaveNeeded(); 1487 SetSaveNeeded();
1405 R_SUCCEED(); 1488 R_SUCCEED();
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index a2258d16d..acbda8b8c 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -48,26 +48,28 @@ public:
48 return result; 48 return result;
49 } 49 }
50 50
51 Result GetExternalSteadyClockSourceId(Common::UUID& out_id); 51 Result GetVibrationMasterVolume(f32& out_volume) const;
52 Result SetExternalSteadyClockSourceId(Common::UUID id); 52 Result SetVibrationMasterVolume(f32 volume);
53 Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); 53 Result GetExternalSteadyClockSourceId(Common::UUID& out_id) const;
54 Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context); 54 Result SetExternalSteadyClockSourceId(const Common::UUID& id);
55 Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name); 55 Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const;
56 Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name); 56 Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& context);
57 Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); 57 Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name) const;
58 Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context); 58 Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name);
59 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); 59 Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const;
60 Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context);
61 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const;
60 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); 62 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled);
61 Result SetExternalSteadyClockInternalOffset(s64 offset); 63 Result SetExternalSteadyClockInternalOffset(s64 offset);
62 Result GetExternalSteadyClockInternalOffset(s64& out_offset); 64 Result GetExternalSteadyClockInternalOffset(s64& out_offset) const;
63 Result GetDeviceTimeZoneLocationUpdatedTime( 65 Result GetDeviceTimeZoneLocationUpdatedTime(
64 Service::PSC::Time::SteadyClockTimePoint& out_time_point); 66 Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
65 Result SetDeviceTimeZoneLocationUpdatedTime( 67 Result SetDeviceTimeZoneLocationUpdatedTime(
66 Service::PSC::Time::SteadyClockTimePoint& time_point); 68 const Service::PSC::Time::SteadyClockTimePoint& time_point);
67 Result GetUserSystemClockAutomaticCorrectionUpdatedTime( 69 Result GetUserSystemClockAutomaticCorrectionUpdatedTime(
68 Service::PSC::Time::SteadyClockTimePoint& out_time_point); 70 Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
69 Result SetUserSystemClockAutomaticCorrectionUpdatedTime( 71 Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
70 Service::PSC::Time::SteadyClockTimePoint time_point); 72 const Service::PSC::Time::SteadyClockTimePoint& time_point);
71 73
72private: 74private:
73 void SetLanguageCode(HLERequestContext& ctx); 75 void SetLanguageCode(HLERequestContext& ctx);
@@ -89,12 +91,17 @@ private:
89 void SetNotificationSettings(HLERequestContext& ctx); 91 void SetNotificationSettings(HLERequestContext& ctx);
90 void GetAccountNotificationSettings(HLERequestContext& ctx); 92 void GetAccountNotificationSettings(HLERequestContext& ctx);
91 void SetAccountNotificationSettings(HLERequestContext& ctx); 93 void SetAccountNotificationSettings(HLERequestContext& ctx);
94 void GetVibrationMasterVolume(HLERequestContext& ctx);
95 void SetVibrationMasterVolume(HLERequestContext& ctx);
92 void GetSettingsItemValueSize(HLERequestContext& ctx); 96 void GetSettingsItemValueSize(HLERequestContext& ctx);
93 void GetSettingsItemValue(HLERequestContext& ctx); 97 void GetSettingsItemValue(HLERequestContext& ctx);
94 void GetTvSettings(HLERequestContext& ctx); 98 void GetTvSettings(HLERequestContext& ctx);
95 void SetTvSettings(HLERequestContext& ctx); 99 void SetTvSettings(HLERequestContext& ctx);
100 void IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx);
101 void SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx);
96 void GetDebugModeFlag(HLERequestContext& ctx); 102 void GetDebugModeFlag(HLERequestContext& ctx);
97 void GetQuestFlag(HLERequestContext& ctx); 103 void GetQuestFlag(HLERequestContext& ctx);
104 void SetQuestFlag(HLERequestContext& ctx);
98 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); 105 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
99 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); 106 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
100 void SetRegionCode(HLERequestContext& ctx); 107 void SetRegionCode(HLERequestContext& ctx);
@@ -103,6 +110,7 @@ private:
103 void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); 110 void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
104 void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); 111 void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
105 void GetPrimaryAlbumStorage(HLERequestContext& ctx); 112 void GetPrimaryAlbumStorage(HLERequestContext& ctx);
113 void SetPrimaryAlbumStorage(HLERequestContext& ctx);
106 void GetNfcEnableFlag(HLERequestContext& ctx); 114 void GetNfcEnableFlag(HLERequestContext& ctx);
107 void SetNfcEnableFlag(HLERequestContext& ctx); 115 void SetNfcEnableFlag(HLERequestContext& ctx);
108 void GetSleepSettings(HLERequestContext& ctx); 116 void GetSleepSettings(HLERequestContext& ctx);
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 @@
14#include "common/common_funcs.h" 14#include "common/common_funcs.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
17#include "core/file_sys/vfs.h" 17#include "core/file_sys/vfs/vfs.h"
18 18
19namespace Core { 19namespace Core {
20class System; 20class System;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index f8225d697..1d96dc4c8 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -12,7 +12,7 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/file_sys/control_metadata.h" 13#include "core/file_sys/control_metadata.h"
14#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs/vfs_offset.h"
16#include "core/hle/kernel/code_set.h" 16#include "core/hle/kernel/code_set.h"
17#include "core/hle/kernel/k_page_table.h" 17#include "core/hle/kernel/k_page_table.h"
18#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
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
68 auto out = json{ 68 auto out = json{
69 {"title_id", fmt::format("{:016X}", title_id)}, 69 {"title_id", fmt::format("{:016X}", title_id)},
70 {"result_raw", fmt::format("{:08X}", result.raw)}, 70 {"result_raw", fmt::format("{:08X}", result.raw)},
71 {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))}, 71 {"result_module", fmt::format("{:08X}", static_cast<u32>(result.GetModule()))},
72 {"result_description", fmt::format("{:08X}", result.description.Value())}, 72 {"result_description", fmt::format("{:08X}", result.GetDescription())},
73 {"timestamp", timestamp}, 73 {"timestamp", timestamp},
74 }; 74 };
75 75
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
index 1cbaa73f7..f3efe3465 100644
--- a/src/frontend_common/content_manager.h
+++ b/src/frontend_common/content_manager.h
@@ -9,7 +9,7 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/common_funcs.h" 10#include "core/file_sys/common_funcs.h"
11#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/mode.h" 12#include "core/file_sys/fs_filesystem.h"
13#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/patch_manager.h" 14#include "core/file_sys/patch_manager.h"
15#include "core/file_sys/registered_cache.h" 15#include "core/file_sys/registered_cache.h"
@@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vf
159 }; 159 };
160 160
161 std::shared_ptr<FileSys::NSP> nsp; 161 std::shared_ptr<FileSys::NSP> nsp;
162 FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read); 162 FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::OpenMode::Read);
163 if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { 163 if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
164 nsp = std::make_shared<FileSys::NSP>(file); 164 nsp = std::make_shared<FileSys::NSP>(file);
165 if (nsp->IsExtractedType()) { 165 if (nsp->IsExtractedType()) {
@@ -224,7 +224,8 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
224 return true; 224 return true;
225 }; 225 };
226 226
227 const auto nca = std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::Mode::Read)); 227 const auto nca =
228 std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::OpenMode::Read));
228 const auto id = nca->GetStatus(); 229 const auto id = nca->GetStatus();
229 230
230 // Game updates necessary are missing base RomFS 231 // Game updates necessary are missing base RomFS
@@ -345,8 +346,8 @@ inline std::vector<std::string> VerifyInstalledContents(
345inline GameVerificationResult VerifyGameContents( 346inline GameVerificationResult VerifyGameContents(
346 Core::System& system, const std::string& game_path, 347 Core::System& system, const std::string& game_path,
347 const std::function<bool(size_t, size_t)>& callback) { 348 const std::function<bool(size_t, size_t)>& callback) {
348 const auto loader = 349 const auto loader = Loader::GetLoader(
349 Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); 350 system, system.GetFilesystem()->OpenFile(game_path, FileSys::OpenMode::Read));
350 if (loader == nullptr) { 351 if (loader == nullptr) {
351 return GameVerificationResult::NotImplemented; 352 return GameVerificationResult::NotImplemented;
352 } 353 }
diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp
index e12e5a77e..819460eb5 100644
--- a/src/hid_core/frontend/emulated_controller.cpp
+++ b/src/hid_core/frontend/emulated_controller.cpp
@@ -110,7 +110,11 @@ void EmulatedController::ReloadFromSettings() {
110 original_npad_type = npad_type; 110 original_npad_type = npad_type;
111 } 111 }
112 112
113 SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active); 113 // Disable special features before disconnecting
114 if (controller.right_polling_mode != Common::Input::PollingMode::Active) {
115 SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
116 }
117
114 Disconnect(); 118 Disconnect();
115 if (player.connected) { 119 if (player.connected) {
116 Connect(); 120 Connect();
@@ -1241,7 +1245,12 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
1241 return false; 1245 return false;
1242 } 1246 }
1243 1247
1244 last_vibration_value = vibration; 1248 // Skip duplicated vibrations
1249 if (last_vibration_value[index] == vibration) {
1250 return Settings::values.vibration_enabled.GetValue();
1251 }
1252
1253 last_vibration_value[index] = vibration;
1245 1254
1246 if (!Settings::values.vibration_enabled) { 1255 if (!Settings::values.vibration_enabled) {
1247 return false; 1256 return false;
@@ -1272,7 +1281,10 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
1272} 1281}
1273 1282
1274VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const { 1283VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const {
1275 return last_vibration_value; 1284 if (device_index >= DeviceIndex::MaxDeviceIndex) {
1285 return Core::HID::DEFAULT_VIBRATION_VALUE;
1286 }
1287 return last_vibration_value[static_cast<std::size_t>(device_index)];
1276} 1288}
1277 1289
1278bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1290bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h
index 168abe089..701b38300 100644
--- a/src/hid_core/frontend/emulated_controller.h
+++ b/src/hid_core/frontend/emulated_controller.h
@@ -581,7 +581,8 @@ private:
581 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 581 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
582 u32 turbo_button_state{0}; 582 u32 turbo_button_state{0};
583 std::size_t nfc_handles{0}; 583 std::size_t nfc_handles{0};
584 VibrationValue last_vibration_value{DEFAULT_VIBRATION_VALUE}; 584 std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
585 DEFAULT_VIBRATION_VALUE};
585 586
586 // Temporary values to avoid doing changes while the controller is in configuring mode 587 // Temporary values to avoid doing changes while the controller is in configuring mode
587 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 588 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h
index 2c3f02f34..a01292a70 100644
--- a/src/hid_core/hid_types.h
+++ b/src/hid_core/hid_types.h
@@ -639,6 +639,15 @@ struct VibrationValue {
639 f32 low_frequency{}; 639 f32 low_frequency{};
640 f32 high_amplitude{}; 640 f32 high_amplitude{};
641 f32 high_frequency{}; 641 f32 high_frequency{};
642 bool operator==(const VibrationValue& b) {
643 if (low_amplitude != b.low_amplitude || high_amplitude != b.high_amplitude) {
644 return false;
645 }
646 if (low_frequency != b.low_amplitude || high_frequency != b.high_frequency) {
647 return false;
648 }
649 return true;
650 }
642}; 651};
643static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); 652static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
644 653
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index ca824b4a3..a2295219a 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -6,6 +6,8 @@
6#include "core/core_timing.h" 6#include "core/core_timing.h"
7#include "core/hle/kernel/k_shared_memory.h" 7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/set/system_settings_server.h"
10#include "core/hle/service/sm/sm.h"
9#include "hid_core/hid_core.h" 11#include "hid_core/hid_core.h"
10#include "hid_core/hid_util.h" 12#include "hid_core/hid_util.h"
11#include "hid_core/resource_manager.h" 13#include "hid_core/resource_manager.h"
@@ -180,7 +182,11 @@ void ResourceManager::InitializeHidCommonSampler() {
180 debug_pad->SetAppletResource(applet_resource, &shared_mutex); 182 debug_pad->SetAppletResource(applet_resource, &shared_mutex);
181 digitizer->SetAppletResource(applet_resource, &shared_mutex); 183 digitizer->SetAppletResource(applet_resource, &shared_mutex);
182 keyboard->SetAppletResource(applet_resource, &shared_mutex); 184 keyboard->SetAppletResource(applet_resource, &shared_mutex);
183 npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config); 185
186 const auto settings =
187 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
188 npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings);
189
184 six_axis->SetAppletResource(applet_resource, &shared_mutex); 190 six_axis->SetAppletResource(applet_resource, &shared_mutex);
185 mouse->SetAppletResource(applet_resource, &shared_mutex); 191 mouse->SetAppletResource(applet_resource, &shared_mutex);
186 debug_mouse->SetAppletResource(applet_resource, &shared_mutex); 192 debug_mouse->SetAppletResource(applet_resource, &shared_mutex);
@@ -373,6 +379,10 @@ Result ResourceManager::SendVibrationValue(u64 aruid,
373 device = GetNSVibrationDevice(handle); 379 device = GetNSVibrationDevice(handle);
374 } 380 }
375 if (device != nullptr) { 381 if (device != nullptr) {
382 // Prevent sending vibrations to an inactive vibration handle
383 if (!device->IsActive()) {
384 return ResultSuccess;
385 }
376 result = device->SendVibrationValue(value); 386 result = device->SendVibrationValue(value);
377 } 387 }
378 return result; 388 return result;
diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
index 4897a2784..36b630c7f 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
+++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
@@ -137,7 +137,7 @@ void NpadAbstractPropertiesHandler::UpdateAllDeviceProperties() {
137 const auto npad_index = NpadIdTypeToIndex(npad_id_type); 137 const auto npad_index = NpadIdTypeToIndex(npad_id_type);
138 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { 138 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
139 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); 139 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
140 if (!data->flag.is_assigned) { 140 if (data == nullptr || !data->flag.is_assigned) {
141 continue; 141 continue;
142 } 142 }
143 auto& npad_entry = data->shared_memory_format->npad.npad_entry[npad_index]; 143 auto& npad_entry = data->shared_memory_format->npad.npad_entry[npad_index];
diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp
index 00ceff7e6..9c9019e8f 100644
--- a/src/hid_core/resources/hid_firmware_settings.cpp
+++ b/src/hid_core/resources/hid_firmware_settings.cpp
@@ -40,6 +40,13 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) {
40 m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug", 40 m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug",
41 "touch_firmware_auto_update_disabled"); 41 "touch_firmware_auto_update_disabled");
42 42
43 bool has_rail_interface{};
44 bool has_sio_mcu{};
45 m_set_sys->GetSettingsItemValue<bool>(has_rail_interface, "hid", "has_rail_interface");
46 m_set_sys->GetSettingsItemValue<bool>(has_sio_mcu, "hid", "has_sio_mcu");
47 platform_config.has_rail_interface.Assign(has_rail_interface);
48 platform_config.has_sio_mcu.Assign(has_sio_mcu);
49
43 is_initialized = true; 50 is_initialized = true;
44} 51}
45 52
@@ -103,4 +110,9 @@ HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId()
103 return features_per_id_disabled; 110 return features_per_id_disabled;
104} 111}
105 112
113Set::PlatformConfig HidFirmwareSettings::GetPlatformConfig() {
114 LoadSettings(false);
115 return platform_config;
116}
117
106} // namespace Service::HID 118} // namespace Service::HID
diff --git a/src/hid_core/resources/hid_firmware_settings.h b/src/hid_core/resources/hid_firmware_settings.h
index 3694fa9a3..7f146f1e6 100644
--- a/src/hid_core/resources/hid_firmware_settings.h
+++ b/src/hid_core/resources/hid_firmware_settings.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/service/set/settings_types.h"
7 8
8namespace Core { 9namespace Core {
9class System; 10class System;
@@ -39,6 +40,7 @@ public:
39 40
40 FirmwareSetting GetFirmwareUpdateFailure(); 41 FirmwareSetting GetFirmwareUpdateFailure();
41 FeaturesPerId FeaturesDisabledPerId(); 42 FeaturesPerId FeaturesDisabledPerId();
43 Set::PlatformConfig GetPlatformConfig();
42 44
43private: 45private:
44 bool is_initialized{}; 46 bool is_initialized{};
@@ -57,6 +59,7 @@ private:
57 bool is_touch_firmware_auto_update_disabled{}; 59 bool is_touch_firmware_auto_update_disabled{};
58 FirmwareSetting is_firmware_update_failure{}; 60 FirmwareSetting is_firmware_update_failure{};
59 FeaturesPerId features_per_id_disabled{}; 61 FeaturesPerId features_per_id_disabled{};
62 Set::PlatformConfig platform_config{};
60 63
61 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; 64 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
62}; 65};
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index d13a489c9..cde84b1bb 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -1080,12 +1080,15 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
1080 1080
1081void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, 1081void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
1082 std::recursive_mutex* shared_mutex, 1082 std::recursive_mutex* shared_mutex,
1083 std::shared_ptr<HandheldConfig> handheld_config) { 1083 std::shared_ptr<HandheldConfig> handheld_config,
1084 std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
1084 applet_resource_holder.applet_resource = resource; 1085 applet_resource_holder.applet_resource = resource;
1085 applet_resource_holder.shared_mutex = shared_mutex; 1086 applet_resource_holder.shared_mutex = shared_mutex;
1086 applet_resource_holder.shared_npad_resource = &npad_resource; 1087 applet_resource_holder.shared_npad_resource = &npad_resource;
1087 applet_resource_holder.handheld_config = handheld_config; 1088 applet_resource_holder.handheld_config = handheld_config;
1088 1089
1090 vibration_handler.SetSettingsService(settings);
1091
1089 for (auto& abstract_pad : abstracted_pads) { 1092 for (auto& abstract_pad : abstracted_pads) {
1090 abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr, 1093 abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr,
1091 &vibration_handler, &hid_core); 1094 &vibration_handler, &hid_core);
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h
index 88289fa2b..502cb9b55 100644
--- a/src/hid_core/resources/npad/npad.h
+++ b/src/hid_core/resources/npad/npad.h
@@ -34,6 +34,10 @@ namespace Service::KernelHelpers {
34class ServiceContext; 34class ServiceContext;
35} // namespace Service::KernelHelpers 35} // namespace Service::KernelHelpers
36 36
37namespace Service::Set {
38class ISystemSettingsServer;
39}
40
37union Result; 41union Result;
38 42
39namespace Service::HID { 43namespace Service::HID {
@@ -128,7 +132,8 @@ public:
128 void UnregisterAppletResourceUserId(u64 aruid); 132 void UnregisterAppletResourceUserId(u64 aruid);
129 void SetNpadExternals(std::shared_ptr<AppletResource> resource, 133 void SetNpadExternals(std::shared_ptr<AppletResource> resource,
130 std::recursive_mutex* shared_mutex, 134 std::recursive_mutex* shared_mutex,
131 std::shared_ptr<HandheldConfig> handheld_config); 135 std::shared_ptr<HandheldConfig> handheld_config,
136 std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
132 137
133 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); 138 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
134 139
diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp
index 05aad4c54..02b1f0290 100644
--- a/src/hid_core/resources/npad/npad_vibration.cpp
+++ b/src/hid_core/resources/npad/npad_vibration.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "core/hle/service/set/system_settings_server.h"
4#include "hid_core/hid_result.h" 5#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_vibration.h" 6#include "hid_core/resources/npad/npad_vibration.h"
6 7
@@ -13,10 +14,11 @@ NpadVibration::~NpadVibration() = default;
13Result NpadVibration::Activate() { 14Result NpadVibration::Activate() {
14 std::scoped_lock lock{mutex}; 15 std::scoped_lock lock{mutex};
15 16
16 const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); 17 f32 master_volume = 1.0f;
17 // if (master_volume < 0.0f || master_volume > 1.0f) { 18 m_set_sys->GetVibrationMasterVolume(master_volume);
18 // return ResultVibrationStrengthOutOfRange; 19 if (master_volume < 0.0f || master_volume > 1.0f) {
19 // } 20 return ResultVibrationStrengthOutOfRange;
21 }
20 22
21 volume = master_volume; 23 volume = master_volume;
22 return ResultSuccess; 24 return ResultSuccess;
@@ -26,6 +28,12 @@ Result NpadVibration::Deactivate() {
26 return ResultSuccess; 28 return ResultSuccess;
27} 29}
28 30
31Result NpadVibration::SetSettingsService(
32 std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
33 m_set_sys = settings;
34 return ResultSuccess;
35}
36
29Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { 37Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
30 std::scoped_lock lock{mutex}; 38 std::scoped_lock lock{mutex};
31 39
@@ -34,7 +42,7 @@ Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
34 } 42 }
35 43
36 volume = master_volume; 44 volume = master_volume;
37 // nn::settings::system::SetVibrationMasterVolume(master_volume); 45 m_set_sys->SetVibrationMasterVolume(master_volume);
38 46
39 return ResultSuccess; 47 return ResultSuccess;
40} 48}
@@ -48,10 +56,11 @@ Result NpadVibration::GetVibrationVolume(f32& out_volume) const {
48Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const { 56Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const {
49 std::scoped_lock lock{mutex}; 57 std::scoped_lock lock{mutex};
50 58
51 const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); 59 f32 master_volume = 1.0f;
52 // if (master_volume < 0.0f || master_volume > 1.0f) { 60 m_set_sys->GetVibrationMasterVolume(master_volume);
53 // return ResultVibrationStrengthOutOfRange; 61 if (master_volume < 0.0f || master_volume > 1.0f) {
54 // } 62 return ResultVibrationStrengthOutOfRange;
63 }
55 64
56 out_volume = master_volume; 65 out_volume = master_volume;
57 return ResultSuccess; 66 return ResultSuccess;
@@ -67,10 +76,11 @@ Result NpadVibration::BeginPermitVibrationSession(u64 aruid) {
67Result NpadVibration::EndPermitVibrationSession() { 76Result NpadVibration::EndPermitVibrationSession() {
68 std::scoped_lock lock{mutex}; 77 std::scoped_lock lock{mutex};
69 78
70 const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); 79 f32 master_volume = 1.0f;
71 // if (master_volume < 0.0f || master_volume > 1.0f) { 80 m_set_sys->GetVibrationMasterVolume(master_volume);
72 // return ResultVibrationStrengthOutOfRange; 81 if (master_volume < 0.0f || master_volume > 1.0f) {
73 // } 82 return ResultVibrationStrengthOutOfRange;
83 }
74 84
75 volume = master_volume; 85 volume = master_volume;
76 session_aruid = 0; 86 session_aruid = 0;
diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h
index d5a95f2a0..6412ca4ab 100644
--- a/src/hid_core/resources/npad/npad_vibration.h
+++ b/src/hid_core/resources/npad/npad_vibration.h
@@ -8,6 +8,10 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/result.h" 9#include "core/hle/result.h"
10 10
11namespace Service::Set {
12class ISystemSettingsServer;
13}
14
11namespace Service::HID { 15namespace Service::HID {
12 16
13class NpadVibration final { 17class NpadVibration final {
@@ -18,6 +22,7 @@ public:
18 Result Activate(); 22 Result Activate();
19 Result Deactivate(); 23 Result Deactivate();
20 24
25 Result SetSettingsService(std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
21 Result SetVibrationMasterVolume(f32 master_volume); 26 Result SetVibrationMasterVolume(f32 master_volume);
22 Result GetVibrationVolume(f32& out_volume) const; 27 Result GetVibrationVolume(f32& out_volume) const;
23 Result GetVibrationMasterVolume(f32& out_volume) const; 28 Result GetVibrationMasterVolume(f32& out_volume) const;
@@ -31,6 +36,8 @@ private:
31 f32 volume{}; 36 f32 volume{};
32 u64 session_aruid{}; 37 u64 session_aruid{};
33 mutable std::mutex mutex; 38 mutable std::mutex mutex;
39
40 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
34}; 41};
35 42
36} // namespace Service::HID 43} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp
index f28d30406..90bff88f4 100644
--- a/src/hid_core/resources/vibration/vibration_base.cpp
+++ b/src/hid_core/resources/vibration/vibration_base.cpp
@@ -23,6 +23,10 @@ Result NpadVibrationBase::Deactivate() {
23 return ResultSuccess; 23 return ResultSuccess;
24} 24}
25 25
26bool NpadVibrationBase::IsActive() const {
27 return ref_counter > 0;
28}
29
26bool NpadVibrationBase::IsVibrationMounted() const { 30bool NpadVibrationBase::IsVibrationMounted() const {
27 return is_mounted; 31 return is_mounted;
28} 32}
diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h
index 69c26e669..8fe35634d 100644
--- a/src/hid_core/resources/vibration/vibration_base.h
+++ b/src/hid_core/resources/vibration/vibration_base.h
@@ -21,6 +21,7 @@ public:
21 virtual Result Activate(); 21 virtual Result Activate();
22 virtual Result Deactivate(); 22 virtual Result Deactivate();
23 23
24 bool IsActive() const;
24 bool IsVibrationMounted() const; 25 bool IsVibrationMounted() const;
25 26
26protected: 27protected:
diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp
index 1dc4f0383..ad35f4126 100644
--- a/src/yuzu/applets/qt_error.cpp
+++ b/src/yuzu/applets/qt_error.cpp
@@ -25,8 +25,8 @@ void QtErrorDisplay::ShowError(Result error, FinishedCallback finished) const {
25 callback = std::move(finished); 25 callback = std::move(finished);
26 emit MainWindowDisplayError( 26 emit MainWindowDisplayError(
27 tr("Error Code: %1-%2 (0x%3)") 27 tr("Error Code: %1-%2 (0x%3)")
28 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 28 .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0'))
29 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 29 .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0'))
30 .arg(error.raw, 8, 16, QChar::fromLatin1('0')), 30 .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
31 tr("An error has occurred.\nPlease try again or contact the developer of the software.")); 31 tr("An error has occurred.\nPlease try again or contact the developer of the software."));
32} 32}
@@ -38,8 +38,8 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t
38 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); 38 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
39 emit MainWindowDisplayError( 39 emit MainWindowDisplayError(
40 tr("Error Code: %1-%2 (0x%3)") 40 tr("Error Code: %1-%2 (0x%3)")
41 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 41 .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0'))
42 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 42 .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0'))
43 .arg(error.raw, 8, 16, QChar::fromLatin1('0')), 43 .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
44 tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the " 44 tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the "
45 "software.") 45 "software.")
@@ -53,8 +53,8 @@ void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text,
53 callback = std::move(finished); 53 callback = std::move(finished);
54 emit MainWindowDisplayError( 54 emit MainWindowDisplayError(
55 tr("Error Code: %1-%2 (0x%3)") 55 tr("Error Code: %1-%2 (0x%3)")
56 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 56 .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0'))
57 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 57 .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0'))
58 .arg(error.raw, 8, 16, QChar::fromLatin1('0')), 58 .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
59 tr("An error has occurred.\n\n%1\n\n%2") 59 tr("An error has occurred.\n\n%1\n\n%2")
60 .arg(QString::fromStdString(dialog_text)) 60 .arg(QString::fromStdString(dialog_text))
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index c8ee46c04..9daae772c 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -11,7 +11,7 @@
11#include <QList> 11#include <QList>
12 12
13#include "configuration/shared_widget.h" 13#include "configuration/shared_widget.h"
14#include "core/file_sys/vfs_types.h" 14#include "core/file_sys/vfs/vfs_types.h"
15#include "frontend_common/config.h" 15#include "frontend_common/config.h"
16#include "vk_device_info.h" 16#include "vk_device_info.h"
17#include "yuzu/configuration/configuration_shared.h" 17#include "yuzu/configuration/configuration_shared.h"
diff --git a/src/yuzu/configuration/configure_per_game_addons.h b/src/yuzu/configuration/configure_per_game_addons.h
index 53db405c1..32dc5dde6 100644
--- a/src/yuzu/configuration/configure_per_game_addons.h
+++ b/src/yuzu/configuration/configure_per_game_addons.h
@@ -8,7 +8,7 @@
8 8
9#include <QList> 9#include <QList>
10 10
11#include "core/file_sys/vfs_types.h" 11#include "core/file_sys/vfs/vfs_types.h"
12 12
13namespace Core { 13namespace Core {
14class System; 14class System;
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 9747e3fb3..0cbf5f45e 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -17,7 +17,7 @@
17#include "core/file_sys/card_image.h" 17#include "core/file_sys/card_image.h"
18#include "core/file_sys/content_archive.h" 18#include "core/file_sys/content_archive.h"
19#include "core/file_sys/control_metadata.h" 19#include "core/file_sys/control_metadata.h"
20#include "core/file_sys/mode.h" 20#include "core/file_sys/fs_filesystem.h"
21#include "core/file_sys/nca_metadata.h" 21#include "core/file_sys/nca_metadata.h"
22#include "core/file_sys/patch_manager.h" 22#include "core/file_sys/patch_manager.h"
23#include "core/file_sys/registered_cache.h" 23#include "core/file_sys/registered_cache.h"
@@ -347,7 +347,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
347 347
348 if (!is_dir && 348 if (!is_dir &&
349 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 349 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
350 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); 350 const auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
351 if (!file) { 351 if (!file) {
352 return true; 352 return true;
353 } 353 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e14410f7d..782bcbb61 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -35,8 +35,8 @@
35#include "configuration/configure_per_game.h" 35#include "configuration/configure_per_game.h"
36#include "configuration/configure_tas.h" 36#include "configuration/configure_tas.h"
37#include "core/file_sys/romfs_factory.h" 37#include "core/file_sys/romfs_factory.h"
38#include "core/file_sys/vfs.h" 38#include "core/file_sys/vfs/vfs.h"
39#include "core/file_sys/vfs_real.h" 39#include "core/file_sys/vfs/vfs_real.h"
40#include "core/frontend/applets/cabinet.h" 40#include "core/frontend/applets/cabinet.h"
41#include "core/frontend/applets/controller.h" 41#include "core/frontend/applets/controller.h"
42#include "core/frontend/applets/general_frontend.h" 42#include "core/frontend/applets/general_frontend.h"
@@ -56,7 +56,7 @@
56// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows 56// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
57// defines. 57// defines.
58static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( 58static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
59 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { 59 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) {
60 return vfs->CreateDirectory(path, mode); 60 return vfs->CreateDirectory(path, mode);
61} 61}
62 62
@@ -1880,7 +1880,7 @@ bool GMainWindow::SelectAndSetCurrentUser(
1880 1880
1881void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { 1881void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
1882 // Ensure all NCAs are registered before launching the game 1882 // Ensure all NCAs are registered before launching the game
1883 const auto file = vfs->OpenFile(filepath, FileSys::Mode::Read); 1883 const auto file = vfs->OpenFile(filepath, FileSys::OpenMode::Read);
1884 if (!file) { 1884 if (!file) {
1885 return; 1885 return;
1886 } 1886 }
@@ -2274,7 +2274,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
2274 open_target = tr("Save Data"); 2274 open_target = tr("Save Data");
2275 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); 2275 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
2276 auto vfs_nand_dir = 2276 auto vfs_nand_dir =
2277 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); 2277 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
2278 2278
2279 if (has_user_save) { 2279 if (has_user_save) {
2280 // User save data 2280 // User save data
@@ -2653,7 +2653,7 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g
2653void GMainWindow::RemoveCacheStorage(u64 program_id) { 2653void GMainWindow::RemoveCacheStorage(u64 program_id) {
2654 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); 2654 const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
2655 auto vfs_nand_dir = 2655 auto vfs_nand_dir =
2656 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); 2656 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
2657 2657
2658 const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( 2658 const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath(
2659 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage, 2659 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage,
@@ -2673,7 +2673,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2673 "cancelled the operation.")); 2673 "cancelled the operation."));
2674 }; 2674 };
2675 2675
2676 const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); 2676 const auto loader =
2677 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
2677 if (loader == nullptr) { 2678 if (loader == nullptr) {
2678 failed(); 2679 failed();
2679 return; 2680 return;
@@ -2717,7 +2718,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2717 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; 2718 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
2718 auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); 2719 auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
2719 2720
2720 const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); 2721 const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite);
2721 2722
2722 if (out == nullptr) { 2723 if (out == nullptr) {
2723 failed(); 2724 failed();
@@ -3015,7 +3016,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
3015 system->GetContentProvider()}; 3016 system->GetContentProvider()};
3016 const auto control = pm.GetControlMetadata(); 3017 const auto control = pm.GetControlMetadata();
3017 const auto loader = 3018 const auto loader =
3018 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); 3019 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
3019 game_title = fmt::format("{:016X}", program_id); 3020 game_title = fmt::format("{:016X}", program_id);
3020 if (control.first != nullptr) { 3021 if (control.first != nullptr) {
3021 game_title = control.first->GetApplicationName(); 3022 game_title = control.first->GetApplicationName();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c3cacf852..c39ace2ec 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -25,7 +25,7 @@
25#include "core/cpu_manager.h" 25#include "core/cpu_manager.h"
26#include "core/crypto/key_manager.h" 26#include "core/crypto/key_manager.h"
27#include "core/file_sys/registered_cache.h" 27#include "core/file_sys/registered_cache.h"
28#include "core/file_sys/vfs_real.h" 28#include "core/file_sys/vfs/vfs_real.h"
29#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
30#include "core/loader/loader.h" 30#include "core/loader/loader.h"
31#include "core/telemetry_session.h" 31#include "core/telemetry_session.h"